dnsutils \
ldnsutils \
libnet-dns-perl \
+ pdns-recursor \
unbound-host
- run:
name: Install jdnssectools
- run:
name: Allow missing tools in verify-dnssec-zone
command: touch regression-tests/tests/verify-dnssec-zone/allow-missing
+ - run:
+ name: Start PowerDNS Recursor in the background
+ command: pdns_recursor
+ background: true
auth-regress:
description: Run one auth regression context
ZONE2LDAP=<< parameters.prefix >>bin/zone2ldap \
PDNSUTIL=<< parameters.prefix >>bin/pdnsutil \
PDNSCONTROL=<< parameters.prefix >>bin/pdns_control \
+ RESOLVERIP=127.0.0.1 \
./start-test-stop 5300 << parameters.context >>
- when:
condition: << parameters.doroot >>
ZONE2LDAP=<< parameters.prefix >>bin/zone2ldap \
PDNSUTIL=<< parameters.prefix >>bin/pdnsutil \
PDNSCONTROL=<< parameters.prefix >>bin/pdns_control \
+ RESOLVERIP=127.0.0.1 \
./start-test-stop 5300 << parameters.context >>
install-recursor-deps:
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"
libssl-dev \
libsystemd0 \
libsodium23 \
+ patch \
protobuf-compiler \
virtualenv
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:
context: gmysql-nsec3-optout-both
- auth-regress:
context: gmysql-nsec3-narrow
+ - auth-regress:
+ context: gmysql_sp-both
test-auth-regress-gpgsql:
resource_class: small
--- /dev/null
+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
+luaffiroundrobin
+luaffirulefunction
+luahooks
+LUAJITPC
+lualib
+luamaintenance
+luamutex
+LUAPC
+luarulefunction
+luaruleparams
+luascript
+luascriptpath
+luasmn
+luaspoof
+luaspoofwithstats
+luatarget
+lw
+lwn
+lwr
+lwres
+lwslay
+LZrd
+lzrp
+LZz
+MACBYTES
+maclen
+madname
+mailinglist
+mailserver
+mainfilter
+mainloop
+mainpage
+mainthread
+maj
+makebackend
+makecontext
+makeindex
+makemetadataonly
+MAKEOPTS
+makerfunc
+MAKEVAR
+mallinfo
+Mallocated
+malloctrace
+malwareset
+manpath
+marvin
+maskl
+maskr
+mastercommunicator
+mastermake
+masterplan
+masterschanged
+MASTERTOK
+matchkey
+matchlen
+matthijs
+maxanswersize
+maxbodysize
+maxbogusttl
+maxcachesize
+maxcachettl
+maxchunkrecords
+maxcleaninterval
+maxconnsperclient
+maxcp
+maxent
+maxevents
+maxfd
+MAXHOSTNAMELEN
+maxminddb
+maxnegttl
+maxnsaddressqperq
+maxobjects
+maxqperq
+maxqueriesperconn
+maxreplylen
+maxrss
+maxthreads
+maxtid
+maxtotusec
+mbox
+MBZ
+mcgrof
+mch
+mcontext
+mcp
+Mcu
+Mdatabase
+mday
+mdb
+MDBIn
+MDBRO
+MDBRW
+mdctx
+mde
+mdiff
+mdname
+mdp
+MDQx
+MDSV
+mediumsizedlabel
+MEMB
+Memcheck
+memchr
+memcmp
+memcpy
+memfree
+memis
+memmove
+memset
+memzero
+MERCHANTAPILITY
+mesg
+mesgsize
+messageid
+messageidstr
+metacache
+metacachelock
+metadatadn
+metamap
+metavar
+MEY
+Mfc
+mflags
+mgmname
+Mgo
+MHd
+mic
+michel
+microsoft
+might've
+migweb
+mincleaninterval
+minicurl
+MINIMIZATON
+mintime
+minttl
+MIPSEB
+mipsel
+mistmatching
+Mixin
+Miy
+Mj
+Mjgw
+mkbindist
+mkdir
+mkinstalldirs
+mkpubsuffixcc
+mkquery
+mkstemp
+mktemp
+mktime
+mlen
+Mlj
+Mlkroefk
+mlock
+mman
+Mmax
+mmc
+mmsghdr
+Mngwcrq
+MNorth
+moadnsparser
+moby
+moduledirs
+modulelibs
+moduleobjects
+mohta
+moreutils
+moz
+MPkb
+Mpl
+Mps
+MPSC
+mq
+mqalatency
+Mqas
+MQlel
+Mqoh
+MRUBY
+MRw
+msdn
+msecmatch
+mseconds
+msgfree
+msgh
+msghdr
+msgid
+msocket
+mstr
+MSVC
+msysmsec
+MTASKERTIMING
+MTest
+mtime
+mtracer
+mul
+multialgo
+MULTIARCH
+multimaint
+multimap
+multiplecookies
+Multiplexermap
+multispoof
+multitext
+munlock
+musermsec
+mustlog
+mutexes
+mval
+MWJu
+MXB
+mxname
+mxtics
+MXZQm
+mydata
+mydb
+myemailhere
+myfile
+myinitlock
+MYMETA
+mynewkey
+mypool
+myproject
+mysecretauthkey
+mysecretenckey
+mysqladmin
+mysqldiff
+mytag
+mytics
+Mzlw
+NAGLE
+namealgoindex
+namebuf
+namecount
+namedconf
+namedfile
+namehash
+nameindex
+namelen
+namemap
+namenum
+namepositions
+Nameret
+nameser
+nameservername
+nameservice
+namesseen
+namestocheck
+nametoindex
+nametype
+namq
+nanosleep
+nargs
+narrowbool
+nbb
+nce
+NCx
+ndays
+NDEBUG
+NDELAY
+ndiff
+NDIy
+NDUx
+needcdb
+needldap
+needlmdb
+needres
+needsqlite
+Negativerustanchor
+negativetrustanchorserver
+negativettl
+negcached
+negcacheentries
+negindic
+NERUUDNz
+netdb
+netflix
+Netscape
+netstat
+netw
+newalgo
+newargv
+newauth
+newconnectioncb
+newdata
+newdh
+newdomains
+newfd
+newgid
+newid
+newkey
+newlate
+newlen
+newlyobserveddomain
+newmap
+newnames
+newnod
+newparams
+newpath
+newpos
+newqtype
+newquery
+newrecord
+newrrs
+newruleaction
+newserial
+newserver
+newsize
+newsock
+newsr
+newstat
+newtarget
+newttl
+newuid
+newval
+nextclean
+nextcloser
+nexthash
+nexthdr
+nextid
+nextpart
+nextupd
+NEZU
+nfc
+nfds
+NFLSd
+NGtr
+nh
+NHo
+nif
+nint
+Njd
+NJKw
+nju
+nk
+NKm
+nlaunch
+Nld
+nlen
+nline
+NLips
+nlnen
+nlnetlabs
+nmasters
+nmemb
+nmgrule
+nmin
+Nml
+nmmatch
+nmp
+nmpair
+nmt
+NNl
+NOACK
+noaddrs
+noalias
+noarch
+noaxfr
+nobackend
+NOBLOCK
+noc
+nocachevialua
+NOCASE
+NOCERTS
+nocheck
+nocn
+nocontext
+nocontinue
+nocreate
+nodcachedir
+noddb
+noddbp
+nodelegated
+nodist
+nodnssec
+NODNSTAPTESTS
+nodot
+nodownstream
+NODUPDATA
+nodyndns
+noecs
+noent
+noerroranswers
+noerrorcount
+noexcept
+NOEXIST
+nof
+nogpgcheck
+NOi
+noinline
+noinput
+noinst
+nologin
+nolua
+nometa
+Nominum
+nonarrow
+NONBLOCK
+NONCEBYTES
+noncnames
+noncopyable
+nonetheripudp
+noneup
+NONINFRINGEMENT
+nonleap
+nonsec
+nop
+nopacketcache
+noparse
+NOQUOTES
+nora
+norbert
+nord
+norecurse
+norecursionavailable
+noreplace
+noreply
+noreturn
+norrsig
+noserver
+noservfailstats
+nosetests
+nostale
+NOSUB
+NOSUCHOBJECT
+nosuchpool
+notallowed
+notdisabled
+NOTFOUND
+notimp
+notimpl
+notincludedir
+notoverridden
+notoverriddenprefixlength
+notpool
+notpowerdns
+NOTRUNNING
+notsetecsaction
+notwcard
+notyouroffset
+nounput
+nowait
+nowildcard
+NOWPLUSTEN
+nowrap
+noyy
+nparams
+npos
+NPxzx
+nq
+nqk
+nqueue
+nrc
+nread
+nrecords
+nrep
+Nro
+nrr
+NRus
+nsa
+nscount
+nsdname
+nsdomain
+nseconds
+nsecrecords
+NSECs
+nsecx
+nsecxrepo
+NSIDTCP
+NSIDUDP
+nsip
+nsiploop
+nsock
+nsone
+nsq
+nsrr
+nss
+nsspeed
+nsspeedsentries
+nst
+nstld
+ntk
+ntohl
+ntohs
+ntop
+nts
+Ntx
+numanswers
+numcores
+numdomains
+numentries
+NUMERICHOST
+numerrors
+numevents
+NUMFAILED
+numloops
+numread
+numreceiveddo
+numrecords
+numsigs
+numsocks
+NUMTESTS
+numthreads
+numwarnings
+numworkers
+Nury
+NUx
+NUxt
+Nvd
+nvect
+NVQ
+nw
+nwaiters
+NWSIW
+Nwv
+Nxbulf
+NXCFg
+nxdo
+NXDOMAI
+nxdomainanswers
+nxdomainme
+nxdomainsuffix
+NXNSECS
+NXQ
+nxqtype
+NXRR
+nxrrset
+nxt
+NXTHDR
+nxwithnorr
+Nyeq
+nz
+nzg
+nztest
+oa
+Oaccuracy
+OAfx
+oarchive
+obf
+OBJECTFILES
+OBJECTLIBS
+OBJECTPROPERTY
+OBJEXT
+OBRACE
+Obsu
+OCc
+ocn
+ocontent
+octetsin
+octetsout
+octx
+ODAx
+ODESC
+ODIy
+odl
+odo
+ODQ
+OEVLOOP
+Oew
+ofc
+ofconf
+Ofdvp
+OFFMASK
+offsetof
+ofile
+OFJJXk
+OFkp
+ofstream
+ofzone
+OGhh
+OGlnb
+OGR
+ohn
+oi
+oid
+OJ
+oknodo
+OKPERCENTAGE
+OKPERCENTAGEINT
+oks
+olc
+oldmode
+oldmsg
+oldnames
+oldrr
+oldsr
+oldt
+olen
+OMG's
+omoerbeek
+OMPQK
+Omqq
+oneline
+onenosoa
+oneshot
+onexit
+ONXJQQ
+Oo
+oob
+oobar
+OOO
+OOOTCP
+oopts
+oor
+Ooutputs
+oowerdns
+opacket
+opcodenotify
+opcodeupdate
+OPEI
+opendir
+opendns
+openf
+openldap
+openlog
+opensource
+openssllocks
+opensslsigners
+opensslv
+operat
+opie
+OPj
+opname
+opos
+optarg
+OPTIn
+optind
+OPTIONSTOK
+optname
+optret
+optsize
+optstring
+optvect
+oq
+oqtaen
+orderindex
+origanswers
+origbetter
+origbetterset
+origctx
+origdnserrors
+origfd
+origid
+originalrequestorsubnet
+originalttl
+origlate
+orignever
+origtimedout
+origunmatched
+osixia
+oss
+ostr
+ostream
+osubgrouping
+OTAy
+ote
+otherdatalen
+otherprovider
+otherrcode
+othersize
+othertag
+Otkjt
+otype
+OUh
+OUHh
+ourcount
+ourdomain
+ourhelpfulservice
+ournum
+ourpos
+oursr
+ourttl
+outerpost
+outfd
+outfile
+outgoingtimeouts
+outlen
+outofzone
+outsigned
+outsock
+overriddenprefixlength
+overriddenprefixlengthvialua
+overriddenvialua
+OWJ
+OXd
+OYna
+ozz
+packetcacheentries
+packetcachehits
+packetcachemisses
+packetcacheservfailttl
+packetcachettl
+packetcaching
+packetheader
+packetperc
+packetsize
+packetwriter
+padawan
+Paf
+pagefaults
+pak
+panix
+paramcount
+PARAMDOC
+paridx
+parm
+parnum
+parsecheck
+parsefail
+parsertest
+passhtru
+passlen
+passwd
+pathbuf
+pathc
+pathconf
+pathv
+patsubst
+PBDNS
+pbegin
+PBh
+Pbi
+PBKDF
+PBpq
+pbtag
+PCas
+PCDNSSEC
+PCKS
+PCmissing
+pcomp
+pcount
+PCPU
+pctx
+pddkcrxy
+pddrw
+pdflatex
+Pdk
+pdnsbackendmysql
+pdnsbackendpgsql
+pdnsconf
+pdnsdbi
+PDNSDEBUG
+pdnsdist
+pdnsdomains
+pdnsexception
+pdnsfeatures
+pdnsinfo
+pdnsload
+PDNSPB
+PDNSRECCONTROL
+PDNSRECURSOR
+PDNSSERVER
+pdnssocket
+pdp
+perc
+periodicall
+PERLMOD
+Perror
+pershard
+pertub
+pevents
+pex
+pfh
+PFm
+pfs
+pfsbox
+PGconn
+PGHOST
+PGPASSWORD
+pgpsigurlmangle
+PGr
+PGRES
+PGresult
+PGUe
+Pgv
+PGw
+pheader
+phitrate
+PICOTLS
+pident
+pidfname
+pipeconnector
+pipefail
+pipefd
+pipefunc
+pipeloader
+PIPESTATUS
+piter
+PKCKQAu
+pkey
+pkgconfig
+pkglib
+pkglibdir
+pkgname
+PKgogeu
+pkgv
+pkill
+pkthdr
+pktinfo
+pktlen
+pkttype
+pldap
+pleasequeryfunc
+pleasequit
+pleaseremotefunc
+pleft
+plen
+plenus
+pline
+plugin
+plumgrid
+pmap
+PMQ
+PMTU
+PMTUDISC
+Pn
+pname
+PNPr
+policyfunc
+policystr
+policytag
+POLLERR
+pollfd
+POLLHUP
+pollin
+pollitem
+POLLOUT
+POLLREMOVE
+polmap
+poolaction
+popen
+popisort
+portev
+portfd
+portsmplexer
+posbegin
+posend
+posix
+postdata
+POSTFIELDS
+POSTFIELDSIZE
+postoutquery
+postpol
+postprepare
+postqueries
+postun
+postvars
+potentialsupermasters
+powerdn
+poweroften
+pparent
+pparts
+ppid
+pply
+pprint
+PQclear
+PQconnectdb
+PQerror
+PQescape
+PQexec
+PQfinish
+PQfreemem
+PQftype
+PQgetisnull
+PQgetvalue
+PQnfields
+PQntuples
+PQreset
+PQresult
+PQsocket
+PQstatus
+prc
+PRecord
+precsize
+PREDEF
+preg
+PRELOAD
+preout
+preparse
+prereq
+presignatures
+presignedcontext
+preun
+prevprev
+prevqname
+prfds
+primev
+printargs
+printf
+printlogs
+printtable
+printvars
+PRIu
+privatedns
+privateoid
+proba
+probds
+probs
+processname
+progname
+PROGRAMLISTING
+programname
+progrm
+progsarray
+promtool
+propol
+PROT
+protbuf
+protoc
+protostr
+proxyheader
+proxymagic
+PROXYMAGICLEN
+PROXYPROTOCOLHEADER
+PRsm
+prv
+psbf
+pseudonymized
+PSEUDOSECTION
+Pstmt
+psy
+ptcp
+Pthv
+pton
+pubkey
+publabel
+publicdomain
+publickey
+PUBLICKEYBYTES
+publicsuffix
+pubsuffix
+pubsuffixloader
+puk
+pullrequest
+pushlightuserdata
+pvars
+pvect
+pw
+pwd
+pwent
+pwgen
+pwtkey
+pyc
+pycache
+pycurl
+pysnmp
+PYTHONUNBUFFERED
+qaint
+qalatency
+qame
+QBD
+qc
+qcachehits
+qcachemisses
+Qccuox
+qckzu
+qclasschaos
+QClasses
+qclassin
+QCmissing
+qcount
+qcounter
+qd
+QDDt
+qdomainwild
+qdomainzone
+qesc
+qf
+qfonh
+QGy
+qhash
+qi
+qids
+Qj
+qk
+qkey
+qla
+qlass
+qlog
+Qlolbd
+Qlq
+QLs
+qmail
+qmin
+Qmsa
+qnamefilter
+qnamelen
+qnamemap
+qnameminfallbacksuccess
+qnameminimization
+qnamewirelength
+QOP
+qoutq
+qowerdns
+qpacket
+qpe
+qpol
+qpos
+qpschart
+qpsgraph
+qpslimit
+qpsnone
+qpspoolaction
+qpsstart
+qpsy
+QPTk
+QPv
+qq
+qrate
+qrateactionnxd
+qrateactionrefused
+qrateactiontruncated
+qraterefused
+qrset
+QSarv
+qsock
+qstats
+qstr
+qstring
+QSvh
+QSy
+QTag
+qtid
+qtnull
+qttl
+qtun
+qtypecounters
+qtypenums
+quantcast
+queryb
+queryfd
+queryring
+querystr
+querytimesec
+querytimeusec
+Queuedo
+queuelength
+queuetimeout
+qufnk
+Qug
+quotedname
+QUOTEDWORD
+quux
+qvalue
+qw
+QWN
+qx
+Qxh
+raddr
+raisd
+randomid
+rarg
+rattr
+RAv
+rawpacket
+rbegin
+RBu
+rca
+rcc
+rclass
+rcodecount
+rcodecounters
+rcontent
+rcounts
+rcp
+rcpgenerator
+rcv
+rcvbuf
+RCVTIMEO
+rdacounts
+rdataclass
+rdataset
+rdatastr
+rdatatype
+rdclass
+rdev
+Rdl
+rdlen
+RDLENGTH
+rdlock
+rdnonra
+rdnonrafs
+rdoc
+rdomains
+RDONLY
+rdqaplot
+rdqcounts
+rds
+rdtype
+RDWR
+readdir
+readlock
+readn
+realinput
+realname
+realnow
+realpath
+realqps
+realreferral
+realrr
+realzone
+Rebm
+RECCONTROL
+reccount
+receiveerrors
+recloc
+recordbuffer
+recordcomment
+recordcontent
+recorddata
+recordheader
+recordlen
+recordname
+recordorder
+recordplace
+recordscount
+recordstart
+recordstorage
+recordttl
+recpacketcache
+recparts
+RECRUN
+recsig
+rectrc
+recursorcache
+recursorcmd
+recursorconf
+recursorlog
+recursortests
+recvbytes
+recvcounter
+RECVDSTADDR
+recvec
+recverrors
+RECVPKTINFO
+recvtv
+reczones
+redirectresponses
+redistributors
+refcnt
+refcursor
+referals
+refferals
+refuseemptyar
+refusefouran
+refusenoan
+refusens
+refuseoptinar
+refusetwoar
+regcomp
+regexec
+regexp
+regexstr
+regfree
+reginfo
+regist
+regm
+regmatch
+reinit
+reldir
+relqname
+remdomains
+remlat
+remlen
+remlong
+remotedosec
+remotelen
+remoteloader
+remotelogger
+remotename
+remotering
+remotesec
+remotetype
+rentry
+reparse
+replen
+reqinfo
+reqs
+requestbuilder
+requestorid
+requestorstr
+requestvb
+requeue
+requeueing
+resanswers
+resetring
+residx
+resizering
+resnum
+reso
+resolvconf
+resolveret
+RESOLVERIP
+resourcelimits
+responsebyterate
+RESPONSEIP
+responsestats
+resprulactions
+respsize
+resquestions
+ress
+restfunc
+restoreflags
+resv
+retargetcount
+retargeted
+retkeyset
+retlen
+retline
+retq
+retre
+returncode
+retval
+reuseaddr
+revents
+revsets
+revzone
+revzonedata
+rfds
+rfind
+Rfv
+RFZJVWl
+rgacogne
+rgba
+rhandle
+rhs
+rhscount
+rhsoopts
+rhspos
+ri
+rightcolumn
+rightiter
+ringmeta
+ringname
+ringsize
+riter
+Rj
+Rjd
+Rjk
+rk
+Rkc
+RKEe
+Rkx
+rl
+rlen
+rlim
+rlimit
+RLNl
+rlocks
+rmailbx
+RMCD
+rmd
+rmdir
+Rminor
+rmtree
+RMz
+rnameservers
+rnow
+RNWc
+rollbackmarker
+romap
+RONLY
+rootdnsname
+roothints
+roothintspath
+rootkey
+rootnodot
+rootoid
+rootptr
+rootupdate
+rootzone
+ROqu
+rotxn
+roundtrip
+roundtripped
+routingtag
+rowid
+roystgnr
+Rpa
+rpacket
+rpc
+rpi
+rplookup
+rpmbuild
+rpmdev
+rpmdevtools
+rpmtest
+Rprj
+Rptim
+rpu
+RPZIXFR
+rpzloader
+RPZNSDNAME
+RPZNSIP
+RPZXFR
+rqclass
+rqname
+rqtype
+RQw
+RRA
+rrc
+rrclass
+rrget
+rrhs
+RRIn
+rrout
+rrscount
+rrsigds
+RRSIGIn
+rrsigkey
+rrsigncomp
+RRSIGTTL
+rrterm
+rrthrowaway
+RRto
+rrudr
+rrvalue
+rsakey
+rsamd
+rsautl
+rset
+RShr
+RSIG
+Rsjs
+rsock
+rstrip
+rtag
+rtf
+rthreads
+RTLD
+rtr
+rttl
+rtype
+ruben
+rubenkerkhof
+rubygems
+RUFM
+rulaction
+ruleparams
+ruleresult
+Rumu
+runcond
+runing
+runlevel
+runtests
+RUNWRAPPER
+rusage
+RUTQ
+rval
+RVARS
+RVe
+RVel
+RVM
+RVpn
+RVSBz
+rwl
+rwtxn
+rwxr
+Rxgrk
+RXXw
+RY
+rz
+rzrp
+SAccept
+saccount
+saddr
+safesearch
+salen
+saltlen
+sanitizerflags
+sanitizers
+sargs
+sasl
+savederrno
+sbf
+SBINARYPATH
+SBind
+sbindir
+Sbn
+sbuf
+scalarmult
+scert
+sched
+schemaversion
+scl
+SCombo
+SConnect
+scontrols
+SCRIPTNAME
+SCSV
+sdata
+sdfdhhgj
+sdfsdfs
+sdfsdfsfd
+SDIGBUFSIZE
+sdist
+sdns
+sdomains
+sdynamic
+searchclass
+SEARCHENGINE
+searchkey
+seckey
+secp
+secpollthread
+secretapikey
+secretbox
+secretcommunity
+SECRETKEYBYTES
+secretpassword
+secretuser
+secsfrac
+sectionname
+SECUREDOFFERS
+securezone
+SEEDBYTES
+seekg
+seenauthsoa
+selfanswered
+selfansweredresprulactions
+selfstat
+sendall
+sendfromto
+sendit
+sendlines
+sendmmsg
+sendout
+SENDSRCADDR
+sendto
+sendupdate
+Sensi
+sepa
+seqinit
+seqnext
+Sequanux
+serialtweaker
+servercmd
+serverdiff
+serverdownmaxfails
+serverdownthrottletime
+serverid
+serveridentity
+serveridstr
+serverlist
+serverparseerrors
+serverproc
+serverset
+servfailanswers
+servfailps
+servfailqueryring
+servfailrate
+servfailratio
+servfailremotering
+servfailremotes
+Servlet
+setbuf
+setcd
+setcdviaaction
+setecsaction
+setenv
+setf
+SETFD
+SETFL
+setgroups
+sethook
+SETID
+setitimer
+setmetatable
+setname
+setnegativeandsoa
+SETOF
+setopt
+setprecision
+setrlimit
+setscheduler
+setsid
+SETSIZE
+setsockopt
+settimeout
+settsigkey
+setuptree
+SEv
+SEZ
+sformat
+SHBt
+SHELTEK
+SHFP
+Shk
+shlibs
+shm
+sholder
+showinitializer
+showserversopts
+showvar
+shrinked
+shuf
+shutil
+Shutterstock
+sideeffect
+sidx
+SIGABRT
+SIGALARM
+SIGALRM
+SIGCHLD
+sigdr
+sigexpire
+sigfigs
+SIGFPE
+SIGHUP
+siginception
+SIGKILL
+siglen
+signaturecache
+signingpipe
+Sigoure
+SIGSEGV
+sigset
+sigterm
+SIGVTALRM
+sillyrecords
+simplea
+simplebind
+siz
+sizecounters
+Sj
+skb
+skeyset
+SKIPIT
+skiplua
+skipreasons
+skiprow
+skiptests
+SKnd
+slavecommunicator
+slaveport
+slaveschanged
+slen
+slist
+SListen
+slo
+Slp
+SLQ
+smallquerylargeresponse
+smaps
+smech
+Smirnov
+smlen
+smokeyjoe
+smp
+smt
+smtarg
+SMy
+smysql
+Smz
+sname
+snaplen
+snd
+SNDTIMEO
+sni
+snmpv
+Snv
+SOAAXFR
+soacount
+SOAIn
+SOANo
+SOANXD
+soaret
+soarr
+soatimes
+SOATTL
+socat
+socketaddress
+socketclose
+socketfamily
+socketname
+socketpair
+SOCKETPATH
+socketprotocol
+sockfd
+sockgroup
+socklen
+sockmode
+sockname
+sockowner
+socktype
+sodbc
+sodcrypto
+sodiumsigners
+sokolov
+somedata
+someiostream
+somekey
+sor
+sortcname
+sourceip
+spacelen
+spacket
+sparam
+sparc
+specifictest
+spectest
+speedtest
+SPg
+spgsql
+splitlines
+splot
+spm
+spongerng
+spoofaction
+spoofedcname
+spoofrawrule
+spoofrule
+spos
+SQda
+SQLCHAR
+sqlcmd
+sqlext
+SQLHANDLE
+SQLHDBC
+SQLHENV
+SQLHSTMT
+SQLINTEGER
+SQLLEN
+SQLPOINTER
+SQLRETURN
+SQLSMALLINT
+sqlstate
+sqlstr
+SQLTCHAR
+SQLULEN
+sqname
+sqt
+sqtype
+srcdir
+srcmask
+SRd
+sresult
+srl
+Sro
+SRPMS
+SRSLY
+SRx
+Ssb
+sscanf
+SSetsockopt
+ssh
+ssize
+sslctx
+SSLECDSADNS
+SSLEDDSADNS
+SSLRSADNS
+sslsock
+SSLTLS
+SSLTLSIO
+SSLv
+SSocket
+ssql
+ssqlite
+ssr
+sstorage
+sstream
+sstuff
+Ssystem
+stackoverflow
+stacktrace
+Starovoitov
+startdir
+startpos
+startqueries
+startrecord
+startrecordpos
+startswith
+starttime
+stateenum
+statemachine
+statesbase
+statfmt
+statfunction
+staticmethod
+statmap
+statnames
+statnode
+statnodesince
+statnumentries
+statnumhit
+statnummiss
+Statsv
+stattid
+statuscode
+statvisitor
+stdcxx
+stddev
+stdev
+stdexcept
+stdint
+stdlib
+stest
+stex
+Stogner
+stoi
+stoll
+stormap
+storvect
+storvector
+stoull
+strart
+strbind
+strcasecmp
+strchr
+strcmp
+strdup
+strerr
+strerror
+strftime
+stringappend
+stringbuffer
+stringerror
+stringfgets
+stringstream
+stringtok
+stringvalue
+STRLIT
+strncasecmp
+strncmp
+strncpy
+strptime
+strptr
+strres
+strsignal
+strstr
+strtod
+strtol
+structs
+sttl
+STX
+stype
+subdir
+subgrouping
+Subnetcheck
+subnetlist
+subqueries
+substr
+SUBSTVARS
+subsys
+suckdomains
+suffixmatch
+suffixother
+suffixtype
+SUh
+SUPERMASTERCONF
+suppliedrecords
+suse
+suseconds
+sval
+svg
+SVj
+svn
+svr
+svstat
+Swfi
+SWUOQ
+SXRFNm
+SYlm
+symlink
+Syq
+sysconf
+sysconfdir
+sysmsec
+sysobjects
+sysperc
+systemdsystemunit
+systemdsystemunitdir
+systm
+sz
+Szd
+Szps
+tac
+TAEw
+tagfile
+tagp
+tagsstr
+tagthis
+tahoe
+taiepoch
+TAMg
+tanhqgv
+TARBALLFILE
+TARBALLPREFIX
+TARBALLVERSION
+tardirname
+targetlen
+tbaggery
+Tbz
+tcache
+tcbit
+TCEDNS
+tcgetattr
+TCO
+tcpa
+tcpaaaa
+tcpavgconnduration
+tcpavgconnectionduration
+tcpavgqueriesperconn
+tcpavgqueriesperconnection
+tcpbench
+tcpbytesanswered
+tcpclient
+tcpclientimeouts
+tcpclientthreads
+tcpcurrentconnections
+tcpdiedreaddingresponse
+tcpdiedreadingquery
+tcpdiedreadingresponse
+tcpdiedsendingquery
+tcpdiedsendingresponse
+tcpdownstreamtimeouts
+tcpdrops
+TCPEDNS
+tcpgaveup
+tcphdr
+TCPIO
+tcpiohandler
+tcpka
+tcpnameser
+TCPNo
+tcpnumanswered
+tcpoutqueries
+tcppacket
+tcpqcounter
+tcpquestions
+tcprange
+tcpreadimeouts
+tcpreadtimeouts
+tcpreceiver
+tcpspeeds
+TCPTLS
+tcpwritetimeouts
+TCSANOW
+tcsetattr
+tdate
+tdi
+TDIBy
+TDja
+TDn
+tdomains
+tdsmap
+Tdsza
+TDt
+TDVXa
+Telekom
+tellg
+tempbf
+tempdi
+tempfailure
+tempfailurettlbinding
+tempfile
+temphash
+templ
+templatecounter
+templateline
+templateparts
+templatestep
+templatestop
+templatize
+termios
+testcase
+testcmd
+TESTDRIVER
+testinstance
+testkeysset
+TESTLIST
+testlock
+testmaster
+testmsg
+testname
+testnonzone
+testnum
+testnz
+testresults
+testring
+testringdnsname
+testschema
+testsuffix
+testsuite
+testt
+testuser
+texlive
+tf
+tfh
+TFILE
+TFN
+tfo
+tfunc
+THandler
+theirserial
+thislock
+thisupd
+thiszone
+thr
+threadcloser
+THREADFLAGS
+threadname
+threadwrapper
+throttledout
+throttledqueries
+throttleentries
+THTM
+thu
+tickinterval
+tidx
+timedelta
+timediff
+timegm
+timeoutsec
+timeoutspec
+timersonly
+timesec
+Timespan
+timespec
+timespent
+timeusec
+tinfo
+TINYDNSDATA
+tinydnsloader
+TINYINT
+TIsy
+Tj
+TJR
+tkdb
+tkey
+TKllk
+tkrc
+tl
+tlen
+Tlh
+TLKA
+tlsdhkeytoosmall
+tlsext
+tlshandshakefailures
+tlsinactiveticketkeys
+tlsinappropriatefallback
+TLSIO
+tlslocals
+tlsnewsessions
+tlsnosharedcipher
+tlsqueries
+tlsresumptions
+TLSSNI
+tlsunknownciphertype
+tlsunknownkeyexchangetype
+tlsunknownprotocol
+tlsunknownticketkeys
+tlsunsupportedec
+tlsunsupportedprotocol
+TLSv
+tlsversion
+tlv
+tmeta
+TMi
+Tmk
+tmout
+tmpbuf
+tmpdh
+tmpdir
+tmpfd
+tmpfile
+tmpkey
+tmpnam
+tmpname
+tmpstr
+tmsg
+tname
+tnameservers
+Tno
+tnode
+tnow
+tns
+toaddr
+tocheck
+TODOLIST
+toh
+tok
+tokenizer
+tokill
+TOLO
+tonumber
+toolong
+toolongtobevalid
+toomuchinfo
+topbar
+toport
+toportstr
+toroot
+TOSBy
+toserial
+toskip
+tosql
+totadd
+totar
+totcount
+totcumul
+totlat
+totmperc
+totpairs
+totpcache
+totrdatalen
+totremove
+tottime
+toupper
+toxml
+toysdig
+tozero
+TPL
+tpos
+tptr
+TQBv
+TQJv
+transactiondomain
+transactiondomainid
+trc
+trecords
+treeview
+trilab
+trillian
+trl
+tro
+trollololoooolll
+Truncateds
+truncatemarker
+trustanchorserver
+trustedkeys
+TRw
+tryrdlock
+tryrwlock
+trysuperdomains
+trywait
+trywrlock
+tscomp
+tsdelta
+tsigalgname
+tsigalgorithm
+tsigkeyname
+tsigprevious
+TSIGTCP
+tsigtimersonly
+tsigutils
+tsigverifier
+tsstorage
+Tstbt
+tsuna
+ttd
+ttdi
+ttdindex
+ttdwatch
+ttllimit
+TTLNo
+TTLNX
+TTLRPZ
+ttltooshort
+ttsig
+Tuk
+TUz
+Tvq
+TVU
+TWk
+TWl
+twopt
+txn
+ty
+typedef
+typedns
+typeenum
+typeid
+typeinfo
+typemap
+typename
+typestr
+TYPETOK
+TYX
+tz
+TZDU
+TZOFF
+TZud
+uapi
+ub
+UBIGINT
+ubsan
+UBXc
+ucf
+ucfq
+ucfr
+uchar
+UCLIBC
+ucontext
+UCPEd
+ucspi
+Uctchk
+udiff
+Udipd
+Udm
+udpanswers
+udpbytesanswered
+udpclientsocks
+UDPECS
+UDPEDNS
+udphdr
+udpin
+udpnumanswered
+udpoverruns
+udpsize
+udpsock
+udpspeeds
+udpv
+udrdbp
+UDRYNm
+UDWORD
+ue
+ueberbackend
+Ueuwr
+ufc
+UFt
+ufx
+uglifyjs
+uhry
+uintptr
+uio
+uitoa
+Ujd
+uki
+Ukj
+Ukvz
+UKyg
+ulen
+ulong
+Ulws
+UML
+ums
+uname
+unauthtcp
+unauthudp
+unavailables
+unboundhost
+uncanon
+uncomment
+uncompress
+unconfigured
+undef
+UNDOC
+unescape
+unhexlify
+uninstall
+uniq
+uniquw
+UNIREGISTRYMARKET
+unitdir
+unittest
+unixconnector
+Unixsocket
+Unknownqueries
+unknownrecordcontent
+unlicense
+unloadable
+unmap
+Unpublishing
+unquotify
+unregist
+unreport
+UNsockaddr
+UNSPEC
+Unthrottling
+UNUTTe
+uo
+UOHk
+uom
+Uor
+uordblks
+UPi
+Uploaders
+UPnhs
+uppercasing
+upq
+upto
+urc
+urljoin
+urllib
+urlmap
+urlparse
+urlsafe
+uroot
+usazzz
+useconds
+uselessdrc
+useradd
+userdata
+userfriendly
+usermsec
+userperc
+USHRT
+usleep
+usm
+USs
+ustar
+utexas
+utils
+utime
+Utmc
+utype
+uupdate
+Uuser
+UVARIABLES
+Uwhjf
+uwisza
+uwopt
+UXsnr
+Uxt
+Uy
+UYx
+vab
+validator
+validkeys
+validns
+validpacket
+validresponses
+validrrsets
+valign
+valiter
+vallid
+valmask
+valr
+valrandom
+vals
+varbinary
+varbind
+varchar
+varlist
+varmap
+varval
+Vbi
+vby
+Vcs
+VDLs
+vec
+vect
+veorq
+verboselog
+verhaaltje
+VERIFYHOST
+VERIFYPEER
+verifyzone
+verisign
+verisignlabs
+VERq
+versionbind
+versioncommand
+versionmangle
+versionpdns
+vertpre
+verylongstringlongerthan
+verysecret
+vf
+vfree
+VInf
+vinfolog
+VIuk
+VIW
+Vj
+Vjda
+VK
+vkuiv
+vla
+vlen
+Vlh
+Vll
+vmbe
+vml
+Vmm
+Vmp
+VPA
+vpacket
+vpos
+VPQ
+vptr
+VQBWQ
+VQda
+Vra
+Vrbp
+Vre
+vrooooom
+vrr
+Vscj
+vstate
+vstring
+vstringtok
+vtable
+VTd
+vtnq
+Vuux
+Vw
+VWlr
+vww
+Vx
+vy
+VYBP
+Vyl
+Vz
+Vzd
+WAITALL
+waitpid
+waitpoint
+waitstatus
+waitstatusenum
+waitval
+WAja
+wantsnsid
+wastcp
+wattr
+Waxyl
+wbaw
+wcard
+wcarddomain
+wcmatch
+WCN
+wcname
+WCOREDUMP
+wcplusencloser
+wctx
+wday
+weanswers
+webetter
+webhndlr
+webkit
+WEBPORT
+webrick
+webserv
+webserveropts
+wednserrors
+weekno
+weirdtxt
+wenever
+Werror
+wetimedout
+weunmatched
+WEV
+WEXITSTATUS
+wfile
+WFQTVE
+Wfxq
+wget
+Wgp
+WGqnuy
+WHg
+Whij
+whitelisting
+whoami
+wichert
+WIFEXITED
+WIFSIGNALED
+wildcarddnsname
+wildcardname
+wildzone
+wiplist
+wirelen
+wireshark
+withecs
+withedns
+withednsecs
+withednsnoecs
+withoutedns
+withport
+withval
+Wj
+WJj
+WJO
+Wklm
+wlat
+wli
+wlist
+Wll
+wlocks
+wlon
+Wmpt
+Wmpx
+WNfpw
+Wno
+WNOHANG
+Woi
+Wor
+wordpad
+workdir
+workflow
+Wowie
+Wpv
+WQ
+wrand
+wret
+Wri
+writeability
+WRITEDATA
+WRITEFUNCTION
+writen
+wrlock
+WRONLY
+Wsg
+wshash
+WSIZE
+Wsqvx
+Wswaps
+WTCSr
+WTERMSIG
+wtest
+Wtl
+Wtqlj
+wv
+WVZHd
+wwa
+wwho
+wwjb
+WWo
+wwv
+wwwds
+wwwezdnsit
+wwwpowerdnscom
+WWWPREFIX
+wwx
+wwz
+WX
+WXA
+Wxb
+Wxe
+Wxm
+WXTAH
+Wyc
+WYjld
+wz
+WZERM
+xa
+xaa
+xaaa
+xad
+XADD
+xaf
+XAPI
+xargs
+Xautonomous
+Xbz
+xca
+xce
+xception
+xchange
+Xcml
+xcrypt
+xcy
+xdigits
+XDqdg
+XEz
+XFDj
+xff
+xffverylongstring
+XFRM
+xfrserver
+XFv
+xfz
+xg
+Xga
+xgp
+XHhl
+xhr
+xib
+xit
+xj
+Xk
+XKpw
+Xkqi
+xlabel
+xluajit
+xm
+XMy
+xn
+XNGCH
+xno
+Xnv
+Xo
+Xof
+xor
+xpfcode
+XPFDATA
+xpfdst
+xpfproto
+xpfsrc
+xpfversion
+Xpk
+xpong
+Xq
+xrange
+XRL
+XRq
+xsalsa
+XSKk
+XSyam
+Xtext
+xtics
+xtrue
+xugtk
+XUgz
+xunit
+xv
+xvf
+XXh
+XXxqrt
+XXXXg
+xy
+xyes
+XYk
+xz
+xzvf
+ybndrfg
+YBSI
+yc
+yday
+Yegst
+YEQRBVK
+yetanother
+Yf
+yfb
+YFLAGS
+YGu
+yh
+Yhh
+yiss
+YJou
+yjxe
+YKMY
+ykyb
+YLa
+ylabel
+YLGg
+Ylh
+ylwrap
+Ymf
+YNBIs
+Yng
+Yogz
+YOia
+yop
+YOUNAMEIT
+Yoyodyne
+yq
+Yqi
+YSa
+YSfp
+YTg
+ytics
+YTyj
+YUTh
+YVs
+Yw
+YWA
+YWJx
+YWJYRXp
+YWls
+YWQ
+ywu
+YWVJf
+yx
+yxdomain
+YXos
+YXRR
+yxrrset
+yydebug
+yyerror
+yyin
+yylex
+yylval
+yyparse
+yyrestart
+YYSTYPE
+yyterminate
+yytext
+yywrap
+yyy
+YYYYMMDDH
+yz
+zackw
+ZAWs
+zbefore
+zc
+Zcdnskey
+ZCLASS
+Zd
+Zdelegated
+zdf
+ZDFi
+Zdnssec
+ZDV
+zeromqrb
+zeroport
+Zexample
+Zg
+ZGq
+Zgta
+Zgw
+Zhc
+ZHE
+ZHJp
+ZHml
+ZIf
+Zinsecure
+ziter
+ZJA
+Zjmco
+Zjq
+zl
+zlib
+Zm
+zmakerfunc
+zmakermap
+Zminimal
+zmq
+zmqconnector
+zname
+Znztest
+ZOMG
+zonecontent
+zonecount
+zonecut
+zonedata
+zonedataline
+zonedomain
+zonefilename
+zoneid
+zoneinfo
+zonekind
+zonelevel
+zonelist
+zonemaster
+zoneparsertng
+zonestring
+ZONETOK
+zp
+zpt
+ZQ
+ZQOZ
+ZQSUOf
+zr
+Zrm
+zrr
+Zsecure
+zskds
+zskeys
+Zstest
+Zsu
+Zsub
+zt
+Ztest
+ZTh
+Ztsig
+ztype
+zu
+zugschlus
+Zuhz
+Zun
+Zv
+ZVNIQn
+Zw
+Zwtest
+ZWTQ
+ZWxz
+Zx
+ZXJETl
+ZZj
+zzz
--- /dev/null
+(?:^|/)ext/
+(?:^|/)go\.mod$
+(?:^|/)go\.sum$
+(?:^|/)m4/
+(?:^|/)package-lock\.json$
+/expected_result
+/test-dnsrecords_cc\.cc$
+SUMS$
+\.ai$
+\.asc$
+\.bmp$
+\.cer$
+\.class$
+\.crl$
+\.crt$
+\.csr$
+\.dll$
+\.DS_Store$
+\.eot$
+\.eps$
+\.exe$
+\.gif$
+\.graffle$
+\.gz$
+\.icns$
+\.ico$
+\.jar$
+\.jpeg$
+\.jpg$
+\.keys?$
+\.lib$
+\.lock$
+\.map$
+\.min\..
+\.mp3$
+\.mp4$
+\.nsec3(?:-optout|)$
+\.otf$
+\.pdf$
+\.pem$
+\.png$
+\.psd$
+\.sig$
+\.so$
+\.svg$
+\.svgz$
+\.tar$
+\.tgz$
+\.ttf$
+\.woff
+\.xcf$
+\.xls
+\.xpm$
+\.yml$
+\.zip$
+^modules/remotebackend/example\.rb$
+^modules/tinydnsbackend/data\.cdb$
+^pdns/dnsdistdist/src_js/
+^pdns/recursordist/html/js/
+^\.github/actions/spell-check/
--- /dev/null
+aaaarecord
+aadaf
+aadceba
+aae
+aaldering
+ababd
+abbb
+ABCD
+abcde
+abcdef
+abfe
+abi
+aborttransaction
+ABQ
+Abraitis
+abspath
+acb
+acd
+acf
+acl
+ACPI
+activatedomainkey
+ada
+adadab
+adb
+adcb
+addc
+adddomainkey
+addingrecords
+addprefix
+addsuffix
+adfdffa
+Adiscon
+aee
+aeefcf
+aef
+afdf
+afdlength
+afe
+Afek
+afl
+afnic
+afsdb
+AFYER
+agentx
+agentxperms
+AHM
+ahupowerdns
+Aips
+Aki
+Alenichev
+alexa
+algo
+aliceblue
+allocs
+Altpeter
+amd
+ANCOUNT
+Anderton
+anewid
+anid
+anonymization
+Anonymize
+anotherid
+ANOTHERIPADDRESS
+anothertype
+ansible
+ANSSI
+Antoin
+anycast
+api
+apikey
+APIv
+AQAB
+AQTQ
+ARCHFLAGS
+ARCOUNT
+arecord
+arecvfrom
+Arentz
+ARGS
+arial
+Arjen
+Arjo
+Arnoud
+arpa
+Arsen
+aruba
+asc
+Ascio
+ASd
+Asenov
+ASEP
+Ashish
+ASIN
+asnum
+aspx
+associateddomain
+asyncresolve
+Atlassian
+atoi
+Atomia
+aton
+attr
+atype
+AUTHIP
+authmethod
+Authoritativedoc
+auths
+authzone
+autobuilt
+autocalculation
+autocomplete
+autoconf
+autodetect
+autodetecting
+autodoc
+autogenerated
+automagically
+automake
+Automattic
+autoptr
+autoreconf
+autoserial
+autoslave
+autotools
+AXF
+axfer
+axfr
+axfrfilter
+Baan
+bacc
+backend
+backgrounding
+backport
+Backtick
+backtraces
+BADALG
+BADCOOKIE
+badips
+BADKEY
+BADMODE
+BADNAME
+badserver
+BADSIG
+BADTIME
+BADTRUNC
+BADVERS
+baf
+Baj
+Bakker
+Baltus
+basedn
+basepath
+Bastiaan
+bayour
+bba
+bbb
+bbc
+bbcbbbe
+bbd
+bc
+bca
+bcb
+bcc
+bccd
+bcce
+bce
+bda
+bdd
+bddd
+bded
+bea
+bearggg
+beb
+beda
+beenthere
+bellis
+Belyshev
+benchmarketing
+Benetasso
+Bernd
+bert
+Besselink
+bestwho
+bfa
+bfb
+bfc
+bfcada
+bfe
+bffa
+bgcolor
+Bheca
+Biege
+bigbank
+bigint
+BIGSERIAL
+Bilik
+bindbackend
+binddn
+BINDTODEVICE
+Binero
+binlog
+bla
+Bleker
+blockfilter
+blockquote
+blog
+blogpost
+blogspot
+bmigrate
+bodyfont
+bodysize
+bodywrapper
+bolditalic
+bonafide
+Bortzmeyer
+botnet
+bpf
+bpo
+Brainspark
+Braunoeder
+breadcrumb
+Bremler
+brendangregg
+Briley
+Broens
+broer
+Bromwich
+Brownworth
+Brynjar
+Brzeski
+bsd
+Btw
+Buf
+bufsize
+bugfix
+bugfixes
+bugzilla
+BUILDDIR
+bulc
+bulletinc
+burstable
+bw
+BXvs
+Byterate
+bytestring
+bzero
+bzip
+caa
+caad
+cachekey
+cae
+Cairney
+calculatesoaserial
+calidns
+Cauquil
+cbb
+cbc
+CBF
+Cbjr
+ccac
+ccache
+ccb
+ccbd
+ccc
+ccd
+cce
+ccounts
+cdb
+cdbe
+CDBKV
+cde
+cdece
+cdeede
+cdnskey
+cds
+ceb
+cec
+cece
+cef
+cefcf
+Cegetel
+Cerb
+certusage
+cfe
+cfea
+cfeb
+CFLAGS
+cgi
+CGroup
+changelog
+changeme
+changeset
+changetype
+charset
+chashed
+chbruyand
+chdir
+Chiavacci
+chmod
+chopoff
+chown
+Chqt
+Christof
+chroot
+chrooting
+CHz
+ci
+CIDR
+cjf
+classmethod
+CLASSNUM
+Cloos
+closesocket
+clusions
+cmouse
+cmsg
+cmsghdr
+cn
+cname
+cnamechainresolution
+CNAMEd
+CNAMEDNS
+cnamerecord
+cnf
+Cnma
+cnn
+cockroachlabs
+Cockroft
+codebgcolor
+codeninja
+codetextcolor
+Colemarcus
+colgroup
+collapsiblesidebar
+colm
+comboaddress
+commandline
+committransaction
+conaxis
+config
+configfile
+configname
+configsetting
+configurability
+conntrack
+Conntracking
+Consolas
+constexpr
+controllen
+controlsocket
+coprocess
+coprocesses
+coredumps
+cornercases
+corpit
+CORS
+costypetrisor
+cout
+coverity
+cpp
+cppcheck
+createdb
+createslavedomain
+Cremers
+CRn
+cron
+Cruft
+crv
+cryptokey
+Cryptoki
+cryptopp
+cryptoshop
+css
+csv
+ctime
+ctor
+ctx
+Cuz
+cve
+cvename
+cvs
+cvstrac
+CWD
+CXXFLAGS
+cz
+daa
+dac
+daee
+daemonizing
+daemontools
+daf
+Daganoto
+Danerklint
+dankamongmen
+Darilion
+darix
+Darron
+dataformat
+datasource
+datastore
+datatracker
+Daugaard
+Davids
+Dayneko
+dbaec
+dbe
+dbedfc
+dbf
+dbfile
+dblfilename
+dbname
+dbpf
+dbr
+DBX
+dccc
+dcd
+dcde
+dce
+DCF
+dcobject
+ddaab
+dde
+ddf
+ddns
+DDo
+deactivatedomainkey
+debian
+deboynepollard
+decls
+ded
+Deduktiva
+dedup
+Deduplicate
+defcontent
+defpol
+defttl
+DENIC
+deref
+descclassname
+descname
+Dessel
+dest
+destname
+Detlef
+devicename
+devtoolset
+df
+dfb
+dfd
+dff
+dh
+DHCID
+DHCP
+dhcpd
+dhcpdupdate
+diffs
+DIGESTALGOS
+Digitalus
+dijk
+dilinger
+Directi
+Disqus
+distro
+djbdns
+DKIM
+dlerror
+dlg
+DLLs
+dlmalloc
+DLV
+dmesg
+Dmitry
+dname
+Dnn
+dns
+dnsapi
+dnsbulktest
+dnscache
+dnscrypt
+dnsdemog
+dnsdist
+dnsdistconf
+dnsdistdist
+dnsdistdoc
+dnsdomain
+dnsext
+dnsgram
+dnsheader
+dnskey
+dnsmessage
+dnsname
+dnsnameset
+dnsop
+dnspacket
+dnsparser
+dnspcap
+DNSQ
+dnsquestion
+DNSR
+dnsrecord
+dnsreplay
+dnsresourcerecord
+dnsscan
+dnsscope
+dnssec
+dnssecfromexisting
+DNSSERVER
+dnsspoof
+dnstap
+dnstcpbench
+dnstree
+dnsttl
+dnsupdate
+dnswasher
+dnszone
+dnt
+Dobrawy
+docnamecachelookup
+doctrees
+documentclass
+documentwrapper
+docutils
+doesnotexist
+dofile
+Dohmen
+domaininfo
+domainmetadata
+domainname
+domainrelatedobject
+Donatas
+dontcare
+downsides
+downstreams
+dport
+dq
+drafiei
+Draschl
+droprate
+DRR
+dscontent
+dsrecord
+DSs
+dst
+DTS
+Dufberg
+dumresp
+dynblock
+dynblocklist
+dynblocksref
+dynbpf
+dyndns
+dynhandler
+dynmodules
+eaa
+eac
+eachother
+EAGAIN
+easydns
+eb
+ebaf
+ebd
+ebe
+ebeb
+ebf
+ebfd
+ebpf
+ebpfblocklist
+EBXN
+ecbf
+ecc
+ECCN
+ecdsa
+ECDSAP
+econds
+ECONNRESET
+ecs
+ECSDA
+ecswho
+eda
+edb
+edc
+ede
+edfa
+editline
+edns
+ednsoptions
+ednsoptionview
+ednssubnet
+EDNSTo
+edu
+eea
+eeb
+eec
+EED
+eef
+efb
+efbf
+efc
+efd
+Eieb
+EINTR
+ejones
+EJUGg
+ek
+Ekkelenkamp
+elgoog
+Emph
+endblock
+Enden
+endian
+endif
+endl
+ENOENT
+ENOTCONN
+ent
+entrypoint
+enum
+envoutput
+EOL
+epel
+epoll
+epub
+eqno
+Eriksson
+errlog
+errno
+errorlevels
+esr
+EUI
+EUips
+evildomain
+EVMu
+EWMA
+examplekey
+exceedfuncs
+execfile
+Exort
+externalrefs
+extrahead
+Ezb
+Ezbu
+ezdns
+fabf
+fadec
+FAEEBC
+Faerch
+faf
+failedservers
+failover
+favicon
+FBAE
+fbc
+fbe
+fbf
+fcbd
+fcc
+fcd
+fcde
+fcf
+fcff
+fcgi
+fcontext
+fd
+fda
+fdc
+fdce
+fdd
+fdde
+fdopen
+fedc
+fedoraproject
+feedents
+feedrecord
+feef
+ffaae
+ffb
+ffd
+ffdd
+fff
+ffi
+ffipolicy
+filedescriptor
+Filesystem
+findclientpolicy
+Firefox
+firewalled
+firewalls
+fixednow
+FIXME
+FJZ
+Fki
+FLln
+Florus
+flto
+FNs
+fontname
+footerbgcolor
+footertextcolor
+forfun
+FORMERR
+Fortiguard
+Fortinet's
+forwardzone
+framestream
+freakshow
+freebsd
+freedesktop
+Freenet
+freesans
+freetds
+freshports
+Froemel
+frontend
+fstrm
+fullname
+fulltoc
+func
+Furnell
+Fusl
+FYhvws
+FZq
+gaba
+gacogne
+gatech
+Gavarret
+gcc
+Gci
+gdpr
+Geijn
+genindex
+geobackend
+geoip
+geoipbackend
+geolocated
+Gergely
+Gerritsen
+Gervai
+Gerwin
+getaddrinfo
+getaddrs
+getalldomainmetadata
+getbeforeandafternamesabsolute
+getdomaininfo
+getdomainkeys
+getdomainmetadata
+gethostname
+getlocaladdress
+getn
+getrandom
+getregisteredname
+gettag
+gettext
+gettime
+gettimeofday
+gettsigkey
+Geuze
+GFm
+gh
+Gibheer
+Gieben
+Gillstrom
+github
+githubusercontent
+Gkey
+Gkkq
+glibc
+gmail
+gmake
+Gmb
+gmtime
+GMy
+gmysql
+gmysqlbackend
+gnutls
+godbc
+godbcbackend
+goodmatch
+google
+googleapis
+goracle
+goraclebackend
+GOST
+gouv
+Goxz
+gpgsql
+gpgsqlbackend
+gprof
+gpsqlbackend
+GQj
+GQNy
+grep
+grepping
+grepq
+GSQ
+gsql
+gsqlite
+gss
+gssapi
+gsub
+gtld
+guilabel
+gy
+Gyh
+Gyselinck
+gz
+gzip
+gzipped
+hackerone
+Hakulinen
+Hannu
+haproxy
+hardcode
+hardcoded
+hardcoding
+hardlink
+Harker
+headbgcolor
+headerlink
+headfont
+headlinkcolor
+headtextcolor
+healthcheck
+Heimhilcher
+Helbekkmo
+HELO
+Hendriks
+Henk
+Hensbergen
+Heredoc
+Heuer
+Hev
+hh
+hidesoadetails
+hidettl
+highlighttable
+Hiljanen
+hinfo
+hitrate
+hkraal
+HKSKRWu
+hll
+hlmann
+hmac
+Hmi
+Hoentjen
+Hofstaedtler
+homepage
+Hooimeijer
+hostmaster
+hostname
+Hotmail
+howto
+hpecorp
+hpiers
+hpp
+HPx
+href
+hsm
+htbp
+htm
+html
+htmlescape
+htmlhelp
+http
+httpapi
+httpdomain
+hubert
+hyperlink
+HZQ
+iana
+icann
+ico
+ict
+idprotect
+idq
+idx
+iers
+ietf
+ifdef
+ifportup
+ifurlup
+IFV
+ihsinme
+IJajghd
+IKOYz
+illumos
+img
+Imhard
+incbin
+includeboilerplate
+includerings
+indexa
+indexassociated
+indextable
+inet
+infolog
+infosecinstitute
+ini
+initscript
+Inno
+innodb
+inode
+installable
+interop
+interoperability
+interoperation
+inzk
+iostream
+iowait
+ip
+IPADDRESS
+IPbackend
+ipc
+ipcipher
+ipcom
+ipcrypt
+ipdecrypt
+ipencrypt
+ipfilter
+IPSECKEY
+iptables
+iputils
+ipv
+IQIT
+IQuery
+irc
+isane
+isc
+ismaster
+isoc
+isp
+ispell
+isql
+isse
+issuecomment
+ixfr
+ixfrdist
+ixplore
+Jakub
+Jakum
+janeczku
+Jatko
+Jaury
+Jauvin
+javascript
+JCf
+Jck
+Jeftovic
+Jelte
+Jermar
+Jeroen
+jessie
+jj
+Joaqu
+Jong
+Jorn
+journalctl
+journald
+jp
+jpmens
+jq
+json
+jsondomain
+JSONP
+jsonstat
+ju
+Juergen
+jumpbox
+Juraj
+jye
+Kaminsky
+Kaseorg
+KCtsq
+kd
+Kdhcp
+Kdhcpdupdate
+Kees
+kerberos
+Kerkhof
+KEYBITS
+keyblock
+keydir
+keyfile
+keygen
+keyname
+keypair
+keypairgen
+keyroll
+keysearch
+keysize
+keytab
+KEYTAG
+keytype
+keywordmatches
+kickdaddy
+Kirill
+Klebermass
+koch
+Kockum
+Kolkman
+kom
+Konqueror
+Koos
+koqv
+Kovacic
+kp
+kqueue
+KQZX
+krb
+Krist
+Krul
+ksk
+kskroll
+kskrollcdnskey
+Kuehrer
+KUXs
+kvs
+KX
+Kxxnux
+Kyc
+Ladot
+Lafon
+Lakkas
+largeanswer
+Laros
+lastcheck
+Lastdrager
+lastnotified
+latexpdf
+latlon
+latlonloc
+latomic
+lauch
+Laurient
+Laursen
+Lbackend
+LCUP
+LDA
+ldap
+ldapbackend
+ldflags
+ldif
+ldns
+LDR
+Leen
+Lemoine
+len
+lessthan
+Lesuisse
+lethalgroup
+letsencrypt
+letterpaper
+LFya
+libatomic
+libc
+libcrypto
+libcryptopp
+libcurl
+libdecaf
+libdir
+libedit
+libfstrm
+libgcc
+libgeoip
+libh
+libmaxminddb
+libmongo
+libmysqlclient
+libnss
+libpcap
+libpq
+libpqpp
+libresolv
+libressl
+librt
+libsodium
+libsofthsm
+libssl
+libsystemd
+libtdsodbc
+libyaml
+libzmq
+Lindqvist
+linenos
+linenum
+linkcolor
+lintian
+linux
+linuxnetworks
+Lior
+listinfo
+literalinclude
+llvm
+lmdb
+lmdbbackend
+LMDBKV
+lnsl
+loadbalancer
+loadbalancing
+localaddr
+localhost
+localip
+LOCALSTATEDIR
+localtime
+localtoc
+locaweb
+lochiiconnectivity
+logfile
+loglevel
+logmessage
+logrotate
+lon
+Loopia
+Lorbach
+lordievader
+Louwers
+loweralpha
+lowerroman
+lresolv
+Lrhazi
+lrt
+lsock
+lsocket
+lte
+lua
+luaaction
+luabackend
+luac
+luajit
+luaroundrobin
+luarule
+luawrapper
+Lutter
+Luuk
+Lwc
+Lwz
+LYg
+lz
+Maik
+Maikel
+MAILA
+MAILB
+Majer
+Makefiles
+malcrafted
+malloc
+malware
+Mamane
+Mandriva
+manpage
+mapasync
+mariadb
+Markmann
+maskv
+Massar
+matchtype
+Matthijs
+maxdepth
+MAXINT
+maxlistdepth
+maxmind
+maxqps
+MAXVALUE
+mbed
+mbedtls
+MBOXFW
+mbytes
+MDB
+Meerwald
+Mekking
+MEMLOCK
+Memusage
+menuselection
+metadata
+metadatabase
+metainformation
+metricnames
+metricscarbon
+Meulen
+Michiel
+Microsoft
+Miek
+Miell
+Mieslinger
+Milas
+Mimimization
+minbody
+mindex
+MINFO
+minipatch
+misconfigured
+mjt
+mkuchar
+mmap
+mmdb
+mname
+mnordhoff
+MOADNS
+Modderman
+modifyingpolicydecisions
+modindex
+monshouwer
+Moq
+motherboards
+mozilla
+mplexer
+Mpqhbg
+MQ
+mrtg
+msdcs
+MSDNS
+msphinx
+mssql
+mtasker
+MTEUl
+mthread
+MUar
+Mulholland
+multiline
+multimaster
+multithreading
+mundsson
+munmap
+Muraro
+musl
+mutex
+Mwaikambo
+mx
+mxrecord
+mybackend
+mycompany
+mydns
+mydnsbackend
+mydomain
+MYec
+myhost
+myinstance
+myname
+mypassword
+mypgsql
+mysecretpassword
+myset
+myspecialmetric
+mysql
+mysqlbackend
+mysqld
+mytsigkey
+myuser
+mywebapp
+NAi
+namedroppers
+nameserver
+nameserving
+namespace
+namserver
+naptr
+Nauck
+Navarrete
+nc
+Ndd
+nearmiss
+nearmisses
+Nederlandse
+nedmalloc
+negativetrustanchor
+negcache
+negquery
+neheb
+Nelless
+neosystem
+Netblock
+netfilter
+netherlabs
+netinet
+netmask
+netmaskgroup
+netsnmp
+NETWORKMASK
+Neue
+Neuf
+newcontent
+nextval
+nf
+nic
+nimber
+Nixu
+nkey
+nmg
+nn
+Nncqx
+noaction
+noad
+noall
+nocache
+nocookie
+nodata
+NODELAY
+noedns
+noerrors
+nometasync
+Nominet
+nonexist
+Nonnekes
+noout
+noping
+noport
+nosniff
+nostrip
+NOSUBDIR
+nosync
+Notaras
+NOTAUTH
+NOTIMP
+NOTPARALLEL
+notrack
+NOTZONE
+Novell
+nproxy
+NPTL
+NSCOUNT
+NSCx
+nsd
+NSECx
+NSes
+nsid
+nsis
+nsname
+NSQ
+nsrecord
+NSS
+nsset
+nsspeeds
+NSTTL
+nsupdate
+nta
+ntei
+ntlworld
+nullptr
+NULs
+NUMA
+numreceived
+nx
+nxd
+NXDATA
+nxdomain
+NXRRSET
+oarc
+oauth
+Obermayer
+obidos
+objectclass
+Obser
+obspm
+ocsp
+odbc
+odbcbackend
+odbcinst
+Oddy
+Odintsov
+Oestreicher
+offsite
+Ofpy
+oftc
+OIDs
+Olafur
+Omroep
+openapi
+openbsd
+opendbx
+openpgpkey
+opensc
+openssl
+opensuse
+openwall
+Opmeer
+optcode
+Opteron
+optmem
+optout
+oraclebackend
+ordername
+orsn
+Oservers
+ostringstream
+OSX
+otherdomain
+otherpool
+ou
+OUHTU
+ourname
+ourserial
+ourtime
+OUTFILE
+outpacket
+outputbuffer
+outqueries
+PACKAGEVERSION
+packetcache
+packethandler
+papersize
+paramater
+PARAMKEYWORDS
+params
+passphrase
+passthrough
+passthru
+PATC
+patchlevels
+pathconfig
+pathto
+pawal
+pb
+Pbackend
+pcap
+PCAPFILE
+pdf
+pdns
+pdnsbackend
+pdnscontrol
+pdnsldap
+pdnslog
+pdnsmgrd
+pdnsodbx
+pdnsrandom
+pdnssec
+pdnstest
+pdnsutil
+Peeters
+Pels
+pem
+Penev
+perl
+Perroud
+Pertubation
+pez
+Pfetzing
+pgmysql
+pgmysqlbackend
+pgp
+pgpsql
+pgsql
+phishing
+phonedph
+php
+pickclosest
+pickrandom
+pickwhashed
+pickwrandom
+pid
+piddir
+pidfile
+pidof
+pieter
+pieterlexis
+pilindex
+Pinski
+pipebackend
+pipermail
+PIV
+pkcs
+PKGLIBDIR
+PKI
+PKTINFO
+placeholders
+PLt
+Plusnet
+plzz
+pmtmr
+pnds
+png
+Poelov
+pointsize
+polarssl
+policyactions
+policyevent
+policykinds
+policyname
+policytypes
+pollmplexer
+Ponomarev
+poolers
+poolname
+portnum
+portnumber
+postgre
+postgresql
+postinst
+postresolve
+powerdns
+powerdnsrecursor
+powerdnssec
+powerldap
+powerpc
+ppc
+pragma
+Predota
+preoutquery
+Preproc
+prequery
+prereleases
+prerpz
+presigned
+presignedness
+PRId
+primetime
+princ
+prioritization
+privatekey
+privs
+PRNG
+Proba
+progid
+protobuf
+PROTOC
+providername
+proxyprotocol
+proxyprotocolvalues
+PROXYv
+pseudonymize
+pseudorecord
+psql
+pthread
+ptr
+ptrrecord
+Publieke
+publishdomainkey
+pullreq
+pwfm
+px
+py
+pygments
+pypi
+Pyry
+PYv
+PZFX
+qa
+Qag
+qclass
+qdcount
+qdomain
+qgen
+Qkj
+qlen
+Qlim
+Qll
+qname
+qperq
+Qpkv
+qps
+QPSIP
+qpslimits
+QRate
+qsize
+QSLj
+qthread
+qtype
+qtypelist
+querycache
+querycount
+quickstart
+QYCIHp
+qytpe
+ragel
+randombackend
+randombit
+randombytes
+randomisation
+randomises
+randomloader
+rapidjson
+raspbian
+rb
+RBL
+RBUb
+rcode
+rcodezero
+Rcvbuf
+rcvmmsg
+rdata
+rdqueries
+rdynamic
+readline
+README
+readonly
+realtime
+reconnections
+recursor
+recursordist
+Recursordoc
+Recuweb
+recv
+recvbuf
+recverr
+recvfrom
+recvmmsg
+recvmsg
+redelegations
+redhat
+redjack
+reentrantly
+refactor
+Refactoring
+refcount
+refman
+refreh
+refuseds
+regex
+reid
+reimplementation
+Reinier
+Rejo
+relbar
+relbarbgcolor
+relbarlinkcolor
+relbartextcolor
+relro
+Remco
+remi
+remoteaddr
+remotebackend
+remoteip
+remoting
+removedomainkey
+replacerrset
+requery
+resolv
+respawn
+respawning
+respout
+respsizes
+Resync
+resynchronise
+retransfering
+reuseds
+reuseport
+Reuwiei
+rfc
+rhel
+Rietz
+rightsidebar
+Rijsdijk
+ringbuffer
+rkey
+RLIMIT
+rname
+rng
+rocommunity
+Roel
+Rosmalen
+roundrobin
+rp
+RPATH
+rping
+RPMS
+rpz
+rpzstatistics
+Rqvg
+rr
+rrcontent
+rrd
+rrdata
+rrdtool
+rrname
+rrset
+rrsig
+rrtype
+rsa
+rsasha
+RSP
+rst
+rsync
+ru
+Rueckert
+Rul
+rulesets
+Ruthensteiner
+rv
+Rvd
+rw
+Rwgj
+rwlock
+Rzs
+Sakaguchi
+saltsa
+sandboxing
+Sangwhan
+SASL
+Saunalahti
+saxfr
+SBF
+sbin
+Sbvka
+scalability
+SCHED
+Scheffler
+Schlich
+Scholten
+Schryver
+Schueler
+schwer
+SCn
+scopebits
+scopemask
+scriptable
+scriptlets
+sdb
+sdfoijdfio
+sdig
+secpoll
+securesphere
+securitypolicy
+securitypolling
+seealso
+segfault
+selectmplexer
+selinux
+senderrors
+Sendetzky
+sendmsg
+sensistive
+Sergey
+servername
+serverpools
+serverselection
+servfail
+setaffinity
+setcontent
+setdomainmetadata
+setgid
+seting
+setkey
+setnotified
+SETPIPE
+settting
+setuptools
+setvariable
+Sgs
+Shafir
+shantikulkarni
+shinsterneck
+Shm
+showdetails
+showflags
+Shukla
+sid
+SIddm
+sidebarbgcolor
+sidebarbtncolor
+sidebarbutton
+sidebarlinkcolor
+sidebarlogo
+sidebarsourcelink
+sidebartextcolor
+sidebarwidth
+sidn
+SIGABR
+sigint
+Sigmod
+signedness
+Signingpiper
+signpipe
+signttl
+signzone
+SIGPIPE
+sigs
+SIGTERM
+SIGUSR
+singlethreaded
+Sinstallation
+Sipek
+sizeof
+Sjoerd
+slapd
+slapindex
+slaveness
+SLES
+smartcard
+smb
+smellyspice
+smfgo
+smimea
+smn
+smtp
+Smurthwaite
+Snarked
+sndbuf
+SNI
+snmp
+snmpd
+SNMPv
+snprintf
+soa
+soadata
+soarecord
+sockaddr
+socketdir
+softhsm
+solaris
+SOLc
+Soldaat
+SOMAXCONN
+somedomain
+Sonix
+Soref
+Soroceanu
+sortlist
+sourcecode
+SOURCEDIR
+sourceforge
+sourceware
+Spaans
+Spackages
+spam
+Sparc
+spf
+SPHINXBUILD
+sphinxcontrib
+sphinxjsondomain
+SPHINXOPTS
+SPHINXPROJ
+sphinxsidebar
+sphinxsidebarwrapper
+splitsetup
+sprezzos
+Spruyt
+SQk
+sql
+sqlite
+srandom
+src
+srcname
+SRecord
+Srule
+srv
+SSH
+sshfp
+ssi
+ssl
+sslmode
+sslrootcert
+SSQ
+SSql
+stacksize
+standalone
+starttls
+starttransaction
+Stasic
+statbag
+statbas
+statisticitem
+statm
+stbuehler
+stderr
+stdin
+stdio
+stdout
+Stef
+Steinbuch
+Stichting
+stickysidebar
+Stillaway
+Stirnimann
+stmt
+Stol
+Storbeck
+stou
+stoul
+strcasestr
+stringmatch
+strlen
+strpos
+stubquery
+stubresolver
+stunnel
+Stussy
+stutiredboy
+stylesheet
+subdomain
+subkey
+subnetmask
+sudo
+suffixmatchtree
+supermaster
+supermasterbackend
+supernotification
+supersecretpassword
+superslave
+superslaving
+supervisord
+Surfnet
+SUSE
+swapcontext
+swe
+swoga
+sx
+Symlink
+syncres
+sys
+sysadmin
+syscall
+SYSCONFDIR
+sysctl
+Sysdream
+syslog
+systemcall
+systemctl
+systemd
+sysv
+SYW
+SZ
+szw
+tarball
+Tarjei
+Tarnell
+tbhandler
+tbody
+tcely
+tcp
+tcpdump
+TCPKEEPALIVE
+TCPv
+Tctk
+td
+teeaction
+Telenet
+testrunner
+testsdir
+texinfo
+textcolor
+tfoot
+Tful
+TGJGVc
+thead
+thel
+thelog
+Thessalonikefs
+Thiago
+thinko
+Thomassen
+threadsafe
+throttlemap
+thrysoee
+Thu
+timedipsetrule
+timedout
+timeframe
+timeline
+timesource
+timeval
+timezone
+tinycdb
+tinydns
+tinydnsbackend
+tisr
+TKEY
+tld
+tls
+tlsa
+tmp
+tmpfs
+tobool
+toc
+toctree
+todo
+toint
+tokenuser
+tolower
+Tolstov
+Toshifumi
+tostring
+Travaille
+tribool
+trunc
+trustanchor
+trusteer
+trx
+trxid
+tsc
+tsig
+tsigalgo
+tsigkey
+tsigname
+tsigsecret
+tstamp
+TSU
+tt
+ttl
+Tuinder
+tunables
+tuomi
+Tushuizen
+Tuu
+Tuxis
+TVJRU
+tw
+tylerneylon
+typedefs
+typenames
+tyu
+TZ
+ualberta
+Ubo
+ubuntu
+udp
+udpqueryresponse
+UDPv
+udr
+Ueber
+Ueli
+uid
+uint
+Uisms
+uj
+uk
+ul
+ulimit
+Umq
+unauth
+unbreak
+uncached
+unescaping
+unfresh
+unhash
+unicode
+uninett
+uninitialised
+Uninstaller
+unistd
+unitialized
+unixodbc
+unixtime
+unparseable
+Unprocessable
+unpublish
+unpublishdomainkey
+unreachables
+unregister
+unshadowing
+untruncated
+unzero
+upd
+updatepolicy
+upperalpha
+upperroman
+urandom
+uri
+url
+urlencoded
+usec
+usecase
+useragent
+userbase
+username
+userspace
+usr
+utf
+UUHJWZg
+uuid
+uwaterloo
+Uwcjbp
+Uwi
+Uyypn
+Valentei
+Valentini
+valgrind
+validationstates
+validators
+Valkenburg
+Vandalon
+vandergaast
+Vandoren
+VARCHAR
+varname
+Vasiliy
+VBG
+Vbt
+VDRAW
+VDz
+Veldhuyzen
+venv
+Verheijen
+Verisign
+Verschuren
+versionadded
+versionchanged
+versionmodified
+vh
+viewcode
+virtualenv
+virtualized
+visitedlinkcolor
+vixie
+vm
+Vn
+Voegeli
+Volker
+voxel
+Vranken
+Vsgoi
+Vwgbclzx
+VY
+vzu
+WAITFORONE
+wal
+wallclock
+warnlog
+Wascwa
+Wbq
+webbased
+webdocs
+webhandler
+webpassword
+webserver
+website
+Webspider
+weightparams
+Weimer
+Welzel
+Wessels
+westes
+Wevers
+wextra
+Wgx
+whashed
+whitelist
+Wia
+Wichert
+Wieger
+Wielicki
+Wijk
+Wijnand
+Wijngaards
+wiki
+wikipedia
+wil
+wildcarded
+wildcards
+Willcott
+windr
+Winfried
+wireformat
+wirelength
+Wisiol
+wmc
+Wmissing
+Wojas
+workaround
+Worldnic
+would've
+wouter
+wpad
+wproduction
+wrandom
+Wrange
+Wredundant
+writev
+Wshadow
+wuh
+ww
+www
+WYbw
+Wzqf
+Wzs
+Xander
+xchacha
+Xchange
+xdb
+Xek
+Xeon
+xf
+xfr
+xh
+xhtml
+XM
+xml
+xorbooter
+xpf
+XPFCODE
+XPFDST
+XPFPROTO
+XPFSRC
+XPFVERSION
+Xpw
+XRecord
+XSalsa
+xss
+XYR
+yahttp
+yaml
+yb
+YDyzc
+Yehuda
+YG
+YHk
+YIBo
+Yiu
+Yj
+yk
+YLCOy
+Ylitalo
+yml
+YMMV
+yourcompany
+yourdomain
+yourorganization
+yoursecret
+yp
+YQ
+yubikey
+Yx
+YXDOMAIN
+YXRRSET
+yy
+YYYYMMD
+YYYYMMDD
+YYYYMMDDSS
+Zealey
+zeha
+Zengers
+Zengin
+zeromq
+Zfndz
+Zhf
+zilopbg
+ZJad
+Zmd
+ZNLY
+Zoag
+zonecryptokey
+zonefile
+zonemetadata
+zonename
+zoneparser
+zonetransfer
+Zonneveld
+ZRev
+zsk
+zskroll
+Zt
+Zumstrull
+Zwane
+zz
+zzyzz
--- /dev/null
+/docs/
+^docs/
--- /dev/null
+(?:0[Xx]|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
+addNSECRecordToLW.*DNSName.*powerdnt.com.*QType::NS.*res->d_records
+data:[a-zA-Z=;,/0-9+]+
+BOOST_CHECK_EQUAL\(b64, "[a-zA-Z=;,/0-9+]+"
+\b([A-Za-z])\1{3,}\b
+C(?:XX|)FLAGS="[^"]*"
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'powerdns'
- dry-run: true
+ dry-run: false
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'powerdns'
fuzz-seconds: 600
- dry-run: true
+ dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v1
if: failure()
--- /dev/null
+name: Spell checking
+on:
+ push:
+ branches:
+ - "**"
+ tags-ignore:
+ - "**"
+ paths:
+ - "docs/**"
+ - "**/docs/**"
+ pull_request:
+ branches:
+ - "**"
+ tags-ignore:
+ - "**"
+ paths:
+ - "docs/**"
+ - "**/docs/**"
+ types: ['opened', 'reopened', 'synchronize']
+
+jobs:
+ build:
+ name: Spell checking
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2.0.0
+ with:
+ fetch-depth: 5
+ - uses: check-spelling/check-spelling@0.0.16-alpha
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ bucket: .github/actions
+ project: spell-check
All documentation can be found on https://doc.powerdns.com/
This file may lag behind at times. For most recent updates, always check
-https://doc.powerdns.com/md/changelog/.
+https://doc.powerdns.com/authoritative/changelog/
Another good place to look for information is:
-https://doc.powerdns.com/md/appendix/compiling-powerdns/
+https://doc.powerdns.com/authoritative/appendices/compiling.html
To file bugs, head towards:
https://github.com/PowerDNS/pdns/issues
COMPILING Authoritative Server
------------------------------
-The PowerDNS Authoritative Server depends on Boost, OpenSSL and requires a
+The PowerDNS Authoritative Server depends on Boost, OpenSSL and Lua, and requires a
compiler with C++-2011 support.
On Debian 9, the following is useful:
The HTML documentation (as seen [on the PowerDNS docs site](https://doc.powerdns.com/authoritative/)) is built from ReStructured Text (rst) files located in `docs`. They are compiled into HTML files using [Sphinx](http://www.sphinx-doc.org/en/master/index.html), a documentation generator tool which is built in Python.
-**Using a normal Python installation**
-
-For those simply contributing to the documentation, this avoids needing to install the various build
-tools and other dependencies.
-
-Install Python 2.7 or Python 3 (preferable) if you don't yet have it installed. On some operating
-systems you may also have to install `python3-pip` or similarly named.
-
-Ubuntu 16.04 / 18.04
-
-```sh
-apt update
-apt install python3 python3-pip python3-venv
-```
-
-macOS (using homebrew)
-
-```sh
-brew install python3
-```
-
-Update your `pip` and install/update `virtualenv` to avoid problems:
-
-```sh
-# for python2, use "pip" instead of "pip3"
-pip3 install -U pip
-pip3 install -U virtualenv
-```
-
-Enter the repository's `docs` folder, set up the virtualenv, and install the requirements
-
-```sh
-cd docs
-# for python2, use "virtualenv .venv" instead
-python3 -m venv .venv
-source .venv/bin/activate
-# The virtualenv may use an older pip, so upgrade it again
-pip3 install -U pip setuptools setuptools-git
-# Now you can install the requirements
-pip3 install -r requirements.txt
-```
-
-Finally, you can build the documentation:
-
-```sh
-sphinx-build . html-docs
-```
-
-Note: If your shell has problems finding sphinx-build, try using `.venv/bin/sphinx-build` instead.
-
-The HTML documentation is now available in `html-docs`.
-
-**Using the build tools**
-
-This method is preferable for those who already have a working build environment for PowerDNS.
-
Install the dependencies under "COMPILING", and run autoreconf if you haven't already:
```sh
The HTML documentation will now be available in `html-docs`.
-Solaris Notes
--------------
-Use a recent gcc (and other build tools), possibly from Solaris 11 IPS.
-
-If you encounter problems with the Solaris make, gmake is advised.
-
FreeBSD Notes
-------------
You need to compile using gmake - regular make only appears to work, but doesn't in fact. Use gmake, not make.
PowerDNS and dnsdist Security Policy
====================================
-If you have a security problem to report, please email us at both security@powerdns.com and ahu@ds9a.nl.
+If you have a security problem to report, please email us at both peter.van.dijk@powerdns.com and remi.gacogne@powerdns.com.
In case you want to encrypt your report using PGP, please use: https://www.powerdns.com/powerdns-keyblock.asc
Please do not mail security issues to public lists, nor file a ticket, unless we do not get back to you in a timely manner.
try:
if access_token:
res = requests.get('https://api.github.com/repos/PowerDNS/pdns/pulls/'
- '{}?access_token={}'.format(pr, access_token))
+ '{}'.format(pr),
+ headers={'Authorization': 'token ' + access_token})
else:
res = requests.get('https://api.github.com/repos/PowerDNS/pdns/pulls/'
'{}'.format(pr), auth=httpAuth)
'omoerbeek']:
try:
if access_token:
- user_info = requests.get(pr_info['user']['url'] + '?access_token=' + access_token, auth=httpAuth).json()
+ user_info = requests.get(pr_info['user']['url'],
+ headers={'Authorization': 'token ' + access_token}).json()
else:
user_info = requests.get(pr_info['user']['url'], auth=httpAuth).json()
except (requests.exceptions.HTTPError, ValueError) as e:
print(e)
sys.exit(1)
- if 'name'in user_info:
+ if 'name' in user_info:
out += ' ({})'.format(user_info['name'])
else:
out += ' (@{})'.format(user_info['login'])
# - `docker build --no-cache --pull --file Dockerfile.auth-41.ubuntu-bionic --tag auth-41.ubuntu-bionic .`
# - `docker run -it auth-41.ubuntu-bionic`
# - `docker run -it auth-41.ubuntu-bionic /bin/bash`
+# - `dnsdist --verbose 9.9.9.9`
# - `pdns_recursor`
+# - `pdns_server`
+#
+# Remi contributed this snippet:
+#
+# #!/bin/bash
+#
+# readonly product=dnsdist-15
+#
+# for version in centos-6 centos-7 centos-8 debian-buster debian-stretch ubuntu-bionic ubuntu-xenial; do
+# docker build --no-cache --pull --file Dockerfile.${product}.${version} --tag ${product}.${version} .
+# done
+#
+# for version in centos-6 centos-7 centos-8 debian-buster debian-stretch ubuntu-bionic ubuntu-xenial; do
+# docker run -it ${product}.${version} dnsdist -v 9.9.9.9
+# done
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 ]"
+ 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
if [ "$VERSION" = "6" -o "$VERSION" = "7" ]; then
cat <<EOF >> Dockerfile.$RELEASE.$OS-$VERSION
RUN yum install -y yum-plugin-priorities
+EOF
+ elif [ "$RELEASE" = "dnsdist-15" -a "$VERSION" = "8" ]; then
+ cat <<EOF >> Dockerfile.$RELEASE.$OS-$VERSION
+RUN dnf install -y 'dnf-command(config-manager)'
+RUN dnf config-manager --set-enabled PowerTools
EOF
fi
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" ]; then
+ if [ "$RELEASE" = "rec-43" -o "$RELEASE" = "rec-44" ]; then
cat <<EOF >> Dockerfile.$RELEASE.$OS-$VERSION
RUN mkdir /var/run/pdns-recursor
cat <<EOF > pdns.list.$RELEASE.$OS-$VERSION
deb [arch=amd64] http://repo.powerdns.com/$OS $VERSION-$RELEASE main
+EOF
+
+ # For the following two maybe only create depending on package, but
+ # it's not really a big deal.
+
+ # if not exists
+ cat <<EOF > dnsdist.debian-and-ubuntu
+Package: dnsdist*
+Pin: origin repo.powerdns.com
+Pin-Priority: 600
EOF
# if not exists
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
- if [ "$RELEASE" = "rec-43" ]; then
- cat <<EOF >> Dockerfile.$RELEASE.$OS-$VERSION
+ if [ "$RELEASE" = "rec-43" -o "$RELEASE" = "rec-44" ]; then
+ cat <<EOF >> Dockerfile.$RELEASE.$OS-$VERSION
RUN mkdir /var/run/pdns-recursor
EOF
if [ "$RELEASE" = "auth-40" ]; then
write_centos 6 pdns pdns_server
write_centos 7 pdns pdns_server
- write_debian jessie pdns-server pdns_server
write_debian stretch pdns-server pdns_server
- write_ubuntu trusty pdns-server pdns_server
write_ubuntu xenial pdns-server pdns_server
elif [ "$RELEASE" = "auth-41" ]; then
write_centos 6 pdns pdns_server
write_centos 7 pdns pdns_server
- write_debian jessie pdns-server pdns_server
write_debian stretch pdns-server pdns_server
- 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" ]; then
+ write_centos 6 pdns pdns_server
+ write_centos 7 pdns pdns_server
+ write_centos 8 pdns pdns_server
+ write_debian stretch pdns-server pdns_server
+ write_debian buster pdns-server pdns_server
+ write_ubuntu xenial pdns-server pdns_server
+ write_ubuntu bionic pdns-server pdns_server
+elif [ "$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_debian buster pdns-server pdns_server
write_ubuntu xenial pdns-server pdns_server
write_ubuntu bionic pdns-server pdns_server
+ write_ubuntu focal pdns-server pdns_server
elif [ "$RELEASE" = "rec-40" ]; then
write_centos 6 pdns-recursor pdns_recursor
write_centos 7 pdns-recursor pdns_recursor
- write_debian jessie pdns-recursor pdns_recursor
write_debian stretch pdns-recursor pdns_recursor
- write_ubuntu trusty pdns-recursor pdns_recursor
write_ubuntu xenial pdns-recursor pdns_recursor
elif [ "$RELEASE" = "rec-41" ]; then
write_centos 6 pdns-recursor pdns_recursor
write_centos 7 pdns-recursor pdns_recursor
- write_debian jessie pdns-recursor pdns_recursor
write_debian stretch pdns-recursor pdns_recursor
- 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" ]; 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
+ 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
+ write_debian stretch dnsdist dnsdist
+ write_debian buster dnsdist dnsdist
+ write_ubuntu xenial dnsdist dnsdist
+ write_ubuntu bionic dnsdist dnsdist
+ write_ubuntu focal dnsdist dnsdist
else
echo "Invalid release: $RELEASE"
exit 1
[ -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
#!/bin/sh
export PDNSRECURSOR=${PDNSRECURSOR:-/usr/sbin/pdns_recursor}
+export PDNSRECCONTROL=${PDNSRECCONTROL:-/usr/bin/rec_control}
export DNSBULKTEST=${DNSBULKTEST:-/usr/bin/dnsbulktest}
if [ "$0" != "./build-scripts/test-recursor-bulk" ]; then
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
export context="${version}_v6:${IPv6}_csv:${CSV%%.*}"
export IPv6
export CSV
- RECURSOR=$PDNSRECURSOR THRESHOLD=0 TRACE=no time ./recursor-test 5401 $domains || EXIT=1
+ RECURSOR=$PDNSRECURSOR RECCONTROL=$PDNSRECCONTROL THRESHOLD=0 TRACE=no time ./recursor-test 5401 $domains || EXIT=1
mv -f recursor.log recursor-${context}.log
sleep 10
done
run "cd .."
run "wget http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip"
run "unzip top-1m.csv.zip -d ${TRAVIS_BUILD_DIR}/regression-tests"
- run 'echo -e "deb [arch=amd64] http://repo.powerdns.com/ubuntu trusty-auth-master main" | sudo tee /etc/apt/sources.list.d/pdns.list'
- run 'echo -e "Package: pdns-*\nPin: origin repo.powerdns.com\nPin-Priority: 9001" | sudo tee /etc/apt/preferences.d/pdns'
- run 'curl https://repo.powerdns.com/CBC8B383-pub.asc | sudo apt-key add - '
- run 'sudo apt-get update'
+ run 'wget https://downloads.powerdns.com/tmp/pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty.tar.bz2'
+ run 'tar xf pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty.tar.bz2'
+ run 'sudo dpkg -i pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-server_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-tools_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-backend-bind_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb ; sudo apt-get -y install -f'
run 'sudo apt-get -y install pdns-server pdns-tools'
run "sudo service pdns stop"
run 'for suffix in {1..40}; do sudo /sbin/ip addr add 10.0.3.$suffix/32 dev lo; done'
# geoip Configuration
#
-# See for more information https://doc.powerdns.com/md/authoritative/backend-geoip/
+# See for more information https://doc.powerdns.com/authoritative/backends/geoip.html
#
-# geoip-database-file=
-# geoip-database-file6=
+# geoip-database-files=
# geoip-zones-file=
# geoip-dnssec-keydir=
modules/gsqlite3backend/3.4.0_to_4.0.0_schema.sqlite3.sql
modules/gsqlite3backend/4.0.0_to_4.2.0_schema.sqlite3.sql
modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
+modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
modules/gsqlite3backend/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
modules/gsqlite3backend/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
modules/gsqlite3backend/schema.sqlite3.sql
override_dh_install:
dh_install
- ./pdns/pdns_server --no-config --config | sed \
+ ./pdns/pdns_server --config=default | sed \
-e 's!# module-dir=.*!!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
-e 's!# launch=.*!&\nlaunch=!' \
# geoip Configuration
#
-# See for more information https://doc.powerdns.com/md/authoritative/backend-geoip/
+# See for more information https://doc.powerdns.com/authoritative/backends/geoip.html
#
-# geoip-database-file=
-# geoip-database-file6=
+# geoip-database-files=
# geoip-zones-file=
# geoip-dnssec-keydir=
modules/gsqlite3backend/3.4.0_to_4.0.0_schema.sqlite3.sql
modules/gsqlite3backend/4.0.0_to_4.2.0_schema.sqlite3.sql
modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
+modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
modules/gsqlite3backend/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
modules/gsqlite3backend/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
modules/gsqlite3backend/schema.sqlite3.sql
override_dh_install:
dh_install
- ./pdns/pdns_server --no-config --config | sed \
+ ./pdns/pdns_server --config=default | sed \
-e 's!# module-dir=.*!!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
-e 's!# launch=.*!&\nlaunch=!' \
# geoip Configuration
#
-# See for more information https://doc.powerdns.com/md/authoritative/backend-geoip/
+# See for more information https://doc.powerdns.com/authoritative/backends/geoip.html
#
-# geoip-database-file=
-# geoip-database-file6=
+# geoip-database-files=
# geoip-zones-file=
# geoip-dnssec-keydir=
modules/gsqlite3backend/3.4.0_to_4.0.0_schema.sqlite3.sql
modules/gsqlite3backend/4.0.0_to_4.2.0_schema.sqlite3.sql
modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
+modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
modules/gsqlite3backend/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
modules/gsqlite3backend/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
modules/gsqlite3backend/schema.sqlite3.sql
override_dh_install:
dh_install
- ./pdns/pdns_server --no-config --config | sed \
+ ./pdns/pdns_server --config=default | sed \
-e 's!# module-dir=.*!!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
-e 's!# launch=.*!&\nlaunch=!' \
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
-# other if a failure occured
+# other if a failure occurred
start-stop-daemon --stop --quiet --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
# Enable hardening features for daemons
-# Note: blhc (build log hardening check) will find these false positivies: CPPFLAGS 2 missing, LDFLAGS 1 missing
+# Note: blhc (build log hardening check) will find these false positives: CPPFLAGS 2 missing, LDFLAGS 1 missing
export DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow,+pie
DPKG_EXPORT_BUILDFLAGS = 1
# Include buildflags.mk so we can append to the vars it sets.
install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
rm -f debian/pdns-recursor/etc/powerdns/recursor.conf-dist
- ./pdns_recursor --no-config --config | sed \
+ ./pdns_recursor --config=default | sed \
-e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
-e 's!# local-address=.*!local-address=127.0.0.1!' \
-e 's!# setgid=.*!setgid=pdns!' \
-e 's!# setuid=.*!setuid=pdns!' \
-e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+ -e '/^# version-string=.*/d' \
> debian/pdns-recursor/etc/powerdns/recursor.conf
override_dh_strip:
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
-# other if a failure occured
+# other if a failure occurred
start-stop-daemon --stop --quiet --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
# Enable hardening features for daemons
-# Note: blhc (build log hardening check) will find these false positivies: CPPFLAGS 2 missing, LDFLAGS 1 missing
+# Note: blhc (build log hardening check) will find these false positives: CPPFLAGS 2 missing, LDFLAGS 1 missing
export DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow,+pie
DPKG_EXPORT_BUILDFLAGS = 1
# Include buildflags.mk so we can append to the vars it sets.
install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
rm -f debian/tmp/etc/powerdns/recursor.conf-dist
- ./pdns_recursor --no-config --config | sed \
+ ./pdns_recursor --config=default | sed \
-e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
-e 's!# local-address=.*!local-address=127.0.0.1!' \
-e 's!# setgid=.*!setgid=pdns!' \
-e 's!# setuid=.*!setuid=pdns!' \
-e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+ -e '/^# version-string=.*/d' \
> debian/tmp/etc/powerdns/recursor.conf
override_dh_strip:
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
-# other if a failure occured
+# other if a failure occurred
start-stop-daemon --stop --quiet --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS)
# Enable hardening features for daemons
-# Note: blhc (build log hardening check) will find these false positivies: CPPFLAGS 2 missing, LDFLAGS 1 missing
+# Note: blhc (build log hardening check) will find these false positives: CPPFLAGS 2 missing, LDFLAGS 1 missing
export DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow,+pie
DPKG_EXPORT_BUILDFLAGS = 1
# Include buildflags.mk so we can append to the vars it sets.
install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
rm -f debian/pdns-recursor/etc/powerdns/recursor.conf-dist
- ./pdns_recursor --no-config --config | sed \
+ ./pdns_recursor --config=default | sed \
-e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
-e 's!# local-address=.*!local-address=127.0.0.1!' \
-e 's!# setgid=.*!setgid=pdns!' \
-e 's!# setuid=.*!setuid=pdns!' \
-e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+ -e '/^# version-string=.*/d' \
> debian/pdns-recursor/etc/powerdns/recursor.conf
override_dh_strip:
FROM dist-base as package-builder
ARG APT_URL
-RUN apt-get -y install devscripts dpkg-dev build-essential python3 equivs
+RUN DEBIAN_FRONTEND=noninteractive apt-get -y install devscripts dpkg-dev build-essential python3 equivs
RUN mkdir /dist /pdns
WORKDIR /pdns
fi
@ENDIF
-# mv accross layers with overlay2 is buggy in some kernel versions (results in empty dirs)
+# mv across layers with overlay2 is buggy in some kernel versions (results in empty dirs)
# See: https://github.com/moby/moby/issues/33733
#RUN mv /root/rpmbuild/RPMS/* /dist/
RUN cp -R /root/rpmbuild/RPMS/* /dist/
# First do the source builds
@INCLUDE Dockerfile.target.sdist
-# This defines the dstribution base layer
+# This defines the distribution base layer
# Put only the bare minimum of common commands here, without dev tools
FROM amazonlinux:2 as dist-base
ARG BUILDER_CACHE_BUSTER=
# First do the source builds
@INCLUDE Dockerfile.target.sdist
-# This defines the dstribution base layer
+# This defines the distribution base layer
# Put only the bare minimum of common commands here, without dev tools
FROM centos:6 as dist-base
ARG BUILDER_CACHE_BUSTER=
# First do the source builds
@INCLUDE Dockerfile.target.sdist
-# This defines the dstribution base layer
+# This defines the distribution base layer
# Put only the bare minimum of common commands here, without dev tools
FROM centos:7 as dist-base
ARG BUILDER_CACHE_BUSTER=
# First do the source builds
@INCLUDE Dockerfile.target.sdist
-# This defines the dstribution base layer
+# This defines the distribution base layer
# Put only the bare minimum of common commands here, without dev tools
FROM centos:8 as dist-base
ARG BUILDER_CACHE_BUSTER=
--- /dev/null
+# First do the source builds
+@INCLUDE Dockerfile.target.sdist
+
+FROM ubuntu:focal as dist-base
+ARG BUILDER_CACHE_BUSTER=
+ARG APT_URL
+RUN apt-get update && apt-get -y dist-upgrade
+
+@INCLUDE Dockerfile.debbuild-prepare
+
+@IF [ ! -z "$M_authoritative" ]
+ADD builder-support/debian/authoritative/debian-buster/ pdns-${BUILDER_VERSION}/debian/
+@ENDIF
+
+@IF [ ! -z "$M_recursor" ]
+ADD builder-support/debian/recursor/debian-buster/ pdns-recursor-${BUILDER_VERSION}/debian/
+@ENDIF
+
+@IF [ ! -z "$M_dnsdist" ]
+ADD builder-support/debian/dnsdist/debian-buster/ dnsdist-${BUILDER_VERSION}/debian/
+@ENDIF
+
+@INCLUDE Dockerfile.debbuild
+
+# Do a test install and verify
+# Can be skipped with skiptests=1 in the environment
+# @EXEC [ "$skiptests" = "" ] && include Dockerfile.debtest
%service_add_post %{name}.service
%endif
%if 0%{?rhel} >= 7
+systemctl daemon-reload ||:
%systemd_post %{name}.service
%endif
%if 0%{?rhel} == 6
chkconfig --add %{name}
%else
+systemctl daemon-reload ||:
%systemd_post %{name}.service
%endif
Summary: MySQL backend for %{name}
Group: System Environment/Daemons
Requires: %{name}%{?_isa} = %{version}-%{release}
+%if 0%{?rhel} < 8
BuildRequires: mysql-devel
+%else
+BuildRequires: mariadb-connector-c-devel
+%endif
%global backends %{backends} gmysql
%description backend-mysql
%{__install} -D -p %{SOURCE1} %{buildroot}%{_initrddir}/pdns
%endif
-%{buildroot}/usr/sbin/pdns_server --no-config --config | sed \
+%{buildroot}/usr/sbin/pdns_server --config=default | sed \
-e 's!# daemon=.*!daemon=no!' \
-e 's!# guardian=.*!guardian=no!' \
-e 's!# launch=.*!&\\nlaunch=!' \
%post
%if 0%{?rhel} >= 7
+systemctl daemon-reload ||:
%systemd_post pdns.service
%else
/sbin/chkconfig --add pdns
%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
# directories that are symbolic links (a Unix filesystem feature) are excluded
# from the input.
-EXCLUDE_SYMLINKS = NO
+EXCLUDE_SYMLINKS = YES
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
-EXCLUDE_PATTERNS =
+EXCLUDE_PATTERNS = test-*
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
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 $CFLAGS"
-CXXFLAGS="-g -O2 -Wall -Wextra -Wshadow -Wno-unused-parameter $CXXFLAGS"
+CFLAGS="-g -O2 -Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls $CFLAGS"
+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],
PDNS_ENABLE_VERBOSE_LOGGING
PDNS_ENABLE_PKCS11
-PDNS_ENABLE_GSS_TSIG
AC_SUBST([socketdir])
socketdir="/var/run"
AS_IF([test "x$enable_experimental_pkcs11" = "xyes"],
[AC_MSG_NOTICE([PKCS-11: yes])]
)
-AS_IF([test "x$enable_experimental_gss_tsig" = "xyes"],
- [AC_MSG_NOTICE([GSS-TSIG: yes])]
-)
AS_IF([test "x$enable_lua_records" = "xyes"],
[AC_MSG_NOTICE([LUA records: yes])]
)
_pdnsutil_helper_local_() {
local cur prev cmd
- local _PDNSUTIL_ALL_CMDS="activate-tsig-key activate-zone-key add-record add-zone-key backend-cmd b2b-migrate bench-db change-slave-zone-master check-zone check-all-zones clear-zone
- create-bind-db create-slave-zone create-zone deactivate-tsig-key deactivate-zone-key delete-rrset delete-tsig-key delete-zone disable-dnssec
- edit-zone export-zone-dnskey export-zone-key generate-tsig-key generate-zone-key get-meta hash-zone-record increase-serial import-tsig-key
- import-zone-key load-zone list-algorithms list-keys list-zone list-all-zones list-tsig-keys rectify-zone rectify-all-zones remove-zone-key
- replace-rrset secure-all-zones secure-zone set-kind set-nsec3 set-presigned set-publish-cdnskey set-publish-cds set-meta show-zone
- unset-nsec3 unset-presigned unset-publish-cdnskey unset-publish-cds test-schema"
+ local _PDNSUTIL_ALL_CMDS="activate-tsig-key activate-zone-key add-record add-supermaster add-zone-key backend-cmd b2b-migrate bench-db change-slave-zone-master
+ check-zone check-all-zones clear-zone create-bind-db create-slave-zone create-zone deactivate-tsig-key deactivate-zone-key delete-rrset
+ delete-tsig-key delete-zone disable-dnssec edit-zone export-zone-dnskey export-zone-key generate-tsig-key generate-zone-key get-meta
+ hash-zone-record increase-serial import-tsig-key import-zone-key load-zone list-algorithms list-keys list-zone list-all-zones
+ list-tsig-keys rectify-zone rectify-all-zones remove-zone-key replace-rrset secure-all-zones secure-zone set-kind set-nsec3 set-presigned
+ set-publish-cdnskey set-publish-cds set-meta show-zone unset-nsec3 unset-presigned unset-publish-cdnskey unset-publish-cds test-schema"
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
End of life statements
======================
-The currently supported release train of PowerDNS Authoritative Server is 4.2.
+We aim to have a release every six months.
+The latest and previous release receive correctness, stability and security updates.
+The release before that gets critical security updates only.
+Older releases are marked end of life and receive no updates at all.
+Pre-releases do not receive immediate security updates.
-PowerDNS Authoritative Server 4.1 will only receive correctness, stability and security updates and will be receiving security updates only after PowerDNS Authoritative Server 4.3 is released.
-It wil be end of life after PowerDNS Authoritative Server 4.4 is released.
+The currently supported release train of PowerDNS Authoritative Server is 4.3.
-PowerDNS Authoritative Server 4.0 will only receive security updates and will be end of life after PowerDNS Authoritative Server 4.3 is released.
+PowerDNS Authoritative Server 4.2 will only receive correctness, stability and security updates and will be receiving security updates only after PowerDNS Authoritative Server 4.4 is released.
+It wil be end of life after PowerDNS Authoritative Server 4.5 is released.
-PowerDNS Authoritative Server 3.x and 2.x are end of life, and will not
+PowerDNS Authoritative Server 4.1 will only receive critical security updates and will be end of life after PowerDNS Authoritative Server 4.4 is released.
+
+PowerDNS Authoritative Server 4.0, 3.x and 2.x are end of life, and will not
receive any updates, not even security fixes.
Note: Users with a commercial agreement with PowerDNS.COM BV or Open-Xchange
can receive extended support for releases which are End Of Life. If you are
such a user, these EOL statements do not apply to you.
+.. list-table:: PowerDNS Authoritative Server Release Life Cycle
+ :header-rows: 1
+
+ * - Version
+ - Release date
+ - Security-Only updates
+ - End of Life
+ * - 4.3
+ - April 7 2020
+ - ~ April 2021
+ - ~ October 2021
+ * - 4.2
+ - August 30 2019
+ - ~ October 2020
+ - ~ April 2021
+ * - 4.1
+ - November 30 2017
+ - April 7 2020
+ - ~ October 2020
+ * - 4.0 and older
+ - EOL
+ - EOL
+ - EOL
+
PowerDNS Authoritative Server 3.x
---------------------------------
1st of December 2017
The PowerDNS Authoritative Server 3.x releases are no longer supported, and
will not receive any further updates, not even for security purposes.
-All users are urged to upgrade to version 4.1. To upgrade from 3.x to 4.x,
+All users are urged to upgrade to the latest version. To upgrade from 3.x to 4.x,
:doc:`follow these instructions <../upgrading>`
If you need help with upgrading, we provide `migration
help to tide you over, we can also provide that as part of a `support
agreement <https://www.powerdns.com/support-services-consulting.html>`__.
-But we urge everyone to move on to PowerDNS Authoritative Server 4.1 or
-later - it is a faster, more standards conforming and more powerful
+But we urge everyone to move on to PowerDNS Authoritative Server 4.x - it is a faster, more standards conforming and more powerful
nameserver!
My masters won't allow PowerDNS to access zones as it is using the wrong local IP address
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, PowerDNS lets the kernel pick the source address.
-To set an explicit source address, use the :ref:`setting-query-local-address` and :ref:`setting-query-local-address6` settings.
+To set an explicit source address, use the :ref:`setting-query-local-address` setting.
PowerDNS does not answer queries on all my IP addresses (and I've ignored the warning I got about that at startup)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
======================
PowerDNS backends are implemented via a simple yet powerful C++
-interface. If your needs are not met by the PipeBackend, you may want to
-write your own. Before doing any PowerDNS development, please read `this blog
+interface. If your needs are not met by the regular backends, including
+the PipeBackend and the RemoteBackend, you may want to write your own.
+Before doing any PowerDNS development, please read `this blog
post <https://blog.powerdns.com/2015/06/23/what-is-a-powerdns-backend-and-how-do-i-make-it-send-an-nxdomain/>`__
which has a FAQ and several pictures that help explain what a backend
is.
figure out the SOA, so if you have no special treatment for SOA records,
where is no need to implement your own ``getSOA()``.
+Figuring out the Start of Authority can require an important number of
+call to ``getSOA()`` if the name has a lot of labels. For example,
+figuring out that the SOA for ``2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.``
+is ``d.0.1.0.0.2.ip6.arpa.`` might involve 26 calls, chopping off one label
+at a time. If your backend has an efficient way to figure out the
+best SOA it has for a given name, it is possible to override the
+default ``getSOA()`` implementation to immediately return the
+``d.0.1.0.0.2.ip6.arpa.`` SOA record to the first
+``2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.``
+``getSOA()`` call.
+
Besides direct queries, PowerDNS also needs to be able to list a zone,
to do zone transfers for example. Each zone has an id which should be
-unique within the backend. To list all records belonging to a zone id,
+unique within the backends. To list all records belonging to a zone id,
the ``list()`` method is used. Conveniently, the domain_id is also
available in the ``SOAData`` structure.
+.. warning::
+ Each zone should have a unique id, even across backends.
+
The following lists the contents of a zone called "powerdns.com".
.. code-block:: cpp
static RandomLoader randomloader;
This simple backend can be used as an 'overlay'. In other words, it only
-knows about a single record, another loaded backend would have to know
-about the SOA and NS records and such. But nothing prevents us from
-loading it without another backend.
+knows about a single name, ``random.powerdns.com``, another loaded backend
+would have to know about the SOA and NS records for the ``powerdns.com`` zone
+and such.
+
+.. warning::
+ Spreading the content of a zone across multiple backends, described above
+ as 'overlay', makes the zone incompatible with some operations that
+ assume that a single zone is always entirely stored in the same backend.
+ Such operations include zone transfers, listing and editing zone content via
+ the API or ``pdnsutil``.
+
+.. warning::
+ When the content of a zone is spread across multiple backends, all the types
+ for a given name should be delegated to the same backend.
+ For example a backend can know about all the types for ``random.powerdns.com``
+ while another backend knows about all the types for ``random2.powerdns.com``,
+ but it is not possible to let one backend handle only ``AAAA`` queries for
+ all names while another one handles only ``A`` queries, for example.
+ This limitation comes from the fact that PowerDNS uses ``ANY`` queries to fetch
+ all types from the backend in one go and that it assumes that once one backend
+ has returned records the other ones do not need to be called.
+ It is also possible to have two backends providing records for the same name
+ and types, for example if the first one does not support DNSSEC and the second
+ does, but that requires some mechanism outside of PowerDNS to keep records in
+ sync between the two backends.
The first part of the code contains the actual logic and should be
pretty straightforward. The second part is a boilerplate 'factory' class
Methods
~~~~~~~
-.. cpp:function:: void DNSBackend::lookup(const QType &qtype, const string &qdomain, DNSPacket *pkt=0, int zoneId=-1)
+.. cpp:function:: void DNSBackend::lookup(const QType &qtype, const string &qdomain, DNSPacket *pkt=nullptr, int zoneId=-1)
This function is used to initiate a straight lookup for a record of name
'qdomain' and type 'qtype'. A QType can be converted into an integer by
is, you can retrieve information about who asked the question with the
``pkt->getRemote()`` method.
+ .. note::
+ Since 4.1.0, 'SOA' lookups are not passed this pointer anymore because
+ PowerDNS doesn't support tailoring whether a whole zone exists or not based
+ on who is asking.
+
Note that **qdomain** can be of any case and that your backend should
make sure it is in effect case insensitive. Furthermore, the case of the
original question should be retained in answers returned by ``get()``!
To indicate the importance of an error, the standard syslog errorlevels
are available. They can be set by outputting ``Logger::Critical``,
``Logger::Error``, ``Logger::Warning``, ``Logger::Notice``,
-``Logger::Info`` or ``Logger::Debug`` to ``L``, in descending order of
+``Logger::Info`` or ``Logger::Debug`` to ``g_log``, in descending order of
graveness.
Declaring and reading configuration details
while(resolver.axfrChunk(recs)) {
for(Resolver::res_t::const_iterator i=recs.begin();i!=recs.end();++i) {
- db->feedRecord(*i);
+ db->feedRecord(*i);
}
}
db->commitTransaction();
must be added to the zone. ``rrset`` can be empty in which case the
method is used to remove a RRset.
+Domain metadata support
+-----------------------
+
+As described in :doc:`../domainmetadata`, 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
-------------
on how to configure PowerDNS to serve records synthesized from ALIAS
records.
+.. _types-apl:
+
+APL
+-----
+
+.. versionadded:: 4.4.0
+
+The APL record, specified in :rfc:`3123`, is used to specify a DNS RR type "APL" for address prefix lists.
+
.. _types-caa:
CAA
: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
for master, slave and superslave operation.
When using the InnoDB storage engine, we suggest adding foreign key
-contraints to the tables in order to automate deletion of records, key
+constraints to the tables in order to automate deletion of records, key
material, and other information upon deletion of a domain from the
domains table. The following SQL does the job:
^^^^^^^^^^^^^^^^^^
.. versionadded:: 4.2.1
-Send the CLIENT_SSL capabily flag to the server. SSL suppport is announced by the server via CLIENT_SSL and is enabled if the client returns the same capability. Default: no.
+Send the CLIENT_SSL capability flag to the server. SSL support is announced by the server via CLIENT_SSL and is enabled if the client returns the same capability. Default: no.
.. _setting-gmysql-timeout:
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
^^^^^^^^^^^^^^^
Currently there are two options: Set ``ldap-method`` to ``strict`` to
have the code automatically derive PTR records from A and AAAA records
in the tree. Or, in ``simple`` and ``tree`` modes, create additional
-objects explictly mapping each address to a PTR record.
+objects explicitly mapping each address to a PTR record.
For ``strict`` or ``simple`` modes, first create an object with an SOA
record for the reverse-lookup zone(s) corresponding to the A and AAAA
associateddomain:1.0.1.10.in-addr.arpa
To use this kind of record, add the dnsdomain2 schema to the
-configuration of ther LDAP server.
+configuration of the LDAP server.
**CAUTION:** ``ldap-method=strict`` can not be used if zone transfers
(AXFR) are needed to other name servers. Distributing zones can only be
Default: mapasync
* ``sync``: LMDB synchronous mode. Safest option, but also slightly slower. Can also be enabled with ``lmdb-sync-mode=``
-* ``nosync``: don't flush systems buffers to disk when committing a transation.
+* ``nosync``: don't flush systems buffers to disk when committing a transaction.
This means a system crash can corrupt the database or lose the last transactions if buffers are not yet flushed to disk.
* ``nometasync``: flush system buffers to disk only once per transaction, omit the metadata flush. This maintains database integrity, but can potentially lose the last committed transaction if the operating system crashes.
* ``mapasync``: (default). Use asynchronous flushes to disk. As with nosync, a system crash can then corrupt the database or lose the last transactions.
integers 16 bits, fixes
`#5443 <https://github.com/PowerDNS/pdns/issues/5443>`__
- `#5346 <https://github.com/PowerDNS/pdns/pull/5346>`__: configure.ac:
- Corrects syntax error in test statement on existance of
+ Corrects syntax error in test statement on existence of
libcrypto\_ecdsa (shinsterneck)
- `#5440 <https://github.com/PowerDNS/pdns/pull/5440>`__: configure.ac:
Fix quoting issue fixes
Changelogs for 4.1.x
====================
+.. changelog::
+ :version: 4.1.14
+ :released: September 2nd 2020
+
+ This release contains the fix for :doc:`PowerDNS Security Advisory 2020-05 <../security-advisories/powerdns-advisory-2020-05>` (CVE-2020-17482)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9500
+
+ Raise an exception on invalid hex content in unknown records.
+
.. changelog::
:version: 4.1.13
:released: August 9th 2019
Existing zone files may now be interpreted differently.
Specifically, where we previously used the SOA minimum field for the default
- TTL if none was set explictly, or no $TTL was set, we now use the TTL from
+ TTL if none was set explicitly, or no $TTL was set, we now use the TTL from
the previous line.
.. change::
Changelogs for 4.2.x
====================
+.. changelog::
+ :version: 4.2.3
+ :released: September 2nd 2020
+
+ This release contains the fix for :doc:`PowerDNS Security Advisory 2020-05 <../security-advisories/powerdns-advisory-2020-05>` (CVE-2020-17482)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9499
+
+ Raise an exception on invalid hex content in unknown records.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9191
+ :tickets: 9181
+
+ mydns: add SOA to list() output
+
+.. changelog::
+ :version: 4.2.2
+ :released: 9th of April 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9010
+
+ fix records ending up in wrong packet section (Kees Monshouwer)
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9003, 8736
+
+ cache: strictly enforce maximum size, and improve cleanup routine
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9001
+
+ avoid IXFR-in corruption when deltas come in close together (please see the :ref:`ixfr-in-corruption-4.2.2` upgrade notes)
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8786
+
+ api: add includerings option to statistics endpoint
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8612
+
+ fix out-of-bound access for zero length "serialized" string when using lmdbbackend. (Kees Monshouwer)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8602
+
+ bind backend: pthread_mutex_t should be inited and destroyed and not be copied
+
.. changelog::
:version: 4.2.1
:released: 2nd of December 2019
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
:pullreq: 5361
:tickets: 3602
- Make requests always return to sender, for usage in multimaster slave zones. Also - made sure that the master that is questioned for updates will be selected randomly, to prevent repeatidally asking a dead master for updates.
+ Make requests always return to sender, for usage in multimaster slave zones. Also - made sure that the master that is questioned for updates will be selected randomly, to prevent repeatedly asking a dead master for updates.
.. change::
:tags: Improvements, API
Changelogs for 4.3.x
====================
+.. changelog::
+ :version: 4.3.1
+ :released: 22nd of September 2020
+
+ This is version 4.3.1 of the Authoritative Server.
+ This release contains the fix for :doc:`PowerDNS Security Advisory 2020-05 <../security-advisories/powerdns-advisory-2020-05>` (CVE-2020-17482).
+ It also contains several other fixes and improvements:
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9498
+
+ Raise an exception on invalid hex content in unknown records.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9444
+
+ Handle the extra single-row result set of MySQL stored procedures (Chris Hofstaedtler)
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9036
+
+ EL8 pkgs: Build mysql backend against mariadb-connector-c-devel
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9219
+
+ gpgsql: Reintroduce prepared statements (Chris Hofstaedtler)
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9233
+
+ gsqlite3backend: add missing indexes (Kees Monshouwer)
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9224
+
+ use real remote for supermaster createSlaveDomain() (Kees Monshouwer)
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9176
+
+ Optimize IXFR-to-AXFR fallback path (Chris Hofstaedtler)
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9013
+
+ Install bind SQL schema files as part of bindbackend (Chris Hofstaedtler)
+
+ .. change::
+ :tags: New Features
+ :pullreq: 9083
+
+ add ubuntu focal target
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9480
+
+ Do not send out of zone lookups to the backends (Kees Monshouwer)
+
+.. changelog::
+ :version: 4.3.0
+ :released: 7th of April 2020
+
+ This is version 4.3.0 of the Authoritative Server.
+ It contains all changes mentioned in the alpha, beta and RC versions below, plus two more bugfixes.
+
+ If you are upgrading from beta2 or rc2, AND ONLY THEN, please read `pull request #8975 <https://github.com/PowerDNS/pdns/pull/8975>`__ very carefully.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8977
+
+ avoid IXFR-in corruption when deltas come in close together (please see the :ref:`ixfr-in-corruption-4.3.0` upgrade notes)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8975
+
+ improve sql schema updates
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8951
+
+ reduce the number of temporary memory allocations
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8973
+
+ Fix NSECx for unpublished DNSKEYs properly
+
+.. changelog::
+ :version: 4.3.0-rc2
+ :released: 18th of March 2020
+
+ This is the first Release Candidate for version 4.3.0 of the Authoritative Server.
+ The version called 4.3.0-rc1 was never released because of the cache cleanup change mentioned below.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8924
+
+ Make sure we look at 10% of all cached items during cleanup (Kees Monshouwer)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8936
+
+ emit correct NSEC/NSEC3 bitmaps in hidden key situations (Robin Geuze)
+
.. changelog::
:version: 4.3.0-beta2
:released: 21st of February 2020
- `commit 016d810 <https://github.com/PowerDNS/pdns/commit/016d810>`__:
improve postgresql detection during ./configure
- `commit dce1e90 <https://github.com/PowerDNS/pdns/commit/dce1e90>`__:
- DNAME: don't sign the synthesised CNAME
+ DNAME: don't sign the synthesized CNAME
- `commit 25e7af3 <https://github.com/PowerDNS/pdns/commit/25e7af3>`__:
send empty SERVFAIL after a backend throws a DBException, instead of
including useless content
`commit
569 <http://wiki.powerdns.com/projects/trac/changeset/569>`__.
- PowerDNS now reports if it is running in 32 or 64 bit mode, useful
- for bi-arch users that need to know if they are benefitting from
+ for bi-arch users that need to know if they are benefiting from
`AMD's great processor <http://www.amd.com>`__. `commit
571 <http://wiki.powerdns.com/projects/trac/changeset/571>`__.
- **dnsscope** compiles again, `commit
.. 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::
.. note::
Actual supported algorithms depend on the crypto-libraries
- PowerDNS was compiled against. To check the supported DNSSEC algoritms
+ PowerDNS was compiled against. To check the supported DNSSEC algorithms
in your build of PowerDNS, run ``pdnsutil list-algorithms``.
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.
GSS-ALLOW-AXFR-PRINCIPAL
------------------------
+ .. versionchanged:: 4.3.1
+ GSS support was removed
Allow this GSS principal to perform AXFR retrieval. Most commonly it is
``host/something@REALM``, ``DNS/something@REALM`` or ``user@REALM``.
GSS-ACCEPTOR-PRINCIPAL
----------------------
+ .. versionchanged:: 4.4.0
+ GSS support was removed
Use this principal for accepting GSS context.
(See :ref:`tsig-gss-tsig`).
* :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
'/servers/{server_id}/zones/{zone_id}/metadata':
get:
- summary: Get all the MetaData associated with the zone.
+ summary: 'Get all the Metadata associated with the zone.'
operationId: listMetadata
tags:
- zonemetadata
in: path
required: true
- name: metadata
- description: List of metadata to add/create
+ description: Metadata object with list of values to create
required: true
in: body
schema:
- type: array
- items:
- $ref: '#/definitions/Metadata'
+ $ref: '#/definitions/Metadata'
responses:
'204':
description: OK
'/servers/{server_id}/zones/{zone_id}/metadata/{metadata_kind}':
get:
- summary: Get the content of a single kind of domain metadata as a list of MetaData objects.
+ summary: 'Get the content of a single kind of domain metadata as a Metadata object.'
operationId: getMetadata
tags:
- zonemetadata
type: string
in: path
required: true
- description: '???'
+ description: The kind of metadata
responses:
'200':
- description: List of Metadata objects
+ description: Metadata object with list of values
schema:
$ref: '#/definitions/Metadata'
put:
- summary: 'Modify the content of a single kind of domain metadata.'
+ summary: 'Replace the content of a single kind of domain metadata.'
+ description: 'Creates a set of metadata entries of given kind for the zone. Existing metadata entries for the zone with the same kind are removed.'
operationId: modifyMetadata
tags:
- zonemetadata
schema:
$ref: '#/definitions/Metadata'
responses:
- '204':
- description: OK
+ '200':
+ description: Metadata object with list of values
+ schema:
+ $ref: '#/definitions/Metadata'
delete:
summary: 'Delete all items of a single kind of domain metadata.'
operationId: deleteMetadata
type: string
in: path
required: true
- description: '???'
+ description: The kind of metadata
responses:
- '204':
+ '200':
description: OK
'/servers/{server_id}/zones/{zone_id}/cryptokeys':
* Changing the Name, this will remove the key with tsigkey_id after adding.
* Changing the Algorithm
* Changing the Key
+
Only the relevant fields have to be provided in the request body.
operationId: putTSIGKey
tags:
description: 'Whether or not the key is in active use'
published:
type: boolean
- descriptioon: 'Whether or not the DNSKEY record is published in the zone'
+ description: 'Whether or not the DNSKEY record is published in the zone'
dnskey:
type: string
description: 'The DNSKEY record for this key'
{"name": "mytsigkey", "algorithm": "hmac-sha256"}
-Will yield a response similar to this (several headers ommitted):
+Will yield a response similar to this (several headers omitted):
.. code-block:: http
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.
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>`_.
+If you have found a bug, please file a bug report on `GitHub <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>`_.
+You can file a feature request on `GitHub <https://github.com/PowerDNS/pdns/issues/new?template=feature_request.md>`__.
Redhat-based Systems
~~~~~~~~~~~~~~~~~~~~
-On RedHat based system there are 2 options to install PowerDNS, from
+On RedHat based systems there are 3 options to install PowerDNS, from
`EPEL <https://fedoraproject.org/wiki/EPEL>`__, the `repository from
Kees Monshouwer <https://www.monshouwer.eu/download/3rd_party/pdns/>`__
or from `the PowerDNS repositories <https://repo.powerdns.com>`__:
$ sudo yum install pdns-backend-$backend
+Note that for some of those package sources, the bind backend is shipped as part of the base ``pdns`` package, and there is no separate ``pdns-backend-bind`` package.
+
FreeBSD
~~~~~~~
DNSName objects
^^^^^^^^^^^^^^^
-A :class:`DNSName` object represents a name in the DNS. It has serveral functions that can manipulate it without conversions to strings.
+A :class:`DNSName` object represents a name in the DNS. It has several functions that can manipulate it without conversions to strings.
Creating a ``DNSName`` is done with the :func:`newDN`::
myname = newDN("www.example.com")
.. method:: DNSName:canonCompare(name) -> bool
- Performs a comparaison of DNS names in canonical order.
+ Performs a comparison of DNS names in canonical order.
Returns true if the DNSName comes before ``name``.
See https://tools.ietf.org/html/rfc4034#section-6
.. method:: DNSName::equal(name) -> bool
- Perform a comparaison of the DNSName to the given ``name``.
+ Perform a comparison of the DNSName to the given ``name``.
You can also compare directly two DNSName objects using
the ``==`` operator
Returns true if ``address`` matches any of the masks in the group.
- :param ComboAddress address: The IP addres to match the netmasks against.
+ :param ComboAddress address: The IP address to match the netmasks against.
Example
-------
-``nsec3dig 8.8.8.8 53 doesntexist.isoc.nl TXT recurse``
+``nsec3dig 8.8.8.8 53 doesnotexist.isoc.nl TXT recurse``
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*
^^^^^^^^^^^^^^^^^^^^^^
See also
--------
-pdns\_server(1)
+pdns\_server (1), pdnsutil (1)
--loglevel=<LEVEL> Set the logging level.
--config Show the currently configuration. There are three optional values:
--config=default show the default configuration.
- --config=diff show modified options in the curent configuration.
+ --config=diff show modified options in the current configuration.
--config=check parse the current configuration, with error checking.
--help To view more options that are available use this program.
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*]..
OTHER TOOLS
-----------
-ipencrypt *IP-ADDRESS* passsword
+ipencrypt *IP-ADDRESS* password
Encrypt an IP address according to the 'ipcipher' standard
-ipdecrypt *IP-ADDRESS* passsword
+ipdecrypt *IP-ADDRESS* password
Encrypt an IP address according to the 'ipcipher' standard
See also
Don't show the SOA serial in the response.
hidettl
Replace TTLs with `[ttl]` in the response.
+proxy *TCP?* *SRC* *DST*
+ Wrap query in PROXYv2 protocol with these parameters. The first parameter accepts 0 for UDP and 1 for TCP. The second and third take IP addresses and port.
recurse
Set the RD bit in the question.
showflags
Show the NSEC3 flags in the response (they are hidden by default).
tcp
Use TCP instead of UDP to send the query.
-xpf *XPFCODE* *XPFVERSION* *XPFPROTO* *XPFSRC* *XPFSRC*
+xpf *XPFCODE* *XPFVERSION* *XPFPROTO* *XPFSRC* *XPFDST*
Send an *XPF* additional with these parameters.
Examples
sdig ::1 53 example.com A recurse
Query to a DNS-over-HTTPS server requesting dnssec and recursion
- sdig https://dns.somesample.net/dns-query 443 example.com A dnssec recurse
-
+ sdig https://dns.example.net/dns-query 443 example.com A dnssec recurse
Make sure the directory is writable for the ``pdns_server`` process and
that :ref:`setting-bind-config` parameter
-references this file. Now start PowerDNS and wait untill all zones are
+references this file. Now start PowerDNS and wait until all zones are
transferred. Now you can change the zone type to 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. 2020030304 10800 3600 604800 10800
+@ 86400 IN SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020092201 10800 3600 604800 10800
@ 3600 IN NS pdns-public-ns1.powerdns.com.
@ 3600 IN NS pdns-public-ns2.powerdns.com.
auth-4.0.5.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-03.html"
auth-4.0.6.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-03.html"
auth-4.0.7.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-03.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-05.html"
-auth-4.0.8.security-status 60 IN TXT "1 OK"
-auth-4.0.9.security-status 60 IN TXT "1 OK"
+auth-4.0.8.security-status 60 IN TXT "2 Unsupported release (EOL)"
+auth-4.0.9.security-status 60 IN TXT "2 Unsupported release (EOL)"
auth-4.1.0-rc1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
auth-4.1.0-rc2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
auth-4.1.0-rc3.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
auth-4.1.7.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-04.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-05.html"
auth-4.1.8.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-04.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-05.html"
auth-4.1.9.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-04.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-05.html"
-auth-4.1.10.security-status 60 IN TXT "1 OK"
-auth-4.1.11.security-status 60 IN TXT "1 OK"
-auth-4.1.12.security-status 60 IN TXT "1 OK"
-auth-4.1.13.security-status 60 IN TXT "1 OK"
+auth-4.1.10.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.1.11.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.1.12.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.1.13.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.1.14.security-status 60 IN TXT "1 OK"
auth-4.2.0-alpha1.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-03.html"
auth-4.2.0-beta1.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-03.html"
auth-4.2.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
auth-4.2.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
auth-4.2.0-rc3.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-auth-4.2.0.security-status 60 IN TXT "1 OK"
-auth-4.2.1.security-status 60 IN TXT "1 OK"
+auth-4.2.0.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.2.1.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.2.2.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.2.3.security-status 60 IN TXT "1 OK"
auth-4.3.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
auth-4.3.0-beta1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-auth-4.3.0-beta2.security-status 60 IN TXT "1 OK"
+auth-4.3.0-beta2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+auth-4.3.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+auth-4.3.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+auth-4.3.0.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2020-05.html"
+auth-4.3.1.security-status 60 IN TXT "1 OK"
; Auth Debian
auth-3.4.1-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-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-3.4.1-4_deb8u5.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2016-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-3.4.1-4_deb8u6.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-3.4.1-4_deb8u7.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
-auth-3.4.1-4_deb8u8.debian.security-status 60 IN TXT "1 OK"
+auth-3.4.1-4_deb8u8.debian.security-status 60 IN TXT "2 Unsupported release (EOL)"
auth-3.4.4-2_bpo8_1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-3.4.5-1_bpo8_1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-4.0.1-7.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-02.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-03.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-04.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-05.html"
auth-4.0.2-1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
auth-4.0.3-1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
-auth-4.0.3-1_deb9u2.debian.security-status 60 IN TXT "1 OK"
+auth-4.0.3-1_deb9u2.debian.security-status 60 IN TXT "2 Unsupported release (EOL)"
; From here on security polling has been disabled for this distribution.
; Auth Ubuntu
recursor-4.1.6.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2018-09.html"
recursor-4.1.7.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2018-09.html"
recursor-4.1.8.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2019-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2019-02.html"
-recursor-4.1.9.security-status 60 IN TXT "1 OK"
-recursor-4.1.10.security-status 60 IN TXT "1 OK"
-recursor-4.1.11.security-status 60 IN TXT "1 OK"
-recursor-4.1.12.security-status 60 IN TXT "1 OK"
-recursor-4.1.13.security-status 60 IN TXT "1 OK"
-recursor-4.1.14.security-status 60 IN TXT "1 OK"
-recursor-4.1.15.security-status 60 IN TXT "1 OK"
-recursor-4.2.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-beta1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0.security-status 60 IN TXT "1 OK"
-recursor-4.2.1.security-status 60 IN TXT "1 OK"
-recursor-4.3.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-alpha2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-alpha3.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-beta1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-beta2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0.security-status 60 IN TXT "1 OK"
+recursor-4.1.9.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.10.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.11.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.12.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.13.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.14.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.15.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.16.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.1.17.security-status 60 IN TXT "1 OK"
+recursor-4.2.0-alpha1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-beta1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-rc1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-rc2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.2.1.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.2.2.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.2.3.security-status 60 IN TXT "1 OK"
+recursor-4.2.4.security-status 60 IN TXT "1 OK"
+recursor-4.3.0-alpha1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-alpha2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-alpha3.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-beta1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-beta2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-rc1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-rc2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.3.1.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.3.2.security-status 60 IN TXT "1 OK"
+recursor-4.3.3.security-status 60 IN TXT "1 OK"
+recursor-4.3.4.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 "3 Unsupported pre-release"
+recursor-4.4.0-beta1.security-status 60 IN TXT "3 Unsupported pre-release"
+recursor-4.4.0-rc1.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-rc4.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
dnsdist-1.4.0-rc5.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
dnsdist-1.4.0.security-status 60 IN TXT "1 OK"
+dnsdist-1.5.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc3.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc4.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0.security-status 60 IN TXT "1 OK"
--- /dev/null
+PowerDNS Security Advisory 2020-05: Leaking uninitialised memory through crafted zone records
+=============================================================================================
+
+- CVE: CVE-2020-17482
+- Date: September 22nd, 2020
+- Affects: PowerDNS Authoritative 4.3.0 and earlier
+- Not affected: 4.3.1 and up, 4.2.3 and up, 4.1.14 and up
+- Severity: Low
+- Impact: Information leak
+- Exploit: This problem can be triggered via crafted records by an authorized user
+- Risk of system compromise: Low
+- Solution: Upgrade to a fixed version
+- Workaround: Do not take zone data from untrusted users
+
+An issue has been found in PowerDNS Authoritative Server before 4.3.1 where an authorized user with the ability to insert crafted records into a zone might be able to leak the content of uninitialized memory.
+Such a user could be a customer inserting data via a control panel, or somebody with access to the REST API.
+Crafted records cannot be inserted via AXFR.
+
+This issue has been assigned CVE-2020-17482.
+
+PowerDNS Authoritative up to and including version 4.3.0 are affected.
+Please note that at the time of writing, PowerDNS Authoritative 4.0 and below are no longer supported, as described in
+https://doc.powerdns.com/authoritative/appendices/EOL.html.
+
+We would like to thank Nathaniel Ferguson for finding and subsequently reporting this issue!
--- /dev/null
+PowerDNS Security Advisory 2020-06: Various issues in our GSS-TSIG support
+==========================================================================
+
+- CVE: CVE-2020-24696, CVE-2020-24697, CVE-2020-24698
+- Date: September 22nd, 2020
+- Affects: PowerDNS Authoritative versions before 4.4.0, when compiled with --enable-experimental-gss-tsig
+- Not affected: 4.4.0 and up, and any version compiled without GSS-TSIG support
+- Severity: Low
+- Impact: Crashes, Information Leaks, Possible code execution
+- Exploit: This problem can be triggered via crafted packets
+- Risk of system compromise: Low
+- Solution: Do not use software built with GSS-TSIG support
+
+Various issues have been found in our GSS-TSIG support, where an unauthorized attacker could cause crashes, possibly leak uninitialised memory, and possibly execute arbitrary code.
+
+These issues have been assigned:
+
+* CVE-2020-24696: A remote, unauthenticated attacker can trigger a race condition leading to a crash, or possibly arbitrary code execution, by sending crafted queries with a GSS-TSIG signature.
+* CVE-2020-24697: A remote, unauthenticated attacker can cause a denial of service by sending crafted queries with a GSS-TSIG signature.
+* CVE-2020-24698: A remote, unauthenticated attacker might be able to cause a double-free, leading to a crash or possibly arbitrary code execution by sending crafted queries with a GSS-TSIG signature.
+
+All PowerDNS Authoritative versions are affected, but *only* if they have been compiled with ``--enable-experimental-gss-tsig``.
+We have never published packages with the feature enabled.
+
+Because of the various issues with the feature (including a complete lack of testing code around it), and no reports of production usage of GSS-TSIG, we have decided to remove the relevant code completely in PowerDNS Authoritative 4.4.0.
+Users of earlier versions that rely on the feature can keep doing so until they upgrade to 4.4.0, but need to be aware of these issues.
+
+We would like to thank Nathaniel Ferguson for finding and subsequently reporting these issues!
- 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:
.. note::
Actual supported algorithms depend on the crypto-libraries
- PowerDNS was compiled against. To check the supported DNSSEC algoritms
+ PowerDNS was compiled against. To check the supported DNSSEC algorithms
in your build of PowerDNS, run ``pdnsutil list-algorithms``.
.. _setting-default-ksk-size:
.. note::
Actual supported algorithms depend on the crypto-libraries
- PowerDNS was compiled against. To check the supported DNSSEC algoritms
+ PowerDNS was compiled against. To check the supported DNSSEC algorithms
in your build of PowerDNS, run ``pdnsutil list-algorithms``.
.. _setting-default-zsk-size:
- Boolean
- Default: yes
+.. versionchanged:: 4.4.0
+ This setting has been removed
+
Perform AAAA additional processing. This sends AAAA records in the
ADDITIONAL section when sending a referral.
.. versionadded:: 4.1.0
-If this is enabled, ALIAS records are expanded (synthesised to their
+If this is enabled, ALIAS records are expanded (synthesized to their
A/AAAA).
If this is disabled (the default), ALIAS records will not be expanded and
``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:
- Boolean
- Default: no
-If this is enabled, ALIAS records are expanded (synthesised to their
+If this is enabled, ALIAS records are expanded (synthesized to their
A/AAAA) during outgoing AXFR. This means slaves will not automatically
follow changes in those A/AAAA records unless you AXFR regularly!
``query-local-address``
-----------------------
+.. versionchanged:: 4.4.0
+ Accepts both IPv4 and IPv6 addresses. Also accept more than one address per
+ address family.
-- IPv4 Address
-- Default: 0.0.0.0
+- IP addresses, separated by spaces or commas
+- Default: `0.0.0.0 ::`
-The IP address to use as a source address for sending queries. Useful if
+The IP addresses to use as a source address for sending queries. Useful if
you have multiple IPs and PowerDNS is not bound to the IP address your
operating system uses by default for outgoing packets.
+PowerDNS will pick the correct address family based on the remote's address (v4
+for outgoing v4, v6 for outgoing v6). However, addresses are selected at random
+without taking into account ip subnet reachability. It is highly recommended to
+use the defaults in that case (the kernel will pick the right source address for
+the network).
+
.. _setting-query-local-address6:
``query-local-address6``
------------------------
+.. deprecated:: 4.4.0
+ Use :ref:`setting-query-local-address`. The default has been changed
+ from '::' to unset.
- IPv6 Address
-- Default: '::'
+- Default: unset
Source IP address for sending IPv6 queries.
- String
- Default: auto
-Specify which random number generator to use. Permissible choises are:
+Specify which random number generator to use. Permissible choices are:
- auto - choose automatically
- sodium - Use libsodium ``randombytes_uniform``
- kiss - Use simple settable deterministic RNG. **FOR TESTING PURPOSES ONLY!**
.. note::
- Not all choises are available on all systems.
+ Not all choices are available on all systems.
.. _setting-security-poll-suffix:
$ 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>`:
GSS-TSIG support
----------------
+ .. versionchanged:: 4.4.0
+ GSS support was removed
GSS-TSIG allows authentication and authorization of DNS updates or AXFR
using Kerberos with TSIG signatures.
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.3.0 to 4.3.1
+--------------
+
+On RHEL/CentOS 8, the gmysql backend now uses ``mariadb-connector-c`` instead of ``mysql-libs``.
+This change was made because the default MySQL implementation for RHEL8 is MariaDB, and MariaDB and MySQL cannot be installed in parallel due to conflicting RPM packages.
+The mariadb client lib will connect to your existing MySQL servers without trouble.
+
+Unknown record encoding (`RFC 3597 <https://tools.ietf.org/html/rfc3597>`__) has become more strict as a result of the fixes for :doc:`PowerDNS Security Advisory 2020-05 <../security-advisories/powerdns-advisory-2020-05>`. Please use ``pdnsutil check-all-zones`` to review your zone contents.
+
4.2.x to 4.3.0
--------------
Schema changes
^^^^^^^^^^^^^^
- The new 'unpublished DNSSEC keys' feature comes with a mandatory schema change for all database backends (including BIND with a DNSSEC database). Please find files named "4.2.0_to_4.3.0_schema.X.sql" for your database backend in our Git repo, tarball, or distro-specific documentation path. For the LMDB backend, please review :ref:`setting-lmdb-schema-version`.
+- If you are upgrading from beta2 or rc2, AND ONLY THEN, please read `pull request #8975 <https://github.com/PowerDNS/pdns/pull/8975>`__ very carefully.
Implicit 5->7 algorithm upgrades
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since version 3.0 (the first version of the PowerDNS Authoritative Server that supported DNSSEC signing), we have automatically, silently, upgraded algorithm 5 (RSASHA1) keys to algorithm 7 (RSASHA1-NSEC3-SHA1) when the user enabled NSEC3. This has been a source of confusion, and because of that, we introduced warnings for users of this feature in 4.0 and 4.1. To see if you are affected, run ``pdnsutil check-all-zones`` from version 4.0 or up. In this release, the automatic upgrade is gone, and affected zones will break if no action is taken.
+.. _ixfr-in-corruption-4.3.0:
+
+IXFR-in corruption
+^^^^^^^^^^^^^^^^^^
+
+A bug in PowerDNS versions before 4.2.2/4.3.0 would cause wrong deletion or addition of records if IXFR deltas came in very quickly (within the query cache timeout, which defaults to 20/60 seconds).
+If you have zones which use inbound IXFR (in other words, the ``IXFR`` metadata item for that zone is set to ``1``), we strongly suggest triggering a completely fresh transfer.
+You could accomplish that by deleting all records in the zone with an SQL query and waiting for a fresh transfer, or (1) disabling IXFR (2) forcing a fresh transfer using ``pdns_control retrieve example.com`` (3) enabling IXFR again.
+
+4.2.X to 4.2.3
+--------------
+
+Unknown record encoding (`RFC 3597 <https://tools.ietf.org/html/rfc3597>`__) has become more strict as a result of the fixes for :doc:`PowerDNS Security Advisory 2020-05 <../security-advisories/powerdns-advisory-2020-05>`. Please use ``pdnsutil check-all-zones`` to review your zone contents.
+
+4.X.X to 4.2.2
+--------------
+
+.. _ixfr-in-corruption-4.2.2:
+
+IXFR-in corruption
+^^^^^^^^^^^^^^^^^^
+
+A bug in PowerDNS versions before 4.2.2/4.3.0 would cause wrong deletion or addition of records if IXFR deltas came in very quickly (within the query cache timeout, which defaults to 20/60 seconds).
+If you have zones which use inbound IXFR (in other words, the ``IXFR`` metadata item for that zone is set to ``1``), we strongly suggest triggering a completely fresh transfer.
+You could accomplish that by deleting all records in the zone with an SQL query and waiting for a fresh transfer, or (1) disabling IXFR (2) forcing a fresh transfer using ``pdns_control retrieve example.com`` (3) enabling IXFR again.
+
+
4.1.X to 4.2.0
--------------
- Autoserial support has been removed. The ``change_date`` column has been removed from the ``records`` table in all gsql backends, but leaving it in is harmless.
- The :doc:`Generic PostgreSQL backend <backends/generic-postgresql>` schema has changed: the ``notified_serial`` column type in the ``domains`` table has been changed from ``INT DEFAULT NULL`` to ``BIGINT DEFAULT NULL``: ``ALTER TABLE domains ALTER notified_serial TYPE bigint USING CASE WHEN notified_serial >= 0 THEN notified_serial::bigint END;``
+4.1.X to 4.1.14
+---------------
+
+Unknown record encoding (`RFC 3597 <https://tools.ietf.org/html/rfc3597>`__) has become more strict as a result of the fixes for :doc:`PowerDNS Security Advisory 2020-05 <../security-advisories/powerdns-advisory-2020-05>`. Please use ``pdnsutil check-all-zones`` to review your zone contents.
+
4.1.0 to 4.1.1
--------------
#include <string.h>
#include <map>
-using namespace std;
+using std::string;
+using std::runtime_error;
+using std::tuple;
+using std::weak_ptr;
static string MDBError(int rc)
{
MDBROTransaction MDBRWTransactionImpl::getROTransaction()
{
- return std::move(getRWTransaction());
+ return getRWTransaction();
}
MDBROTransaction MDBEnv::getROTransaction()
#include <vector>
#include <algorithm>
-// apple compiler somehow has string_view even in c++11!
-#if __cplusplus < 201703L && !defined(__APPLE__)
+#ifdef __cpp_lib_string_view
+using std::string_view;
+#else
#include <boost/version.hpp>
#if BOOST_VERSION >= 106100
#include <boost/utility/string_view.hpp>
using boost::string_view;
-#else
+#elif BOOST_VERSION >= 105300
#include <boost/utility/string_ref.hpp>
using string_view = boost::string_ref;
+#else
+using string_view = std::string;
#endif
-#else // C++17
-using std::string_view;
#endif
const auto traceBack = readTopAndPop<std::string>(state, std::move(traceBackRef)); // stack top: error
PushedObject errorCode{state, 1};
- // an error occured during execution, either an error message or a std::exception_ptr was pushed on the stack
+ // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack
if (pcallReturnValue == LUA_ERRMEM) {
throw std::bad_alloc{};
namespace YaHTTP {
template class AsyncLoader<Request>;
+ template class AsyncLoader<Response>;
bool isspace(char c) {
return std::isspace(c) != 0;
- the auth, dnsdist and rec packet caches (fuzz_target_packetcache and
fuzz_target_dnsdistcache) ;
- MOADNSParser (fuzz_target_moadnsparser) ;
+- the Proxy Protocol parser (fuzz_target_proxyprotocol) ;
- ZoneParserTNG (fuzz_target_zoneparsertng).
By default the targets are linked against a standalone target,
This directory contains a few files used for continuous fuzzing
of the PowerDNS products.
-The 'corpus' directory contains two sub-directories:
+The 'corpus' directory contains three sub-directories:
+- proxy-protocol-raw-packets/ contains DNS queries prefixed with a Proxy
+ Protocol v2 header, used by fuzz_target_proxyprotocol ;
- raw-dns-packets/ contains DNS queries and responses as captured on
the wire. These are used by the fuzz_target_dnsdistcache,
fuzz_target_moadnsparser and fuzz_target_packetcache targets ;
AC_DEFUN([PDNS_CHECK_DNSTAP], [
+ AC_REQUIRE([PDNS_WITH_PROTOBUF])
AC_MSG_CHECKING([whether we will have dnstap])
AC_ARG_ENABLE([dnstap],
AS_HELP_STRING([--enable-dnstap],[enable dnstap support @<:@default=$1@:>@]),
AS_IF([test x"$FSTRM_LIBS" = "x"], [
AC_MSG_ERROR([dnstap requested but libfstrm was not found])
])
+ AS_IF([test "x$PROTOBUF_LIBS" = "x" -o x"$PROTOC" = "x"], [
+ AC_MSG_ERROR([dnstap requested but protobuf was not found])
+ ])
])
])
+++ /dev/null
-AC_DEFUN([PDNS_ENABLE_GSS_TSIG],[
- AC_MSG_CHECKING([whether to enable experimental GSS-TSIG support])
- AC_ARG_ENABLE([experimental_gss_tsig],
- AS_HELP_STRING([--enable-experimental-gss-tsig],
- [enable experimental GSS-TSIG support @<:@default=no@:>@]
- ),
- [enable_experimental_gss_tsig=$enableval],
- [enable_experimental_gss_tsig=no]
- )
-
- AC_MSG_RESULT([$enable_experimental_gss_tsig])
-
- AM_CONDITIONAL([GSS_TSIG],[test "x$enable_experimental_gss_tsig" != "xno"])
- AC_SUBST(GSS_TSIG)
- AS_IF([test "x$enable_experimental_gss_tsig" != "xno"],
- [PKG_CHECK_MODULES([GSS], [krb5 krb5-gssapi gss],
- [
- AC_DEFINE([ENABLE_GSS_TSIG], [1], [Define to 1 if you want to enable GSS-TSIG support])
- GSS_TSIG=yes
- ],
- [AC_MSG_ERROR([Required libraries for GSS-TSIG not found])]
- )],
- [GSS_TSIG=no])
-])
# @aio, @sync, @chown, @setuid, @memlock, @signal and @timer in 235
systemd_system_call_filter=y
fi
+ if test $_systemd_version -ge 239; then
+ systemd_private_mounts=y
+ fi
+ if test $_systemd_version -ge 242; then
+ systemd_protect_hostname=y
+ systemd_restrict_suidsgid=y
+ fi
+ if test $_systemd_version -ge 244; then
+ systemd_protect_kernel_logs=y
+ fi
+ if test $_systemd_version -ge 245; then
+ systemd_protect_clock=y
+ fi
])
])
AM_CONDITIONAL([HAVE_SYSTEMD_DYNAMIC_USER], [ test x"$systemd_dynamic_user" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_LOCK_PERSONALITY], [ test x"$systemd_lock_personality" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_MEMORY_DENY_WRITE_EXECUTE], [ test x"$systemd_memory_deny_write_execute" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PRIVATE_DEVICES], [ test x"$systemd_private_devices" = "xy" ])
+ AM_CONDITIONAL([HAVE_SYSTEMD_PRIVATE_MOUNTS], [ test x"$systemd_private_mounts" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PRIVATE_TMP], [ test x"$systemd_private_tmp" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PRIVATE_USERS], [ test x"$systemd_private_users" = "xy" ])
+ AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_CLOCK], [ test x"$systemd_protect_clock" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_CONTROL_GROUPS], [ test x"$systemd_protect_control_groups" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_HOME], [ test x"$systemd_protect_home" = "xy" ])
+ AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_HOSTNAME], [ test x"$systemd_protect_hostname" = "xy" ])
+ AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_KERNEL_LOGS], [ test x"$systemd_protect_kernel_logs" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_KERNEL_MODULES], [ test x"$systemd_protect_kernel_modules" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_KERNEL_TUNABLES], [ test x"$systemd_protect_kernel_tunables" = "xy" ])
AM_CONDITIONAL([HAVE_SYSTEMD_PROTECT_SYSTEM], [ test x"$systemd_protect_system" = "xy" ])
../../pdns/bindlexer.l \
../../pdns/bindparser.yy
+dist_doc_DATA = \
+ ../../pdns/bind-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql \
+ ../../pdns/bind-dnssec.schema.sqlite3.sql
+
EXTRA_DIST = OBJECTFILES OBJECTLIBS
libbindbackend_la_SOURCES = \
int Bind2Backend::s_first=1;
bool Bind2Backend::s_ignore_broken_records=false;
-pthread_rwlock_t Bind2Backend::s_state_lock=PTHREAD_RWLOCK_INITIALIZER;
-pthread_mutex_t Bind2Backend::s_supermaster_config_lock=PTHREAD_MUTEX_INITIALIZER; // protects writes to config file
-pthread_mutex_t Bind2Backend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
+ReadWriteLock Bind2Backend::s_state_lock;
+std::mutex Bind2Backend::s_supermaster_config_lock; // protects writes to config file
+std::mutex Bind2Backend::s_startup_lock;
string Bind2Backend::s_binddirectory;
template <typename T>
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
(*ips).insert(str);
}
}
- ReadLock rl(&s_state_lock);
+ ReadLock rl(&s_state_lock);
for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
if(i->d_name == domain) {
for(set<string>::iterator it = i->d_also_notify.begin(); it != i->d_also_notify.end(); it++) {
ostringstream ret;
if (parts.size() > 1) {
- for (const auto& part : parts) {
+ for (vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
BB2DomainInfo bbd;
- if (safeGetBBDomainInfo(DNSName(part), &bbd)) {
+ if (safeGetBBDomainInfo(DNSName(*i), &bbd)) {
printDomainExtendedStatus(ret, bbd);
}
else {
- ret << part << " no such domain" << std::endl;
+ ret << *i << " no such domain" << std::endl;
}
}
}
if (!loadZones && d_hybrid)
return;
- Lock l(&s_startup_lock);
+ std::lock_guard<std::mutex> l(s_startup_lock);
setupDNSSEC();
if(!s_first) {
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;
}
<< "' from supermaster " << ip << endl;
{
- Lock l2(&s_supermaster_config_lock);
+ std::lock_guard<std::mutex> l2(s_supermaster_config_lock);
ofstream c_of(getArg("supermaster-config").c_str(), std::ios::app);
if (!c_of) {
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);
void getAllDomains(vector<DomainInfo> *domains, bool include_disabled=false) override;
static DNSBackend *maker();
- static pthread_mutex_t s_startup_lock;
+ static std::mutex s_startup_lock;
void setFresh(uint32_t domain_id) override;
void setNotified(uint32_t id, uint32_t serial) override;
ordered_unique<tag<NameTag>, member<BB2DomainInfo, DNSName, &BB2DomainInfo::d_name> >
> > state_t;
static state_t s_state;
- static pthread_rwlock_t s_state_lock;
+ static ReadWriteLock s_state_lock;
void parseZoneFile(BB2DomainInfo *bbd);
void rediscover(string *status=nullptr) override;
// for supermaster support
bool superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db) override;
- static pthread_mutex_t s_supermaster_config_lock;
+ static std::mutex s_supermaster_config_lock;
bool createSlaveDomain(const string &ip, const DNSName &domain, const string &nameserver, const string &account) override;
private:
#include <fstream>
#include <yaml-cpp/yaml.h>
-pthread_rwlock_t GeoIPBackend::s_state_lock=PTHREAD_RWLOCK_INITIALIZER;
+ReadWriteLock GeoIPBackend::s_state_lock;
struct GeoIPDNSResourceRecord: DNSResourceRecord {
int weight;
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) {
return "";
}
-bool queryGeoLocation(const Netmask& addr, GeoIPNetmask& gl, double& lat, double& lon,
+static bool queryGeoLocation(const Netmask& addr, GeoIPNetmask& gl, double& lat, double& lon,
boost::optional<int>& alt, boost::optional<int>& prec)
{
for(auto const& gi: s_geoip_files) {
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);
}
};
bool unpublishDomainKey(const DNSName& name, unsigned int id) override;
private:
- static pthread_rwlock_t s_state_lock;
+ static ReadWriteLock s_state_lock;
void initialize();
string format2str(string format, const Netmask &addr, GeoIPNetmask& gl);
-ALTER TABLE cryptokeys ADD COLUMN published BOOL DEFAULT 1;
-
+ALTER TABLE cryptokeys ADD published BOOL NULL DEFAULT 1 AFTER active;
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);
}
static thread_local MySQLThreadCloser threadcloser;
bool SMySQL::s_dolog;
-pthread_mutex_t SMySQL::s_myinitlock = PTHREAD_MUTEX_INITIALIZER;
+std::mutex SMySQL::s_myinitlock;
class SMySQLStatement: public SSqlStatement
{
}
SSqlStatement* execute() {
- int err;
-
prepareStatement();
if (!d_stmt) return this;
d_dtime.set();
}
- if ((err = mysql_stmt_bind_param(d_stmt, d_req_bind))) {
+ if (mysql_stmt_bind_param(d_stmt, d_req_bind) != 0) {
string error(mysql_stmt_error(d_stmt));
releaseStatement();
throw SSqlException("Could not bind mysql statement: " + d_query + string(": ") + error);
}
- if ((err = mysql_stmt_execute(d_stmt))) {
+ if (mysql_stmt_execute(d_stmt) != 0) {
string error(mysql_stmt_error(d_stmt));
releaseStatement();
throw SSqlException("Could not execute mysql statement: " + d_query + string(": ") + error);
}
// MySQL documentation says you can call this safely for all queries
- if ((err = mysql_stmt_store_result(d_stmt))) {
+ if (mysql_stmt_store_result(d_stmt) != 0) {
string error(mysql_stmt_error(d_stmt));
releaseStatement();
throw SSqlException("Could not store mysql statement: " + d_query + string(": ") + error);
stmt->bind_result_done to false, causing the second to reset the existing binding),
and we can't bind it right after the call to mysql_stmt_store_result() if it returned
no rows, because then the statement 'contains no metadata' */
- if (d_res_bind != nullptr && (err = mysql_stmt_bind_result(d_stmt, d_res_bind))) {
+ if (d_res_bind != nullptr && mysql_stmt_bind_result(d_stmt, d_res_bind) != 0) {
string error(mysql_stmt_error(d_stmt));
releaseStatement();
throw SSqlException("Could not bind parameters to mysql statement: " + d_query + string(": ") + error);
if (d_residx >= d_resnum) {
mysql_stmt_free_result(d_stmt);
while(!mysql_stmt_next_result(d_stmt)) {
- if ((err = mysql_stmt_store_result(d_stmt))) {
+ if (mysql_stmt_store_result(d_stmt) != 0) {
string error(mysql_stmt_error(d_stmt));
releaseStatement();
throw SSqlException("Could not store mysql statement while processing additional sets: " + d_query + string(": ") + error);
d_resnum = mysql_stmt_num_rows(d_stmt);
// XXX: For some reason mysql_stmt_result_metadata returns NULL here, so we cannot
// ensure row field count matches first result set.
- if (d_resnum > 0) { // ignore empty result set
- if (d_res_bind != nullptr && (err = mysql_stmt_bind_result(d_stmt, d_res_bind))) {
+ // We need to check the field count as stored procedure return the final values of OUT and INOUT parameters
+ // as an extra single-row result set following any result sets produced by the procedure itself.
+ // mysql_stmt_field_count() will return 0 for those.
+ if (mysql_stmt_field_count(d_stmt) > 0 && d_resnum > 0) { // ignore empty result set
+ if (d_res_bind != nullptr && mysql_stmt_bind_result(d_stmt, d_res_bind) != 0) {
string error(mysql_stmt_error(d_stmt));
releaseStatement();
throw SSqlException("Could not bind parameters to mysql statement: " + d_query + string(": ") + error);
private:
void prepareStatement() {
- int err;
-
if (d_prepared) return;
if (d_query.empty()) {
d_prepared = true;
if ((d_stmt = mysql_stmt_init(d_db))==NULL)
throw SSqlException("Could not initialize mysql statement, out of memory: " + d_query);
- if ((err = mysql_stmt_prepare(d_stmt, d_query.c_str(), d_query.size()))) {
+ if (mysql_stmt_prepare(d_stmt, d_query.c_str(), d_query.size()) != 0) {
string error(mysql_stmt_error(d_stmt));
releaseStatement();
throw SSqlException("Could not prepare statement: " + d_query + string(": ") + error);
{
int retry=1;
- Lock l(&s_myinitlock);
+ std::lock_guard<std::mutex> l(s_myinitlock);
if (d_threadCleanup) {
threadcloser.enable();
}
SSqlException SMySQL::sPerrorException(const string &reason)
{
- return SSqlException(reason+string(": ")+mysql_error(&d_db));
+ return SSqlException(reason+string(": ERROR ")+std::to_string(mysql_errno(&d_db))+" ("+string(mysql_sqlstate(&d_db))+"): "+mysql_error(&d_db));
}
std::unique_ptr<SSqlStatement> SMySQL::prepare(const string& query, int nparams)
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
+#include <mutex>
+
#include <mysql.h>
#include "pdns/backends/gsql/ssql.hh"
#include "pdns/utility.hh"
void connect();
static bool s_dolog;
- static pthread_mutex_t s_myinitlock;
+ static std::mutex s_myinitlock;
MYSQL d_db;
std::string d_database;
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 );
}
-ALTER TABLE cryptokeys ADD COLUMN published BOOL DEFAULT true;
+BEGIN;
+ ALTER TABLE cryptokeys ADD COLUMN published BOOL DEFAULT TRUE;
+
+ ALTER TABLE cryptokeys ADD COLUMN content_new TEXT;
+ UPDATE cryptokeys SET content_new = content;
+ ALTER TABLE cryptokeys DROP COLUMN content;
+ ALTER TABLE cryptokeys RENAME COLUMN content_new TO content;
+COMMIT;
{
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);
}
domain_id INT REFERENCES domains(id) ON DELETE CASCADE,
flags INT NOT NULL,
active BOOL,
- published BOOL DEFAULT true,
+ published BOOL DEFAULT TRUE,
content TEXT
);
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;
};
-ALTER TABLE cryptokeys ADD published BOOL DEFAULT 1;
+BEGIN TRANSACTION;
+ CREATE TABLE cryptokeys_temp (
+ id INTEGER PRIMARY KEY,
+ domain_id INT NOT NULL,
+ flags INT NOT NULL,
+ active BOOL,
+ published BOOL DEFAULT 1,
+ content TEXT,
+ FOREIGN KEY(domain_id) REFERENCES domains(id) ON DELETE CASCADE ON UPDATE CASCADE
+ );
+
+ INSERT INTO cryptokeys_temp SELECT id,domain_id,flags,active,1,content FROM cryptokeys;
+ DROP TABLE cryptokeys;
+ ALTER TABLE cryptokeys_temp RENAME TO cryptokeys;
+
+ CREATE INDEX domainidindex ON cryptokeys(domain_id);
+COMMIT;
--- /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);
}
LdapGssapiAuthenticator::LdapGssapiAuthenticator( const std::string& kt, const std::string &ccache, int tmout )
- : d_logPrefix( "[LDAP GSSAPI] " ), d_keytabFile( kt ), d_cCacheFile( ccache ), d_timeout( tmout )
+ : d_logPrefix( "[LDAP GSSAPI] " ), d_keytabFile( kt ), d_cCacheFile( ccache )
{
krb5_error_code code;
return false;
}
else if ( code == -2 ) {
- // Here it may be possible to retry after obtainting a fresh ticket
+ // Here it may be possible to retry after obtaining a fresh ticket
g_log<<Logger::Debug << d_logPrefix << "No TGT found, trying to acquire a new one" << std::endl;
code = updateTgt();
std::string d_logPrefix;
std::string d_keytabFile;
std::string d_cCacheFile;
- int d_timeout;
std::string d_lastError;
krb5_context d_context;
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 );
}
}
-std::string serializeContent(uint16_t qtype, const DNSName& domain, const std::string& content)
+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);
}
-std::shared_ptr<DNSRecordContent> unserializeContentZR(uint16_t qtype, const DNSName& qname, const std::string& content)
+static std::shared_ptr<DNSRecordContent> deserializeContentZR(uint16_t qtype, const DNSName& qname, const std::string& content)
{
if(qtype == QType::A && content.size() == 4) {
return std::make_shared<ARecordContent>(*((uint32_t*)content.c_str()));
}
- return DNSRecordContent::unserialize(qname, qtype, content);
+ return DNSRecordContent::deserialize(qname, qtype, content);
}
}
DNSName relqname = qdomain.makeRelative(hunt);
+
+ if(relqname.empty()) {
+ throw DBException("lookup for out of zone rrset");
+ }
+
// cout<<"get will look for "<<relqname<< " in zone "<<hunt<<" with id "<<zoneId<<endl;
d_rotxn = getRecordsROTransaction(zoneId, d_rwtxn);
rr.dr.d_name = compoundOrdername::getQName(key) + d_lookupdomain;
rr.domain_id = compoundOrdername::getDomainID(key);
rr.dr.d_ttl = drr.ttl;
- rr.dr.d_content = unserializeContentZR(rr.dr.d_type, rr.dr.d_name, drr.content);
+ rr.dr.d_content = deserializeContentZR(rr.dr.d_type, rr.dr.d_name, drr.content);
rr.auth = drr.auth;
if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) {
}
-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);
}
GEM
remote: https://rubygems.org/
specs:
- ffi (1.9.24)
- ffi-rzmq (2.0.1)
- ffi-rzmq-core (>= 1.0.1)
- ffi-rzmq-core (1.0.3)
- ffi (~> 1.9)
- json (1.8.5)
- sqlite3 (1.3.9)
+ ffi (1.10.0)
+ ffi-rzmq (2.0.7)
+ ffi-rzmq-core (>= 1.0.7)
+ ffi-rzmq-core (1.0.7)
+ ffi
+ json (2.3.0)
+ sqlite3 (1.3.13)
webrick (1.4.2)
zeromqrb (0.1.3)
ffi-rzmq
zeromqrb
BUNDLED WITH
- 1.10.2
+ 1.16.1
../../pdns/nameserver.cc \
../../pdns/rcpgenerator.cc \
../../pdns/unix_utility.cc \
- ../../pdns/gss_context.cc ../../pdns/gss_context.hh \
../../pdns/json.hh ../../pdns/json.cc \
+ ../../pdns/shuffle.hh ../../pdns/shuffle.cc \
httpconnector.cc \
pipeconnector.cc \
unixconnector.cc \
$(P11KIT1_CFLAGS)
endif
-if GSS_TSIG
-libtestremotebackend_la_LIBADD += \
- $(GSS_LIBS)
-libtestremotebackend_la_CPPFLAGS+= \
- $(GSS_CFLAGS)
-endif
-
remotebackend_http_test_SOURCES = \
test-remotebackend.cc \
test-remotebackend-http.cc \
if (d_socket == nullptr ) return -1; // cannot receive :(
char buffer[4096];
int rd = -1;
- bool fail = false;
time_t t0;
arl.initialize(&resp);
throw NetworkError(std::string(strerror(rd)));
arl.feed(std::string(buffer, rd));
}
- // timeout occured.
+ // timeout occurred.
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;
+++ /dev/null
-GEM
- remote: https://rubygems.org/
- specs:
- ffi (1.9.24)
- ffi-rzmq (2.0.1)
- ffi-rzmq-core (>= 1.0.1)
- ffi-rzmq-core (1.0.3)
- ffi (~> 1.9)
- json (1.8.2)
- sqlite3 (1.3.9)
- webrick (1.4.2)
- zeromqrb (0.1.3)
- ffi-rzmq
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- json
- sqlite3
- webrick
- zeromqrb
--- /dev/null
+../Gemfile.lock
\ No newline at end of file
* 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());
static string backendname="[TinyDNSBackend] ";
uint32_t TinyDNSBackend::s_lastId;
-pthread_mutex_t TinyDNSBackend::s_domainInfoLock=PTHREAD_MUTEX_INITIALIZER;
+std::mutex TinyDNSBackend::s_domainInfoLock;
TinyDNSBackend::TDI_suffix_t TinyDNSBackend::s_domainInfo;
vector<string> TinyDNSBackend::getLocations()
}
void TinyDNSBackend::getUpdatedMasters(vector<DomainInfo>* retDomains) {
- Lock l(&s_domainInfoLock); //TODO: We could actually lock less if we do it per suffix.
+ std::lock_guard<std::mutex> l(s_domainInfoLock); //TODO: We could actually lock less if we do it per suffix.
if (! s_domainInfo.count(d_suffix)) {
TDI_t tmp;
}
void TinyDNSBackend::setNotified(uint32_t id, uint32_t serial) {
- Lock l(&s_domainInfoLock);
+ std::lock_guard<std::mutex> l(s_domainInfoLock);
if (!s_domainInfo.count(d_suffix)) {
throw PDNSException("Can't get list of domains to set the serial.");
}
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);
}
};
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
+#include <mutex>
using namespace ::boost;
using namespace ::boost::multi_index;
string d_suffix;
// Statics
- static pthread_mutex_t s_domainInfoLock;
+ static std::mutex s_domainInfoLock;
static TDI_suffix_t s_domainInfo;
static uint32_t s_lastId; // used to give a domain an id.
};
/fuzz_target_dnsdistcache
/fuzz_target_moadnsparser
/fuzz_target_packetcache
+/fuzz_target_proxyprotocol
/fuzz_target_zoneparsertng
AM_CPPFLAGS +=$(LUA_CFLAGS)
endif
-if GSS_TSIG
-AM_CPPFLAGS +=$(GSS_CFLAGS)
-endif
-
if LIBSODIUM
AM_CPPFLAGS +=$(LIBSODIUM_CFLAGS)
endif
effective_tld_names.dat \
mtasker.cc \
inflighter.cc \
- bind-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql \
- bind-dnssec.schema.sqlite3.sql \
bindparser.h \
named.conf.parsertest \
pdns.service.in \
dynmessenger.hh \
ednsoptions.cc ednsoptions.hh \
ednssubnet.cc ednssubnet.hh \
- gss_context.cc gss_context.hh \
iputils.cc iputils.hh \
ixfr.cc ixfr.hh \
json.cc json.hh \
packethandler.cc packethandler.hh \
pdnsexception.hh \
qtype.cc qtype.hh \
+ query-local-address.hh query-local-address.cc \
rcpgenerator.cc \
receiver.cc \
resolver.cc resolver.hh \
+ axfr-retriever.cc axfr-retriever.hh \
responsestats.cc responsestats.hh responsestats-auth.cc \
rfc2136handler.cc \
secpoll.cc secpoll.hh \
secpoll-auth.cc secpoll-auth.hh \
serialtweaker.cc \
sha.hh \
+ shuffle.cc shuffle.hh \
signingpipe.cc signingpipe.hh \
sillyrecords.cc \
slavecommunicator.cc \
unix_utility.cc \
utility.hh \
version.cc version.hh \
+ views.hh \
webserver.cc webserver.hh \
ws-api.cc ws-api.hh \
ws-auth.cc ws-auth.hh \
pdns_server_LDADD += $(LUA_LIBS)
endif
-if GSS_TSIG
-pdns_server_LDADD += $(GSS_LIBS)
-endif
-
pdnsutil_SOURCES = \
arguments.cc \
auth-caches.cc auth-caches.hh \
dynlistener.cc \
ednsoptions.cc ednsoptions.hh \
ednssubnet.cc \
- gss_context.cc gss_context.hh \
ipcipher.cc ipcipher.hh \
iputils.cc iputils.hh \
json.cc \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
serialtweaker.cc \
+ shuffle.cc shuffle.hh \
signingpipe.cc \
sillyrecords.cc \
sstuff.hh \
pdnsutil_LDADD += $(LUA_LIBS)
endif
-if GSS_TSIG
-pdnsutil_LDADD += $(GSS_LIBS)
-endif
-
zone2sql_SOURCES = \
arguments.cc \
base32.cc \
logger.cc \
misc.cc misc.hh \
nsecrecords.cc \
+ proxy-protocol.cc proxy-protocol.hh \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
sdig.cc \
dnslabeltext.cc \
dnsname.cc dnsname.hh \
dumresp.cc \
+ iputils.cc iputils.hh \
logger.cc \
misc.cc misc.hh \
statbag.cc \
dnsrecords.cc \
dnssecinfra.cc \
dnswriter.cc dnswriter.hh \
- gss_context.cc gss_context.hh \
iputils.cc \
logger.cc \
misc.cc misc.hh \
saxfr_LDADD += $(P11KIT1_LIBS)
endif
-if GSS_TSIG
-saxfr_LDADD += $(GSS_LIBS)
-endif
-
ixfrdist_SOURCES = \
arguments.cc \
base32.cc \
dnsrecords.cc \
dnssecinfra.cc \
dnswriter.cc dnswriter.hh \
- gss_context.cc gss_context.hh \
iputils.hh iputils.cc \
ixfr.cc ixfr.hh \
ixfrdist.cc \
misc.cc misc.hh \
mplexer.hh \
nsecrecords.cc \
+ query-local-address.hh query-local-address.cc \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
resolver.cc \
+ axfr-retriever.cc \
pollmplexer.cc \
sillyrecords.cc \
sstuff.hh \
ixfrdist_LDADD += $(P11KIT1_LIBS)
endif
-if GSS_TSIG
-ixfrdist_LDADD += $(GSS_LIBS)
-endif
-
-
ixplore_SOURCES = \
arguments.cc \
base32.cc \
dnsrecords.cc \
dnssecinfra.cc \
dnswriter.cc dnswriter.hh \
- gss_context.cc gss_context.hh \
iputils.cc \
logger.cc \
misc.cc misc.hh \
nsecrecords.cc \
+ query-local-address.hh query-local-address.cc \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
resolver.cc \
+ axfr-retriever.cc \
ixfr.cc ixfr.hh \
ixfrutils.cc ixfrutils.hh \
ixplore.cc \
ixplore_LDADD += $(P11KIT1_LIBS)
endif
-if GSS_TSIG
-ixplore_LDADD += $(GSS_LIBS)
-endif
-
-
dnstcpbench_SOURCES = \
base32.cc \
base64.cc base64.hh \
dnsrecords.cc \
dnssecinfra.cc \
dnswriter.cc dnswriter.hh \
- gss_context.cc gss_context.hh \
iputils.cc \
logger.cc \
misc.cc misc.hh \
nsec3dig_LDADD += $(P11KIT1_LIBS)
endif
-if GSS_TSIG
-nsec3dig_LDADD += $(GSS_LIBS)
-endif
-
toysdig_SOURCES = \
base32.cc \
base64.cc base64.hh \
dnswriter.cc dnswriter.hh \
ednssubnet.cc ednssubnet.hh \
filterpo.hh \
- gss_context.cc gss_context.hh \
iputils.cc \
logger.cc \
misc.cc misc.hh \
$(LIBCRYPTO_LDFLAGS)
toysdig_LDADD = $(LIBCRYPTO_LIBS)
-if GSS_TSIG
-toysdig_LDADD += $(GSS_LIBS)
-endif
-
if PKCS11
toysdig_SOURCES += pkcs11signers.cc pkcs11signers.hh
toysdig_LDADD += $(P11KIT1_LIBS)
endif
tsig_tests_SOURCES = \
+ axfr-retriever.cc \
arguments.cc \
base32.cc \
base64.cc base64.hh \
dnsrecords.cc \
dnssecinfra.cc \
dnswriter.cc dnswriter.hh \
- gss_context.cc gss_context.hh \
+ iputils.cc \
logger.cc \
misc.cc misc.hh \
nsecrecords.cc \
+ query-local-address.cc \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
resolver.cc \
tsig_tests_LDADD += $(P11KIT1_LIBS)
endif
-if GSS_TSIG
-tsig_tests_LDADD += $(GSS_LIBS)
-endif
-
speedtest_SOURCES = \
base32.cc \
base64.cc base64.hh \
rcpgenerator.cc \
sillyrecords.cc \
statbag.cc \
- unix_utility.cc
+ unix_utility.cc \
+ arguments.cc arguments.hh \
+ dns_random.cc dns_random.hh
dnsbulktest_LDFLAGS = \
$(AM_LDFLAGS) \
if LIBSODIUM
pdns_notify_LDADD += $(LIBSODIUM_LIBS)
+dnsbulktest_LDADD += $(LIBSODIUM_LIBS)
endif
dnsscope_SOURCES = \
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 \
ednscookies.cc ednscookies.hh \
ednssubnet.cc \
gettime.cc gettime.hh \
- gss_context.cc gss_context.hh \
ipcipher.cc ipcipher.hh \
iputils.cc \
ixfr.cc ixfr.hh \
nsecrecords.cc \
opensslsigners.cc opensslsigners.hh \
pollmplexer.cc \
+ proxy-protocol.cc proxy-protocol.hh \
qtype.cc \
rcpgenerator.cc \
responsestats.cc \
responsestats-auth.cc \
+ shuffle.cc shuffle.hh \
sillyrecords.cc \
statbag.cc \
test-arguments_cc.cc \
test-nameserver_cc.cc \
test-packetcache_cc.cc \
test-packetcache_hh.cc \
+ test-proxy_protocol_cc.cc \
test-rcpgenerator_cc.cc \
test-signers.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
fuzz_target_dnsdistcache \
fuzz_target_moadnsparser \
fuzz_target_packetcache \
+ fuzz_target_proxyprotocol \
fuzz_target_zoneparsertng
fuzz_targets: $(fuzz_targets_programs)
fuzz_target_packetcache_LDFLAGS = $(fuzz_targets_ldflags)
fuzz_target_packetcache_LDADD = $(fuzz_targets_libs)
+fuzz_target_proxyprotocol_SOURCES = \
+ fuzz_proxyprotocol.cc \
+ iputils.hh \
+ proxy-protocol.cc \
+ proxy-protocol.hh
+
+fuzz_target_proxyprotocol_DEPENDENCIES = $(fuzz_targets_deps)
+fuzz_target_proxyprotocol_LDFLAGS = $(fuzz_targets_ldflags)
+fuzz_target_proxyprotocol_LDADD = $(fuzz_targets_libs)
+
fuzz_target_dnsdistcache_SOURCES = \
fuzz_dnsdistcache.cc \
dnsdist-cache.cc dnsdist-cache.hh \
if !HAVE_SYSTEMD_PRIVATE_TMP
$(AM_V_GEN)perl -ni -e 'print unless /^PrivateTmp/' $@
endif
+if !HAVE_SYSTEMD_PRIVATE_USERS
+ $(AM_V_GEN)perl -ni -e 'print unless /^PrivateUsers/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_CLOCK
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectClock/' $@
+endif
if !HAVE_SYSTEMD_PROTECT_CONTROL_GROUPS
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectControlGroups/' $@
endif
if !HAVE_SYSTEMD_PROTECT_HOME
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectHome/' $@
endif
+if !HAVE_SYSTEMD_PROTECT_HOSTNAME
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectHostname/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_KERNEL_LOGS
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelLogs/' $@
+endif
if !HAVE_SYSTEMD_PROTECT_KERNEL_MODULES
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelModules/' $@
endif
if !HAVE_SYSTEMD_RESTRICT_REALTIME
$(AM_V_GEN)perl -ni -e 'print unless /^RestrictRealtime/' $@
endif
+if !HAVE_SYSTEMD_RESTRICT_SUIDSGID
+ $(AM_V_GEN)perl -ni -e 'print unless /^RestrictSUIDSGID/' $@
+endif
if !HAVE_SYSTEMD_SYSTEM_CALL_ARCHITECTURES
$(AM_V_GEN)perl -ni -e 'print unless /^SystemCallArchitectures/' $@
endif
pdns@.service: pdns.service
$(AM_V_GEN)sed -e 's!/pdns_server!& --config-name=%i!' \
-e 's!Authoritative Server!& %i!' \
+ -e 's!RuntimeDirectory=.*!&-%i!' \
+ -e 's!SyslogIdentifier=.*!&-%i!' \
< $< > $@
systemdsystemunitdir = $(SYSTEMD_DIR)
if IXFRDIST
ixfrdist.service: ixfrdist.service.in
$(AM_V_GEN)sed -e 's![@]bindir[@]!$(bindir)!' < $< > $@
+if !HAVE_SYSTEMD_LOCK_PERSONALITY
+ $(AM_V_GEN)perl -ni -e 'print unless /^LockPersonality/' $@
+endif
+if !HAVE_SYSTEMD_PRIVATE_DEVICES
+ $(AM_V_GEN)perl -ni -e 'print unless /^PrivateDevices/' $@
+endif
+if !HAVE_SYSTEMD_PRIVATE_TMP
+ $(AM_V_GEN)perl -ni -e 'print unless /^PrivateTmp/' $@
+endif
+if !HAVE_SYSTEMD_PRIVATE_USERS
+ $(AM_V_GEN)perl -ni -e 'print unless /^PrivateUsers/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_CLOCK
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectClock/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_CONTROL_GROUPS
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectControlGroups/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_HOME
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectHome/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_HOSTNAME
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectHostname/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_KERNEL_LOGS
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelLogs/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_KERNEL_MODULES
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelModules/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_KERNEL_TUNABLES
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelTunables/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_SYSTEM
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectSystem/' $@
+endif
+if !HAVE_SYSTEMD_RESTRICT_ADDRESS_FAMILIES
+ $(AM_V_GEN)perl -ni -e 'print unless /^RestrictAddressFamilies/' $@
+endif
+if !HAVE_SYSTEMD_RESTRICT_NAMESPACES
+ $(AM_V_GEN)perl -ni -e 'print unless /^RestrictNamespaces/' $@
+endif
+if !HAVE_SYSTEMD_RESTRICT_REALTIME
+ $(AM_V_GEN)perl -ni -e 'print unless /^RestrictRealtime/' $@
+endif
+if !HAVE_SYSTEMD_RESTRICT_SUIDSGID
+ $(AM_V_GEN)perl -ni -e 'print unless /^RestrictSUIDSGID/' $@
+endif
+if !HAVE_SYSTEMD_SYSTEM_CALL_ARCHITECTURES
+ $(AM_V_GEN)perl -ni -e 'print unless /^SystemCallArchitectures/' $@
+endif
+if !HAVE_SYSTEMD_SYSTEM_CALL_FILTER
+ $(AM_V_GEN)perl -ni -e 'print unless /^SystemCallFilter/' $@
+endif
ixfrdist@.service: ixfrdist.service
$(AM_V_GEN)sed -e 's!/ixfrdist!& --config $(sysconfdir)/ixfrdist-%i.yml!' \
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "auth-caches.hh"
#include "auth-querycache.hh"
#include "auth-packetcache.hh"
string namespace_name=arg()["carbon-namespace"];
string hostname=arg()["carbon-ourname"];
- if(hostname.empty()) {
- char tmp[80];
- memset(tmp, 0, sizeof(tmp));
- gethostname(tmp, sizeof(tmp));
- char *p = strchr(tmp, '.');
- if(p) *p=0;
- hostname=tmp;
- boost::replace_all(hostname, ".", "_");
+ if (hostname.empty()) {
+ try {
+ hostname = getCarbonHostName();
+ }
+ catch(const std::exception& e) {
+ throw std::runtime_error(std::string("The 'carbon-ourname' setting has not been set and we are unable to determine the system's hostname: ") + e.what());
+ }
}
string instance_name=arg()["carbon-instance"];
{
S.declare("packetcache-hit", "Number of hits on the packet cache");
S.declare("packetcache-miss", "Number of misses on the packet cache");
- S.declare("packetcache-size", "Number of entries in the packet cache");
+ S.declare("packetcache-size", "Number of entries in the packet cache", StatType::gauge);
S.declare("deferred-packetcache-inserts","Amount of packet cache inserts that were deferred because of maintenance");
S.declare("deferred-packetcache-lookup","Amount of packet cache lookups that were deferred because of maintenance");
AuthPacketCache::~AuthPacketCache()
{
try {
- vector<WriteLock*> locks;
+ vector<WriteLock> locks;
for(auto& mc : d_maps) {
- locks.push_back(new WriteLock(&mc.d_mut));
- }
- for(auto wl : locks) {
- delete wl;
+ locks.push_back(WriteLock(mc.d_mut));
}
+ locks.clear();
}
catch(...) {
}
cleanupIfNeeded();
- uint32_t hash = canHashPacket(p.getString());
+ uint32_t hash = canHashPacket(p.getString(), /* don't skip ECS */ false);
p.setHash(hash);
string value;
bool AuthPacketCache::entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp)
{
- return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname);
+ static const std::unordered_set<uint16_t> skippedEDNSTypes{ EDNSOptionCode::COOKIE };
+ return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname, skippedEDNSTypes);
}
void AuthPacketCache::insert(DNSPacket& q, DNSPacket& r, unsigned int maxTTL)
struct MapCombo
{
MapCombo() {
- pthread_rwlock_init(&d_mut, nullptr);
}
~MapCombo() {
- pthread_rwlock_destroy(&d_mut);
}
MapCombo(const MapCombo&) = delete;
MapCombo& operator=(const MapCombo&) = delete;
void reserve(size_t numberOfEntries);
- pthread_rwlock_t d_mut;
+ ReadWriteLock d_mut;
cmap_t d_map;
};
{
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");
AuthQueryCache::~AuthQueryCache()
{
try {
- vector<WriteLock*> locks;
+ vector<WriteLock> locks;
for(auto& mc : d_maps) {
- locks.push_back(new WriteLock(&mc.d_mut));
- }
- for(auto wl : locks) {
- delete wl;
+ locks.push_back(WriteLock(mc.d_mut));
}
+ locks.clear();
}
catch(...) {
}
struct MapCombo
{
MapCombo() {
- pthread_rwlock_init(&d_mut, nullptr);
}
~MapCombo() {
- pthread_rwlock_destroy(&d_mut);
}
MapCombo(const MapCombo &) = delete;
MapCombo & operator=(const MapCombo &) = delete;
void reserve(size_t numberOfEntries);
- pthread_rwlock_t d_mut;
+ ReadWriteLock d_mut;
cmap_t d_map;
};
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "axfr-retriever.hh"
+#include "arguments.hh"
+#include "dns_random.hh"
+#include "utility.hh"
+#include "resolver.hh"
+#include "query-local-address.hh"
+
+using pdns::resolver::parseResult;
+
+AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
+ const DNSName& domain,
+ const TSIGTriplet& tt,
+ const ComboAddress* laddr,
+ size_t maxReceivedBytes,
+ uint16_t timeout)
+ : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
+{
+ ComboAddress local;
+ if (laddr != nullptr) {
+ local = ComboAddress(*laddr);
+ } else {
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
+ throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". Address family is not configured for outgoing queries");
+ }
+ local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
+ }
+ d_sock = -1;
+ try {
+ d_sock = makeQuerySocket(local, false); // make a TCP socket
+ if (d_sock < 0)
+ throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
+ d_buf = shared_array<char>(new char[65536]);
+ d_remote = remote; // mostly for error reporting
+ this->connect(timeout);
+ d_soacount = 0;
+
+ vector<uint8_t> packet;
+ DNSPacketWriter pw(packet, domain, QType::AXFR);
+ pw.getHeader()->id = dns_random_uint16();
+
+ if(!tt.name.empty()) {
+ if (tt.algo == DNSName("hmac-md5"))
+ d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
+ else
+ d_trc.d_algoName = tt.algo;
+ d_trc.d_time = time(0);
+ d_trc.d_fudge = 300;
+ d_trc.d_origID=ntohs(pw.getHeader()->id);
+ d_trc.d_eRcode=0;
+ addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
+ }
+
+ uint16_t replen=htons(packet.size());
+ Utility::iovec iov[2];
+ iov[0].iov_base=reinterpret_cast<char*>(&replen);
+ iov[0].iov_len=2;
+ iov[1].iov_base=packet.data();
+ iov[1].iov_len=packet.size();
+
+ int ret=Utility::writev(d_sock, iov, 2);
+ if(ret < 0)
+ throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
+ if(ret != (int)(2+packet.size())) {
+ throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
+ }
+
+ int res = waitForData(d_sock, timeout, 0);
+
+ if(!res)
+ throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
+ if(res<0)
+ throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
+ }
+ catch(...) {
+ if(d_sock >= 0)
+ close(d_sock);
+ d_sock = -1;
+ throw;
+ }
+}
+
+AXFRRetriever::~AXFRRetriever()
+{
+ close(d_sock);
+}
+
+
+
+int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
+{
+ if(d_soacount > 1)
+ return false;
+
+ // d_sock is connected and is about to spit out a packet
+ int len=getLength(timeout);
+ if(len<0)
+ throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
+
+ if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
+ throw ResolverException("Reached the maximum number of received bytes during AXFR");
+
+ timeoutReadn(len, timeout);
+
+ d_receivedBytes += (uint16_t) len;
+
+ MOADNSParser mdp(false, d_buf.get(), len);
+
+ int err = mdp.d_header.rcode;
+
+ if(err) {
+ throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
+ }
+
+ try {
+ d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
+ }
+ catch(const std::runtime_error& re) {
+ throw ResolverException(re.what());
+ }
+
+ if(!records) {
+ err = parseResult(mdp, DNSName(), 0, 0, &res);
+
+ if (!err) {
+ for(const auto& answer : mdp.d_answers)
+ if (answer.first.d_type == QType::SOA)
+ d_soacount++;
+ }
+ }
+ else {
+ records->clear();
+ records->reserve(mdp.d_answers.size());
+
+ for(auto& r: mdp.d_answers) {
+ if (r.first.d_type == QType::SOA) {
+ d_soacount++;
+ }
+
+ records->push_back(std::move(r.first));
+ }
+ }
+
+ return true;
+}
+
+void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
+{
+ time_t start=time(nullptr);
+ int n=0;
+ int numread;
+ while(n<bytes) {
+ int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
+ if(res<0)
+ throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+ if(!res)
+ throw ResolverException("Timeout while reading data from remote nameserver over TCP");
+
+ numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
+ if(numread<0)
+ throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+ if(numread==0)
+ throw ResolverException("Remote nameserver closed TCP connection");
+ n+=numread;
+ }
+}
+
+void AXFRRetriever::connect(uint16_t timeout)
+{
+ setNonBlocking( d_sock );
+
+ int err;
+
+ if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
+ try {
+ closesocket(d_sock);
+ }
+ catch(const PDNSException& e) {
+ d_sock=-1;
+ throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
+ }
+
+ throw ResolverException("connect: "+stringerror());
+ }
+
+ if(!err)
+ goto done;
+
+ err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
+
+ if(!err) {
+ try {
+ closesocket(d_sock); // timeout
+ }
+ catch(const PDNSException& e) {
+ d_sock=-1;
+ throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
+ }
+
+ d_sock=-1;
+ errno=ETIMEDOUT;
+
+ throw ResolverException("Timeout connecting to server");
+ }
+ else if(err < 0) {
+ throw ResolverException("Error connecting: "+stringerror());
+ }
+ else {
+ Utility::socklen_t len=sizeof(err);
+ if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
+ throw ResolverException("Error connecting: "+stringerror()); // Solaris
+
+ if(err)
+ throw ResolverException("Error connecting: "+string(strerror(err)));
+ }
+
+ done:
+ setBlocking( d_sock );
+ // d_sock now connected
+}
+
+int AXFRRetriever::getLength(uint16_t timeout)
+{
+ timeoutReadn(2, timeout);
+ return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
+}
+
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+#include <boost/utility.hpp>
+
+#include "iputils.hh"
+#include "dnsname.hh"
+#include "resolver.hh"
+
+class AXFRRetriever : public boost::noncopyable
+{
+ public:
+ AXFRRetriever(const ComboAddress& remote,
+ const DNSName& zone,
+ const TSIGTriplet& tt = TSIGTriplet(),
+ const ComboAddress* laddr = NULL,
+ size_t maxReceivedBytes=0,
+ uint16_t timeout=10);
+ ~AXFRRetriever();
+ int getChunk(Resolver::res_t &res, vector<DNSRecord>* records=0, uint16_t timeout=10);
+
+ private:
+ void connect(uint16_t timeout);
+ int getLength(uint16_t timeout);
+ void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10);
+
+ shared_array<char> d_buf;
+ string d_domain;
+ int d_sock;
+ int d_soacount;
+ ComboAddress d_remote;
+ TSIGTCPVerifier d_tsigVerifier;
+
+ size_t d_receivedBytes;
+ size_t d_maxReceivedBytes;
+ TSIGRecordContent d_trc;
+};
GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
{
setArgPrefix(mode+suffix);
- d_db=0;
+ d_db = nullptr;
d_logprefix="["+mode+"Backend"+suffix+"] ";
try
d_InfoOfAllSlaveDomainsQuery=getArg("info-all-slaves-query");
d_SuperMasterInfoQuery=getArg("supermaster-query");
d_GetSuperMasterIPs=getArg("supermaster-name-to-ips");
+ d_AddSuperMaster=getArg("supermaster-add");
d_InsertZoneQuery=getArg("insert-zone-query");
d_InsertRecordQuery=getArg("insert-record-query");
d_UpdateMasterOfZoneQuery=getArg("update-master-query");
d_InfoOfAllSlaveDomainsQuery_stmt = NULL;
d_SuperMasterInfoQuery_stmt = NULL;
d_GetSuperMasterIPs_stmt = NULL;
+ d_AddSuperMaster_stmt = NULL;
d_InsertZoneQuery_stmt = NULL;
d_InsertRecordQuery_stmt = NULL;
d_InsertEmptyNonTerminalOrderQuery_stmt = NULL;
}
}
-bool GSQLBackend::setMaster(const DNSName &domain, const string &ip)
+bool GSQLBackend::setMasters(const DNSName &domain, const vector<ComboAddress> &masters)
{
+ vector<string> masters_s;
+ for (const auto& master : masters) {
+ masters_s.push_back(master.toStringWithPortExcept(53));
+ }
+
+ auto tmp = boost::join(masters_s, ", ");
+
try {
reconnectIfNeeded();
d_UpdateMasterOfZoneQuery_stmt->
- bind("master", ip)->
+ bind("master", tmp)->
bind("domain", domain)->
execute()->
reset();
}
catch (SSqlException &e) {
- throw PDNSException("GSQLBackend unable to set master of domain '"+domain.toLogString()+"' to IP address " + ip + ": "+e.txtReason());
+ throw PDNSException("GSQLBackend unable to set masters of domain '"+domain.toLogString()+"' to " + tmp + ": "+e.txtReason());
}
return true;
}
}
}
catch(const std::exception& exp) {
- g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone.toLogString()<<"': "<<exp.what()<<endl;
+ g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone<<"': "<<exp.what()<<endl;
continue;
}
catch(...) {
- g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone.toLogString()<<"', skipping"<<endl;
+ g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone<<"', skipping"<<endl;
continue;
}
}
return false;
}
+bool GSQLBackend::superMasterAdd(const string &ip, const string &nameserver, const string &account)
+{
+ try{
+ reconnectIfNeeded();
+
+ d_AddSuperMaster_stmt ->
+ bind("ip",ip)->
+ bind("nameserver",nameserver)->
+ bind("account",account)->
+ execute()->
+ reset();
+
+ }
+ catch (SSqlException &e){
+ throw PDNSException("GSQLBackend unable to insert a supermaster with IP " + ip + " and nameserver name '" + nameserver + "' and account '" + account + "': " + e.txtReason());
+ }
+ return true;
+
+}
+
bool GSQLBackend::superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **ddb)
{
// check if we know the ip/ns couple in the database
return false;
}
-bool GSQLBackend::createDomain(const DNSName &domain, const string &type, const string &masters, const string &account)
+bool GSQLBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
{
+ vector<string> masters_s;
+ for (const auto& master : masters) {
+ masters_s.push_back(master.toStringWithPortExcept(53));
+ }
+
try {
reconnectIfNeeded();
d_InsertZoneQuery_stmt->
- bind("type", type)->
+ bind("type", toUpper(DomainInfo::getKindString(kind)))->
bind("domain", domain)->
- bind("masters", masters)->
+ bind("masters", boost::join(masters_s, ", "))->
bind("account", account)->
execute()->
reset();
bool GSQLBackend::createSlaveDomain(const string &ip, const DNSName &domain, const string &nameserver, const string &account)
{
string name;
- string masters(ip);
+ vector<ComboAddress> masters({ComboAddress(ip, 53)});
try {
if (!nameserver.empty()) {
// figure out all IP addresses for the master
reset();
if (!d_result.empty()) {
// collect all IP addresses
- vector<string> tmp;
+ vector<ComboAddress> tmp;
for(const auto& row: d_result) {
if (account == row[1])
- tmp.push_back(row[0]);
+ tmp.emplace_back(row[0], 53);
}
// set them as domain's masters, comma separated
- masters = boost::join(tmp, ", ");
+ masters = tmp;
}
}
- createDomain(domain, "SLAVE", masters, account);
+ createDomain(domain, DomainInfo::Slave, masters, account);
}
catch(SSqlException &e) {
throw PDNSException("Database error trying to insert new slave domain '"+domain.toLogString()+"': "+ e.txtReason());
d_db=std::unique_ptr<SSql>(db);
if (d_db) {
d_db->setLog(::arg().mustDo("query-logging"));
- allocateStatements();
}
}
- void allocateStatements()
+protected:
+ virtual void allocateStatements()
{
if (d_db) {
d_NoIdQuery_stmt = d_db->prepare(d_NoIdQuery, 2);
d_InfoOfAllSlaveDomainsQuery_stmt = d_db->prepare(d_InfoOfAllSlaveDomainsQuery, 0);
d_SuperMasterInfoQuery_stmt = d_db->prepare(d_SuperMasterInfoQuery, 2);
d_GetSuperMasterIPs_stmt = d_db->prepare(d_GetSuperMasterIPs, 2);
+ d_AddSuperMaster_stmt = d_db->prepare(d_AddSuperMaster, 3);
d_InsertZoneQuery_stmt = d_db->prepare(d_InsertZoneQuery, 4);
d_InsertRecordQuery_stmt = d_db->prepare(d_InsertRecordQuery, 9);
d_InsertEmptyNonTerminalOrderQuery_stmt = d_db->prepare(d_InsertEmptyNonTerminalOrderQuery, 4);
}
}
- void freeStatements() {
+ virtual void freeStatements() {
d_NoIdQuery_stmt.reset();
d_IdQuery_stmt.reset();
d_ANYNoIdQuery_stmt.reset();
d_InfoOfAllSlaveDomainsQuery_stmt.reset();
d_SuperMasterInfoQuery_stmt.reset();
d_GetSuperMasterIPs_stmt.reset();
+ d_AddSuperMaster_stmt.reset();
d_InsertZoneQuery_stmt.reset();
d_InsertRecordQuery_stmt.reset();
d_InsertEmptyNonTerminalOrderQuery_stmt.reset();
d_SearchCommentsQuery_stmt.reset();
}
+public:
void lookup(const QType &, const DNSName &qdomain, int zoneId, DNSPacket *p=nullptr) override;
bool list(const DNSName &target, int domain_id, bool include_disabled=false) override;
bool get(DNSResourceRecord &r) override;
bool feedRecord(const DNSResourceRecord &r, const DNSName &ordername, bool ordernameIsNSEC3=false) override;
bool feedEnts(int domain_id, map<DNSName,bool>& nonterm) override;
bool feedEnts3(int domain_id, const DNSName &domain, map<DNSName,bool> &nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow) override;
- bool createDomain(const DNSName &domain) override {
- return createDomain(domain, "NATIVE", "", "");
- };
+ bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account) override;
bool createSlaveDomain(const string &ip, const DNSName &domain, const string &nameserver, const string &account) override;
bool deleteDomain(const DNSName &domain) override;
+ bool superMasterAdd(const string &ip, const string &nameserver, const string &account) override;
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;
/* based on freebsd:src/contrib/opie/libopie/btoe.c extract: get bit ranges from a char* */
/* NOTE: length should not exceed 8; all callers inside PowerDNS only pass length=5 though */
-unsigned char extract_bits(const char *s, int start, int length)
+static unsigned char extract_bits(const char *s, int start, int length)
{
uint16_t x;
unsigned char cl, cc;
-ALTER TABLE cryptokeys ADD published BOOL DEFAULT 1;
+BEGIN TRANSACTION;
+ CREATE TABLE cryptokeys_temp (
+ id INTEGER PRIMARY KEY,
+ domain VARCHAR(255) COLLATE NOCASE,
+ flags INT NOT NULL,
+ active BOOL,
+ published BOOL DEFAULT 1,
+ content TEXT
+ );
+
+ INSERT INTO cryptokeys_temp SELECT id,domain,flags,active,1,content FROM cryptokeys;
+ DROP TABLE cryptokeys;
+ ALTER TABLE cryptokeys_temp RENAME TO cryptokeys;
+
+ CREATE INDEX domainnameindex ON cryptokeys(domain);
+COMMIT;
domain VARCHAR(255) COLLATE NOCASE,
flags INT NOT NULL,
active BOOL,
- published BOOL,
+ published BOOL DEFAULT 1,
content TEXT
);
int yyparse(void);
int yylex(void);
void yyrestart(FILE *);
- int yywrap()
+ int yywrap(void);
+ int yywrap(void)
{
return 1;
}
// 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);
}
}
for (auto& mc : maps) {
const typename C::lock l(mc);
- mc.d_cachecachevalid = false;
+ mc.invalidate();
auto& sidx = boost::multi_index::get<S>(mc.d_map);
uint64_t erased = 0, lookedAt = 0;
for (auto i = sidx.begin(); i != sidx.end(); lookedAt++) {
toTrim -= totErased;
- while (toTrim > 0) {
+ while (true) {
size_t pershard = toTrim / maps_size + 1;
for (auto& mc : maps) {
const typename C::lock l(mc);
- mc.d_cachecachevalid = false;
+ mc.invalidate();
auto& sidx = boost::multi_index::get<S>(mc.d_map);
size_t removed = 0;
for (auto i = sidx.begin(); i != sidx.end() && removed < pershard; removed++) {
totErased++;
toTrim--;
if (toTrim == 0) {
- break;
+ return totErased;
}
}
}
}
+ // Not reached
return totErased;
}
}
template<typename S, typename Index>
-std::pair<typename Index::iterator,bool>
-lruReplacingInsert(Index& i,const typename Index::value_type& x)
+bool lruReplacingInsert(Index& i, const typename Index::value_type& x)
{
- std::pair<typename Index::iterator,bool> res = i.insert(x);
- if (!res.second) {
- moveCacheItemToBack<S>(i, res.first);
- res.second = i.replace(res.first, x);
+ auto inserted = i.insert(x);
+ if (!inserted.second) {
+ moveCacheItemToBack<S>(i, inserted.first);
+ i.replace(inserted.first, x);
+ return false;
}
- return res;
+ return true;
}
}
unknown.emplace_back(std::make_shared<vector<uint8_t>>(packet));
}
- random_shuffle(unknown.begin(), unknown.end());
+
+ shuffle(unknown.begin(), unknown.end(), pdns::dns_random_engine());
if (!g_quiet) {
cout<<"Generated "<<unknown.size()<<" ready to use queries"<<endl;
}
for(;n < total; ++n) {
toSend.push_back(known[dns_random(known.size())].get());
}
- random_shuffle(toSend.begin(), toSend.end());
+
+ shuffle(toSend.begin(), toSend.end(), pdns::dns_random_engine());
g_recvcounter.store(0);
g_recvbytes=0;
DTime dt;
#include "dnsseckeeper.hh"
#include "threadname.hh"
#include "misc.hh"
+#include "query-local-address.hh"
#include <thread>
{
::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR;
::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
- ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns when unset and not chrooted" )="";
+ ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns when unset and not chrooted"
+#ifdef HAVE_SYSTEMD
+ + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")="";
+ auto runtimeDir = getenv("RUNTIME_DIRECTORY");
+ if (runtimeDir != nullptr) {
+ ::arg().set("socket-dir") = runtimeDir;
+ }
+#else
+ )="";
+#endif
::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
::arg().set("chroot","If set, chroot to this directory for more security")="";
::arg().set("logging-facility","Log under a specific facility")="";
::arg().setSwitch("local-address-nonexist-fail","Fail to start if one or more of the local-address's do not exist on this server")="yes";
::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options")="no";
::arg().setSwitch("reuseport","Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket")="no";
- ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
- ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::";
+ ::arg().set("query-local-address","Source IP addresses for sending queries")="0.0.0.0 ::";
+ ::arg().set("query-local-address6","DEPRECATED: Use query-local-address. Source IPv6 address for sending queries")="";
::arg().set("overload-queue-length","Maximum queuelength moving to packetcache only")="0";
::arg().set("max-queue-length","Maximum queuelength before considering situation lost")="5000";
::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
::arg().set("webserver-max-bodysize","Webserver/API maximum request/response body size in megabytes")="2";
- ::arg().setSwitch("do-ipv6-additional-processing", "Do AAAA additional processing")="yes";
::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
::arg().set("carbon-namespace", "If set overwrites the first part of the carbon string")="pdns";
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");
_exit(1);
}
-static void* dummyThread(void *)
+static void dummyThread()
{
- void* ignore=0;
- pthread_exit(ignore);
}
static void triggerLoadOfLibraries()
{
- pthread_t tid;
- pthread_create(&tid, 0, dummyThread, 0);
- void* res;
- pthread_join(tid, &res);
+ std::thread dummy(dummyThread);
+ dummy.join();
}
void mainthread()
}
}
+ pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
+ if (!::arg()["query-local-address6"].empty()) {
+ g_log<<Logger::Warning<<"query-local-address6 is deprecated and will be removed in a future version. Please use query-local-address for IPv6 addresses as well"<<endl;
+ pdns::parseQueryLocalAddress(::arg()["query-local-address6"]);
+ }
+
// NOW SAFE TO CREATE THREADS!
dl->go();
extern vector<std::shared_ptr<UDPNameserver> > g_udpReceivers;
extern int avg_latency;
extern std::unique_ptr<TCPNameserver> TN;
-extern ArgvMap & arg( void );
extern void declareArguments();
extern void declareStats();
extern void mainthread();
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+
+#include <set>
+#include <thread>
+#include <boost/utility.hpp>
+
#include "packetcache.hh"
#include "utility.hh"
#include "communicator.hh"
-#include <set>
-#include <boost/utility.hpp>
#include "dnsbackend.hh"
#include "ueberbackend.hh"
#include "packethandler.hh"
-#include "resolver.hh"
#include "logger.hh"
#include "dns.hh"
#include "arguments.hh"
d_suck_sem.wait();
SuckRequest sr;
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
if(d_suckdomains.empty())
continue;
sr=d_suckdomains.front();
d_suckdomains.pop_front();
}
- suck(sr.domain, sr.master);
+ suck(sr.domain, sr.master, sr.force);
}
}
_exit(1);
}
- pthread_t tid;
- pthread_create(&tid,0,&launchhelper,this); // Starts CommunicatorClass::mainloop()
- for(int n=0; n < ::arg().asNum("retrieval-threads", 1); ++n)
- pthread_create(&tid, 0, &retrieveLaunchhelper, this); // Starts CommunicatorClass::retrievalLoopThread()
+ std::thread mainT(std::bind(&CommunicatorClass::mainloop, this));
+ mainT.detach();
+
+ for(int n=0; n < ::arg().asNum("retrieval-threads", 1); ++n) {
+ std::thread retrieve(std::bind(&CommunicatorClass::retrievalLoopThread, this));
+ retrieve.detach();
+ }
d_preventSelfNotification = ::arg().mustDo("prevent-self-notification");
bool extraSlaveRefresh = false;
Utility::sleep(1);
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
if (d_tocheck.size())
extraSlaveRefresh = true;
}
{
DNSName domain;
ComboAddress master;
+ bool force;
bool operator<(const SuckRequest& b) const
{
return tie(domain, master) < tie(b.domain, b.master);
public:
CommunicatorClass()
{
- pthread_mutex_init(&d_lock,0);
- pthread_mutex_init(&d_holelock,0);
-
d_tickinterval=60;
d_masterschanged=d_slaveschanged=true;
d_nsock4 = -1;
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);
void mainloop();
void retrievalLoopThread();
void sendNotification(int sock, const DNSName &domain, const ComboAddress& remote, uint16_t id, UeberBackend* B);
-
- static void *launchhelper(void *p)
- {
- static_cast<CommunicatorClass *>(p)->mainloop();
- return 0;
- }
- static void *retrieveLaunchhelper(void *p)
- {
- static_cast<CommunicatorClass *>(p)->retrievalLoopThread();
- return 0;
- }
bool notifyDomain(const DNSName &domain, UeberBackend* B);
private:
void loadArgsIntoSet(const char *listname, set<string> &listset);
void queueNotifyDomain(const DomainInfo& di, UeberBackend* B);
int d_nsock4, d_nsock6;
map<pair<DNSName,string>,time_t>d_holes;
- pthread_mutex_t d_holelock;
- void suck(const DNSName &domain, const ComboAddress& remote);
+ std::mutex d_holelock;
+ 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);
void slaveRefresh(PacketHandler *P);
void masterUpdateCheck(PacketHandler *P);
- pthread_mutex_t d_lock;
+ std::mutex d_lock;
UniQueue d_suckdomains;
set<DNSName> d_inprogress;
~RemoveSentinel()
{
try {
- Lock l(&d_cc->d_lock);
+ std::lock_guard<std::mutex> l(d_cc->d_lock);
d_cc->d_inprogress.erase(d_dn);
}
catch(...) {
DNSSECKeeper::keycache_t DNSSECKeeper::s_keycache;
DNSSECKeeper::metacache_t DNSSECKeeper::s_metacache;
-pthread_rwlock_t DNSSECKeeper::s_metacachelock = PTHREAD_RWLOCK_INITIALIZER;
-pthread_rwlock_t DNSSECKeeper::s_keycachelock = PTHREAD_RWLOCK_INITIALIZER;
+int64_t DNSSECKeeper::s_metaCacheCleanActions = 0;
+ReadWriteLock DNSSECKeeper::s_metacachelock;
+ReadWriteLock DNSSECKeeper::s_keycachelock;
AtomicCounter DNSSECKeeper::s_ops;
time_t DNSSECKeeper::s_last_prune;
size_t DNSSECKeeper::s_maxEntries = 0;
return meta=="1";
}
+
bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, int64_t& id, int bits, bool active, bool published)
{
if(!bits) {
dspk.setKey(dpk);
dspk.d_algorithm = algorithm;
dspk.d_flags = setSEPBit ? 257 : 256;
- return addKey(name, dspk, id, active, published);
+ return addKey(name, dspk, id, active, published) && clearKeyCache(name);
}
void DNSSECKeeper::clearAllCaches() {
s_metacache.clear();
}
-void DNSSECKeeper::clearCaches(const DNSName& name)
+
+bool DNSSECKeeper::clearKeyCache(const DNSName& name)
+{
+ WriteLock l(&s_keycachelock);
+ s_keycache.erase(name);
+ return true;
+}
+
+bool DNSSECKeeper::clearMetaCache(const DNSName& name)
{
- {
- WriteLock l(&s_keycachelock);
- 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);
+ ++s_metaCacheCleanActions;
+ return true;
}
+void DNSSECKeeper::clearCaches(const DNSName& name)
+{
+ clearKeyCache(name);
+ clearMetaCache(name);
+}
bool DNSSECKeeper::addKey(const DNSName& name, const DNSSECPrivateKey& dpk, int64_t& id, bool active, bool published)
{
- clearCaches(name);
DNSBackend::KeyData kd;
kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
kd.active = active;
kd.published = published;
kd.content = dpk.getKey()->convertToISC();
// now store it
- return d_keymetadb->addDomainKey(name, kd, id);
+ return d_keymetadb->addDomainKey(name, kd, id) && clearKeyCache(name);
}
bool DNSSECKeeper::removeKey(const DNSName& zname, unsigned int id)
{
- clearCaches(zname);
- return d_keymetadb->removeDomainKey(zname, id);
+ return d_keymetadb->removeDomainKey(zname, id) && clearKeyCache(zname);
}
bool DNSSECKeeper::deactivateKey(const DNSName& zname, unsigned int id)
{
- clearCaches(zname);
- return d_keymetadb->deactivateDomainKey(zname, id);
+ return d_keymetadb->deactivateDomainKey(zname, id) && clearKeyCache(zname);
}
bool DNSSECKeeper::activateKey(const DNSName& zname, unsigned int id)
{
- clearCaches(zname);
- return d_keymetadb->activateDomainKey(zname, id);
+ return d_keymetadb->activateDomainKey(zname, id) && clearKeyCache(zname);
}
bool DNSSECKeeper::unpublishKey(const DNSName& zname, unsigned int id)
{
- clearCaches(zname);
- return d_keymetadb->unpublishDomainKey(zname, id);
+ return d_keymetadb->unpublishDomainKey(zname, id) && clearKeyCache(zname);
}
bool DNSSECKeeper::publishKey(const DNSName& zname, unsigned int id)
{
- clearCaches(zname);
- return d_keymetadb->publishDomainKey(zname, id);
+ return d_keymetadb->publishDomainKey(zname, id) && clearKeyCache(zname);
}
void DNSSECKeeper::getFromMetaOrDefault(const DNSName& zname, const std::string& key, std::string& value, const std::string& defaultvalue)
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);
- metacache_t::const_iterator iter = s_metacache.find(tie(zname, key));
+ bool ret = false;
+ bool fromCache = false;
+ METAValues meta;
+
+ 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;
+ }
+ else {
+ d_metaCacheCleanAction = s_metaCacheCleanActions;
}
}
- 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);
+ if(d_metaCacheCleanAction != s_metaCacheCleanActions) {
+ return false;
+ }
lruReplacingInsert<SequencedTag>(s_metacache, nce);
}
}
- return isset;
+
+ return ret;
}
void DNSSECKeeper::getSoaEdit(const DNSName& zname, std::string& value)
uint64_t DNSSECKeeper::dbdnssecCacheSizes(const std::string& str)
{
if(str=="meta-cache-size") {
- ReadLock l(&s_metacachelock);
+ ReadLock l(&s_metacachelock);
return s_metacache.size();
}
else if(str=="key-cache-size") {
if (!checkNSEC3PARAM(ns3p, error_msg))
throw runtime_error("NSEC3PARAMs provided for zone '"+zname.toLogString()+"' are invalid: " + error_msg);
- clearCaches(zname);
string descr = ns3p.getZoneRepresentation();
vector<string> meta;
meta.push_back(descr);
if(narrow)
meta.push_back("1");
- return d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", meta);
+ return d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", meta) && clearMetaCache(zname);
}
return false;
}
bool DNSSECKeeper::unsetNSEC3PARAM(const DNSName& zname)
{
- clearCaches(zname);
- return (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", vector<string>()) && d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", vector<string>()));
+ return (d_keymetadb->setDomainMetadata(zname, "NSEC3PARAM", vector<string>()) && d_keymetadb->setDomainMetadata(zname, "NSEC3NARROW", vector<string>())) && clearMetaCache(zname);
}
bool DNSSECKeeper::setPresigned(const DNSName& zname)
{
- clearCaches(zname);
vector<string> meta;
meta.push_back("1");
- return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", meta);
+ return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", meta) && clearMetaCache(zname);
}
bool DNSSECKeeper::unsetPresigned(const DNSName& zname)
{
- clearCaches(zname);
- return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", vector<string>());
+ return d_keymetadb->setDomainMetadata(zname, "PRESIGNED", vector<string>()) && clearMetaCache(zname);
}
/**
*/
bool DNSSECKeeper::setPublishCDS(const DNSName& zname, const string& digestAlgos)
{
- clearCaches(zname);
vector<string> meta;
meta.push_back(digestAlgos);
- return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", meta);
+ return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", meta) && clearMetaCache(zname);
}
void DNSSECKeeper::getPublishCDS(const DNSName& zname, std::string& value)
*/
bool DNSSECKeeper::unsetPublishCDS(const DNSName& zname)
{
- clearCaches(zname);
- return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", vector<string>());
+ return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDS", vector<string>()) && clearMetaCache(zname);
}
/**
*/
bool DNSSECKeeper::setPublishCDNSKEY(const DNSName& zname)
{
- clearCaches(zname);
vector<string> meta;
meta.push_back("1");
- return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", meta);
+ return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", meta) && clearMetaCache(zname);
}
void DNSSECKeeper::getPublishCDNSKEY(const DNSName& zname, std::string& value)
*/
bool DNSSECKeeper::unsetPublishCDNSKEY(const DNSName& zname)
{
- clearCaches(zname);
- return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", vector<string>());
+ return d_keymetadb->setDomainMetadata(zname, "PUBLISH-CDNSKEY", vector<string>()) && clearMetaCache(zname);
}
/**
}
}
-template<class T>
-bool ObjectPipe<T>::read(T* t)
-{
- T* ptr;
- int ret = ::read(d_fds[0], &ptr, sizeof(ptr));
-
- if(ret < 0)
- unixDie("read");
- if(ret==0)
- return false;
- if(ret != sizeof(ptr))
- throw std::runtime_error("Partial read, should not happen");
- *t=*ptr;
- delete ptr;
- return true;
-}
-
template<class T>
int ObjectPipe<T>::readTimeout(T* t, double msec)
{
- T* ptr;
-
- int ret = waitForData(d_fds[0], 0, 1000*msec);
- if(ret < 0)
- unixDie("waiting for data in object pipe");
- if(ret == 0)
- return -1;
-
- ret = ::read(d_fds[0], &ptr, sizeof(ptr)); // this is BLOCKING!
-
- if(ret < 0)
- unixDie("read");
- if(ret==0)
- return false;
- if(ret != sizeof(ptr))
- throw std::runtime_error("Partial read, should not happen 2");
- *t=*ptr;
- delete ptr;
- return 1;
+ while (true) {
+ int ret = waitForData(d_fds[0], 0, 1000*msec);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ unixDie("waiting for data in object pipe");
+ }
+ else if (ret == 0) {
+ return -1;
+ }
+
+ T* ptr = nullptr;
+ ret = ::read(d_fds[0], &ptr, sizeof(ptr)); // this is BLOCKING!
+
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ unixDie("read");
+ }
+ else if (ret == 0) {
+ return false;
+ }
+
+ if (ret != sizeof(ptr)) {
+ throw std::runtime_error("Partial read, should not happen 2");
+ }
+
+ *t = *ptr;
+ delete ptr;
+ return 1;
+ }
}
ObjectPipe();
~ObjectPipe();
void write(T& t);
- bool read(T* t); // returns false on EOF
- int readTimeout(T* t, double msec); //!< -1 is timeout, 0 is no data, 1 is data. msec<0 waits infinitely wrong. msec==0 = undefined
+ int readTimeout(T* t, double msec); //!< -1 is timeout, 0 is no data, 1 is data. msec<0 waits infinitely long. msec==0 = undefined
void close();
private:
int d_fds[2];
void DevPollFDMultiplexer::getAvailableFDs(std::vector<int>& fds, int timeout)
{
+ std::vector<struct pollfd> pollfds(d_readCallbacks.size() + d_writeCallbacks.size());
struct dvpoll dvp;
dvp.dp_nfds = d_readCallbacks.size() + d_writeCallbacks.size();
- dvp.dp_fds = new pollfd[dvp.dp_nfds];
+ dvp.dp_fds = pollfds.data();
dvp.dp_timeout = timeout;
int ret=ioctl(d_devpollfd, DP_POLL, &dvp);
if(ret < 0 && errno!=EINTR) {
- delete[] dvp.dp_fds;
throw FDMultiplexerException("/dev/poll returned error: "+stringerror());
}
for(int n=0; n < ret; ++n) {
- fds.push_back(dvp.dp_fds[n].fd);
+ fds.push_back(pollfds.at(n).fd);
}
-
- delete[] dvp.dp_fds;
}
int DevPollFDMultiplexer::run(struct timeval* now, int timeout)
if(d_inrun) {
throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
}
+ std::vector<struct pollfd> fds(d_readCallbacks.size() + d_writeCallbacks.size());
struct dvpoll dvp;
dvp.dp_nfds = d_readCallbacks.size() + d_writeCallbacks.size();
- dvp.dp_fds = new pollfd[dvp.dp_nfds];
+ dvp.dp_fds = fds.data();
dvp.dp_timeout = timeout;
int ret=ioctl(d_devpollfd, DP_POLL, &dvp);
int err = errno;
gettimeofday(now,0); // MANDATORY!
if(ret < 0 && err!=EINTR) {
- delete[] dvp.dp_fds;
throw FDMultiplexerException("/dev/poll returned error: "+stringerror(err));
}
if(ret < 1) { // thanks AB!
- delete[] dvp.dp_fds;
return 0;
}
d_inrun=true;
for(int n=0; n < ret; ++n) {
- d_iter=d_readCallbacks.find(dvp.dp_fds[n].fd);
+ d_iter=d_readCallbacks.find(fds.at(n).fd);
if(d_iter != d_readCallbacks.end()) {
d_iter->d_callback(d_iter->d_fd, d_iter->d_parameter);
continue; // so we don't refind ourselves as writable!
}
- d_iter=d_writeCallbacks.find(dvp.dp_fds[n].fd);
+ d_iter=d_writeCallbacks.find(fds.at(n).fd);
if(d_iter != d_writeCallbacks.end()) {
d_iter->d_callback(d_iter->d_fd, d_iter->d_parameter);
}
}
- delete[] dvp.dp_fds;
+
d_inrun=false;
return ret;
}
#include <deque>
#include <queue>
#include <vector>
+#include <thread>
#include <pthread.h>
#include "threadname.hh"
#include <unistd.h>
MultiThreadDistributor(int n);
typedef std::function<void(std::unique_ptr<Answer>&)> callback_t;
int question(Question&, callback_t callback) override; //!< Submit a question to the Distributor
- static void* makeThread(void *); //!< helper function to create our n threads
+ void distribute(int n);
int getQueueSize() override {
return d_queued;
}
time_t d_last_started;
unsigned int d_overloadQueueLength, d_maxQueueLength;
int d_num_threads;
- std::atomic<unsigned int> d_queued{0}, d_running{0};
+ std::atomic<unsigned int> d_queued{0};
std::vector<std::pair<int,int>> d_pipes;
};
g_log<<Logger::Error<<"Distributor caught fatal exception: "<<AE.reason<<endl;
_exit(1);
}
+ catch(const std::exception& e) {
+ g_log<<Logger::Error<<"Distributor caught fatal exception: "<<e.what()<<endl;
+ _exit(1);
+ }
catch(...) {
g_log<<Logger::Error<<"Caught an unknown exception when creating backend, probably"<<endl;
_exit(1);
nextid=0;
d_last_started=time(0);
- pthread_t tid;
-
-
for(int i=0; i < n; ++i) {
int fds[2];
if(pipe(fds) < 0)
g_log<<Logger::Warning<<"About to create "<<n<<" backend threads for UDP"<<endl;
for(int i=0;i<n;i++) {
- pthread_create(&tid,0,&makeThread,static_cast<void *>(this));
+ std::thread t(std::bind(&MultiThreadDistributor<Answer,Question,Backend>::distribute, this, i));
+ t.detach();
Utility::usleep(50000); // we've overloaded mysql in the past :-)
}
g_log<<Logger::Warning<<"Done launching threads, ready to distribute questions"<<endl;
// start of a new thread
-template<class Answer, class Question, class Backend>void *MultiThreadDistributor<Answer,Question,Backend>::makeThread(void *p)
+template<class Answer, class Question, class Backend>void MultiThreadDistributor<Answer,Question,Backend>::distribute(int ournum)
{
setThreadName("pdns/distributo");
- pthread_detach(pthread_self());
- MultiThreadDistributor *us=static_cast<MultiThreadDistributor *>(p);
- int ournum=us->d_running++;
try {
std::unique_ptr<Backend> b= make_unique<Backend>(); // this will answer our questions
for(;;) {
QuestionData* tempQD = nullptr;
- if(read(us->d_pipes[ournum].first, &tempQD, sizeof(tempQD)) != sizeof(tempQD))
+ if(read(d_pipes.at(ournum).first, &tempQD, sizeof(tempQD)) != sizeof(tempQD))
unixDie("read");
- --us->d_queued;
+ --d_queued;
std::unique_ptr<QuestionData> QD = std::unique_ptr<QuestionData>(tempQD);
tempQD = nullptr;
std::unique_ptr<Answer> a = nullptr;
g_log<<Logger::Error<<"Distributor caught fatal exception: "<<AE.reason<<endl;
_exit(1);
}
+ catch(const std::exception& e) {
+ g_log<<Logger::Error<<"Distributor caught fatal exception: "<<e.what()<<endl;
+ _exit(1);
+ }
catch(...) {
g_log<<Logger::Error<<"Caught an unknown exception when creating backend, probably"<<endl;
_exit(1);
}
- return 0;
}
template<class Answer, class Question, class Backend>int SingleThreadDistributor<Answer,Question,Backend>::question(Question& q, callback_t callback)
QD->callback=callback;
++d_queued;
- if(write(d_pipes[QD->id % d_pipes.size()].second, &QD, sizeof(QD)) != sizeof(QD)) {
+ if(write(d_pipes.at(QD->id % d_pipes.size()).second, &QD, sizeof(QD)) != sizeof(QD)) {
--d_queued;
delete QD;
unixDie("write");
DNSName wildcardname;
string content; //!< what this record points to. Example: 10.1.2.3
- // Aligned on 8-byte boundries on systems where time_t is 8 bytes and int
+ // Aligned on 8-byte boundaries on systems where time_t is 8 bytes and int
// is 4 bytes, aka modern linux on x86_64
time_t last_modified; //!< For autocalculating SOA serial numbers - the backend needs to fill this in
kiss_init(unsigned int seed)
{
kiss_seed = seed;
- kiss_jsr = 0x5eed5eed; /* simply musn't be 0 */
+ kiss_jsr = 0x5eed5eed; /* simply mustn't be 0 */
kiss_z = 1 ^ (kiss_w = kiss_jcong = seed); /* w=z=0 is bad, see Rose */
}
*/
#pragma once
#include <cstdint>
+#include <limits>
void dns_random_init(const std::string& data = "", bool force_reinit = false);
uint32_t dns_random(uint32_t n);
uint16_t dns_random_uint16();
+
+namespace pdns {
+ struct dns_random_engine {
+
+ typedef uint32_t result_type;
+
+ static constexpr result_type min()
+ {
+ return 0;
+ }
+
+ static constexpr result_type max()
+ {
+ return std::numeric_limits<result_type>::max() - 1;
+ }
+
+ result_type operator()()
+ {
+ return dns_random(std::numeric_limits<result_type>::max());
+ }
+ };
+}
+
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()
{
vector<string> parts;
stringtok(parts,instr,", ");
- for (const auto part : parts)
+ for (const auto& part : parts)
if (count(parts.begin(), parts.end(), part) > 1)
throw ArgException("Refusing to launch multiple backends with the same name '" + part + "', verify all 'launch' statements in your configuration");
}
}
-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();
#include "misc.hh"
#include "dnswriter.hh"
#include "dnsrecords.hh"
+#include "dns_random.hh"
+#include "arguments.hh"
using namespace boost::accumulators;
namespace po = boost::program_options;
StatBag S;
+ArgvMap &arg()
+{
+ static ArgvMap theArg;
+ return theArg;
+}
+
bool g_quiet=false;
bool g_envoutput=false;
unsigned int d_receiveds, d_receiveerrors, d_senderrors;
};
-void usage(po::options_description &desc) {
+static void usage(po::options_description &desc) {
cerr << "Usage: dnsbulktest [OPTION].. IPADDRESS PORTNUMBER [LIMIT]"<<endl;
cerr << desc << "\n";
}
int main(int argc, char** argv)
try
{
+ ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto";
+ ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
+
po::options_description desc("Allowed options");
desc.add_options()
("help,h", "produce help message")
domains.push_back(TypedQuery("www."+split.second, qtype));
}
cerr<<"Read "<<domains.size()<<" domains!"<<endl;
- random_shuffle(domains.begin(), domains.end());
+ shuffle(domains.begin(), domains.end(), pdns::dns_random_engine());
boost::format datafmt("%s %|20t|%+15s %|40t|%s %|60t|%+15s\n");
#include "dolog.hh"
#include "dnscrypt.hh"
#include "dnswriter.hh"
-#include "lock.hh"
DNSCryptPrivateKey::DNSCryptPrivateKey()
{
DNSCryptContext::~DNSCryptContext() {
- pthread_rwlock_destroy(&d_lock);
}
DNSCryptContext::DNSCryptContext(const std::string& pName, const std::vector<CertKeyPaths>& certKeys): d_certKeyPaths(certKeys), providerName(pName)
{
- pthread_rwlock_init(&d_lock, 0);
-
reloadCertificates();
}
DNSCryptContext::DNSCryptContext(const std::string& pName, const DNSCryptCert& certificate, const DNSCryptPrivateKey& pKey): providerName(pName)
{
- pthread_rwlock_init(&d_lock, 0);
-
addNewCertificate(certificate, pKey);
}
dh->rcode = RCode::NoError;
ReadLock r(&d_lock);
- for (const auto pair : d_certs) {
+ for (const auto& pair : d_certs) {
if (!pair->active || !pair->cert.isValid(now)) {
continue;
}
unsigned char nonce[DNSCRYPT_NONCE_SIZE];
static_assert(sizeof(nonce) == (2* sizeof(d_header.clientNonce)), "Nonce should be larger than clientNonce (half)");
- static_assert(sizeof(d_header.clientPK) == DNSCRYPT_PUBLIC_KEY_SIZE, "Client Publick key size is not right");
+ static_assert(sizeof(d_header.clientPK) == DNSCRYPT_PUBLIC_KEY_SIZE, "Client Public key size is not right");
static_assert(sizeof(d_pair->privateKey.key) == DNSCRYPT_PRIVATE_KEY_SIZE, "Private key size is not right");
memcpy(nonce, &d_header.clientNonce, sizeof(d_header.clientNonce));
#include <sodium.h>
#include "dnsname.hh"
+#include "lock.hh"
#define DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE (crypto_sign_ed25519_PUBLICKEYBYTES)
#define DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE (crypto_sign_ed25519_SECRETKEYBYTES)
void addNewCertificate(std::shared_ptr<DNSCryptCertificatePair>& newCert, bool reload=false);
- pthread_rwlock_t d_lock;
+ ReadWriteLock d_lock;
std::vector<std::shared_ptr<DNSCryptCertificatePair>> d_certs;
std::vector<CertKeyPaths> d_certKeyPaths;
DNSName providerName;
#include "dnsdist-ecs.hh"
#include "ednsoptions.hh"
#include "ednssubnet.hh"
+#include "packetcache.hh"
DNSDistPacketCache::DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL, uint32_t minTTL, uint32_t tempFailureTTL, uint32_t maxNegativeTTL, uint32_t staleTTL, bool dontAge, uint32_t shards, bool deferrableInsertLock, bool parseECS): d_maxEntries(maxEntries), d_shardCount(shards), d_maxTTL(maxTTL), d_tempFailureTTL(tempFailureTTL), d_maxNegativeTTL(maxNegativeTTL), d_minTTL(minTTL), d_staleTTL(staleTTL), d_dontAge(dontAge), d_deferrableInsertLock(deferrableInsertLock), d_parseECS(parseECS)
{
bool DNSDistPacketCache::get(const DNSQuestion& dq, uint16_t consumed, uint16_t queryId, char* response, uint16_t* responseLen, uint32_t* keyOut, boost::optional<Netmask>& subnet, bool dnssecOK, uint32_t allowExpired, bool skipAging)
{
- std::string dnsQName(dq.qname->toDNSString());
+ const auto& dnsQName = dq.qname->getStorage();
uint32_t key = getKey(dnsQName, consumed, reinterpret_cast<const unsigned char*>(dq.dh), dq.len, dq.tcp);
- if (keyOut)
+ if (keyOut) {
*keyOut = key;
+ }
if (d_parseECS) {
getClientSubnet(reinterpret_cast<const char*>(dq.dh), consumed, dq.len, subnet);
return getDNSPacketMinTTL(packet, length, seenNoDataSOA);
}
-uint32_t DNSDistPacketCache::getKey(const std::string& qname, uint16_t consumed, const unsigned char* packet, uint16_t packetLen, bool tcp)
+uint32_t DNSDistPacketCache::getKey(const DNSName::string_t& qname, uint16_t consumed, const unsigned char* packet, uint16_t packetLen, bool tcp)
{
uint32_t result = 0;
/* skip the query ID */
- if (packetLen < sizeof(dnsheader))
- throw std::range_error("Computing packet cache key for an invalid packet size");
+ if (packetLen < sizeof(dnsheader)) {
+ throw std::range_error("Computing packet cache key for an invalid packet size (" + std::to_string(packetLen) +")");
+ }
+
result = burtle(packet + 2, sizeof(dnsheader) - 2, result);
- string lc(toLower(qname));
- result = burtle((const unsigned char*) lc.c_str(), lc.length(), result);
+ result = burtleCI((const unsigned char*) qname.c_str(), qname.length(), result);
if (packetLen < sizeof(dnsheader) + consumed) {
- throw std::range_error("Computing packet cache key for an invalid packet");
+ throw std::range_error("Computing packet cache key for an invalid packet (" + std::to_string(packetLen) + " < " + std::to_string(sizeof(dnsheader) + consumed) + ")");
}
if (packetLen > ((sizeof(dnsheader) + consumed))) {
- result = burtle(packet + sizeof(dnsheader) + consumed, packetLen - (sizeof(dnsheader) + consumed), result);
+ if (!d_cookieHashing) {
+ /* skip EDNS Cookie options if any */
+ result = PacketCache::hashAfterQname(pdns_string_view(reinterpret_cast<const char*>(packet), packetLen), result, sizeof(dnsheader) + consumed, false);
+ }
+ else {
+ result = burtle(packet + sizeof(dnsheader) + consumed, packetLen - (sizeof(dnsheader) + consumed), result);
+ }
}
result = burtle((const unsigned char*) &tcp, sizeof(tcp), result);
return result;
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);
ReadLock w(&d_shards.at(shardIndex).d_lock);
auto& map = d_shards[shardIndex].d_map;
- for(const auto entry : map) {
+ for (const auto& entry : map) {
const CacheValue& value = entry.second;
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;
}
uint64_t dump(int fd);
bool isECSParsingEnabled() const { return d_parseECS; }
+ bool isCookieHashingEnabled() const { return d_cookieHashing; }
bool keepStaleData() const
{
d_keepStaleData = keep;
}
+ void setCookieHashing(bool hashing)
+ {
+ d_cookieHashing = hashing;
+ }
+
+ void setECSParsingEnabled(bool enabled)
+ {
+ d_parseECS = enabled;
+ }
+
+ uint32_t getKey(const DNSName::string_t& qname, uint16_t consumed, const unsigned char* packet, uint16_t packetLen, bool tcp);
+
static uint32_t getMinTTL(const char* packet, uint16_t length, bool* seenNoDataSOA);
- static uint32_t getKey(const std::string& qname, uint16_t consumed, const unsigned char* packet, uint16_t packetLen, bool tcp);
static bool getClientSubnet(const char* packet, unsigned int consumed, uint16_t len, boost::optional<Netmask>& subnet);
private:
public:
CacheShard(): d_entriesCount(0)
{
- pthread_rwlock_init(&d_lock, nullptr);
}
CacheShard(const CacheShard& old): d_entriesCount(0)
{
- pthread_rwlock_init(&d_lock, nullptr);
- }
- ~CacheShard() {
- pthread_rwlock_destroy(&d_lock);
}
+
void setSize(size_t maxSize)
{
d_map.reserve(maxSize);
}
std::unordered_map<uint32_t,CacheValue> d_map;
- pthread_rwlock_t d_lock;
+ ReadWriteLock d_lock;
std::atomic<uint64_t> d_entriesCount;
};
bool d_deferrableInsertLock;
bool d_parseECS;
bool d_keepStaleData{false};
+ bool d_cookieHashing{false};
};
const auto& server = conf.server;
const std::string& namespace_name = conf.namespace_name;
std::string hostname = conf.ourname;
- if(hostname.empty()) {
- char tmp[80];
- memset(tmp, 0, sizeof(tmp));
- gethostname(tmp, sizeof(tmp));
- char *p = strchr(tmp, '.');
- if(p) *p=0;
- hostname=tmp;
- boost::replace_all(hostname, ".", "_");
+ if (hostname.empty()) {
+ try {
+ hostname = getCarbonHostName();
+ }
+ catch(const std::exception& e) {
+ throw std::runtime_error(std::string("The 'ourname' setting in 'carbonServer()' has not been set and we are unable to determine the system's hostname: ") + e.what());
+ }
}
const std::string& instance_name = conf.instance_name;
}
auto states = g_dstates.getLocal();
for(const auto& state : *states) {
- string serverName = state->getName().empty() ? (state->remote.toString() + ":" + std::to_string(state->remote.getPort())) : state->getName();
+ string serverName = state->getName().empty() ? state->remote.toStringWithPort() : state->getName();
boost::replace_all(serverName, ".", "_");
const string base = namespace_name + "." + hostname + "." + instance_name + ".servers." + serverName + ".";
str<<base<<"queries" << ' ' << state->queries.load() << " " << now << "\r\n";
if (front->udpFD == -1 && front->tcpFD == -1)
continue;
- string frontName = front->local.toString() + ":" + std::to_string(front->local.getPort()) + (front->udpFD >= 0 ? "_udp" : "_tcp");
+ string frontName = front->local.toStringWithPort() + (front->udpFD >= 0 ? "_udp" : "_tcp");
boost::replace_all(frontName, ".", "_");
auto dupPair = frontendDuplicates.insert({frontName, 1});
if (!dupPair.second) {
#include <thread>
#if defined (__OpenBSD__) || defined(__NetBSD__)
+// If this is not undeffed, __attribute__ wil be redefined by /usr/include/readline/rlstdc.h
+#undef __STRICT_ANSI__
#include <readline/readline.h>
#include <readline/history.h>
#else
}
string histfile = historyFile();
- set<string> dupper;
{
ifstream history(histfile);
string line;
void doConsole()
{
string histfile = historyFile(true);
- set<string> dupper;
{
ifstream history(histfile);
string line;
{ "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" },
{ "addDOHLocal", true, "addr, certFile, keyFile [, urls [, vars]]", "listen to incoming DNS over HTTPS queries on the specified address using the specified certificate and key. The last two parameters are tables" },
{ "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
- { "addDynBlockSMT", true, "names, msessage[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
+ { "addDynBlockSMT", true, "names, message[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
{ "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" },
{ "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" },
{ "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" },
{ "AndRule", true, "list of DNS rules", "matches if all sub-rules matches" },
{ "benchRule", true, "DNS Rule [, iterations [, suffix]]", "bench the specified DNS rule" },
{ "carbonServer", true, "serverIP, [ourname], [interval]", "report statistics to serverIP using our hostname, or 'ourname' if provided, every 'interval' seconds" },
- { "controlSocket", true, "addr", "open a control socket on this address / connect to this address in client mode" },
+ { "clearConsoleHistory", true, "", "clear the internal (in-memory) history of console commands" },
{ "clearDynBlocks", true, "", "clear all dynamic blocks" },
{ "clearQueryCounters", true, "", "clears the query counter buffer" },
{ "clearRules", true, "", "remove all current rules" },
+ { "controlSocket", true, "addr", "open a control socket on this address / connect to this address in client mode" },
{ "ContinueAction", true, "action", "execute the specified action and continue the processing of the remaining rules, regardless of the return of the action" },
{ "DelayAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
{ "DelayResponseAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
{ "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'" },
{ "newDNSName", true, "name", "make a DNSName based on this .-terminated name" },
{ "newDNSNameSet", true, "", "returns a new DNSNameSet" },
{ "newDynBPFFilter", true, "bpf", "Return a new dynamic eBPF filter associated to a given BPF Filter" },
- { "newFrameStreamTcpLogger", true, "addr", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
- { "newFrameStreamUnixLogger", true, "socket", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
+ { "newFrameStreamTcpLogger", true, "addr [, options]", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
+ { "newFrameStreamUnixLogger", true, "socket [, options]", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
#ifdef HAVE_LMDB
{ "newLMDBKVStore", true, "fname, dbName", "Return a new KeyValueStore object associated to the corresponding LMDB database" },
#endif
{ "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" },
{ "setMaxTCPQueriesPerConnection", true, "n", "set the maximum number of queries in an incoming TCP connection. 0 means unlimited" },
{ "setMaxTCPQueuedConnections", true, "n", "set the maximum number of TCP connections queued (waiting to be picked up by a client thread)" },
{ "setMaxUDPOutstanding", true, "n", "set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time and defaults to 65535" },
- { "SetNegativeAndSOAAction", "true", "nxd, zone, ttl, mname, rname, serial, refresh, retry, expire, minimum [, options]", "Turn a query into a NXDomain or NoData answer and sets a SOA record in the additional section" },
+ { "SetNegativeAndSOAAction", true, "nxd, zone, ttl, mname, rname, serial, refresh, retry, expire, minimum [, options]", "Turn a query into a NXDomain or NoData answer and sets a SOA record in the additional section" },
{ "setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses" },
{ "setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy" },
- { "setPoolServerPolicyLua", true, "name, func, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
+ { "setPoolServerPolicyLua", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
+ { "setPoolServerPolicyLuaFFI", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
+ { "setPoolServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy for this pool to one named 'name' and returned by the Lua FFI code passed in 'code'" },
{ "setPreserveTrailingData", true, "bool", "set whether trailing data should be preserved while adding ECS or XPF records to incoming queries" },
{ "setQueryCount", true, "bool", "set whether queries should be counted" },
{ "setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted" },
{ "setServerPolicy", true, "policy", "set server selection policy to that policy" },
{ "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" },
{ "setServerPolicyLuaFFI", true, "name, function", "set server selection policy to one named 'name' and provided by the Lua FFI 'function'" },
+ { "setServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy to one named 'name' and returned by the Lua FFI code passed in 'code'" },
{ "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" },
{ "setStaleCacheEntriesTTL", true, "n", "allows using cache entries expired for at most n seconds when there is no backend available to answer for a query" },
{ "setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON" },
{ "showTLSErrorCounters", true, "", "show metrics about TLS handshake failures" },
{ "showVersion", true, "", "show the current version" },
{ "shutdown", true, "", "shut down `dnsdist`" },
+ { "SetProxyProtocolValuesAction", true, "values", "Set the Proxy-Protocol values for this queries to 'values'" },
{ "SkipCacheAction", true, "", "Don’t lookup the cache for this query, don’t store the answer" },
{ "SNIRule", true, "name", "Create a rule which matches on the incoming TLS SNI value, if any (DoT or DoH)" },
{ "snmpAgent", true, "enableTraps [, masterSocket]", "enable `SNMP` support. `enableTraps` is a boolean indicating whether traps should be sent and `masterSocket` an optional string specifying how to connect to the master agent"},
{ "topCacheHitResponseRule", true, "", "move the last cache hit response rule to the first position" },
{ "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" },
{ "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" },
- { "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=ServFail), as grouped when optionally cut down to 'labels' labels" },
+ { "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=NXDomain), as grouped when optionally cut down to 'labels' labels" },
{ "topResponseRule", true, "", "move the last response rule to the first position" },
{ "topRule", true, "", "move the last rule to the first position" },
{ "topSelfAnsweredResponseRule", true, "", "move the last self-answered response rule to the first position" },
};
extern "C" {
-char* my_generator(const char* text, int state)
+static char* my_generator(const char* text, int state)
{
string t(text);
/* to keep it readable, we try to keep only 4 keywords per line
close(fd);
errlog("Control connection died: %s", e.what());
}
+
+void clearConsoleHistory()
+{
+ clear_history();
+ g_confDelta.clear();
+}
char** my_completion( const char * text , int start, int end);
}
void controlThread(int fd, ComboAddress local);
+void clearConsoleHistory();
/* 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;
dq.ednsOptions = std::make_shared<std::map<uint16_t, EDNSOptionView> >();
- if (ntohs(dq.dh->ancount) != 0 || ntohs(dq.dh->nscount) != 0 || (ntohs(dq.dh->arcount) != 0 && ntohs(dq.dh->arcount) != 1)) {
+ if (ntohs(dq.dh->arcount) == 0) {
+ /* nothing in additional so no EDNS */
+ return false;
+ }
+
+ if (ntohs(dq.dh->ancount) != 0 || ntohs(dq.dh->nscount) != 0 || ntohs(dq.dh->arcount) > 1) {
return slowParseEDNSOptions(reinterpret_cast<const char*>(dq.dh), dq.len, dq.ednsOptions);
}
struct DownstreamState;
-struct ServerPolicy
+struct PerThreadPoliciesState;
+
+class ServerPolicy
{
+public:
template <class T> using NumberedVector = std::vector<std::pair<unsigned int, T> >;
using NumberedServerVector = NumberedVector<shared_ptr<DownstreamState>>;
typedef std::function<shared_ptr<DownstreamState>(const NumberedServerVector& servers, const DNSQuestion*)> policyfunc_t;
typedef std::function<unsigned int(dnsdist_ffi_servers_list_t* servers, dnsdist_ffi_dnsquestion_t* dq)> ffipolicyfunc_t;
- ServerPolicy(const std::string& name_, policyfunc_t policy_, bool isLua_): name(name_), policy(policy_), isLua(isLua_)
+ ServerPolicy(const std::string& name_, policyfunc_t policy_, bool isLua_): d_name(name_), d_policy(policy_), d_isLua(isLua_)
{
}
- ServerPolicy(const std::string& name_, ffipolicyfunc_t policy_): name(name_), ffipolicy(policy_), isLua(true), isFFI(true)
+
+ ServerPolicy(const std::string& name_, ffipolicyfunc_t policy_): d_name(name_), d_ffipolicy(policy_), d_isLua(true), d_isFFI(true)
{
}
+
+ /* create a per-thread FFI policy */
+ ServerPolicy(const std::string& name_, const std::string& code);
+
ServerPolicy()
{
}
- string name;
- policyfunc_t policy;
- ffipolicyfunc_t ffipolicy;
- bool isLua{false};
- bool isFFI{false};
+ std::shared_ptr<DownstreamState> getSelectedBackend(const ServerPolicy::NumberedServerVector& servers, DNSQuestion& dq) const;
+
+ const std::string& getName() const
+ {
+ return d_name;
+ }
std::string toString() const {
- return string("ServerPolicy") + (isLua ? " (Lua)" : "") + " \"" + name + "\"";
+ return string("ServerPolicy") + (d_isLua ? " (Lua)" : "") + " \"" + d_name + "\"";
}
+
+private:
+ struct PerThreadState
+ {
+ LuaContext d_luaContext;
+ std::unordered_map<std::string, ffipolicyfunc_t> d_policies;
+ bool d_initialized{false};
+ };
+
+ const ffipolicyfunc_t& getPerThreadPolicy() const;
+ static thread_local PerThreadState t_perThreadState;
+
+
+public:
+ std::string d_name;
+ std::string d_perThreadPolicyCode;
+
+ policyfunc_t d_policy;
+ ffipolicyfunc_t d_ffipolicy;
+
+ bool d_isLua{false};
+ bool d_isFFI{false};
+ bool d_isPerThread{false};
};
struct ServerPool;
-using pools_t=map<std::string,std::shared_ptr<ServerPool>>;
+using pools_t = map<std::string, std::shared_ptr<ServerPool>>;
std::shared_ptr<ServerPool> getPool(const pools_t& pools, const std::string& poolName);
std::shared_ptr<ServerPool> createPoolIfNotExists(pools_t& pools, const string& poolName);
void setPoolPolicy(pools_t& pools, const string& poolName, std::shared_ptr<ServerPolicy> policy);
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);
std::shared_ptr<DownstreamState> chashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
std::shared_ptr<DownstreamState> chashedFromHash(const ServerPolicy::NumberedServerVector& servers, size_t hash);
std::shared_ptr<DownstreamState> roundrobin(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
-std::shared_ptr<DownstreamState> getSelectedBackendFromPolicy(const ServerPolicy& policy, const ServerPolicy::NumberedServerVector& servers, DNSQuestion& dq);
extern double g_consistentHashBalancingFactor;
extern double g_weightedBalancingFactor;
func_t d_func;
};
+thread_local std::default_random_engine SpoofAction::t_randomEngine;
+
DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
{
uint16_t qtype = dq->qtype;
uint16_t numberOfRecords = 0;
if (!d_cname.empty()) {
qtype = QType::CNAME;
- totrdatalen += d_cname.toDNSString().size();
+ totrdatalen += d_cname.getStorage().size();
numberOfRecords = 1;
} else if (!d_rawResponse.empty()) {
totrdatalen += d_rawResponse.size();
}
}
- if(addrs.size() > 1)
- random_shuffle(addrs.begin(), addrs.end());
+ if (addrs.size() > 1) {
+ shuffle(addrs.begin(), addrs.end(), t_randomEngine);
+ }
unsigned int consumed=0;
DNSName ignore((char*)dq->dh, dq->len, sizeof(dnsheader), false, 0, 0, &consumed);
bool raw = false;
if (qtype == QType::CNAME) {
- const std::string wireData = d_cname.toDNSString(); // Note! This doesn't do compression!
+ const auto& wireData = d_cname.getStorage(); // Note! This doesn't do compression!
uint16_t rdataLen = htons(wireData.length());
qtype = htons(qtype);
memcpy(&recordstart[2], &qtype, sizeof(qtype));
}
else {
if (d_binary) {
- std::string out = dq->qname->toDNSString();
+ const auto& out = dq->qname->getStorage();
if (d_includeTimestamp) {
uint64_t tv_sec = static_cast<uint64_t>(dq->queryTime->tv_sec);
uint32_t tv_nsec = static_cast<uint32_t>(dq->queryTime->tv_nsec);
bool d_nxd;
};
+class SetProxyProtocolValuesAction : public DNSAction
+{
+public:
+ SetProxyProtocolValuesAction(const std::vector<std::pair<uint8_t, std::string>>& values)
+ {
+ d_values.reserve(values.size());
+ for (const auto& value : values) {
+ d_values.push_back({value.second, value.first});
+ }
+ }
+
+ DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
+ {
+ if (!dq->proxyProtocolValues) {
+ dq->proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>();
+ }
+
+ *(dq->proxyProtocolValues) = d_values;
+
+ return Action::None;
+ }
+
+ std::string toString() const override
+ {
+ return "set Proxy-Protocol values";
+ }
+
+private:
+ std::vector<ProxyProtocolValue> d_values;
+};
+
template<typename T, typename ActionT>
static void addAction(GlobalStateHolder<vector<T> > *someRulActions, const luadnsrule_t& var, const std::shared_ptr<ActionT>& action, boost::optional<luaruleparams_t>& params) {
setLuaSideEffect();
}
}
-void setupLuaActions()
+void setupLuaActions(LuaContext& luaCtx)
{
- g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action, boost::optional<luaruleparams_t> params) {
+ luaCtx.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action, boost::optional<luaruleparams_t> params) {
boost::uuids::uuid uuid;
uint64_t creationOrder;
parseRuleParams(params, uuid, creationOrder);
return std::make_shared<DNSDistRuleAction>(ra);
});
- g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
+ luaCtx.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
if (era.type() != typeid(std::shared_ptr<DNSAction>)) {
throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?");
}
addAction(&g_rulactions, var, boost::get<std::shared_ptr<DNSAction> >(era), params);
});
- g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
+ luaCtx.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era, boost::optional<luaruleparams_t> params) {
if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
}
addAction(&g_resprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
});
- g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
+ luaCtx.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
throw std::runtime_error("addCacheHitResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
}
addAction(&g_cachehitresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
});
- g_lua.writeFunction("addSelfAnsweredResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
+ luaCtx.writeFunction("addSelfAnsweredResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
throw std::runtime_error("addSelfAnsweredResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
}
addAction(&g_selfansweredresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
});
- g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
+ luaCtx.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
setLuaNoSideEffect();
auto stats = ta.getStats();
for(const auto& s : stats) {
}
});
- g_lua.writeFunction("getAction", [](unsigned int num) {
+ luaCtx.writeFunction("getAction", [](unsigned int num) {
setLuaNoSideEffect();
boost::optional<std::shared_ptr<DNSAction>> ret;
auto rulactions = g_rulactions.getCopy();
return ret;
});
- g_lua.registerFunction("getStats", &DNSAction::getStats);
+ luaCtx.registerFunction("getStats", &DNSAction::getStats);
- g_lua.writeFunction("LuaAction", [](LuaAction::func_t func) {
+ luaCtx.writeFunction("LuaAction", [](LuaAction::func_t func) {
setLuaSideEffect();
return std::shared_ptr<DNSAction>(new LuaAction(func));
});
- g_lua.writeFunction("LuaFFIAction", [](LuaFFIAction::func_t func) {
+ luaCtx.writeFunction("LuaFFIAction", [](LuaFFIAction::func_t func) {
setLuaSideEffect();
return std::shared_ptr<DNSAction>(new LuaFFIAction(func));
});
- g_lua.writeFunction("NoRecurseAction", []() {
+ luaCtx.writeFunction("NoRecurseAction", []() {
return std::shared_ptr<DNSAction>(new NoRecurseAction);
});
- g_lua.writeFunction("MacAddrAction", [](int code) {
+ luaCtx.writeFunction("MacAddrAction", [](int code) {
return std::shared_ptr<DNSAction>(new MacAddrAction(code));
});
- g_lua.writeFunction("PoolAction", [](const std::string& a) {
+ luaCtx.writeFunction("PoolAction", [](const std::string& a) {
return std::shared_ptr<DNSAction>(new PoolAction(a));
});
- g_lua.writeFunction("QPSAction", [](int limit) {
+ luaCtx.writeFunction("QPSAction", [](int limit) {
return std::shared_ptr<DNSAction>(new QPSAction(limit));
});
- g_lua.writeFunction("QPSPoolAction", [](int limit, const std::string& a) {
+ luaCtx.writeFunction("QPSPoolAction", [](int limit, const std::string& a) {
return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
});
- g_lua.writeFunction("SpoofAction", [](boost::variant<std::string,vector<pair<int, std::string>>> inp, boost::optional<std::string> b, boost::optional<responseParams_t> vars) {
+ luaCtx.writeFunction("SpoofAction", [](boost::variant<std::string,vector<pair<int, std::string>>> inp, boost::optional<std::string> b, boost::optional<responseParams_t> vars) {
vector<ComboAddress> addrs;
if(auto s = boost::get<std::string>(&inp))
addrs.push_back(ComboAddress(*s));
return ret;
});
- g_lua.writeFunction("SpoofCNAMEAction", [](const std::string& a, boost::optional<responseParams_t> vars) {
+ luaCtx.writeFunction("SpoofCNAMEAction", [](const std::string& a, boost::optional<responseParams_t> vars) {
auto ret = std::shared_ptr<DNSAction>(new SpoofAction(DNSName(a)));
auto sa = std::dynamic_pointer_cast<SpoofAction>(ret);
parseResponseConfig(vars, sa->d_responseConfig);
return ret;
});
- g_lua.writeFunction("SpoofRawAction", [](const std::string& raw, boost::optional<responseParams_t> vars) {
+ luaCtx.writeFunction("SpoofRawAction", [](const std::string& raw, boost::optional<responseParams_t> vars) {
auto ret = std::shared_ptr<DNSAction>(new SpoofAction(raw));
auto sa = std::dynamic_pointer_cast<SpoofAction>(ret);
parseResponseConfig(vars, sa->d_responseConfig);
return ret;
});
- g_lua.writeFunction("DropAction", []() {
+ luaCtx.writeFunction("DropAction", []() {
return std::shared_ptr<DNSAction>(new DropAction);
});
- g_lua.writeFunction("AllowAction", []() {
+ luaCtx.writeFunction("AllowAction", []() {
return std::shared_ptr<DNSAction>(new AllowAction);
});
- g_lua.writeFunction("NoneAction", []() {
+ luaCtx.writeFunction("NoneAction", []() {
return std::shared_ptr<DNSAction>(new NoneAction);
});
- g_lua.writeFunction("DelayAction", [](int msec) {
+ luaCtx.writeFunction("DelayAction", [](int msec) {
return std::shared_ptr<DNSAction>(new DelayAction(msec));
});
- g_lua.writeFunction("TCAction", []() {
+ luaCtx.writeFunction("TCAction", []() {
return std::shared_ptr<DNSAction>(new TCAction);
});
- g_lua.writeFunction("DisableValidationAction", []() {
+ luaCtx.writeFunction("DisableValidationAction", []() {
return std::shared_ptr<DNSAction>(new DisableValidationAction);
});
- g_lua.writeFunction("LogAction", [](boost::optional<std::string> fname, boost::optional<bool> binary, boost::optional<bool> append, boost::optional<bool> buffered, boost::optional<bool> verboseOnly, boost::optional<bool> includeTimestamp) {
+ luaCtx.writeFunction("LogAction", [](boost::optional<std::string> fname, boost::optional<bool> binary, boost::optional<bool> append, boost::optional<bool> buffered, boost::optional<bool> verboseOnly, boost::optional<bool> includeTimestamp) {
return std::shared_ptr<DNSAction>(new LogAction(fname ? *fname : "", binary ? *binary : true, append ? *append : false, buffered ? *buffered : false, verboseOnly ? *verboseOnly : true, includeTimestamp ? *includeTimestamp : false));
});
- g_lua.writeFunction("LogResponseAction", [](boost::optional<std::string> fname, boost::optional<bool> append, boost::optional<bool> buffered, boost::optional<bool> verboseOnly, boost::optional<bool> includeTimestamp) {
+ luaCtx.writeFunction("LogResponseAction", [](boost::optional<std::string> fname, boost::optional<bool> append, boost::optional<bool> buffered, boost::optional<bool> verboseOnly, boost::optional<bool> includeTimestamp) {
return std::shared_ptr<DNSResponseAction>(new LogResponseAction(fname ? *fname : "", append ? *append : false, buffered ? *buffered : false, verboseOnly ? *verboseOnly : true, includeTimestamp ? *includeTimestamp : false));
});
- g_lua.writeFunction("RCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
+ luaCtx.writeFunction("RCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
auto ret = std::shared_ptr<DNSAction>(new RCodeAction(rcode));
auto rca = std::dynamic_pointer_cast<RCodeAction>(ret);
parseResponseConfig(vars, rca->d_responseConfig);
return ret;
});
- g_lua.writeFunction("ERCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
+ luaCtx.writeFunction("ERCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
auto ret = std::shared_ptr<DNSAction>(new ERCodeAction(rcode));
auto erca = std::dynamic_pointer_cast<ERCodeAction>(ret);
parseResponseConfig(vars, erca->d_responseConfig);
return ret;
});
- g_lua.writeFunction("SkipCacheAction", []() {
+ luaCtx.writeFunction("SkipCacheAction", []() {
return std::shared_ptr<DNSAction>(new SkipCacheAction);
});
- g_lua.writeFunction("TempFailureCacheTTLAction", [](int maxTTL) {
+ luaCtx.writeFunction("TempFailureCacheTTLAction", [](int maxTTL) {
return std::shared_ptr<DNSAction>(new TempFailureCacheTTLAction(maxTTL));
});
- g_lua.writeFunction("DropResponseAction", []() {
+ luaCtx.writeFunction("DropResponseAction", []() {
return std::shared_ptr<DNSResponseAction>(new DropResponseAction);
});
- g_lua.writeFunction("AllowResponseAction", []() {
+ luaCtx.writeFunction("AllowResponseAction", []() {
return std::shared_ptr<DNSResponseAction>(new AllowResponseAction);
});
- g_lua.writeFunction("DelayResponseAction", [](int msec) {
+ luaCtx.writeFunction("DelayResponseAction", [](int msec) {
return std::shared_ptr<DNSResponseAction>(new DelayResponseAction(msec));
});
- g_lua.writeFunction("LuaResponseAction", [](LuaResponseAction::func_t func) {
+ luaCtx.writeFunction("LuaResponseAction", [](LuaResponseAction::func_t func) {
setLuaSideEffect();
return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
});
- g_lua.writeFunction("LuaFFIResponseAction", [](LuaFFIResponseAction::func_t func) {
+ luaCtx.writeFunction("LuaFFIResponseAction", [](LuaFFIResponseAction::func_t func) {
setLuaSideEffect();
return std::shared_ptr<DNSResponseAction>(new LuaFFIResponseAction(func));
});
- g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSQuestion*, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<std::unordered_map<std::string, std::string>> vars) {
+ luaCtx.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSQuestion*, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<std::unordered_map<std::string, std::string>> vars) {
if (logger) {
// avoids potentially-evaluated-expression warning with clang.
RemoteLoggerInterface& rl = *logger.get();
#endif
});
- g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSResponse*, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME, boost::optional<std::unordered_map<std::string, std::string>> vars) {
+ luaCtx.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSResponse*, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME, boost::optional<std::unordered_map<std::string, std::string>> vars) {
if (logger) {
// avoids potentially-evaluated-expression warning with clang.
RemoteLoggerInterface& rl = *logger.get();
#endif
});
- g_lua.writeFunction("DnstapLogAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSQuestion*, DnstapMessage*)> > alterFunc) {
+ luaCtx.writeFunction("DnstapLogAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSQuestion*, DnstapMessage*)> > alterFunc) {
#ifdef HAVE_PROTOBUF
return std::shared_ptr<DNSAction>(new DnstapLogAction(identity, logger, alterFunc));
#else
#endif
});
- g_lua.writeFunction("DnstapLogResponseAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSResponse*, DnstapMessage*)> > alterFunc) {
+ luaCtx.writeFunction("DnstapLogResponseAction", [](const std::string& identity, std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSResponse*, DnstapMessage*)> > alterFunc) {
#ifdef HAVE_PROTOBUF
return std::shared_ptr<DNSResponseAction>(new DnstapLogResponseAction(identity, logger, alterFunc));
#else
#endif
});
- g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
+ luaCtx.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
});
- g_lua.writeFunction("ECSPrefixLengthAction", [](uint16_t v4PrefixLength, uint16_t v6PrefixLength) {
+ luaCtx.writeFunction("ECSPrefixLengthAction", [](uint16_t v4PrefixLength, uint16_t v6PrefixLength) {
return std::shared_ptr<DNSAction>(new ECSPrefixLengthAction(v4PrefixLength, v6PrefixLength));
});
- g_lua.writeFunction("ECSOverrideAction", [](bool ecsOverride) {
+ luaCtx.writeFunction("ECSOverrideAction", [](bool ecsOverride) {
return std::shared_ptr<DNSAction>(new ECSOverrideAction(ecsOverride));
});
- g_lua.writeFunction("DisableECSAction", []() {
+ luaCtx.writeFunction("DisableECSAction", []() {
return std::shared_ptr<DNSAction>(new DisableECSAction());
});
- g_lua.writeFunction("SetECSAction", [](const std::string v4, boost::optional<std::string> v6) {
+ luaCtx.writeFunction("SetECSAction", [](const std::string v4, boost::optional<std::string> v6) {
if (v6) {
return std::shared_ptr<DNSAction>(new SetECSAction(Netmask(v4), Netmask(*v6)));
}
return std::shared_ptr<DNSAction>(new SetECSAction(Netmask(v4)));
});
- g_lua.writeFunction("SNMPTrapAction", [](boost::optional<std::string> reason) {
+ luaCtx.writeFunction("SNMPTrapAction", [](boost::optional<std::string> reason) {
#ifdef HAVE_NET_SNMP
return std::shared_ptr<DNSAction>(new SNMPTrapAction(reason ? *reason : ""));
#else
#endif /* HAVE_NET_SNMP */
});
- g_lua.writeFunction("SNMPTrapResponseAction", [](boost::optional<std::string> reason) {
+ luaCtx.writeFunction("SNMPTrapResponseAction", [](boost::optional<std::string> reason) {
#ifdef HAVE_NET_SNMP
return std::shared_ptr<DNSResponseAction>(new SNMPTrapResponseAction(reason ? *reason : ""));
#else
#endif /* HAVE_NET_SNMP */
});
- g_lua.writeFunction("TagAction", [](std::string tag, std::string value) {
+ luaCtx.writeFunction("TagAction", [](std::string tag, std::string value) {
return std::shared_ptr<DNSAction>(new TagAction(tag, value));
});
- g_lua.writeFunction("TagResponseAction", [](std::string tag, std::string value) {
+ luaCtx.writeFunction("TagResponseAction", [](std::string tag, std::string value) {
return std::shared_ptr<DNSResponseAction>(new TagResponseAction(tag, value));
});
- g_lua.writeFunction("ContinueAction", [](std::shared_ptr<DNSAction> action) {
+ luaCtx.writeFunction("ContinueAction", [](std::shared_ptr<DNSAction> action) {
return std::shared_ptr<DNSAction>(new ContinueAction(action));
});
#ifdef HAVE_DNS_OVER_HTTPS
- g_lua.writeFunction("HTTPStatusAction", [](uint16_t status, std::string body, boost::optional<std::string> contentType, boost::optional<responseParams_t> vars) {
+ luaCtx.writeFunction("HTTPStatusAction", [](uint16_t status, std::string body, boost::optional<std::string> contentType, boost::optional<responseParams_t> vars) {
auto ret = std::shared_ptr<DNSAction>(new HTTPStatusAction(status, body, contentType ? *contentType : ""));
auto hsa = std::dynamic_pointer_cast<HTTPStatusAction>(ret);
parseResponseConfig(vars, hsa->d_responseConfig);
});
#endif /* HAVE_DNS_OVER_HTTPS */
- g_lua.writeFunction("KeyValueStoreLookupAction", [](std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag) {
+ luaCtx.writeFunction("KeyValueStoreLookupAction", [](std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag) {
return std::shared_ptr<DNSAction>(new KeyValueStoreLookupAction(kvs, lookupKey, destinationTag));
});
- g_lua.writeFunction("SetNegativeAndSOAAction", [](bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum, boost::optional<responseParams_t> vars) {
+ luaCtx.writeFunction("SetNegativeAndSOAAction", [](bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum, boost::optional<responseParams_t> vars) {
auto ret = std::shared_ptr<DNSAction>(new SetNegativeAndSOAAction(nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), serial, refresh, retry, expire, minimum));
auto action = std::dynamic_pointer_cast<SetNegativeAndSOAAction>(ret);
parseResponseConfig(vars, action->d_responseConfig);
return ret;
});
+
+ luaCtx.writeFunction("SetProxyProtocolValuesAction", [](const std::vector<std::pair<uint8_t, std::string>>& values) {
+ return std::shared_ptr<DNSAction>(new SetProxyProtocolValuesAction(values));
+ });
}
#include "dnsdist-lua.hh"
#include "dnsparser.hh"
-void setupLuaBindingsDNSQuestion()
+void setupLuaBindingsDNSQuestion(LuaContext& luaCtx)
{
/* DNSQuestion */
/* PowerDNS DNSQuestion compat */
- g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.local; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; });
- g_lua.registerMember<const DNSName (DNSQuestion::*)>("qname", [](const DNSQuestion& dq) -> const DNSName { return *dq.qname; }, [](DNSQuestion& dq, const DNSName newName) { (void) newName; });
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; });
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; });
- g_lua.registerMember<int (DNSQuestion::*)>("rcode", [](const DNSQuestion& dq) -> int { return dq.dh->rcode; }, [](DNSQuestion& dq, int newRCode) { dq.dh->rcode = newRCode; });
- g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.remote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; });
+ luaCtx.registerMember<const ComboAddress (DNSQuestion::*)>("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.local; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; });
+ luaCtx.registerMember<const DNSName (DNSQuestion::*)>("qname", [](const DNSQuestion& dq) -> const DNSName { return *dq.qname; }, [](DNSQuestion& dq, const DNSName newName) { (void) newName; });
+ luaCtx.registerMember<uint16_t (DNSQuestion::*)>("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; });
+ luaCtx.registerMember<uint16_t (DNSQuestion::*)>("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; });
+ luaCtx.registerMember<int (DNSQuestion::*)>("rcode", [](const DNSQuestion& dq) -> int { return dq.dh->rcode; }, [](DNSQuestion& dq, int newRCode) { dq.dh->rcode = newRCode; });
+ luaCtx.registerMember<const ComboAddress (DNSQuestion::*)>("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.remote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; });
/* DNSDist DNSQuestion */
- g_lua.registerMember("dh", &DNSQuestion::dh);
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("len", [](const DNSQuestion& dq) -> uint16_t { return dq.len; }, [](DNSQuestion& dq, uint16_t newlen) { dq.len = newlen; });
- g_lua.registerMember<uint8_t (DNSQuestion::*)>("opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void) newOpcode; });
- g_lua.registerMember<size_t (DNSQuestion::*)>("size", [](const DNSQuestion& dq) -> size_t { return dq.size; }, [](DNSQuestion& dq, size_t newSize) { (void) newSize; });
- g_lua.registerMember<bool (DNSQuestion::*)>("tcp", [](const DNSQuestion& dq) -> bool { return dq.tcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; });
- g_lua.registerMember<bool (DNSQuestion::*)>("skipCache", [](const DNSQuestion& dq) -> bool { return dq.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
- g_lua.registerMember<bool (DNSQuestion::*)>("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; });
- g_lua.registerMember<bool (DNSQuestion::*)>("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; });
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; });
- g_lua.registerMember<boost::optional<uint32_t> (DNSQuestion::*)>("tempFailureTTL",
+ luaCtx.registerMember("dh", &DNSQuestion::dh);
+ luaCtx.registerMember<uint16_t (DNSQuestion::*)>("len", [](const DNSQuestion& dq) -> uint16_t { return dq.len; }, [](DNSQuestion& dq, uint16_t newlen) { dq.len = newlen; });
+ luaCtx.registerMember<uint8_t (DNSQuestion::*)>("opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void) newOpcode; });
+ luaCtx.registerMember<size_t (DNSQuestion::*)>("size", [](const DNSQuestion& dq) -> size_t { return dq.size; }, [](DNSQuestion& dq, size_t newSize) { (void) newSize; });
+ luaCtx.registerMember<bool (DNSQuestion::*)>("tcp", [](const DNSQuestion& dq) -> bool { return dq.tcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; });
+ luaCtx.registerMember<bool (DNSQuestion::*)>("skipCache", [](const DNSQuestion& dq) -> bool { return dq.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
+ luaCtx.registerMember<bool (DNSQuestion::*)>("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; });
+ luaCtx.registerMember<bool (DNSQuestion::*)>("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; });
+ luaCtx.registerMember<uint16_t (DNSQuestion::*)>("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; });
+ luaCtx.registerMember<boost::optional<uint32_t> (DNSQuestion::*)>("tempFailureTTL",
[](const DNSQuestion& dq) -> boost::optional<uint32_t> {
return dq.tempFailureTTL;
},
dq.tempFailureTTL = newValue;
}
);
- g_lua.registerFunction<bool(DNSQuestion::*)()>("getDO", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<bool(DNSQuestion::*)()>("getDO", [](const DNSQuestion& dq) {
return getEDNSZ(dq) & EDNS_HEADER_FLAG_DO;
});
- g_lua.registerFunction<std::map<uint16_t, EDNSOptionView>(DNSQuestion::*)()>("getEDNSOptions", [](DNSQuestion& dq) {
+ luaCtx.registerFunction<std::map<uint16_t, EDNSOptionView>(DNSQuestion::*)()>("getEDNSOptions", [](DNSQuestion& dq) {
if (dq.ednsOptions == nullptr) {
parseEDNSOptions(dq);
}
return *dq.ednsOptions;
});
- g_lua.registerFunction<std::string(DNSQuestion::*)(void)>("getTrailingData", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<std::string(DNSQuestion::*)(void)>("getTrailingData", [](const DNSQuestion& dq) {
return dq.getTrailingData();
});
- g_lua.registerFunction<bool(DNSQuestion::*)(std::string)>("setTrailingData", [](DNSQuestion& dq, const std::string& tail) {
+ luaCtx.registerFunction<bool(DNSQuestion::*)(std::string)>("setTrailingData", [](DNSQuestion& dq, const std::string& tail) {
return dq.setTrailingData(tail);
});
- g_lua.registerFunction<std::string(DNSQuestion::*)()>("getServerNameIndication", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<std::string(DNSQuestion::*)()>("getServerNameIndication", [](const DNSQuestion& dq) {
return dq.sni;
});
- g_lua.registerFunction<void(DNSQuestion::*)(std::string)>("sendTrap", [](const DNSQuestion& dq, boost::optional<std::string> reason) {
+ luaCtx.registerFunction<void(DNSQuestion::*)(std::string)>("sendTrap", [](const DNSQuestion& dq, boost::optional<std::string> reason) {
#ifdef HAVE_NET_SNMP
if (g_snmpAgent && g_snmpTrapsEnabled) {
g_snmpAgent->sendDNSTrap(dq, reason ? *reason : "");
#endif /* HAVE_NET_SNMP */
});
- g_lua.registerFunction<void(DNSQuestion::*)(std::string, std::string)>("setTag", [](DNSQuestion& dq, const std::string& strLabel, const std::string& strValue) {
+ luaCtx.registerFunction<void(DNSQuestion::*)(std::string, std::string)>("setTag", [](DNSQuestion& dq, const std::string& strLabel, const std::string& strValue) {
if(dq.qTag == nullptr) {
dq.qTag = std::make_shared<QTag>();
}
dq.qTag->insert({strLabel, strValue});
});
- g_lua.registerFunction<void(DNSQuestion::*)(vector<pair<string, string>>)>("setTagArray", [](DNSQuestion& dq, const vector<pair<string, string>>&tags) {
+ luaCtx.registerFunction<void(DNSQuestion::*)(vector<pair<string, string>>)>("setTagArray", [](DNSQuestion& dq, const vector<pair<string, string>>&tags) {
if (!dq.qTag) {
dq.qTag = std::make_shared<QTag>();
}
dq.qTag->insert({tag.first, tag.second});
}
});
- g_lua.registerFunction<string(DNSQuestion::*)(std::string)>("getTag", [](const DNSQuestion& dq, const std::string& strLabel) {
+ luaCtx.registerFunction<string(DNSQuestion::*)(std::string)>("getTag", [](const DNSQuestion& dq, const std::string& strLabel) {
if (!dq.qTag) {
return string();
}
}
return it->second;
});
- g_lua.registerFunction<QTag(DNSQuestion::*)(void)>("getTagArray", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<QTag(DNSQuestion::*)(void)>("getTagArray", [](const DNSQuestion& dq) {
if (!dq.qTag) {
QTag empty;
return empty;
return *dq.qTag;
});
+ luaCtx.registerFunction<void(DNSQuestion::*)(std::vector<std::pair<uint8_t, std::string>>)>("setProxyProtocolValues", [](DNSQuestion& dq, const std::vector<std::pair<uint8_t, std::string>>& values) {
+ if (!dq.proxyProtocolValues) {
+ dq.proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>();
+ }
+
+ dq.proxyProtocolValues->clear();
+ dq.proxyProtocolValues->reserve(values.size());
+ for (const auto& value : values) {
+ dq.proxyProtocolValues->push_back({value.second, value.first});
+ }
+ });
+
/* LuaWrapper doesn't support inheritance */
- g_lua.registerMember<const ComboAddress (DNSResponse::*)>("localaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.local; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void) newLocal; });
- g_lua.registerMember<const DNSName (DNSResponse::*)>("qname", [](const DNSResponse& dq) -> const DNSName { return *dq.qname; }, [](DNSResponse& dq, const DNSName newName) { (void) newName; });
- g_lua.registerMember<uint16_t (DNSResponse::*)>("qtype", [](const DNSResponse& dq) -> uint16_t { return dq.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void) newType; });
- g_lua.registerMember<uint16_t (DNSResponse::*)>("qclass", [](const DNSResponse& dq) -> uint16_t { return dq.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void) newClass; });
- g_lua.registerMember<int (DNSResponse::*)>("rcode", [](const DNSResponse& dq) -> int { return dq.dh->rcode; }, [](DNSResponse& dq, int newRCode) { dq.dh->rcode = newRCode; });
- g_lua.registerMember<const ComboAddress (DNSResponse::*)>("remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.remote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void) newRemote; });
- g_lua.registerMember<dnsheader* (DNSResponse::*)>("dh", [](const DNSResponse& dr) -> dnsheader* { return dr.dh; }, [](DNSResponse& dr, dnsheader * newdh) { dr.dh = newdh; });
- g_lua.registerMember<uint16_t (DNSResponse::*)>("len", [](const DNSResponse& dq) -> uint16_t { return dq.len; }, [](DNSResponse& dq, uint16_t newlen) { dq.len = newlen; });
- g_lua.registerMember<uint8_t (DNSResponse::*)>("opcode", [](const DNSResponse& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void) newOpcode; });
- g_lua.registerMember<size_t (DNSResponse::*)>("size", [](const DNSResponse& dq) -> size_t { return dq.size; }, [](DNSResponse& dq, size_t newSize) { (void) newSize; });
- g_lua.registerMember<bool (DNSResponse::*)>("tcp", [](const DNSResponse& dq) -> bool { return dq.tcp; }, [](DNSResponse& dq, bool newTcp) { (void) newTcp; });
- g_lua.registerMember<bool (DNSResponse::*)>("skipCache", [](const DNSResponse& dq) -> bool { return dq.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
- g_lua.registerFunction<void(DNSResponse::*)(std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc)>("editTTLs", [](const DNSResponse& dr, std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc) {
+ luaCtx.registerMember<const ComboAddress (DNSResponse::*)>("localaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.local; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void) newLocal; });
+ luaCtx.registerMember<const DNSName (DNSResponse::*)>("qname", [](const DNSResponse& dq) -> const DNSName { return *dq.qname; }, [](DNSResponse& dq, const DNSName newName) { (void) newName; });
+ luaCtx.registerMember<uint16_t (DNSResponse::*)>("qtype", [](const DNSResponse& dq) -> uint16_t { return dq.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void) newType; });
+ luaCtx.registerMember<uint16_t (DNSResponse::*)>("qclass", [](const DNSResponse& dq) -> uint16_t { return dq.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void) newClass; });
+ luaCtx.registerMember<int (DNSResponse::*)>("rcode", [](const DNSResponse& dq) -> int { return dq.dh->rcode; }, [](DNSResponse& dq, int newRCode) { dq.dh->rcode = newRCode; });
+ luaCtx.registerMember<const ComboAddress (DNSResponse::*)>("remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.remote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void) newRemote; });
+ luaCtx.registerMember<dnsheader* (DNSResponse::*)>("dh", [](const DNSResponse& dr) -> dnsheader* { return dr.dh; }, [](DNSResponse& dr, dnsheader * newdh) { dr.dh = newdh; });
+ luaCtx.registerMember<uint16_t (DNSResponse::*)>("len", [](const DNSResponse& dq) -> uint16_t { return dq.len; }, [](DNSResponse& dq, uint16_t newlen) { dq.len = newlen; });
+ luaCtx.registerMember<uint8_t (DNSResponse::*)>("opcode", [](const DNSResponse& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void) newOpcode; });
+ luaCtx.registerMember<size_t (DNSResponse::*)>("size", [](const DNSResponse& dq) -> size_t { return dq.size; }, [](DNSResponse& dq, size_t newSize) { (void) newSize; });
+ luaCtx.registerMember<bool (DNSResponse::*)>("tcp", [](const DNSResponse& dq) -> bool { return dq.tcp; }, [](DNSResponse& dq, bool newTcp) { (void) newTcp; });
+ luaCtx.registerMember<bool (DNSResponse::*)>("skipCache", [](const DNSResponse& dq) -> bool { return dq.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
+ luaCtx.registerFunction<void(DNSResponse::*)(std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc)>("editTTLs", [](const DNSResponse& dr, std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc) {
editDNSPacketTTL((char*) dr.dh, dr.len, editFunc);
});
- g_lua.registerFunction<std::string(DNSResponse::*)(void)>("getTrailingData", [](const DNSResponse& dq) {
+ luaCtx.registerFunction<std::string(DNSResponse::*)(void)>("getTrailingData", [](const DNSResponse& dq) {
return dq.getTrailingData();
});
- g_lua.registerFunction<bool(DNSResponse::*)(std::string)>("setTrailingData", [](DNSResponse& dq, const std::string& tail) {
+ luaCtx.registerFunction<bool(DNSResponse::*)(std::string)>("setTrailingData", [](DNSResponse& dq, const std::string& tail) {
return dq.setTrailingData(tail);
});
- g_lua.registerFunction<void(DNSResponse::*)(std::string, std::string)>("setTag", [](DNSResponse& dr, const std::string& strLabel, const std::string& strValue) {
+ luaCtx.registerFunction<void(DNSResponse::*)(std::string, std::string)>("setTag", [](DNSResponse& dr, const std::string& strLabel, const std::string& strValue) {
if(dr.qTag == nullptr) {
dr.qTag = std::make_shared<QTag>();
}
dr.qTag->insert({strLabel, strValue});
});
- g_lua.registerFunction<void(DNSResponse::*)(vector<pair<string, string>>)>("setTagArray", [](DNSResponse& dr, const vector<pair<string, string>>&tags) {
+ luaCtx.registerFunction<void(DNSResponse::*)(vector<pair<string, string>>)>("setTagArray", [](DNSResponse& dr, const vector<pair<string, string>>&tags) {
if (!dr.qTag) {
dr.qTag = std::make_shared<QTag>();
}
dr.qTag->insert({tag.first, tag.second});
}
});
- g_lua.registerFunction<string(DNSResponse::*)(std::string)>("getTag", [](const DNSResponse& dr, const std::string& strLabel) {
+ luaCtx.registerFunction<string(DNSResponse::*)(std::string)>("getTag", [](const DNSResponse& dr, const std::string& strLabel) {
if (!dr.qTag) {
return string();
}
}
return it->second;
});
- g_lua.registerFunction<QTag(DNSResponse::*)(void)>("getTagArray", [](const DNSResponse& dr) {
+ luaCtx.registerFunction<QTag(DNSResponse::*)(void)>("getTagArray", [](const DNSResponse& dr) {
if (!dr.qTag) {
QTag empty;
return empty;
return *dr.qTag;
});
- g_lua.registerFunction<void(DNSResponse::*)(std::string)>("sendTrap", [](const DNSResponse& dr, boost::optional<std::string> reason) {
+ luaCtx.registerFunction<void(DNSResponse::*)(std::string)>("sendTrap", [](const DNSResponse& dr, boost::optional<std::string> reason) {
#ifdef HAVE_NET_SNMP
if (g_snmpAgent && g_snmpTrapsEnabled) {
g_snmpAgent->sendDNSTrap(dr, reason ? *reason : "");
});
#ifdef HAVE_DNS_OVER_HTTPS
- g_lua.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPPath", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPPath", [](const DNSQuestion& dq) {
if (dq.du == nullptr) {
return std::string();
}
return dq.du->getHTTPPath();
});
- g_lua.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPQueryString", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPQueryString", [](const DNSQuestion& dq) {
if (dq.du == nullptr) {
return std::string();
}
return dq.du->getHTTPQueryString();
});
- g_lua.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPHost", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPHost", [](const DNSQuestion& dq) {
if (dq.du == nullptr) {
return std::string();
}
return dq.du->getHTTPHost();
});
- g_lua.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPScheme", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<std::string(DNSQuestion::*)(void)>("getHTTPScheme", [](const DNSQuestion& dq) {
if (dq.du == nullptr) {
return std::string();
}
return dq.du->getHTTPScheme();
});
- g_lua.registerFunction<std::unordered_map<std::string, std::string>(DNSQuestion::*)(void)>("getHTTPHeaders", [](const DNSQuestion& dq) {
+ luaCtx.registerFunction<std::unordered_map<std::string, std::string>(DNSQuestion::*)(void)>("getHTTPHeaders", [](const DNSQuestion& dq) {
if (dq.du == nullptr) {
return std::unordered_map<std::string, std::string>();
}
return dq.du->getHTTPHeaders();
});
- g_lua.registerFunction<void(DNSQuestion::*)(uint16_t statusCode, const std::string& body, const boost::optional<std::string> contentType)>("setHTTPResponse", [](DNSQuestion& dq, uint16_t statusCode, const std::string& body, const boost::optional<std::string> contentType) {
+ luaCtx.registerFunction<void(DNSQuestion::*)(uint16_t statusCode, const std::string& body, const boost::optional<std::string> contentType)>("setHTTPResponse", [](DNSQuestion& dq, uint16_t statusCode, const std::string& body, const boost::optional<std::string> contentType) {
if (dq.du == nullptr) {
return;
}
});
#endif /* HAVE_DNS_OVER_HTTPS */
- g_lua.registerFunction<bool(DNSQuestion::*)(bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum)>("setNegativeAndAdditionalSOA", [](DNSQuestion& dq, bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) {
+ luaCtx.registerFunction<bool(DNSQuestion::*)(bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum)>("setNegativeAndAdditionalSOA", [](DNSQuestion& dq, bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) {
return setNegativeAndAdditionalSOA(dq, nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), serial, refresh, retry, expire, minimum);
});
}
#include "dolog.hh"
-void setupLuaBindings(bool client)
+void setupLuaBindings(LuaContext& luaCtx, bool client)
{
- g_lua.writeFunction("infolog", [](const string& arg) {
+ luaCtx.writeFunction("infolog", [](const string& arg) {
infolog("%s", arg);
});
- g_lua.writeFunction("errlog", [](const string& arg) {
+ luaCtx.writeFunction("errlog", [](const string& arg) {
errlog("%s", arg);
});
- g_lua.writeFunction("warnlog", [](const string& arg) {
+ luaCtx.writeFunction("warnlog", [](const string& arg) {
warnlog("%s", arg);
});
- g_lua.writeFunction("show", [](const string& arg) {
+ luaCtx.writeFunction("show", [](const string& arg) {
g_outputBuffer+=arg;
g_outputBuffer+="\n";
});
/* Exceptions */
- g_lua.registerFunction<string(std::exception_ptr::*)()>("__tostring", [](const std::exception_ptr& eptr) {
+ luaCtx.registerFunction<string(std::exception_ptr::*)()>("__tostring", [](const std::exception_ptr& eptr) {
try {
if (eptr) {
std::rethrow_exception(eptr);
return string("No exception");
});
/* ServerPolicy */
- g_lua.writeFunction("newServerPolicy", [](string name, ServerPolicy::policyfunc_t policy) { return std::make_shared<ServerPolicy>(name, policy, true);});
- g_lua.registerMember("name", &ServerPolicy::name);
- g_lua.registerMember("policy", &ServerPolicy::policy);
- g_lua.registerMember("ffipolicy", &ServerPolicy::ffipolicy);
- g_lua.registerMember("isLua", &ServerPolicy::isLua);
- g_lua.registerMember("isFFI", &ServerPolicy::isFFI);
- g_lua.registerFunction("toString", &ServerPolicy::toString);
-
- g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable, false});
- g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin, false});
- g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom, false});
- g_lua.writeVariable("whashed", ServerPolicy{"whashed", whashed, false});
- g_lua.writeVariable("chashed", ServerPolicy{"chashed", chashed, false});
- g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding, false});
+ luaCtx.writeFunction("newServerPolicy", [](string name, ServerPolicy::policyfunc_t policy) { return std::make_shared<ServerPolicy>(name, policy, true);});
+ luaCtx.registerMember("name", &ServerPolicy::d_name);
+ luaCtx.registerMember("policy", &ServerPolicy::d_policy);
+ luaCtx.registerMember("ffipolicy", &ServerPolicy::d_ffipolicy);
+ luaCtx.registerMember("isLua", &ServerPolicy::d_isLua);
+ luaCtx.registerMember("isFFI", &ServerPolicy::d_isFFI);
+ luaCtx.registerMember("isPerThread", &ServerPolicy::d_isPerThread);
+ luaCtx.registerFunction("toString", &ServerPolicy::toString);
+
+ luaCtx.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable, false});
+ luaCtx.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin, false});
+ luaCtx.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom, false});
+ luaCtx.writeVariable("whashed", ServerPolicy{"whashed", whashed, false});
+ luaCtx.writeVariable("chashed", ServerPolicy{"chashed", chashed, false});
+ luaCtx.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding, false});
/* ServerPool */
- g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)(std::shared_ptr<DNSDistPacketCache>)>("setCache", [](std::shared_ptr<ServerPool> pool, std::shared_ptr<DNSDistPacketCache> cache) {
+ luaCtx.registerFunction<void(std::shared_ptr<ServerPool>::*)(std::shared_ptr<DNSDistPacketCache>)>("setCache", [](std::shared_ptr<ServerPool> pool, std::shared_ptr<DNSDistPacketCache> cache) {
if (pool) {
pool->packetCache = cache;
}
});
- g_lua.registerFunction("getCache", &ServerPool::getCache);
- g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)()>("unsetCache", [](std::shared_ptr<ServerPool> pool) {
+ luaCtx.registerFunction("getCache", &ServerPool::getCache);
+ luaCtx.registerFunction<void(std::shared_ptr<ServerPool>::*)()>("unsetCache", [](std::shared_ptr<ServerPool> pool) {
if (pool) {
pool->packetCache = nullptr;
}
});
- g_lua.registerFunction("getECS", &ServerPool::getECS);
- g_lua.registerFunction("setECS", &ServerPool::setECS);
+ luaCtx.registerFunction("getECS", &ServerPool::getECS);
+ luaCtx.registerFunction("setECS", &ServerPool::setECS);
/* DownstreamState */
- g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
- g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("addPool", [](std::shared_ptr<DownstreamState> s, string pool) {
+ luaCtx.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
+ luaCtx.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("addPool", [](std::shared_ptr<DownstreamState> s, string pool) {
auto localPools = g_pools.getCopy();
addServerToPool(localPools, pool, s);
g_pools.setState(localPools);
s->pools.insert(pool);
});
- g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("rmPool", [](std::shared_ptr<DownstreamState> s, string pool) {
+ luaCtx.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("rmPool", [](std::shared_ptr<DownstreamState> s, string pool) {
auto localPools = g_pools.getCopy();
removeServerFromPool(localPools, pool, s);
g_pools.setState(localPools);
s->pools.erase(pool);
});
- g_lua.registerFunction<uint64_t(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { return s.outstanding.load(); });
- g_lua.registerFunction("isUp", &DownstreamState::isUp);
- g_lua.registerFunction("setDown", &DownstreamState::setDown);
- g_lua.registerFunction("setUp", &DownstreamState::setUp);
- g_lua.registerFunction<void(DownstreamState::*)(boost::optional<bool> newStatus)>("setAuto", [](DownstreamState& s, boost::optional<bool> newStatus) {
+ luaCtx.registerFunction<uint64_t(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { return s.outstanding.load(); });
+ luaCtx.registerFunction<double(DownstreamState::*)()>("getLatency", [](const DownstreamState& s) { return s.latencyUsec; });
+ luaCtx.registerFunction("isUp", &DownstreamState::isUp);
+ luaCtx.registerFunction("setDown", &DownstreamState::setDown);
+ luaCtx.registerFunction("setUp", &DownstreamState::setUp);
+ luaCtx.registerFunction<void(DownstreamState::*)(boost::optional<bool> newStatus)>("setAuto", [](DownstreamState& s, boost::optional<bool> newStatus) {
if (newStatus) {
s.upStatus = *newStatus;
}
s.setAuto();
});
- g_lua.registerFunction<std::string(DownstreamState::*)()>("getName", [](const DownstreamState& s) { return s.getName(); });
- g_lua.registerFunction<std::string(DownstreamState::*)()>("getNameWithAddr", [](const DownstreamState& s) { return s.getNameWithAddr(); });
- g_lua.registerMember("upStatus", &DownstreamState::upStatus);
- g_lua.registerMember<int (DownstreamState::*)>("weight",
+ luaCtx.registerFunction<std::string(DownstreamState::*)()>("getName", [](const DownstreamState& s) { return s.getName(); });
+ luaCtx.registerFunction<std::string(DownstreamState::*)()>("getNameWithAddr", [](const DownstreamState& s) { return s.getNameWithAddr(); });
+ luaCtx.registerMember("upStatus", &DownstreamState::upStatus);
+ luaCtx.registerMember<int (DownstreamState::*)>("weight",
[](const DownstreamState& s) -> int {return s.weight;},
[](DownstreamState& s, int newWeight) {s.setWeight(newWeight);}
);
- g_lua.registerMember("order", &DownstreamState::order);
- g_lua.registerMember<const std::string(DownstreamState::*)>("name", [](const DownstreamState& backend) -> const std::string { return backend.getName(); }, [](DownstreamState& backend, const std::string& newName) { backend.setName(newName); });
- g_lua.registerFunction<std::string(DownstreamState::*)()>("getID", [](const DownstreamState& s) { return boost::uuids::to_string(s.id); });
+ luaCtx.registerMember("order", &DownstreamState::order);
+ luaCtx.registerMember<const std::string(DownstreamState::*)>("name", [](const DownstreamState& backend) -> const std::string { return backend.getName(); }, [](DownstreamState& backend, const std::string& newName) { backend.setName(newName); });
+ luaCtx.registerFunction<std::string(DownstreamState::*)()>("getID", [](const DownstreamState& s) { return boost::uuids::to_string(s.id); });
/* dnsheader */
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
+ luaCtx.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
dh.rd=v;
});
- g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
+ luaCtx.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
return (bool)dh.rd;
});
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setRA", [](dnsheader& dh, bool v) {
+ luaCtx.registerFunction<void(dnsheader::*)(bool)>("setRA", [](dnsheader& dh, bool v) {
dh.ra=v;
});
- g_lua.registerFunction<bool(dnsheader::*)()>("getRA", [](dnsheader& dh) {
+ luaCtx.registerFunction<bool(dnsheader::*)()>("getRA", [](dnsheader& dh) {
return (bool)dh.ra;
});
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setAD", [](dnsheader& dh, bool v) {
+ luaCtx.registerFunction<void(dnsheader::*)(bool)>("setAD", [](dnsheader& dh, bool v) {
dh.ad=v;
});
- g_lua.registerFunction<bool(dnsheader::*)()>("getAD", [](dnsheader& dh) {
+ luaCtx.registerFunction<bool(dnsheader::*)()>("getAD", [](dnsheader& dh) {
return (bool)dh.ad;
});
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setAA", [](dnsheader& dh, bool v) {
+ luaCtx.registerFunction<void(dnsheader::*)(bool)>("setAA", [](dnsheader& dh, bool v) {
dh.aa=v;
});
- g_lua.registerFunction<bool(dnsheader::*)()>("getAA", [](dnsheader& dh) {
+ luaCtx.registerFunction<bool(dnsheader::*)()>("getAA", [](dnsheader& dh) {
return (bool)dh.aa;
});
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
+ luaCtx.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
dh.cd=v;
});
- g_lua.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
+ luaCtx.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
return (bool)dh.cd;
});
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
+ luaCtx.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
dh.tc=v;
if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
});
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
+ luaCtx.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
dh.qr=v;
});
/* ComboAddress */
- g_lua.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
- g_lua.writeFunction("newCAFromRaw", [](const std::string& raw, boost::optional<uint16_t> port) {
+ luaCtx.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
+ luaCtx.writeFunction("newCAFromRaw", [](const std::string& raw, boost::optional<uint16_t> port) {
if (raw.size() == 4) {
struct sockaddr_in sin4;
memset(&sin4, 0, sizeof(sin4));
}
return ComboAddress();
});
- g_lua.registerFunction<string(ComboAddress::*)()>("tostring", [](const ComboAddress& ca) { return ca.toString(); });
- g_lua.registerFunction<string(ComboAddress::*)()>("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
- g_lua.registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
- g_lua.registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
- g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
- g_lua.registerFunction<void(ComboAddress::*)(unsigned int)>("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); });
- g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; });
- g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; });
- g_lua.registerFunction<bool(ComboAddress::*)()>("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); });
- g_lua.registerFunction<ComboAddress(ComboAddress::*)()>("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); });
- g_lua.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match", [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
+ luaCtx.registerFunction<string(ComboAddress::*)()>("tostring", [](const ComboAddress& ca) { return ca.toString(); });
+ luaCtx.registerFunction<string(ComboAddress::*)()>("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
+ luaCtx.registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
+ luaCtx.registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
+ luaCtx.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
+ luaCtx.registerFunction<void(ComboAddress::*)(unsigned int)>("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); });
+ luaCtx.registerFunction<bool(ComboAddress::*)()>("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; });
+ luaCtx.registerFunction<bool(ComboAddress::*)()>("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; });
+ luaCtx.registerFunction<bool(ComboAddress::*)()>("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); });
+ luaCtx.registerFunction<ComboAddress(ComboAddress::*)()>("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); });
+ luaCtx.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match", [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
/* DNSName */
- g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
- g_lua.registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
- g_lua.registerFunction<unsigned int(DNSName::*)()>("countLabels", [](const DNSName& name) { return name.countLabels(); });
- g_lua.registerFunction<size_t(DNSName::*)()>("hash", [](const DNSName& name) { return name.hash(); });
- g_lua.registerFunction<size_t(DNSName::*)()>("wirelength", [](const DNSName& name) { return name.wirelength(); });
- g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
- g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
- g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
- g_lua.writeFunction("newDNSNameFromRaw", [](const std::string& name) { return DNSName(name.c_str(), name.size(), 0, false); });
- g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
- g_lua.writeFunction("newDNSNameSet", []() { return DNSNameSet(); });
+ luaCtx.registerFunction("isPartOf", &DNSName::isPartOf);
+ luaCtx.registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
+ luaCtx.registerFunction<unsigned int(DNSName::*)()>("countLabels", [](const DNSName& name) { return name.countLabels(); });
+ luaCtx.registerFunction<size_t(DNSName::*)()>("hash", [](const DNSName& name) { return name.hash(); });
+ luaCtx.registerFunction<size_t(DNSName::*)()>("wirelength", [](const DNSName& name) { return name.wirelength(); });
+ luaCtx.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
+ luaCtx.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
+ luaCtx.registerFunction<string(DNSName::*)()>("toDNSString", [](const DNSName&dn ) { return dn.toDNSString(); });
+ luaCtx.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
+ luaCtx.writeFunction("newDNSNameFromRaw", [](const std::string& name) { return DNSName(name.c_str(), name.size(), 0, false); });
+ luaCtx.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
+ luaCtx.writeFunction("newDNSNameSet", []() { return DNSNameSet(); });
/* DNSNameSet */
- g_lua.registerFunction<string(DNSNameSet::*)()>("toString", [](const DNSNameSet&dns ) { return dns.toString(); });
- g_lua.registerFunction<void(DNSNameSet::*)(DNSName&)>("add", [](DNSNameSet& dns, DNSName& dn) { dns.insert(dn); });
- g_lua.registerFunction<bool(DNSNameSet::*)(DNSName&)>("check", [](DNSNameSet& dns, DNSName& dn) { return dns.find(dn) != dns.end(); });
- g_lua.registerFunction("delete",(size_t (DNSNameSet::*)(const DNSName&)) &DNSNameSet::erase);
- g_lua.registerFunction("size",(size_t (DNSNameSet::*)() const) &DNSNameSet::size);
- g_lua.registerFunction("clear",(void (DNSNameSet::*)()) &DNSNameSet::clear);
- g_lua.registerFunction("empty",(bool (DNSNameSet::*)()) &DNSNameSet::empty);
+ luaCtx.registerFunction<string(DNSNameSet::*)()>("toString", [](const DNSNameSet&dns ) { return dns.toString(); });
+ luaCtx.registerFunction<void(DNSNameSet::*)(DNSName&)>("add", [](DNSNameSet& dns, DNSName& dn) { dns.insert(dn); });
+ luaCtx.registerFunction<bool(DNSNameSet::*)(DNSName&)>("check", [](DNSNameSet& dns, DNSName& dn) { return dns.find(dn) != dns.end(); });
+ luaCtx.registerFunction("delete",(size_t (DNSNameSet::*)(const DNSName&)) &DNSNameSet::erase);
+ luaCtx.registerFunction("size",(size_t (DNSNameSet::*)() const) &DNSNameSet::size);
+ luaCtx.registerFunction("clear",(void (DNSNameSet::*)()) &DNSNameSet::clear);
+ luaCtx.registerFunction("empty",(bool (DNSNameSet::*)()) &DNSNameSet::empty);
/* SuffixMatchNode */
- g_lua.registerFunction<void (SuffixMatchNode::*)(const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name)>("add", [](SuffixMatchNode &smn, const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name) {
+ luaCtx.registerFunction<void (SuffixMatchNode::*)(const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name)>("add", [](SuffixMatchNode &smn, const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name) {
if (name.type() == typeid(DNSName)) {
auto n = boost::get<DNSName>(name);
smn.add(n);
}
if (name.type() == typeid(vector<pair<int, DNSName>>)) {
auto names = boost::get<vector<pair<int, DNSName>>>(name);
- for (auto const n : names) {
+ for (const auto& n : names) {
smn.add(n.second);
}
return;
}
if (name.type() == typeid(vector<pair<int, string>>)) {
auto names = boost::get<vector<pair<int, string>>>(name);
- for (auto const n : names) {
+ for (const auto& n : names) {
smn.add(n.second);
}
return;
}
});
- g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
+ luaCtx.registerFunction<void (SuffixMatchNode::*)(const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name)>("remove", [](SuffixMatchNode &smn, const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name) {
+ if (name.type() == typeid(DNSName)) {
+ auto n = boost::get<DNSName>(name);
+ smn.remove(n);
+ return;
+ }
+ if (name.type() == typeid(string)) {
+ auto n = boost::get<string>(name);
+ DNSName d(n);
+ smn.remove(d);
+ return;
+ }
+ if (name.type() == typeid(vector<pair<int, DNSName>>)) {
+ auto names = boost::get<vector<pair<int, DNSName>>>(name);
+ for (const auto& n : names) {
+ smn.remove(n.second);
+ }
+ return;
+ }
+ if (name.type() == typeid(vector<pair<int, string>>)) {
+ auto names = boost::get<vector<pair<int, string>>>(name);
+ for (const auto& n : names) {
+ DNSName d(n.second);
+ smn.remove(d);
+ }
+ return;
+ }
+ });
+
+ luaCtx.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
/* Netmask */
- g_lua.writeFunction("newNetmask", [](boost::variant<std::string,ComboAddress> s, boost::optional<uint8_t> bits) {
+ luaCtx.writeFunction("newNetmask", [](boost::variant<std::string,ComboAddress> s, boost::optional<uint8_t> bits) {
if (s.type() == typeid(ComboAddress)) {
auto ca = boost::get<ComboAddress>(s);
if (bits) {
}
throw std::runtime_error("Invalid parameter passed to 'newNetmask()'");
});
- g_lua.registerFunction("empty", &Netmask::empty);
- g_lua.registerFunction("getBits", &Netmask::getBits);
- g_lua.registerFunction<ComboAddress(Netmask::*)()>("getNetwork", [](const Netmask& nm) { return nm.getNetwork(); } ); // const reference makes this necessary
- g_lua.registerFunction<ComboAddress(Netmask::*)()>("getMaskedNetwork", [](const Netmask& nm) { return nm.getMaskedNetwork(); } );
- g_lua.registerFunction("isIpv4", &Netmask::isIPv4);
- g_lua.registerFunction("isIPv4", &Netmask::isIPv4);
- g_lua.registerFunction("isIpv6", &Netmask::isIPv6);
- g_lua.registerFunction("isIPv6", &Netmask::isIPv6);
- g_lua.registerFunction("match", (bool (Netmask::*)(const string&) const)&Netmask::match);
- g_lua.registerFunction("toString", &Netmask::toString);
- g_lua.registerEqFunction(&Netmask::operator==);
- g_lua.registerToStringFunction(&Netmask::toString);
+ luaCtx.registerFunction("empty", &Netmask::empty);
+ luaCtx.registerFunction("getBits", &Netmask::getBits);
+ luaCtx.registerFunction<ComboAddress(Netmask::*)()>("getNetwork", [](const Netmask& nm) { return nm.getNetwork(); } ); // const reference makes this necessary
+ luaCtx.registerFunction<ComboAddress(Netmask::*)()>("getMaskedNetwork", [](const Netmask& nm) { return nm.getMaskedNetwork(); } );
+ luaCtx.registerFunction("isIpv4", &Netmask::isIPv4);
+ luaCtx.registerFunction("isIPv4", &Netmask::isIPv4);
+ luaCtx.registerFunction("isIpv6", &Netmask::isIPv6);
+ luaCtx.registerFunction("isIPv6", &Netmask::isIPv6);
+ luaCtx.registerFunction("match", (bool (Netmask::*)(const string&) const)&Netmask::match);
+ luaCtx.registerFunction("toString", &Netmask::toString);
+ luaCtx.registerEqFunction(&Netmask::operator==);
+ luaCtx.registerToStringFunction(&Netmask::toString);
/* NetmaskGroup */
- g_lua.writeFunction("newNMG", []() { return NetmaskGroup(); });
- g_lua.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
+ luaCtx.writeFunction("newNMG", []() { return NetmaskGroup(); });
+ luaCtx.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
{
nmg.addMask(mask);
});
- g_lua.registerFunction<void(NetmaskGroup::*)(const std::map<ComboAddress,int>& map)>("addMasks", [](NetmaskGroup&nmg, const std::map<ComboAddress,int>& map)
+ luaCtx.registerFunction<void(NetmaskGroup::*)(const std::map<ComboAddress,int>& map)>("addMasks", [](NetmaskGroup&nmg, const std::map<ComboAddress,int>& map)
{
for (const auto& entry : map) {
nmg.addMask(Netmask(entry.first));
}
});
- g_lua.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
- g_lua.registerFunction("size", &NetmaskGroup::size);
- g_lua.registerFunction("clear", &NetmaskGroup::clear);
- g_lua.registerFunction<string(NetmaskGroup::*)()>("toString", [](const NetmaskGroup& nmg ) { return "NetmaskGroup " + nmg.toString(); });
+ luaCtx.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
+ luaCtx.registerFunction("size", &NetmaskGroup::size);
+ luaCtx.registerFunction("clear", &NetmaskGroup::clear);
+ luaCtx.registerFunction<string(NetmaskGroup::*)()>("toString", [](const NetmaskGroup& nmg ) { return "NetmaskGroup " + nmg.toString(); });
/* QPSLimiter */
- g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
- g_lua.registerFunction("check", &QPSLimiter::check);
+ luaCtx.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
+ luaCtx.registerFunction("check", &QPSLimiter::check);
/* ClientState */
- g_lua.registerFunction<std::string(ClientState::*)()>("toString", [](const ClientState& fe) {
+ luaCtx.registerFunction<std::string(ClientState::*)()>("toString", [](const ClientState& fe) {
setLuaNoSideEffect();
return fe.local.toStringWithPort();
});
- g_lua.registerMember("muted", &ClientState::muted);
+ luaCtx.registerMember("muted", &ClientState::muted);
#ifdef HAVE_EBPF
- g_lua.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
+ luaCtx.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
if (bpf) {
frontend.attachFilter(bpf);
}
});
- g_lua.registerFunction<void(ClientState::*)()>("detachFilter", [](ClientState& frontend) {
+ luaCtx.registerFunction<void(ClientState::*)()>("detachFilter", [](ClientState& frontend) {
frontend.detachFilter();
});
#endif /* HAVE_EBPF */
/* BPF Filter */
#ifdef HAVE_EBPF
- g_lua.writeFunction("newBPFFilter", [client](uint32_t maxV4, uint32_t maxV6, uint32_t maxQNames) {
+ luaCtx.writeFunction("newBPFFilter", [client](uint32_t maxV4, uint32_t maxV6, uint32_t maxQNames) {
if (client) {
return std::shared_ptr<BPFFilter>(nullptr);
}
return std::make_shared<BPFFilter>(maxV4, maxV6, maxQNames);
});
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("block", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
+ luaCtx.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("block", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
if (bpf) {
return bpf->block(ca);
}
});
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("blockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
+ luaCtx.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("blockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
if (bpf) {
return bpf->block(qname, qtype ? *qtype : 255);
}
});
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
+ luaCtx.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
if (bpf) {
return bpf->unblock(ca);
}
});
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("unblockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
+ luaCtx.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("unblockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
if (bpf) {
return bpf->unblock(qname, qtype ? *qtype : 255);
}
});
- g_lua.registerFunction<std::string(std::shared_ptr<BPFFilter>::*)()>("getStats", [](const std::shared_ptr<BPFFilter> bpf) {
+ luaCtx.registerFunction<std::string(std::shared_ptr<BPFFilter>::*)()>("getStats", [](const std::shared_ptr<BPFFilter> bpf) {
setLuaNoSideEffect();
std::string res;
if (bpf) {
return res;
});
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter> bpf) {
+ luaCtx.registerFunction<void(std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter> bpf) {
std::string res;
if (bpf) {
for (const auto& frontend : g_frontends) {
}
});
- g_lua.writeFunction("newDynBPFFilter", [client](std::shared_ptr<BPFFilter> bpf) {
+ luaCtx.writeFunction("newDynBPFFilter", [client](std::shared_ptr<BPFFilter> bpf) {
if (client) {
return std::shared_ptr<DynBPFFilter>(nullptr);
}
return std::make_shared<DynBPFFilter>(bpf);
});
- g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(const ComboAddress& addr, boost::optional<int> seconds)>("block", [](std::shared_ptr<DynBPFFilter> dbpf, const ComboAddress& addr, boost::optional<int> seconds) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(const ComboAddress& addr, boost::optional<int> seconds)>("block", [](std::shared_ptr<DynBPFFilter> dbpf, const ComboAddress& addr, boost::optional<int> seconds) {
if (dbpf) {
struct timespec until;
clock_gettime(CLOCK_MONOTONIC, &until);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)()>("purgeExpired", [](std::shared_ptr<DynBPFFilter> dbpf) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)()>("purgeExpired", [](std::shared_ptr<DynBPFFilter> dbpf) {
if (dbpf) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBPFFilter> dbpf, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBPFFilter> dbpf, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
dbpf->excludeRange(Netmask(range.second));
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBPFFilter> dbpf, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBPFFilter> dbpf, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
dbpf->includeRange(Netmask(range.second));
#endif /* HAVE_EBPF */
/* EDNSOptionView */
- g_lua.registerFunction<size_t(EDNSOptionView::*)()>("count", [](const EDNSOptionView& option) {
+ luaCtx.registerFunction<size_t(EDNSOptionView::*)()>("count", [](const EDNSOptionView& option) {
return option.values.size();
});
- g_lua.registerFunction<std::vector<string>(EDNSOptionView::*)()>("getValues", [] (const EDNSOptionView& option) {
+ luaCtx.registerFunction<std::vector<string>(EDNSOptionView::*)()>("getValues", [] (const EDNSOptionView& option) {
std::vector<string> values;
for (const auto& value : option.values) {
values.push_back(std::string(value.content, value.size));
return values;
});
- g_lua.writeFunction("newDOHResponseMapEntry", [](const std::string& regex, uint16_t status, const std::string& content, boost::optional<std::map<std::string, std::string>> customHeaders) {
+ luaCtx.writeFunction("newDOHResponseMapEntry", [](const std::string& regex, uint16_t status, const std::string& content, boost::optional<std::map<std::string, std::string>> customHeaders) {
boost::optional<std::vector<std::pair<std::string, std::string>>> headers{boost::none};
if (customHeaders) {
headers = std::vector<std::pair<std::string, std::string>>();
});
}
-void setupLuaInspection()
+void setupLuaInspection(LuaContext& luaCtx)
{
- g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
+ luaCtx.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
setLuaNoSideEffect();
auto top = top_.get_value_or(10);
map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts;
g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str();
});
- g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
+ luaCtx.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
setLuaNoSideEffect();
map<DNSName, unsigned int> counts;
unsigned int total=0;
});
- g_lua.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
+ luaCtx.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
- g_lua.writeFunction("getResponseRing", []() {
+ luaCtx.writeFunction("getResponseRing", []() {
setLuaNoSideEffect();
size_t totalEntries = 0;
std::vector<boost::circular_buffer<Rings::Response>> rings;
return ret;
});
- g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
+ luaCtx.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; });
});
- g_lua.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
+ luaCtx.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
- g_lua.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) {
+ luaCtx.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) {
return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; });
});
- g_lua.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
+ luaCtx.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
- g_lua.writeFunction("getTopBandwidth", [](unsigned int top) {
+ luaCtx.writeFunction("getTopBandwidth", [](unsigned int top) {
setLuaNoSideEffect();
return g_rings.getTopBandwidth(top);
});
- g_lua.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
+ luaCtx.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
- g_lua.writeFunction("delta", []() {
+ luaCtx.writeFunction("delta", []() {
setLuaNoSideEffect();
// we hold the lua lock already!
for(const auto& d : g_confDelta) {
}
});
- g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
+ luaCtx.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
setLuaNoSideEffect();
boost::optional<Netmask> nm;
boost::optional<DNSName> dn;
if(msec==-1) {
for(const auto& c : qr) {
bool nmmatch=true, dnmatch=true;
- if(nm)
+ if (nm) {
nmmatch = nm->match(c.requestor);
- if(dn)
- dnmatch = c.name.isPartOf(*dn);
- if(nmmatch && dnmatch) {
+ }
+ if (dn) {
+ if (c.name.empty()) {
+ dnmatch = false;
+ }
+ else {
+ dnmatch = c.name.isPartOf(*dn);
+ }
+ }
+ if (nmmatch && dnmatch) {
QType qt(c.qtype);
std::string extra;
if (c.dh.opcode != 0) {
string extra;
for(const auto& c : rr) {
bool nmmatch=true, dnmatch=true, msecmatch=true;
- if(nm)
+ if (nm) {
nmmatch = nm->match(c.requestor);
- if(dn)
- dnmatch = c.name.isPartOf(*dn);
- if(msec != -1)
+ }
+ if (dn) {
+ if (c.name.empty()) {
+ dnmatch = false;
+ }
+ else {
+ dnmatch = c.name.isPartOf(*dn);
+ }
+ }
+ if (msec != -1) {
msecmatch=(c.usec/1000 > (unsigned int)msec);
+ }
- if(nmmatch && dnmatch && msecmatch) {
+ if (nmmatch && dnmatch && msecmatch) {
QType qt(c.qtype);
- if(!c.dh.rcode)
+ if (!c.dh.rcode) {
extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";
- else
+ }
+ else {
extra.clear();
- if(c.usec != std::numeric_limits<decltype(c.usec)>::max())
+ }
+
+ if (c.usec != std::numeric_limits<decltype(c.usec)>::max()) {
out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % (c.usec/1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
- else
+ }
+ else {
out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
+ }
- if(limit && *limit==++num)
+ if (limit && *limit == ++num) {
break;
+ }
}
}
}
});
- g_lua.writeFunction("showResponseLatency", []() {
+ luaCtx.writeFunction("showResponseLatency", []() {
setLuaNoSideEffect();
map<double, unsigned int> histo;
double bin=100;
}
});
- g_lua.writeFunction("showTCPStats", [] {
+ luaCtx.writeFunction("showTCPStats", [] {
setLuaNoSideEffect();
ostringstream ret;
boost::format fmt("%-12d %-12d %-12d %-12d");
g_outputBuffer=ret.str();
});
- g_lua.writeFunction("showTLSErrorCounters", [] {
+ luaCtx.writeFunction("showTLSErrorCounters", [] {
setLuaNoSideEffect();
ostringstream ret;
boost::format fmt("%-3d %-20.20s %-23d %-23d %-23d %-23d %-23d %-23d %-23d %-23d");
g_outputBuffer=ret.str();
});
- g_lua.writeFunction("dumpStats", [] {
+ luaCtx.writeFunction("dumpStats", [] {
setLuaNoSideEffect();
vector<string> leftcolumn, rightcolumn;
}
});
- g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
+ luaCtx.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
setLuaNoSideEffect();
return exceedRCode(rate, seconds, RCode::ServFail);
});
- g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
+ luaCtx.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
setLuaNoSideEffect();
return exceedRCode(rate, seconds, RCode::NXDomain);
});
- g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
+ luaCtx.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
setLuaNoSideEffect();
return exceedRespByterate(rate, seconds);
});
- g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
+ luaCtx.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
setLuaNoSideEffect();
return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
if(q.qtype==type)
});
});
- g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
+ luaCtx.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
setLuaNoSideEffect();
return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
counts[q.requestor]++;
});
});
- g_lua.writeFunction("getRespRing", getRespRing);
+ luaCtx.writeFunction("getRespRing", getRespRing);
/* StatNode */
- g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
+ luaCtx.registerFunction<StatNode, unsigned int()>("numChildren",
[](StatNode& sn) -> unsigned int {
return sn.children.size();
} );
- g_lua.registerMember("fullname", &StatNode::fullname);
- g_lua.registerMember("labelsCount", &StatNode::labelsCount);
- g_lua.registerMember("servfails", &StatNode::Stat::servfails);
- g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
- g_lua.registerMember("queries", &StatNode::Stat::queries);
- g_lua.registerMember("noerrors", &StatNode::Stat::noerrors);
- g_lua.registerMember("drops", &StatNode::Stat::drops);
- g_lua.registerMember("bytes", &StatNode::Stat::bytes);
-
- g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
+ luaCtx.registerMember("fullname", &StatNode::fullname);
+ luaCtx.registerMember("labelsCount", &StatNode::labelsCount);
+ luaCtx.registerMember("servfails", &StatNode::Stat::servfails);
+ luaCtx.registerMember("nxdomains", &StatNode::Stat::nxdomains);
+ luaCtx.registerMember("queries", &StatNode::Stat::queries);
+ luaCtx.registerMember("noerrors", &StatNode::Stat::noerrors);
+ luaCtx.registerMember("drops", &StatNode::Stat::drops);
+ luaCtx.registerMember("bytes", &StatNode::Stat::bytes);
+
+ luaCtx.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
statNodeRespRing(visitor, seconds ? *seconds : 0);
});
/* DynBlockRulesGroup */
- g_lua.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); });
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQueryRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+ luaCtx.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); });
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQueryRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
if (group) {
group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setResponseByteRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setResponseByteRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
if (group) {
group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, DynBlockRulesGroup::smtVisitor_t visitor) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, DynBlockRulesGroup::smtVisitor_t visitor) {
if (group) {
group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, dnsdist_ffi_stat_node_visitor_t visitor) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, dnsdist_ffi_stat_node_visitor_t visitor) {
if (group) {
group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
if (group) {
group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional<DNSAction::Action>, boost::optional<double>)>("setRCodeRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional<DNSAction::Action> action, boost::optional<double> warningRatio) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional<DNSAction::Action>, boost::optional<double>)>("setRCodeRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional<DNSAction::Action> action, boost::optional<double> warningRatio) {
if (group) {
group->setRCodeRatio(rcode, ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQTypeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQTypeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
if (group) {
group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
group->excludeRange(Netmask(range.second));
group->excludeRange(Netmask(*boost::get<std::string>(&ranges)));
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
group->includeRange(Netmask(range.second));
group->includeRange(Netmask(*boost::get<std::string>(&ranges)));
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeDomains", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> domains) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeDomains", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> domains) {
if (domains.type() == typeid(std::vector<std::pair<int, std::string>>)) {
for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&domains)) {
group->excludeDomain(DNSName(range.second));
group->excludeDomain(DNSName(*boost::get<std::string>(&domains)));
}
});
- g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) {
+ luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) {
group->apply();
});
- g_lua.registerFunction("setQuiet", &DynBlockRulesGroup::setQuiet);
- g_lua.registerFunction("toString", &DynBlockRulesGroup::toString);
+ luaCtx.registerFunction("setQuiet", &DynBlockRulesGroup::setQuiet);
+ luaCtx.registerFunction("toString", &DynBlockRulesGroup::toString);
}
someRespRulActions->setState(std::move(rules));
}
-void setupLuaRules()
+void setupLuaRules(LuaContext& luaCtx)
{
- g_lua.writeFunction("makeRule", makeRule);
+ luaCtx.writeFunction("makeRule", makeRule);
- g_lua.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
+ luaCtx.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
- g_lua.writeFunction("showResponseRules", [](boost::optional<ruleparams_t> vars) {
+ luaCtx.writeFunction("showResponseRules", [](boost::optional<ruleparams_t> vars) {
showRules(&g_resprulactions, vars);
});
- g_lua.writeFunction("rmResponseRule", [](boost::variant<unsigned int, std::string> id) {
+ luaCtx.writeFunction("rmResponseRule", [](boost::variant<unsigned int, std::string> id) {
rmRule(&g_resprulactions, id);
});
- g_lua.writeFunction("topResponseRule", []() {
+ luaCtx.writeFunction("topResponseRule", []() {
topRule(&g_resprulactions);
});
- g_lua.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
+ luaCtx.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
mvRule(&g_resprulactions, from, to);
});
- g_lua.writeFunction("showCacheHitResponseRules", [](boost::optional<ruleparams_t> vars) {
+ luaCtx.writeFunction("showCacheHitResponseRules", [](boost::optional<ruleparams_t> vars) {
showRules(&g_cachehitresprulactions, vars);
});
- g_lua.writeFunction("rmCacheHitResponseRule", [](boost::variant<unsigned int, std::string> id) {
+ luaCtx.writeFunction("rmCacheHitResponseRule", [](boost::variant<unsigned int, std::string> id) {
rmRule(&g_cachehitresprulactions, id);
});
- g_lua.writeFunction("topCacheHitResponseRule", []() {
+ luaCtx.writeFunction("topCacheHitResponseRule", []() {
topRule(&g_cachehitresprulactions);
});
- g_lua.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) {
+ luaCtx.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) {
mvRule(&g_cachehitresprulactions, from, to);
});
- g_lua.writeFunction("showSelfAnsweredResponseRules", [](boost::optional<ruleparams_t> vars) {
+ luaCtx.writeFunction("showSelfAnsweredResponseRules", [](boost::optional<ruleparams_t> vars) {
showRules(&g_selfansweredresprulactions, vars);
});
- g_lua.writeFunction("rmSelfAnsweredResponseRule", [](boost::variant<unsigned int, std::string> id) {
+ luaCtx.writeFunction("rmSelfAnsweredResponseRule", [](boost::variant<unsigned int, std::string> id) {
rmRule(&g_selfansweredresprulactions, id);
});
- g_lua.writeFunction("topSelfAnsweredResponseRule", []() {
+ luaCtx.writeFunction("topSelfAnsweredResponseRule", []() {
topRule(&g_selfansweredresprulactions);
});
- g_lua.writeFunction("mvSelfAnsweredResponseRule", [](unsigned int from, unsigned int to) {
+ luaCtx.writeFunction("mvSelfAnsweredResponseRule", [](unsigned int from, unsigned int to) {
mvRule(&g_selfansweredresprulactions, from, to);
});
- g_lua.writeFunction("rmRule", [](boost::variant<unsigned int, std::string> id) {
+ luaCtx.writeFunction("rmRule", [](boost::variant<unsigned int, std::string> id) {
rmRule(&g_rulactions, id);
});
- g_lua.writeFunction("topRule", []() {
+ luaCtx.writeFunction("topRule", []() {
topRule(&g_rulactions);
});
- g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
+ luaCtx.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
mvRule(&g_rulactions, from, to);
});
- g_lua.writeFunction("clearRules", []() {
+ luaCtx.writeFunction("clearRules", []() {
setLuaSideEffect();
g_rulactions.modify([](decltype(g_rulactions)::value_type& rulactions) {
rulactions.clear();
});
});
- g_lua.writeFunction("setRules", [](const std::vector<std::pair<int, std::shared_ptr<DNSDistRuleAction>>>& newruleactions) {
+ luaCtx.writeFunction("setRules", [](const std::vector<std::pair<int, std::shared_ptr<DNSDistRuleAction>>>& newruleactions) {
setLuaSideEffect();
g_rulactions.modify([newruleactions](decltype(g_rulactions)::value_type& gruleactions) {
gruleactions.clear();
});
});
- g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc, boost::optional<int> burst, boost::optional<unsigned int> expiration, boost::optional<unsigned int> cleanupDelay, boost::optional<unsigned int> scanFraction) {
+ luaCtx.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc, boost::optional<int> burst, boost::optional<unsigned int> expiration, boost::optional<unsigned int> cleanupDelay, boost::optional<unsigned int> scanFraction) {
return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, burst.get_value_or(qps), ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64), expiration.get_value_or(300), cleanupDelay.get_value_or(60), scanFraction.get_value_or(10)));
});
- g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
+ luaCtx.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
if(!burst)
return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
else
return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
});
- g_lua.writeFunction("RegexRule", [](const std::string& str) {
+ luaCtx.writeFunction("RegexRule", [](const std::string& str) {
return std::shared_ptr<DNSRule>(new RegexRule(str));
});
#ifdef HAVE_DNS_OVER_HTTPS
- g_lua.writeFunction("HTTPHeaderRule", [](const std::string& header, const std::string& regex) {
+ luaCtx.writeFunction("HTTPHeaderRule", [](const std::string& header, const std::string& regex) {
return std::shared_ptr<DNSRule>(new HTTPHeaderRule(header, regex));
});
- g_lua.writeFunction("HTTPPathRule", [](const std::string& path) {
+ luaCtx.writeFunction("HTTPPathRule", [](const std::string& path) {
return std::shared_ptr<DNSRule>(new HTTPPathRule(path));
});
- g_lua.writeFunction("HTTPPathRegexRule", [](const std::string& regex) {
+ luaCtx.writeFunction("HTTPPathRegexRule", [](const std::string& regex) {
return std::shared_ptr<DNSRule>(new HTTPPathRegexRule(regex));
});
#endif
#ifdef HAVE_RE2
- g_lua.writeFunction("RE2Rule", [](const std::string& str) {
+ luaCtx.writeFunction("RE2Rule", [](const std::string& str) {
return std::shared_ptr<DNSRule>(new RE2Rule(str));
});
#endif
- g_lua.writeFunction("SNIRule", [](const std::string& name) {
+ luaCtx.writeFunction("SNIRule", [](const std::string& name) {
return std::shared_ptr<DNSRule>(new SNIRule(name));
});
- g_lua.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional<bool> quiet) {
+ luaCtx.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional<bool> quiet) {
return std::shared_ptr<DNSRule>(new SuffixMatchNodeRule(smn, quiet ? *quiet : false));
});
- g_lua.writeFunction("NetmaskGroupRule", [](const NetmaskGroup& nmg, boost::optional<bool> src, boost::optional<bool> quiet) {
+ luaCtx.writeFunction("NetmaskGroupRule", [](const NetmaskGroup& nmg, boost::optional<bool> src, boost::optional<bool> quiet) {
return std::shared_ptr<DNSRule>(new NetmaskGroupRule(nmg, src ? *src : true, quiet ? *quiet : false));
});
- g_lua.writeFunction("benchRule", [](std::shared_ptr<DNSRule> rule, boost::optional<int> times_, boost::optional<string> suffix_) {
+ luaCtx.writeFunction("benchRule", [](std::shared_ptr<DNSRule> rule, boost::optional<int> times_, boost::optional<string> suffix_) {
setLuaNoSideEffect();
int times = times_.get_value_or(100000);
DNSName suffix(suffix_.get_value_or("powerdns.com"));
});
- g_lua.writeFunction("AllRule", []() {
+ luaCtx.writeFunction("AllRule", []() {
return std::shared_ptr<DNSRule>(new AllRule());
});
- g_lua.writeFunction("ProbaRule", [](double proba) {
+ luaCtx.writeFunction("ProbaRule", [](double proba) {
return std::shared_ptr<DNSRule>(new ProbaRule(proba));
});
- g_lua.writeFunction("QNameRule", [](const std::string& qname) {
+ luaCtx.writeFunction("QNameRule", [](const std::string& qname) {
return std::shared_ptr<DNSRule>(new QNameRule(DNSName(qname)));
});
- g_lua.writeFunction("QTypeRule", [](boost::variant<int, std::string> str) {
+ luaCtx.writeFunction("QTypeRule", [](boost::variant<int, std::string> str) {
uint16_t qtype;
if(auto dir = boost::get<int>(&str)) {
qtype = *dir;
return std::shared_ptr<DNSRule>(new QTypeRule(qtype));
});
- g_lua.writeFunction("QClassRule", [](int c) {
+ luaCtx.writeFunction("QClassRule", [](int c) {
return std::shared_ptr<DNSRule>(new QClassRule(c));
});
- g_lua.writeFunction("OpcodeRule", [](uint8_t code) {
+ luaCtx.writeFunction("OpcodeRule", [](uint8_t code) {
return std::shared_ptr<DNSRule>(new OpcodeRule(code));
});
- g_lua.writeFunction("AndRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
+ luaCtx.writeFunction("AndRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
return std::shared_ptr<DNSRule>(new AndRule(a));
});
- g_lua.writeFunction("OrRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
+ luaCtx.writeFunction("OrRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
return std::shared_ptr<DNSRule>(new OrRule(a));
});
- g_lua.writeFunction("DSTPortRule", [](uint16_t port) {
+ luaCtx.writeFunction("DSTPortRule", [](uint16_t port) {
return std::shared_ptr<DNSRule>(new DSTPortRule(port));
});
- g_lua.writeFunction("TCPRule", [](bool tcp) {
+ luaCtx.writeFunction("TCPRule", [](bool tcp) {
return std::shared_ptr<DNSRule>(new TCPRule(tcp));
});
- g_lua.writeFunction("DNSSECRule", []() {
+ luaCtx.writeFunction("DNSSECRule", []() {
return std::shared_ptr<DNSRule>(new DNSSECRule());
});
- g_lua.writeFunction("NotRule", [](std::shared_ptr<DNSRule>rule) {
+ luaCtx.writeFunction("NotRule", [](std::shared_ptr<DNSRule>rule) {
return std::shared_ptr<DNSRule>(new NotRule(rule));
});
- g_lua.writeFunction("RecordsCountRule", [](uint8_t section, uint16_t minCount, uint16_t maxCount) {
+ luaCtx.writeFunction("RecordsCountRule", [](uint8_t section, uint16_t minCount, uint16_t maxCount) {
return std::shared_ptr<DNSRule>(new RecordsCountRule(section, minCount, maxCount));
});
- g_lua.writeFunction("RecordsTypeCountRule", [](uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount) {
+ luaCtx.writeFunction("RecordsTypeCountRule", [](uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount) {
return std::shared_ptr<DNSRule>(new RecordsTypeCountRule(section, type, minCount, maxCount));
});
- g_lua.writeFunction("TrailingDataRule", []() {
+ luaCtx.writeFunction("TrailingDataRule", []() {
return std::shared_ptr<DNSRule>(new TrailingDataRule());
});
- g_lua.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
+ luaCtx.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
return std::shared_ptr<DNSRule>(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount));
});
- g_lua.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
+ luaCtx.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
});
- g_lua.writeFunction("RCodeRule", [](uint8_t rcode) {
+ luaCtx.writeFunction("RCodeRule", [](uint8_t rcode) {
return std::shared_ptr<DNSRule>(new RCodeRule(rcode));
});
- g_lua.writeFunction("ERCodeRule", [](uint8_t rcode) {
+ luaCtx.writeFunction("ERCodeRule", [](uint8_t rcode) {
return std::shared_ptr<DNSRule>(new ERCodeRule(rcode));
});
- g_lua.writeFunction("EDNSVersionRule", [](uint8_t version) {
+ luaCtx.writeFunction("EDNSVersionRule", [](uint8_t version) {
return std::shared_ptr<DNSRule>(new EDNSVersionRule(version));
});
- g_lua.writeFunction("EDNSOptionRule", [](uint16_t optcode) {
+ luaCtx.writeFunction("EDNSOptionRule", [](uint16_t optcode) {
return std::shared_ptr<DNSRule>(new EDNSOptionRule(optcode));
});
- g_lua.writeFunction("showRules", [](boost::optional<ruleparams_t> vars) {
+ luaCtx.writeFunction("showRules", [](boost::optional<ruleparams_t> vars) {
showRules(&g_rulactions, vars);
});
- g_lua.writeFunction("RDRule", []() {
+ luaCtx.writeFunction("RDRule", []() {
return std::shared_ptr<DNSRule>(new RDRule());
});
- g_lua.writeFunction("TagRule", [](std::string tag, boost::optional<std::string> value) {
+ luaCtx.writeFunction("TagRule", [](std::string tag, boost::optional<std::string> value) {
return std::shared_ptr<DNSRule>(new TagRule(tag, value));
});
- g_lua.writeFunction("TimedIPSetRule", []() {
+ luaCtx.writeFunction("TimedIPSetRule", []() {
return std::shared_ptr<TimedIPSetRule>(new TimedIPSetRule());
});
- g_lua.writeFunction("PoolAvailableRule", [](std::string poolname) {
+ luaCtx.writeFunction("PoolAvailableRule", [](std::string poolname) {
return std::shared_ptr<DNSRule>(new PoolAvailableRule(poolname));
});
- g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("clear", [](std::shared_ptr<TimedIPSetRule> tisr) {
+ luaCtx.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("clear", [](std::shared_ptr<TimedIPSetRule> tisr) {
tisr->clear();
});
- g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("cleanup", [](std::shared_ptr<TimedIPSetRule> tisr) {
+ luaCtx.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("cleanup", [](std::shared_ptr<TimedIPSetRule> tisr) {
tisr->cleanup();
});
- g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr<TimedIPSetRule> tisr, const ComboAddress& ca, int t) {
+ luaCtx.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr<TimedIPSetRule> tisr, const ComboAddress& ca, int t) {
tisr->add(ca, time(0)+t);
});
- g_lua.registerFunction<std::shared_ptr<DNSRule>(std::shared_ptr<TimedIPSetRule>::*)()>("slice", [](std::shared_ptr<TimedIPSetRule> tisr) {
+ luaCtx.registerFunction<std::shared_ptr<DNSRule>(std::shared_ptr<TimedIPSetRule>::*)()>("slice", [](std::shared_ptr<TimedIPSetRule> tisr) {
return std::dynamic_pointer_cast<DNSRule>(tisr);
});
- g_lua.writeFunction("QNameSetRule", [](const DNSNameSet& names) {
+ luaCtx.writeFunction("QNameSetRule", [](const DNSNameSet& names) {
return std::shared_ptr<DNSRule>(new QNameSetRule(names));
});
- g_lua.writeFunction("KeyValueStoreLookupRule", [](std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey) {
+ luaCtx.writeFunction("KeyValueStoreLookupRule", [](std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey) {
return std::shared_ptr<DNSRule>(new KeyValueStoreLookupRule(kvs, lookupKey));
});
- g_lua.writeFunction("LuaRule", [](LuaRule::func_t func) {
+ luaCtx.writeFunction("LuaRule", [](LuaRule::func_t func) {
return std::shared_ptr<DNSRule>(new LuaRule(func));
});
- g_lua.writeFunction("LuaFFIRule", [](LuaFFIRule::func_t func) {
+ luaCtx.writeFunction("LuaFFIRule", [](LuaFFIRule::func_t func) {
return std::shared_ptr<DNSRule>(new LuaFFIRule(func));
});
}
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dnsdist.hh"
+#include "dnsdist-lua.hh"
#include "ednsoptions.hh"
#undef BADSIG // signal.h SIG_ERR
-void setupLuaVars()
+void setupLuaVars(LuaContext& luaCtx)
{
- g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
+ luaCtx.writeVariable("DNSAction", std::unordered_map<string,int>{
{"Drop", (int)DNSAction::Action::Drop},
{"Nxdomain", (int)DNSAction::Action::Nxdomain},
{"Refused", (int)DNSAction::Action::Refused},
{"NoRecurse", (int)DNSAction::Action::NoRecurse}
});
- g_lua.writeVariable("DNSResponseAction", std::unordered_map<string,int>{
+ luaCtx.writeVariable("DNSResponseAction", std::unordered_map<string,int>{
{"Allow", (int)DNSResponseAction::Action::Allow },
{"Delay", (int)DNSResponseAction::Action::Delay },
{"Drop", (int)DNSResponseAction::Action::Drop },
{"None", (int)DNSResponseAction::Action::None }
});
- g_lua.writeVariable("DNSClass", std::unordered_map<string,int>{
+ luaCtx.writeVariable("DNSClass", std::unordered_map<string,int>{
{"IN", QClass::IN },
{"CHAOS", QClass::CHAOS },
{"NONE", QClass::NONE },
{"ANY", QClass::ANY }
});
- g_lua.writeVariable("DNSOpcode", std::unordered_map<string,int>{
+ luaCtx.writeVariable("DNSOpcode", std::unordered_map<string,int>{
{"Query", Opcode::Query },
{"IQuery", Opcode::IQuery },
{"Status", Opcode::Status },
{"Update", Opcode::Update }
});
- g_lua.writeVariable("DNSSection", std::unordered_map<string,int>{
+ luaCtx.writeVariable("DNSSection", std::unordered_map<string,int>{
{"Question", 0 },
{"Answer", 1 },
{"Authority", 2 },
{"Additional",3 }
});
- g_lua.writeVariable("EDNSOptionCode", std::unordered_map<string,int>{
+ luaCtx.writeVariable("EDNSOptionCode", std::unordered_map<string,int>{
{"NSID", EDNSOptionCode::NSID },
{"DAU", EDNSOptionCode::DAU },
{"DHU", EDNSOptionCode::DHU },
{"KEYTAG", EDNSOptionCode::KEYTAG }
});
- g_lua.writeVariable("DNSRCode", std::unordered_map<string, int>{
+ luaCtx.writeVariable("DNSRCode", std::unordered_map<string, int>{
{"NOERROR", RCode::NoError },
{"FORMERR", RCode::FormErr },
{"SERVFAIL", RCode::ServFail },
vector<pair<string, int> > dd;
for(const auto& n : QType::names)
dd.push_back({n.first, n.second});
- g_lua.writeVariable("DNSQType", dd);
+ luaCtx.writeVariable("DNSQType", dd);
- g_lua.executeCode(R"LUA(
+ luaCtx.executeCode(R"LUA(
local tables = {
DNSQType = DNSQType,
DNSRCode = DNSRCode
);
#ifdef HAVE_DNSCRYPT
- g_lua.writeVariable("DNSCryptExchangeVersion", std::unordered_map<string,int>{
+ luaCtx.writeVariable("DNSCryptExchangeVersion", std::unordered_map<string,int>{
{ "VERSION1", DNSCryptExchangeVersion::VERSION1 },
{ "VERSION2", DNSCryptExchangeVersion::VERSION2 },
});
#endif /* LUAJIT_VERSION */
#include "dnsdist-rings.hh"
#include "dnsdist-secpoll.hh"
+#include "dnsdist-web.hh"
#include "base64.hh"
#include "dnswriter.hh"
using std::thread;
-static vector<std::function<void(void)>>* g_launchWork = nullptr;
+static boost::optional<std::vector<std::function<void(void)>>> g_launchWork = boost::none;
boost::tribool g_noLuaSideEffect;
static bool g_included{false};
typedef std::unordered_map<std::string, boost::variant<bool, int, std::string, std::vector<std::pair<int,int> >, std::vector<std::pair<int, std::string> >, std::map<std::string,std::string> > > localbind_t;
-static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus)
+static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus, int& tcpListenQueueSize)
{
if (vars) {
if (vars->count("reusePort")) {
if (vars->count("tcpFastOpenQueueSize")) {
tcpFastOpenQueueSize = boost::get<int>((*vars)["tcpFastOpenQueueSize"]);
}
+ if (vars->count("tcpListenQueueSize")) {
+ tcpListenQueueSize = boost::get<int>((*vars)["tcpListenQueueSize"]);
+ }
if (vars->count("interface")) {
interface = boost::get<std::string>((*vars)["interface"]);
}
if (vars->count("cpus")) {
- for (const auto cpu : boost::get<std::vector<std::pair<int,int>>>((*vars)["cpus"])) {
+ for (const auto& cpu : boost::get<std::vector<std::pair<int,int>>>((*vars)["cpus"])) {
cpus.insert(cpu.second);
}
}
#endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
-static void setupLuaConfig(bool client, bool configCheck)
+static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
{
typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> >, DownstreamState::checkfunc_t > > newserver_t;
- g_lua.writeFunction("inClientStartup", [client]() {
+ luaCtx.writeFunction("inClientStartup", [client]() {
return client && !g_configurationDone;
});
- g_lua.writeFunction("inConfigCheck", [configCheck]() {
+ luaCtx.writeFunction("inConfigCheck", [configCheck]() {
return !configCheck;
});
- g_lua.writeFunction("newServer",
+ luaCtx.writeFunction("newServer",
[client, configCheck](boost::variant<string,newserver_t> pvars, boost::optional<int> qps) {
setLuaSideEffect();
ret->useECS=boost::get<bool>(vars["useClientSubnet"]);
}
+ if(vars.count("useProxyProtocol")) {
+ ret->useProxyProtocol = boost::get<bool>(vars["useProxyProtocol"]);
+ }
+
if(vars.count("disableZeroScope")) {
ret->disableZeroScope=boost::get<bool>(vars["disableZeroScope"]);
}
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"])) {
+ for (const auto& cpu : boost::get<vector<pair<int,string>>>(vars["cpus"])) {
cpus.insert(std::stoi(cpu.second));
}
}
if (ret->connected) {
ret->threadStarted.test_and_set();
- if(g_launchWork) {
+ if (g_launchWork) {
g_launchWork->push_back([ret,cpus]() {
ret->tid = thread(responderThread, ret);
if (!cpus.empty()) {
return ret;
} );
- g_lua.writeFunction("rmServer",
+ luaCtx.writeFunction("rmServer",
[](boost::variant<std::shared_ptr<DownstreamState>, int, std::string> var)
{
setLuaSideEffect();
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("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
+ luaCtx.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
+ luaCtx.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
- g_lua.writeFunction("addACL", [](const std::string& domain) {
+ luaCtx.writeFunction("addACL", [](const std::string& domain) {
setLuaSideEffect();
g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
});
- g_lua.writeFunction("rmACL", [](const std::string& netmask) {
+ luaCtx.writeFunction("rmACL", [](const std::string& netmask) {
setLuaSideEffect();
g_ACL.modify([netmask](NetmaskGroup& nmg) { nmg.deleteMask(netmask); });
});
- g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
+ luaCtx.writeFunction("setLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
setLuaSideEffect();
if(client)
return;
}
bool reusePort = false;
int tcpFastOpenQueueSize = 0;
+ int tcpListenQueueSize = 0;
std::string interface;
std::set<int> cpus;
- parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus);
+ parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
try {
ComboAddress loc(addr, 53);
// only works pre-startup, so no sync necessary
g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)));
- g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus)));
+ auto tcpCS = std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
+ if (tcpListenQueueSize > 0) {
+ tcpCS->tcpListenQueueSize = tcpListenQueueSize;
+ }
+ g_frontends.push_back(std::move(tcpCS));
}
catch(const std::exception& e) {
g_outputBuffer="Error: "+string(e.what())+"\n";
}
});
- g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
+ luaCtx.writeFunction("addLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
setLuaSideEffect();
if(client)
return;
}
bool reusePort = false;
int tcpFastOpenQueueSize = 0;
+ int tcpListenQueueSize = 0;
std::string interface;
std::set<int> cpus;
- parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus);
+ parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
try {
ComboAddress loc(addr, 53);
// only works pre-startup, so no sync necessary
g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)));
- g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus)));
+ auto tcpCS = std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
+ if (tcpListenQueueSize > 0) {
+ tcpCS->tcpListenQueueSize = tcpListenQueueSize;
+ }
+ g_frontends.push_back(std::move(tcpCS));
}
catch(std::exception& e) {
g_outputBuffer="Error: "+string(e.what())+"\n";
}
});
- g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
+ luaCtx.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
setLuaSideEffect();
NetmaskGroup nmg;
if(auto str = boost::get<string>(&inp)) {
g_ACL.setState(nmg);
});
- g_lua.writeFunction("showACL", []() {
+ luaCtx.writeFunction("showACL", []() {
setLuaNoSideEffect();
vector<string> vec;
});
- g_lua.writeFunction("shutdown", []() {
+ luaCtx.writeFunction("shutdown", []() {
#ifdef HAVE_SYSTEMD
sd_notify(0, "STOPPING=1");
#endif /* HAVE_SYSTEMD */
typedef std::unordered_map<std::string, boost::variant<bool, std::string> > showserversopts_t;
- g_lua.writeFunction("showServers", [](boost::optional<showserversopts_t> vars) {
+ luaCtx.writeFunction("showServers", [](boost::optional<showserversopts_t> vars) {
setLuaNoSideEffect();
bool showUUIDs = false;
if (vars) {
}
});
- g_lua.writeFunction("getServers", []() {
+ luaCtx.writeFunction("getServers", []() {
setLuaNoSideEffect();
vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
int count=1;
return ret;
});
- g_lua.writeFunction("getPoolServers", [](string pool) {
- return getDownstreamCandidates(g_pools.getCopy(), pool);
+ luaCtx.writeFunction("getPoolServers", [](string pool) {
+ const auto poolServers = getDownstreamCandidates(g_pools.getCopy(), pool);
+ return *poolServers;
});
- g_lua.writeFunction("getServer", [client](boost::variant<unsigned int, std::string> i) {
+ luaCtx.writeFunction("getServer", [client](boost::variant<unsigned int, std::string> i) {
if (client) {
return std::make_shared<DownstreamState>(ComboAddress());
}
return std::shared_ptr<DownstreamState>(nullptr);
});
- g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
+ luaCtx.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
boost::optional<unsigned int> interval, boost::optional<string> namespace_name,
boost::optional<string> instance_name) {
setLuaSideEffect();
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) {
+ luaCtx.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();
};
- if(g_launchWork)
+ if (g_launchWork) {
g_launchWork->push_back(launch);
- else
+ }
+ else {
launch();
+ }
}
catch(std::exception& e) {
g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
typedef std::unordered_map<std::string, boost::variant<std::string, std::map<std::string, std::string>> > webserveropts_t;
- g_lua.writeFunction("setWebserverConfig", [](boost::optional<webserveropts_t> vars) {
+ luaCtx.writeFunction("setWebserverConfig", [](boost::optional<webserveropts_t> vars) {
setLuaSideEffect();
if (!vars) {
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"));
}
});
- g_lua.writeFunction("controlSocket", [client,configCheck](const std::string& str) {
+ luaCtx.writeFunction("controlSocket", [client,configCheck](const std::string& str) {
setLuaSideEffect();
ComboAddress local(str, 5199);
thread t(controlThread, sock, local);
t.detach();
};
- if(g_launchWork)
+ if (g_launchWork) {
g_launchWork->push_back(launch);
- else
+ }
+ else {
launch();
-
+ }
}
catch(std::exception& e) {
g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
}
});
- g_lua.writeFunction("addConsoleACL", [](const std::string& netmask) {
+ luaCtx.writeFunction("addConsoleACL", [](const std::string& netmask) {
setLuaSideEffect();
#ifndef HAVE_LIBSODIUM
warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
});
- g_lua.writeFunction("setConsoleACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
+ luaCtx.writeFunction("setConsoleACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
setLuaSideEffect();
#ifndef HAVE_LIBSODIUM
g_consoleACL.setState(nmg);
});
- g_lua.writeFunction("showConsoleACL", []() {
+ luaCtx.writeFunction("showConsoleACL", []() {
setLuaNoSideEffect();
#ifndef HAVE_LIBSODIUM
}
});
- g_lua.writeFunction("clearQueryCounters", []() {
+ luaCtx.writeFunction("clearQueryCounters", []() {
unsigned int size{0};
{
WriteLock wl(&g_qcount.queryLock);
g_outputBuffer = (fmt % size).str();
});
- g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
+ luaCtx.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
setLuaNoSideEffect();
ReadLock rl(&g_qcount.queryLock);
g_outputBuffer = "query counting is currently: ";
}
});
- g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
+ luaCtx.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
- g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
+ luaCtx.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
g_qcount.filter = func;
});
- g_lua.writeFunction("makeKey", []() {
+ luaCtx.writeFunction("makeKey", []() {
setLuaNoSideEffect();
g_outputBuffer="setKey("+newKey()+")\n";
});
- g_lua.writeFunction("setKey", [](const std::string& key) {
+ luaCtx.writeFunction("setKey", [](const std::string& key) {
if(!g_configurationDone && ! g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
return; // but later setKeys() trump the -k value again
}
g_consoleKey=newkey;
});
- g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
+ luaCtx.writeFunction("clearConsoleHistory", []() {
+ clearConsoleHistory();
+ });
+
+ luaCtx.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
{
setLuaNoSideEffect();
#ifdef HAVE_LIBSODIUM
#endif
});
- g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
+ luaCtx.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
- g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
+ luaCtx.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
- g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
+ luaCtx.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
- g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
+ luaCtx.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
if (!g_configurationDone) {
g_maxOutstanding = max;
} else {
}
});
- g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
+ luaCtx.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
if (!g_configurationDone) {
g_maxTCPClientThreads = max;
} else {
}
});
- g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
+ luaCtx.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
if (!g_configurationDone) {
g_maxTCPQueuedConnections = max;
} else {
}
});
- g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
+ luaCtx.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
if (!g_configurationDone) {
g_maxTCPQueriesPerConn = max;
} else {
}
});
- g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
+ luaCtx.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
if (!g_configurationDone) {
g_maxTCPConnectionsPerClient = max;
} else {
}
});
- g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
+ luaCtx.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
if (!g_configurationDone) {
g_maxTCPConnectionDuration = max;
} else {
}
});
- g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
+ luaCtx.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
- g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
+ luaCtx.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
- g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
+ luaCtx.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
- g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
+ luaCtx.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
- g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
+ luaCtx.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
- g_lua.writeFunction("setPreserveTrailingData", [](bool preserve) { g_preserveTrailingData = preserve; });
+ luaCtx.writeFunction("setPreserveTrailingData", [](bool preserve) { g_preserveTrailingData = preserve; });
- g_lua.writeFunction("showDynBlocks", []() {
+ luaCtx.writeFunction("showDynBlocks", []() {
setLuaNoSideEffect();
auto slow = g_dynblockNMG.getCopy();
struct timespec now;
});
- g_lua.writeFunction("clearDynBlocks", []() {
+ luaCtx.writeFunction("clearDynBlocks", []() {
setLuaSideEffect();
nmts_t nmg;
g_dynblockNMG.setState(nmg);
g_dynblockSMT.setState(smt);
});
- g_lua.writeFunction("addDynBlocks",
+ luaCtx.writeFunction("addDynBlocks",
[](const std::unordered_map<ComboAddress,unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual>& m, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
if (m.empty()) {
return;
g_dynblockNMG.setState(slow);
});
- g_lua.writeFunction("addDynBlockSMT",
+ luaCtx.writeFunction("addDynBlockSMT",
[](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
if (names.empty()) {
return;
g_dynblockSMT.setState(slow);
});
- g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
+ luaCtx.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
if (!g_configurationDone) {
if (action == DNSAction::Action::Drop || action == DNSAction::Action::NoOp || action == DNSAction::Action::Nxdomain || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate || action == DNSAction::Action::NoRecurse) {
g_dynBlockAction = action;
}
});
- g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles, boost::optional<localbind_t> vars) {
+ luaCtx.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles, boost::optional<localbind_t> vars) {
if (g_configurationDone) {
g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
return;
#ifdef HAVE_DNSCRYPT
bool reusePort = false;
int tcpFastOpenQueueSize = 0;
+ int tcpListenQueueSize = 0;
std::string interface;
std::set<int> cpus;
std::vector<DNSCryptContext::CertKeyPaths> certKeys;
- parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus);
+ parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
auto certFile = boost::get<std::string>(certFiles);
/* TCP */
cs = std::unique_ptr<ClientState>(new ClientState(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus));
cs->dnscryptCtx = ctx;
+ if (tcpListenQueueSize > 0) {
+ cs->tcpListenQueueSize = tcpListenQueueSize;
+ }
+
g_frontends.push_back(std::move(cs));
}
catch(std::exception& e) {
});
- g_lua.writeFunction("showDNSCryptBinds", []() {
+ luaCtx.writeFunction("showDNSCryptBinds", []() {
setLuaNoSideEffect();
#ifdef HAVE_DNSCRYPT
ostringstream ret;
#endif
});
- g_lua.writeFunction("getDNSCryptBind", [](size_t idx) {
+ luaCtx.writeFunction("getDNSCryptBind", [](size_t idx) {
setLuaNoSideEffect();
#ifdef HAVE_DNSCRYPT
std::shared_ptr<DNSCryptContext> ret = nullptr;
#endif
});
- g_lua.writeFunction("getDNSCryptBindCount", []() {
+ luaCtx.writeFunction("getDNSCryptBindCount", []() {
setLuaNoSideEffect();
return g_dnsCryptLocals.size();
});
- g_lua.writeFunction("generateDNSCryptProviderKeys", [client](const std::string& publicKeyFile, const std::string privateKeyFile) {
+ luaCtx.writeFunction("generateDNSCryptProviderKeys", [client](const std::string& publicKeyFile, const std::string privateKeyFile) {
setLuaNoSideEffect();
#ifdef HAVE_DNSCRYPT
if (client) {
#endif
});
- g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
+ luaCtx.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
setLuaNoSideEffect();
#ifdef HAVE_DNSCRYPT
unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
});
#ifdef HAVE_DNSCRYPT
- g_lua.writeFunction("generateDNSCryptCertificate", [client](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
+ luaCtx.writeFunction("generateDNSCryptCertificate", [client](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
setLuaNoSideEffect();
if (client) {
return;
});
#endif
- g_lua.writeFunction("showPools", []() {
+ luaCtx.writeFunction("showPools", []() {
setLuaNoSideEffect();
try {
ostringstream ret;
const string& name = entry.first;
const std::shared_ptr<ServerPool> pool = entry.second;
string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
- string policy = g_policy.getLocal()->name;
+ string policy = g_policy.getLocal()->getName();
if (pool->policy != nullptr) {
- policy = pool->policy->name;
+ policy = pool->policy->getName();
}
string servers;
- for (const auto& server: pool->getServers()) {
+ const auto poolServers = pool->getServers();
+ for (const auto& server: *poolServers) {
if (!servers.empty()) {
servers += ", ";
}
}catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
});
- g_lua.writeFunction("getPool", [client](const string& poolName) {
+ luaCtx.writeFunction("getPool", [client](const string& poolName) {
if (client) {
return std::make_shared<ServerPool>();
}
return pool;
});
- g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
- g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
+ luaCtx.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
+ luaCtx.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
- g_lua.writeFunction("showBinds", []() {
+ luaCtx.writeFunction("showBinds", []() {
setLuaNoSideEffect();
try {
ostringstream ret;
}catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
});
- g_lua.writeFunction("getBind", [](size_t num) {
+ luaCtx.writeFunction("getBind", [](size_t num) {
setLuaNoSideEffect();
ClientState* ret = nullptr;
if(num < g_frontends.size()) {
return ret;
});
- g_lua.writeFunction("getBindCount", []() {
+ luaCtx.writeFunction("getBindCount", []() {
setLuaNoSideEffect();
return g_frontends.size();
});
- g_lua.writeFunction("help", [](boost::optional<std::string> command) {
+ luaCtx.writeFunction("help", [](boost::optional<std::string> command) {
setLuaNoSideEffect();
g_outputBuffer = "";
for (const auto& keyword : g_consoleKeywords) {
}
});
- g_lua.writeFunction("showVersion", []() {
+ luaCtx.writeFunction("showVersion", []() {
setLuaNoSideEffect();
g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
});
- g_lua.writeFunction("showSecurityStatus", []() {
+ luaCtx.writeFunction("showSecurityStatus", []() {
setLuaNoSideEffect();
g_outputBuffer = std::to_string(g_stats.securityStatus) + "\n";
});
#ifdef HAVE_EBPF
- g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
+ luaCtx.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
if (g_configurationDone) {
g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
return;
g_defaultBPFFilter = bpf;
});
- g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
+ luaCtx.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
if (dbpf) {
g_dynBPFFilters.push_back(dbpf);
}
});
- g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
+ luaCtx.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
if (dbpf) {
for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
if (*it == dbpf) {
}
});
- g_lua.writeFunction("addBPFFilterDynBlocks", [](const std::unordered_map<ComboAddress,unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds, boost::optional<std::string> msg) {
+ luaCtx.writeFunction("addBPFFilterDynBlocks", [](const std::unordered_map<ComboAddress,unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds, boost::optional<std::string> msg) {
setLuaSideEffect();
struct timespec until, now;
clock_gettime(CLOCK_MONOTONIC, &now);
#endif /* HAVE_EBPF */
- g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
+ luaCtx.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
setLuaNoSideEffect();
std::unordered_map<string,uint64_t> res;
for(const auto& entry : g_stats.entries) {
return res;
});
- g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
+ luaCtx.writeFunction("includeDirectory", [&luaCtx](const std::string& dirname) {
if (g_configurationDone) {
errlog("includeDirectory() cannot be used at runtime!");
g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
vinfolog("Read configuration from '%s'", *file);
}
- g_lua.executeCode(ifs);
+ luaCtx.executeCode(ifs);
}
g_included = false;
});
- g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
+ luaCtx.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
setLuaSideEffect();
g_apiReadWrite = writable;
if (apiConfigDir) {
}
});
- g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
+ luaCtx.writeFunction("setServFailWhenNoServer", [](bool servfail) {
setLuaSideEffect();
g_servFailOnNoPolicy = servfail;
});
- g_lua.writeFunction("setRoundRobinFailOnNoServer", [](bool fail) {
+ luaCtx.writeFunction("setRoundRobinFailOnNoServer", [](bool fail) {
setLuaSideEffect();
g_roundrobinFailOnNoServer = fail;
});
- g_lua.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
+ luaCtx.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
setLuaSideEffect();
if (factor >= 1.0) {
g_consistentHashBalancingFactor = factor;
}
});
- g_lua.writeFunction("setWeightedBalancingFactor", [](double factor) {
+ luaCtx.writeFunction("setWeightedBalancingFactor", [](double factor) {
setLuaSideEffect();
if (factor >= 1.0) {
g_weightedBalancingFactor = factor;
}
});
- g_lua.writeFunction("setRingBuffersSize", [](size_t capacity, boost::optional<size_t> numberOfShards) {
+ luaCtx.writeFunction("setRingBuffersSize", [](size_t capacity, boost::optional<size_t> numberOfShards) {
setLuaSideEffect();
if (g_configurationDone) {
errlog("setRingBuffersSize() cannot be used at runtime!");
g_rings.setCapacity(capacity, numberOfShards ? *numberOfShards : 1);
});
- g_lua.writeFunction("setRingBuffersLockRetries", [](size_t retries) {
+ luaCtx.writeFunction("setRingBuffersLockRetries", [](size_t retries) {
setLuaSideEffect();
g_rings.setNumberOfLockRetries(retries);
});
- g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
+ luaCtx.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
setLuaSideEffect();
g_hashperturb = pertub;
});
- g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
+ luaCtx.writeFunction("setTCPUseSinglePipe", [](bool flag) {
if (g_configurationDone) {
g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
return;
g_useTCPSinglePipe = flag;
});
- g_lua.writeFunction("snmpAgent", [client,configCheck](bool enableTraps, boost::optional<std::string> masterSocket) {
+ luaCtx.writeFunction("snmpAgent", [client,configCheck](bool enableTraps, boost::optional<std::string> masterSocket) {
if(client || configCheck)
return;
#ifdef HAVE_NET_SNMP
#endif /* HAVE_NET_SNMP */
});
- g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
+ luaCtx.writeFunction("sendCustomTrap", [](const std::string& str) {
if (g_snmpAgent && g_snmpTrapsEnabled) {
g_snmpAgent->sendCustomTrap(str);
}
});
- g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
+ luaCtx.writeFunction("setServerPolicy", [](ServerPolicy policy) {
setLuaSideEffect();
g_policy.setState(policy);
});
- g_lua.writeFunction("setServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy) {
+ luaCtx.writeFunction("setServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy) {
setLuaSideEffect();
g_policy.setState(ServerPolicy{name, policy, true});
});
- g_lua.writeFunction("setServerPolicyLuaFFI", [](string name, ServerPolicy::ffipolicyfunc_t policy) {
+ luaCtx.writeFunction("setServerPolicyLuaFFI", [](string name, ServerPolicy::ffipolicyfunc_t policy) {
setLuaSideEffect();
auto pol = ServerPolicy(name, policy);
g_policy.setState(std::move(pol));
});
- g_lua.writeFunction("showServerPolicy", []() {
+ luaCtx.writeFunction("setServerPolicyLuaFFIPerThread", [](string name, const std::string& policyCode) {
+ setLuaSideEffect();
+ auto pol = ServerPolicy(name, policyCode);
+ g_policy.setState(std::move(pol));
+ });
+
+ luaCtx.writeFunction("showServerPolicy", []() {
setLuaSideEffect();
- g_outputBuffer=g_policy.getLocal()->name+"\n";
+ g_outputBuffer=g_policy.getLocal()->getName()+"\n";
});
- g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
+ luaCtx.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
setLuaSideEffect();
auto localPools = g_pools.getCopy();
setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
g_pools.setState(localPools);
});
- g_lua.writeFunction("setPoolServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy, string pool) {
+ luaCtx.writeFunction("setPoolServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy, string pool) {
setLuaSideEffect();
auto localPools = g_pools.getCopy();
setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy, true}));
g_pools.setState(localPools);
});
- g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
+ luaCtx.writeFunction("setPoolServerPolicyLuaFFI", [](string name, ServerPolicy::ffipolicyfunc_t policy, string pool) {
+ setLuaSideEffect();
+ auto localPools = g_pools.getCopy();
+ setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy}));
+ g_pools.setState(localPools);
+ });
+
+ luaCtx.writeFunction("setPoolServerPolicyLuaFFIPerThread", [](string name, const std::string& policyCode, string pool) {
+ setLuaSideEffect();
+ auto localPools = g_pools.getCopy();
+ setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policyCode}));
+ g_pools.setState(localPools);
+ });
+
+ luaCtx.writeFunction("showPoolServerPolicy", [](string pool) {
setLuaSideEffect();
auto localPools = g_pools.getCopy();
auto poolObj = getPool(localPools, pool);
if (poolObj->policy == nullptr) {
- g_outputBuffer=g_policy.getLocal()->name+"\n";
+ g_outputBuffer=g_policy.getLocal()->getName()+"\n";
} else {
- g_outputBuffer=poolObj->policy->name+"\n";
+ g_outputBuffer=poolObj->policy->getName()+"\n";
}
});
- g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
+ luaCtx.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
setLuaSideEffect();
g_downstreamTCPCleanupInterval = interval;
});
- g_lua.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
+ luaCtx.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
g_logConsoleConnections = enabled;
});
- g_lua.writeFunction("setConsoleOutputMaxMsgSize", [](uint32_t size) {
+ luaCtx.writeFunction("setConsoleOutputMaxMsgSize", [](uint32_t size) {
g_consoleOutputMsgMaxSize = size;
});
- g_lua.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
+ luaCtx.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
if (g_configurationDone) {
errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
g_outputBuffer="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
#endif
});
- g_lua.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
+ luaCtx.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
g_addEDNSToSelfGeneratedResponses = add;
});
- g_lua.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint16_t payloadSize) {
+ luaCtx.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint16_t payloadSize) {
if (payloadSize < 512) {
warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
g_outputBuffer="setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
g_PayloadSizeSelfGenAnswers = payloadSize;
});
- g_lua.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) {
+ luaCtx.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) {
if (g_configurationDone) {
g_outputBuffer="setSecurityPollSuffix() cannot be used at runtime!\n";
return;
g_secPollSuffix = suffix;
});
- g_lua.writeFunction("setSecurityPollInterval", [](time_t newInterval) {
+ luaCtx.writeFunction("setSecurityPollInterval", [](time_t newInterval) {
if (newInterval <= 0) {
warnlog("setSecurityPollInterval() should be > 0, skipping");
g_outputBuffer="setSecurityPollInterval() should be > 0, skipping";
g_secPollInterval = newInterval;
});
- g_lua.writeFunction("setSyslogFacility", [](int facility) {
+ luaCtx.writeFunction("setSyslogFacility", [](int facility) {
setLuaSideEffect();
if (g_configurationDone) {
g_outputBuffer="setSyslogFacility cannot be used at runtime!\n";
setSyslogFacility(facility);
});
- g_lua.writeFunction("addDOHLocal", [client](const std::string& addr, boost::optional<boost::variant<std::string, std::vector<std::pair<int,std::string>>>> certFiles, boost::optional<boost::variant<std::string, std::vector<std::pair<int,std::string>>>> keyFiles, boost::optional<boost::variant<std::string, vector<pair<int, std::string> > > > urls, boost::optional<localbind_t> vars) {
+ luaCtx.writeFunction("addDOHLocal", [client](const std::string& addr, boost::optional<boost::variant<std::string, std::vector<std::pair<int,std::string>>>> certFiles, boost::optional<boost::variant<std::string, std::vector<std::pair<int,std::string>>>> keyFiles, boost::optional<boost::variant<std::string, vector<pair<int, std::string> > > > urls, boost::optional<localbind_t> vars) {
if (client) {
return;
}
bool reusePort = false;
int tcpFastOpenQueueSize = 0;
+ int tcpListenQueueSize = 0;
std::string interface;
std::set<int> cpus;
- if(vars) {
- parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus);
+ if (vars) {
+ parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
if (vars->count("idleTimeout")) {
frontend->d_idleTimeout = boost::get<int>((*vars)["idleTimeout"]);
frontend->d_sendCacheControlHeaders = boost::get<bool>((*vars)["sendCacheControlHeaders"]);
}
+ if (vars->count("trustForwardedForHeader")) {
+ 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);
auto cs = std::unique_ptr<ClientState>(new ClientState(frontend->d_local, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
cs->dohFrontend = frontend;
+ if (tcpListenQueueSize > 0) {
+ cs->tcpListenQueueSize = tcpListenQueueSize;
+ }
+
g_frontends.push_back(std::move(cs));
#else
throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
#endif
});
- g_lua.writeFunction("showDOHFrontends", []() {
+ luaCtx.writeFunction("showDOHFrontends", []() {
#ifdef HAVE_DNS_OVER_HTTPS
setLuaNoSideEffect();
try {
ret << (fmt % "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl;
size_t counter = 0;
for (const auto& ctx : g_dohlocals) {
- ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_httpconnects % ctx->d_http1Stats.d_nbQueries % ctx->d_http1Stats.d_nbQueries % ctx->d_getqueries % ctx->d_postqueries % ctx->d_badrequests % ctx->d_errorresponses % ctx->d_redirectresponses % ctx->d_validresponses % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
+ ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_httpconnects % ctx->d_http1Stats.d_nbQueries % ctx->d_http2Stats.d_nbQueries % ctx->d_getqueries % ctx->d_postqueries % ctx->d_badrequests % ctx->d_errorresponses % ctx->d_redirectresponses % ctx->d_validresponses % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
counter++;
}
g_outputBuffer = ret.str();
#endif
});
- g_lua.writeFunction("showDOHResponseCodes", []() {
+ luaCtx.writeFunction("showDOHResponseCodes", []() {
#ifdef HAVE_DNS_OVER_HTTPS
setLuaNoSideEffect();
try {
#endif
});
- g_lua.writeFunction("getDOHFrontend", [client](size_t index) {
+ luaCtx.writeFunction("getDOHFrontend", [client](size_t index) {
std::shared_ptr<DOHFrontend> result = nullptr;
if (client) {
return result;
return result;
});
- g_lua.writeFunction("getDOHFrontendCount", []() {
+ luaCtx.writeFunction("getDOHFrontendCount", []() {
setLuaNoSideEffect();
return g_dohlocals.size();
});
- g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<DOHFrontend> frontend) {
+ luaCtx.registerFunction<void(std::shared_ptr<DOHFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<DOHFrontend> frontend) {
if (frontend != nullptr) {
frontend->reloadCertificates();
}
});
- g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<DOHFrontend> frontend) {
+ luaCtx.registerFunction<void(std::shared_ptr<DOHFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<DOHFrontend> frontend) {
if (frontend != nullptr) {
frontend->rotateTicketsKey(time(nullptr));
}
});
- g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<DOHFrontend> frontend, const std::string& file) {
+ luaCtx.registerFunction<void(std::shared_ptr<DOHFrontend>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<DOHFrontend> frontend, const std::string& file) {
if (frontend != nullptr) {
frontend->loadTicketsKeys(file);
}
});
- g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)(const std::map<int, std::shared_ptr<DOHResponseMapEntry>>&)>("setResponsesMap", [](std::shared_ptr<DOHFrontend> frontend, const std::map<int, std::shared_ptr<DOHResponseMapEntry>>& map) {
+ luaCtx.registerFunction<void(std::shared_ptr<DOHFrontend>::*)(const std::map<int, std::shared_ptr<DOHResponseMapEntry>>&)>("setResponsesMap", [](std::shared_ptr<DOHFrontend> frontend, const std::map<int, std::shared_ptr<DOHResponseMapEntry>>& map) {
if (frontend != nullptr) {
std::vector<std::shared_ptr<DOHResponseMapEntry>> newMap;
newMap.reserve(map.size());
}
});
- g_lua.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles, boost::optional<localbind_t> vars) {
+ luaCtx.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles, boost::optional<localbind_t> vars) {
if (client) {
return;
}
bool reusePort = false;
int tcpFastOpenQueueSize = 0;
+ int tcpListenQueueSize = 0;
std::string interface;
std::set<int> cpus;
if (vars) {
- parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus);
+ parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
if (vars->count("provider")) {
frontend->d_provider = boost::get<const string>((*vars)["provider"]);
// only works pre-startup, so no sync necessary
auto cs = std::unique_ptr<ClientState>(new ClientState(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
cs->tlsFrontend = frontend;
+ if (tcpListenQueueSize > 0) {
+ cs->tcpListenQueueSize = tcpListenQueueSize;
+ }
g_tlslocals.push_back(cs->tlsFrontend);
g_frontends.push_back(std::move(cs));
}
#endif
});
- g_lua.writeFunction("showTLSContexts", []() {
+ luaCtx.writeFunction("showTLSContexts", []() {
#ifdef HAVE_DNS_OVER_TLS
setLuaNoSideEffect();
try {
#endif
});
- g_lua.writeFunction("getTLSContext", [](size_t index) {
+ luaCtx.writeFunction("getTLSContext", [](size_t index) {
std::shared_ptr<TLSCtx> result = nullptr;
#ifdef HAVE_DNS_OVER_TLS
setLuaNoSideEffect();
return result;
});
- g_lua.writeFunction("getTLSFrontend", [](size_t index) {
+ luaCtx.writeFunction("getTLSFrontend", [](size_t index) {
std::shared_ptr<TLSFrontend> result = nullptr;
#ifdef HAVE_DNS_OVER_TLS
setLuaNoSideEffect();
return result;
});
- g_lua.writeFunction("getTLSFrontendCount", []() {
+ luaCtx.writeFunction("getTLSFrontendCount", []() {
setLuaNoSideEffect();
return g_tlslocals.size();
});
- g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
+ luaCtx.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
if (ctx != nullptr) {
ctx->rotateTicketsKey(time(nullptr));
}
});
- g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx> ctx, const std::string& file) {
+ luaCtx.registerFunction<void(std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx> ctx, const std::string& file) {
if (ctx != nullptr) {
ctx->loadTicketsKeys(file);
}
});
- g_lua.registerFunction<void(std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles) {
+ luaCtx.registerFunction<void(std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles) {
#ifdef HAVE_DNS_OVER_TLS
if (loadTLSCertificateAndKeys("loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
frontend->setupTLS();
#endif
});
- g_lua.writeFunction("reloadAllCertificates", []() {
+ luaCtx.writeFunction("reloadAllCertificates", []() {
for (auto& frontend : g_frontends) {
if (!frontend) {
continue;
}
});
- g_lua.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse=allow; });
+ luaCtx.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse=allow; });
#if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN)
- g_lua.writeFunction("generateOCSPResponse", [client](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
+ luaCtx.writeFunction("generateOCSPResponse", [client](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
if (client) {
return;
}
#endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN*/
}
-vector<std::function<void(void)>> setupLua(bool client, bool configCheck, const std::string& config)
+vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config)
{
- g_launchWork= new vector<std::function<void(void)>>();
-
- setupLuaActions();
- setupLuaConfig(client, configCheck);
- setupLuaBindings(client);
- setupLuaBindingsDNSCrypt();
- setupLuaBindingsDNSQuestion();
- setupLuaBindingsKVS(client);
- setupLuaBindingsPacketCache();
- setupLuaBindingsProtoBuf(client, configCheck);
- setupLuaInspection();
- setupLuaRules();
- setupLuaVars();
+ // this needs to exist only during the parsing of the configuration
+ // and cannot be captured by lambdas
+ g_launchWork = std::vector<std::function<void(void)>>();
+
+ setupLuaActions(luaCtx);
+ setupLuaConfig(luaCtx, client, configCheck);
+ setupLuaBindings(luaCtx, client);
+ setupLuaBindingsDNSCrypt(luaCtx);
+ setupLuaBindingsDNSQuestion(luaCtx);
+ setupLuaBindingsKVS(luaCtx, client);
+ setupLuaBindingsPacketCache(luaCtx);
+ setupLuaBindingsProtoBuf(luaCtx, client, configCheck);
+ setupLuaInspection(luaCtx);
+ setupLuaRules(luaCtx);
+ setupLuaVars(luaCtx);
#ifdef LUAJIT_VERSION
- g_lua.executeCode(getLuaFFIWrappers());
+ luaCtx.executeCode(getLuaFFIWrappers());
#endif
std::ifstream ifs(config);
else
vinfolog("Read configuration from '%s'", config);
- g_lua.executeCode(ifs);
+ luaCtx.executeCode(ifs);
auto ret = *g_launchWork;
- delete g_launchWork;
- g_launchWork = nullptr;
+ g_launchWork = boost::none;
return ret;
}
*/
#pragma once
+#include <random>
+
struct ResponseConfig
{
boost::optional<bool> setAA{boost::none};
ResponseConfig d_responseConfig;
private:
+ static thread_local std::default_random_engine t_randomEngine;
std::vector<ComboAddress> d_addrs;
std::set<uint16_t> d_types;
std::string d_rawResponse;
typedef NetmaskTree<DynBlock> nmts_t;
-vector<std::function<void(void)>> setupLua(bool client, bool configCheck, const std::string& config);
-void setupLuaActions();
-void setupLuaBindings(bool client);
-void setupLuaBindingsDNSCrypt();
-void setupLuaBindingsDNSQuestion();
-void setupLuaBindingsKVS(bool client);
-void setupLuaBindingsPacketCache();
-void setupLuaBindingsProtoBuf(bool client, bool configCheck);
-void setupLuaRules();
-void setupLuaInspection();
-void setupLuaVars();
+vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config);
+void setupLuaActions(LuaContext& luaCtx);
+void setupLuaBindings(LuaContext& luaCtx, bool client);
+void setupLuaBindingsDNSCrypt(LuaContext& luaCtx);
+void setupLuaBindingsDNSQuestion(LuaContext& luaCtx);
+void setupLuaBindingsKVS(LuaContext& luaCtx, bool client);
+void setupLuaBindingsPacketCache(LuaContext& luaCtx);
+void setupLuaBindingsProtoBuf(LuaContext& luaCtx, bool client, bool configCheck);
+void setupLuaRules(LuaContext& luaCtx);
+void setupLuaInspection(LuaContext& luaCtx);
+void setupLuaVars(LuaContext& luaCtx);
+void setupLuaLoadBalancingContext(LuaContext& luaCtx);
*/
#include "dnsdist.hh"
#include "dnsdist-ecs.hh"
+#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-xpf.hh"
return d_enableFastOpen;
}
+ bool canBeReused() const
+ {
+ /* we can't reuse a connection where a proxy protocol payload has been sent,
+ since:
+ - it cannot be reused for a different client
+ - we might have different TLV values for each query
+ */
+ if (d_ds && d_ds->useProxyProtocol) {
+ return false;
+ }
+ return true;
+ }
+
+ bool matches(const std::shared_ptr<DownstreamState>& ds) const
+ {
+ if (!ds || !d_ds) {
+ return false;
+ }
+ return ds == d_ds;
+ }
+
private:
std::unique_ptr<Socket> d_socket{nullptr};
std::shared_ptr<DownstreamState> d_ds{nullptr};
return;
}
+ if (!conn->canBeReused()) {
+ conn.reset();
+ return;
+ }
+
const auto& remote = conn->getRemote();
const auto& it = t_downstreamConnections.find(remote);
if (it != t_downstreamConnections.end()) {
would block.
*/
// XXX could probably be implemented as a TCPIOHandler
-IOState tryRead(int fd, std::vector<uint8_t>& buffer, size_t& pos, size_t toRead)
+static IOState tryRead(int fd, std::vector<uint8_t>& buffer, size_t& pos, size_t toRead)
{
if (buffer.size() < (pos + toRead)) {
throw std::out_of_range("Calling tryRead() with a too small buffer (" + std::to_string(buffer.size()) + ") for a read of " + std::to_string(toRead) + " bytes starting at " + std::to_string(pos));
bool d_isXFR{false};
bool d_xfrStarted{false};
bool d_selfGeneratedResponse{false};
+ bool d_proxyProtocolPayloadAdded{false};
+ bool d_proxyProtocolPayloadHasTLV{false};
};
static void handleIOCallback(int fd, FDMultiplexer::funcparam_t& param);
state->d_state = IncomingTCPConnectionState::State::sendingQueryToBackend;
state->d_currentPos = 0;
state->d_firstResponsePacket = true;
- state->d_downstreamConnection.reset();
if (state->d_xfrStarted) {
/* sorry, but we are not going to resume a XFR if we have already sent some packets
return;
}
- if (state->d_downstreamFailures < state->d_ds->retries) {
- try {
- state->d_downstreamConnection = getConnectionToDownstream(ds, state->d_downstreamFailures, now);
+ if (!state->d_downstreamConnection) {
+ if (state->d_downstreamFailures < state->d_ds->retries) {
+ try {
+ state->d_downstreamConnection = getConnectionToDownstream(ds, state->d_downstreamFailures, now);
+ }
+ catch (const std::runtime_error& e) {
+ state->d_downstreamConnection.reset();
+ }
}
- catch (const std::runtime_error& e) {
- state->d_downstreamConnection.reset();
+
+ if (!state->d_downstreamConnection) {
+ ++ds->tcpGaveUp;
+ ++state->d_ci.cs->tcpGaveUp;
+ vinfolog("Downstream connection to %s failed %d times in a row, giving up.", ds->getName(), state->d_downstreamFailures);
+ return;
}
- }
- if (!state->d_downstreamConnection) {
- ++ds->tcpGaveUp;
- ++state->d_ci.cs->tcpGaveUp;
- vinfolog("Downstream connection to %s failed %d times in a row, giving up.", ds->getName(), state->d_downstreamFailures);
- return;
+ if (ds->useProxyProtocol && !state->d_proxyProtocolPayloadAdded) {
+ /* we know there is no TLV values to add, otherwise we would not have tried
+ to reuse the connection and d_proxyProtocolPayloadAdded would be true already */
+ addProxyProtocol(state->d_buffer, true, state->d_ci.remote, state->d_ids.origDest, std::vector<ProxyProtocolValue>());
+ state->d_proxyProtocolPayloadAdded = true;
+ }
}
vinfolog("Got query for %s|%s from %s (%s), relayed to %s", state->d_ids.qname.toLogString(), QType(state->d_ids.qtype).getName(), state->d_ci.remote.toStringWithPort(), (state->d_ci.cs->tlsFrontend ? "DoT" : "TCP"), ds->getName());
}
state->d_readingFirstQuery = false;
+ state->d_proxyProtocolPayloadAdded = false;
++state->d_queriesCount;
++state->d_ci.cs->queries;
++g_stats.queries;
return;
}
- state->d_buffer.resize(dq.len);
setIDStateFromDNSQuestion(state->d_ids, dq, std::move(qname));
const uint8_t sizeBytes[] = { static_cast<uint8_t>(dq.len / 256), static_cast<uint8_t>(dq.len % 256) };
that could occur if we had to deal with the size during the processing,
especially alignment issues */
state->d_buffer.insert(state->d_buffer.begin(), sizeBytes, sizeBytes + 2);
+ dq.len = dq.len + 2;
+ dq.dh = reinterpret_cast<dnsheader*>(&state->d_buffer.at(0));
+ dq.size = state->d_buffer.size();
+ state->d_buffer.resize(dq.len);
+
+ if (state->d_ds->useProxyProtocol) {
+ /* if we ever sent a TLV over a connection, we can never go back */
+ if (!state->d_proxyProtocolPayloadHasTLV) {
+ state->d_proxyProtocolPayloadHasTLV = dq.proxyProtocolValues && !dq.proxyProtocolValues->empty();
+ }
+
+ if (state->d_downstreamConnection && !state->d_proxyProtocolPayloadHasTLV && state->d_downstreamConnection->matches(state->d_ds)) {
+ /* we have an existing connection, on which we already sent a Proxy Protocol header with no values
+ (in the previous query had TLV values we would have reset the connection afterwards),
+ so let's reuse it as long as we still don't have any values */
+ state->d_proxyProtocolPayloadAdded = false;
+ }
+ else {
+ state->d_downstreamConnection.reset();
+ addProxyProtocol(state->d_buffer, true, state->d_ci.remote, state->d_ids.origDest, dq.proxyProtocolValues ? *dq.proxyProtocolValues : std::vector<ProxyProtocolValue>());
+ state->d_proxyProtocolPayloadAdded = true;
+ }
+ }
+
sendQueryToBackend(state, now);
}
/* but don't reset it either, we will need to read more messages */
}
else {
- releaseDownstreamConnection(std::move(state->d_downstreamConnection));
+ /* if we did not send a Proxy Protocol header, let's pool the connection */
+ if (state->d_ds && state->d_ds->useProxyProtocol == false) {
+ releaseDownstreamConnection(std::move(state->d_downstreamConnection));
+ }
+ else {
+ if (state->d_proxyProtocolPayloadHasTLV) {
+ /* sent a Proxy Protocol header with TLV values, we can't reuse it */
+ state->d_downstreamConnection.reset();
+ }
+ else {
+ /* if we did but there was no TLV values, let's try to reuse it but only
+ for this incoming connection */
+ }
+ }
}
fd = -1;
}
if (connectionDied) {
+ state->d_downstreamConnection.reset();
sendQueryToBackend(state, now);
}
}
/* spawn as many of these as required, they call Accept on a socket on which they will accept queries, and
they will hand off to worker threads & spawn more of them if required
*/
-void tcpAcceptorThread(void* p)
+void tcpAcceptorThread(ClientState* cs)
{
setThreadName("dnsdist/tcpAcce");
- ClientState* cs = (ClientState*) p;
+
bool tcpClientCountIncremented = false;
ComboAddress remote;
remote.sin4.sin_family = cs->local.sin4.sin_family;
* 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");
{ "packetcache-misses", 0},
{ "over-capacity-drops", 0 },
{ "too-old-drops", 0 },
- { "server-policy", g_policy.getLocal()->name}
+ { "server-policy", g_policy.getLocal()->getName()}
};
for(const auto& e : g_stats.entries) {
}
// Latency histogram buckets
- output << "# HELP dnsdist_latency Histogram of responses by latency\n";
+ output << "# HELP dnsdist_latency Histogram of responses by latency (in milliseconds)\n";
output << "# TYPE dnsdist_latency histogram\n";
uint64_t latency_amounts = g_stats.latency0_1;
output << "dnsdist_latency_bucket{le=\"1\"} " << latency_amounts << "\n";
if (front->udpFD == -1 && front->tcpFD == -1)
continue;
- const string frontName = front->local.toString() + ":" + std::to_string(front->local.getPort());
+ const string frontName = front->local.toStringWithPort();
const string proto = front->getType();
const string fullName = frontName + "_" + proto;
uint64_t threadNumber = 0;
}
}
+ output << "# HELP dnsdist_info " << "Info from dnsdist, value is always 1" << "\n";
+ output << "# TYPE dnsdist_info " << "gauge" << "\n";
+ output << "dnsdist_info{version=\"" << VERSION << "\"} " << "1" << "\n";
+
resp.body = output.str();
resp.headers["Content-Type"] = "text/plain";
}
{ "ecs-source-prefix-v6", (double) g_ECSSourcePrefixV6 },
{ "fixup-case", g_fixupCase },
{ "max-outstanding", (double) g_maxOutstanding },
- { "server-policy", g_policy.getLocal()->name },
+ { "server-policy", g_policy.getLocal()->getName() },
{ "stale-cache-entries-ttl", (double) g_staleCacheEntriesTTL },
{ "tcp-recv-timeout", (double) g_tcpRecvTimeout },
{ "tcp-send-timeout", (double) g_tcpSendTimeout },
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>
#include "dnsdist-ecs.hh"
#include "dnsdist-healthchecks.hh"
#include "dnsdist-lua.hh"
+#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-secpoll.hh"
#include "dnsdist-xpf.hh"
}
const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(response);
+ if (dh->qr == 0) {
+ ++g_stats.nonCompliantResponses;
+ return false;
+ }
+
if (dh->qdcount == 0) {
if ((dh->rcode != RCode::NoError && dh->rcode != RCode::NXDomain) || g_allowEmptyResponse) {
return true;
return true;
}
- if(g_fixupCase) {
- string realname = qname.toDNSString();
+ if (g_fixupCase) {
+ const auto& realname = qname.getStorage();
if (*responseLen >= (sizeof(dnsheader) + realname.length())) {
memcpy(*response + sizeof(dnsheader), realname.c_str(), realname.length());
}
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 = policy.getSelectedBackend(*servers, dq);
uint16_t cachedResponseSize = dq.size;
uint32_t allowExpired = selectedBackend ? 0 : g_staleCacheEntriesTTL;
dh->id = idOffset;
+ if (ss->useProxyProtocol) {
+ addProxyProtocol(dq);
+ }
+
int fd = pickBackendSocketForSending(ss);
ssize_t ret = udpClientSendRequestToBackend(ss, fd, query, dq.len);
/* initialize the structures needed to receive our messages */
for (size_t idx = 0; idx < vectSize; idx++) {
recvData[idx].remote.sin4.sin_family = cs->local.sin4.sin_family;
- fillMSGHdr(&msgVec[idx].msg_hdr, &recvData[idx].iov, &recvData[idx].cbuf, sizeof(recvData[idx].cbuf), recvData[idx].packet, s_udpIncomingBufferSize, &recvData[idx].remote);
+ fillMSGHdr(&msgVec[idx].msg_hdr, &recvData[idx].iov, &recvData[idx].cbuf, sizeof(recvData[idx].cbuf), recvData[idx].packet, cs->dnscryptCtx ? sizeof(recvData[idx].packet) : s_udpIncomingBufferSize, &recvData[idx].remote);
}
/* go now */
ComboAddress remote;
ComboAddress dest;
remote.sin4.sin_family = cs->local.sin4.sin_family;
- fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), packet, s_udpIncomingBufferSize, &remote);
+ fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), packet, cs->dnscryptCtx ? sizeof(packet) : s_udpIncomingBufferSize, &remote);
for(;;) {
ssize_t got = recvmsg(cs->udpFD, &msgh, 0);
std::atomic<uint16_t> g_cacheCleaningDelay{60};
std::atomic<uint16_t> g_cacheCleaningPercentage{100};
-void maintThread()
+static void maintThread()
{
setThreadName("dnsdist/main");
int interval = 1;
}
if (cs->reuseport) {
-#ifdef SO_REUSEPORT
- SSetsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1);
-#else
- if (warn) {
- /* no need to warn again if configured but support is not available, we already did for UDP */
- warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", cs->local.toStringWithPort());
+ if (!setReusePort(fd)) {
+ if (warn) {
+ /* no need to warn again if configured but support is not available, we already did for UDP */
+ warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", cs->local.toStringWithPort());
+ }
}
-#endif
}
- if (!cs->tcp) {
- if (cs->local.isIPv4()) {
- try {
- setSocketIgnorePMTU(cs->udpFD);
- }
- catch(const std::exception& e) {
- warnlog("Failed to set IP_MTU_DISCOVER on UDP server socket for local address '%s': %s", cs->local.toStringWithPort(), e.what());
- }
+ /* Only set this on IPv4 UDP sockets.
+ Don't set it for DNSCrypt binds. DNSCrypt pads queries for privacy
+ purposes, so we do receive large, sometimes fragmented datagrams. */
+ if (!cs->tcp && cs->local.isIPv4() && !cs->dnscryptCtx) {
+ try {
+ setSocketIgnorePMTU(cs->udpFD);
+ }
+ catch(const std::exception& e) {
+ warnlog("Failed to set IP_MTU_DISCOVER on UDP server socket for local address '%s': %s", cs->local.toStringWithPort(), e.what());
}
}
SBind(fd, cs->local);
if (cs->tcp) {
- SListen(cs->tcpFD, SOMAXCONN);
+ SListen(cs->tcpFD, cs->tcpListenQueueSize);
+
if (cs->tlsFrontend != nullptr) {
warnlog("Listening on %s for TLS", cs->local.toStringWithPort());
}
g_policy.setState(leastOutstandingPol);
if(g_cmdLine.beClient || !g_cmdLine.command.empty()) {
- setupLua(true, false, g_cmdLine.config);
+ setupLua(g_lua, true, false, g_cmdLine.config);
if (clientAddress != ComboAddress())
g_serverControl = clientAddress;
doClient(g_serverControl, g_cmdLine.command);
g_consoleACL.setState(consoleACL);
if (g_cmdLine.checkConfig) {
- setupLua(false, true, g_cmdLine.config);
+ setupLua(g_lua, false, true, g_cmdLine.config);
// No exception was thrown
infolog("Configuration '%s' OK!", g_cmdLine.config);
_exit(EXIT_SUCCESS);
}
- auto todo=setupLua(false, false, g_cmdLine.config);
+ auto todo = setupLua(g_lua, false, false, g_cmdLine.config);
auto localPools = g_pools.getCopy();
{
bool precompute = false;
- if (g_policy.getLocal()->name == "chashed") {
+ if (g_policy.getLocal()->getName() == "chashed") {
precompute = true;
} else {
for (const auto& entry: localPools) {
- if (entry.second->policy != nullptr && entry.second->policy->name == "chashed") {
+ if (entry.second->policy != nullptr && entry.second->policy->getName() == "chashed") {
precompute = true;
break ;
}
// pre compute hashes
auto backends = g_dstates.getLocal();
for (auto& backend: *backends) {
+ if (backend->weight < 100) {
+ vinfolog("Warning, the backend '%s' has a very low weight (%d), which will not yield a good distribution of queries with the 'chashed' policy. Please consider raising it to at least '100'.", backend->getName(), backend->weight);
+ }
+
backend->hash();
}
}
#include "sholder.hh"
#include "tcpiohandler.hh"
#include "uuid-utils.hh"
+#include "proxy-protocol.hh"
void carbonDumpThread();
uint64_t uptimeOfProcess(const std::string& str);
const ComboAddress* local{nullptr};
const ComboAddress* remote{nullptr};
std::shared_ptr<QTag> qTag{nullptr};
+ std::unique_ptr<std::vector<ProxyProtocolValue>> proxyProtocolValues{nullptr};
std::shared_ptr<std::map<uint16_t, EDNSOptionView> > ednsOptions;
std::shared_ptr<DNSCryptQuery> dnsCryptQuery{nullptr};
std::shared_ptr<DNSDistPacketCache> packetCache{nullptr};
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},
struct QueryCount {
QueryCount()
{
- pthread_rwlock_init(&queryLock, nullptr);
}
~QueryCount()
{
- pthread_rwlock_destroy(&queryLock);
}
QueryCountRecords records;
QueryCountFilter filter;
- pthread_rwlock_t queryLock;
+ ReadWriteLock queryLock;
bool enabled{false};
};
std::atomic<double> tcpAvgConnectionDuration{0.0};
int udpFD{-1};
int tcpFD{-1};
+ int tcpListenQueueSize{SOMAXCONN};
int fastOpenQueueSize{0};
bool muted{false};
bool tcp;
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;
- }
- }
- pthread_rwlock_destroy(&d_lock);
- }
+ ~DownstreamState();
+
boost::uuids::uuid id;
std::vector<unsigned int> hashes;
- mutable pthread_rwlock_t d_lock;
+ mutable ReadWriteLock d_lock;
std::vector<int> sockets;
const std::string sourceItfName;
std::mutex socketsLock;
bool mustResolve{false};
bool upStatus{false};
bool useECS{false};
+ bool useProxyProtocol{false};
bool setCD{false};
bool disableZeroScope{false};
std::atomic<bool> connected{false};
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>())
{
- pthread_rwlock_init(&d_lock, nullptr);
}
+
~ServerPool()
{
- pthread_rwlock_destroy(&d_lock);
}
const std::shared_ptr<DNSDistPacketCache> getCache() const { return packetCache; };
{
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;
- pthread_rwlock_t d_lock;
+ std::shared_ptr<ServerPolicy::NumberedServerVector> d_servers;
+ ReadWriteLock d_lock;
bool d_useECS{false};
};
struct dnsheader;
-void controlThread(int fd, ComboAddress local);
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);
+void tcpAcceptorThread(ClientState* p);
#ifdef HAVE_DNS_OVER_HTTPS
void dohThread(ClientState* cs);
#endif /* HAVE_DNS_OVER_HTTPS */
+-- == 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-lua-vars.cc \
dnsdist-prometheus.hh \
dnsdist-protobuf.cc dnsdist-protobuf.hh \
+ dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
dnsdist-rings.cc dnsdist-rings.hh \
dnsdist-rules.hh \
dnsdist-secpoll.cc dnsdist-secpoll.hh \
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 \
misc.cc misc.hh \
mplexer.hh \
namespaces.hh \
+ packetcache.hh \
pdnsexception.hh \
protobuf.cc protobuf.hh \
+ proxy-protocol.cc proxy-protocol.hh \
dnstap.cc dnstap.hh \
qtype.cc qtype.hh \
remote_logger.cc remote_logger.hh \
tcpiohandler.cc tcpiohandler.hh \
threadname.hh threadname.cc \
uuid-utils.hh uuid-utils.cc \
+ views.hh \
xpf.cc xpf.hh \
ext/luawrapper/include/LuaContext.hpp \
ext/json11/json11.cpp \
test-dnsparser_cc.cc \
test-iputils_hh.cc \
test-mplexer.cc \
+ test-proxy_protocol_cc.cc \
+ bpf-filter.cc bpf-filter.hh \
cachecleaner.hh \
circular_buffer.hh \
dnsdist.hh \
dnsdist-backend.cc \
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-dynblocks.cc dnsdist-dynblocks.hh \
+ dnsdist-dynbpf.cc dnsdist-dynbpf.hh \
dnsdist-ecs.cc dnsdist-ecs.hh \
dnsdist-kvs.cc dnsdist-kvs.hh \
dnsdist-lbpolicies.cc dnsdist-lbpolicies.hh \
+ dnsdist-lua-bindings.cc \
+ dnsdist-lua-bindings-dnsquestion.cc \
+ dnsdist-lua-bindings-kvs.cc \
dnsdist-lua-ffi.cc dnsdist-lua-ffi.hh \
dnsdist-lua-ffi-interface.h dnsdist-lua-ffi-interface.inc \
+ dnsdist-lua-vars.cc \
dnsdist-rings.hh \
dnsdist-xpf.cc dnsdist-xpf.hh \
dnscrypt.cc dnscrypt.hh \
namespaces.hh \
pdnsexception.hh \
pollmplexer.cc \
+ proxy-protocol.cc proxy-protocol.hh \
qtype.cc qtype.hh \
sholder.hh \
sodcrypto.cc \
if !HAVE_SYSTEMD_PRIVATE_TMP
$(AM_V_GEN)perl -ni -e 'print unless /^PrivateTmp/' $@
endif
+if !HAVE_SYSTEMD_PRIVATE_USERS
+ $(AM_V_GEN)perl -ni -e 'print unless /^PrivateUsers/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_CLOCK
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectClock/' $@
+endif
if !HAVE_SYSTEMD_PROTECT_CONTROL_GROUPS
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectControlGroups/' $@
endif
if !HAVE_SYSTEMD_PROTECT_HOME
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectHome/' $@
endif
+if !HAVE_SYSTEMD_PROTECT_HOSTNAME
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectHostname/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_KERNEL_LOGS
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelLogs/' $@
+endif
if !HAVE_SYSTEMD_PROTECT_KERNEL_MODULES
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelModules/' $@
endif
if !HAVE_SYSTEMD_RESTRICT_REALTIME
$(AM_V_GEN)perl -ni -e 'print unless /^RestrictRealtime/' $@
endif
+if !HAVE_SYSTEMD_RESTRICT_SUIDSGID
+ $(AM_V_GEN)perl -ni -e 'print unless /^RestrictSUIDSGID/' $@
+endif
if !HAVE_SYSTEMD_SYSTEM_CALL_ARCHITECTURES
$(AM_V_GEN)perl -ni -e 'print unless /^SystemCallArchitectures/' $@
endif
endif
dnsdist@.service: dnsdist.service
- $(AM_V_GEN)sed -e 's!/dnsdist !&--config $(sysconfdir)/dnsdist-%i.conf !' < $< >$@
+ $(AM_V_GEN)sed -e 's!/dnsdist !&--config $(sysconfdir)/dnsdist-%i.conf !' \
+ -e 's!RuntimeDirectory=.*!&-%i!' \
+ < $< >$@
systemdsystemunitdir = $(SYSTEMD_DIR)
LT_INIT([disable-static])
CFLAGS="-g -O3 -Wall -Wextra -Wshadow -Wno-unused-parameter $CFLAGS"
-CXXFLAGS="-g -O3 -Wall -Wextra -Wshadow -Wno-unused-parameter $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],
for (auto& fd : sockets) {
if (fd != -1) {
if (sockets.size() > 1) {
- std::lock_guard<std::mutex> lock(socketsLock);
- mplexer->removeReadFD(fd);
+ try {
+ std::lock_guard<std::mutex> lock(socketsLock);
+ mplexer->removeReadFD(fd);
+ }
+ catch (const FDMultiplexerException& e) {
+ /* some sockets might not have been added to the multiplexer
+ yet, that's fine */
+ }
}
/* shutdown() is needed to wake up recv() in the responderThread */
shutdown(fd, SHUT_RDWR);
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);
DownstreamState::DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf_, const std::string& sourceItfName_, size_t numberOfSockets, bool connect=true): sourceItfName(sourceItfName_), remote(remote_), sourceAddr(sourceAddr_), sourceItf(sourceItf_), name(remote_.toStringWithPort()), nameWithAddr(remote_.toStringWithPort())
{
- pthread_rwlock_init(&d_lock, nullptr);
id = getUniqueID();
threadStarted.clear();
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()) {
dnsheader * requestHeader = dpw.getHeader();
*requestHeader = checkHeader;
+ if (ds->useProxyProtocol) {
+ auto payload = makeLocalProxyHeader();
+ packet.insert(packet.begin(), payload.begin(), payload.end());
+ }
+
Socket sock(ds->remote.sin4.sin_family, SOCK_DGRAM);
sock.setNonBlocking();
if (!IsAnyAddress(ds->sourceAddr)) {
DNSResponse makeDNSResponseFromIDState(IDState& ids, struct dnsheader* dh, size_t bufferSize, uint16_t responseLen, bool isTCP)
{
-
DNSResponse dr(&ids.qname, ids.qtype, ids.qclass, ids.qname.wirelength(), &ids.origDest, &ids.origRemote, dh, bufferSize, responseLen, isTCP, &ids.sentTime.d_start);
dr.origFlags = ids.origFlags;
dr.ecsAdded = ids.ecsAdded;
dr.dnsCryptQuery = std::move(ids.dnsCryptQuery);
}
- return dr;
+ return dr;
}
void setIDStateFromDNSQuestion(IDState& ids, DNSQuestion& dq, DNSName&& qname)
ids.useZeroScope = dq.useZeroScope;
ids.qTag = dq.qTag;
ids.dnssecOK = dq.dnssecOK;
-
+
ids.dnsCryptQuery = std::move(dq.dnsCryptQuery);
-
+
#ifdef HAVE_PROTOBUF
ids.uniqueId = std::move(dq.uniqueId);
#endif
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;
CDBKVStore::CDBKVStore(const std::string& fname, time_t refreshDelay): d_fname(fname), d_refreshDelay(refreshDelay)
{
- pthread_rwlock_init(&d_lock, nullptr);
d_refreshing.clear();
time_t now = time(nullptr);
}
CDBKVStore::~CDBKVStore() {
- pthread_rwlock_destroy(&d_lock);
}
bool CDBKVStore::reload(const struct stat& st)
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
std::unique_ptr<CDB> d_cdb{nullptr};
std::string d_fname;
- pthread_rwlock_t d_lock;
+ ReadWriteLock d_lock;
time_t d_mtime{0};
time_t d_nextCheck{0};
time_t d_refreshDelay{0};
#include "dnsdist.hh"
#include "dnsdist-lbpolicies.hh"
+#include "dnsdist-lua.hh"
#include "dnsdist-lua-ffi.hh"
#include "dolog.hh"
shared_ptr<DownstreamState> roundrobin(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
{
- ServerPolicy::NumberedServerVector poss;
+ if (servers.empty()) {
+ return shared_ptr<DownstreamState>();
+ }
- for(auto& d : servers) {
- if(d.second->isUp()) {
- poss.push_back(d);
+ vector<size_t> candidates;
+ candidates.reserve(servers.size());
+
+ for (auto& d : servers) {
+ if (d.second->isUp()) {
+ candidates.push_back(d.first);
}
}
- const auto *res=&poss;
- if(poss.empty() && !g_roundrobinFailOnNoServer)
- res = &servers;
-
- if(res->empty())
- return shared_ptr<DownstreamState>();
+ if (candidates.empty()) {
+ if (g_roundrobinFailOnNoServer) {
+ return shared_ptr<DownstreamState>();
+ }
+ for (auto& d : servers) {
+ candidates.push_back(d.first);
+ }
+ }
static unsigned int counter;
- return (*res)[(counter++) % res->size()].second;
+ return servers.at(candidates.at((counter++) % candidates.size()) - 1).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();
{
std::shared_ptr<ServerPool> pool = createPoolIfNotExists(pools, poolName);
if (!poolName.empty()) {
- vinfolog("Setting pool %s server selection policy to %s", poolName, policy->name);
+ vinfolog("Setting pool %s server selection policy to %s", poolName, policy->getName());
} else {
- vinfolog("Setting default pool server selection policy to %s", policy->name);
+ vinfolog("Setting default pool server selection policy to %s", policy->getName());
}
pool->policy = policy;
}
return it->second;
}
-std::shared_ptr<DownstreamState> getSelectedBackendFromPolicy(const ServerPolicy& policy, const ServerPolicy::NumberedServerVector& servers, DNSQuestion& dq)
+ServerPolicy::ServerPolicy(const std::string& name_, const std::string& code): d_name(name_), d_perThreadPolicyCode(code), d_isLua(true), d_isFFI(true), d_isPerThread(true)
+{
+ LuaContext tmpContext;
+ setupLuaLoadBalancingContext(tmpContext);
+ auto ret = tmpContext.executeCode<ServerPolicy::ffipolicyfunc_t>(code);
+}
+
+thread_local ServerPolicy::PerThreadState ServerPolicy::t_perThreadState;
+
+const ServerPolicy::ffipolicyfunc_t& ServerPolicy::getPerThreadPolicy() const
+{
+ auto& state = t_perThreadState;
+ if (!state.d_initialized) {
+ setupLuaLoadBalancingContext(state.d_luaContext);
+ state.d_initialized = true;
+ }
+
+ const auto& it = state.d_policies.find(d_name);
+ if (it != state.d_policies.end()) {
+ return it->second;
+ }
+
+ auto newPolicy = state.d_luaContext.executeCode<ServerPolicy::ffipolicyfunc_t>(d_perThreadPolicyCode);
+ state.d_policies[d_name] = std::move(newPolicy);
+ return state.d_policies.at(d_name);
+}
+
+std::shared_ptr<DownstreamState> ServerPolicy::getSelectedBackend(const ServerPolicy::NumberedServerVector& servers, DNSQuestion& dq) const
{
std::shared_ptr<DownstreamState> selectedBackend{nullptr};
- if (policy.isLua) {
- if (!policy.isFFI) {
+ if (d_isLua) {
+ if (!d_isFFI) {
std::lock_guard<std::mutex> lock(g_luamutex);
- selectedBackend = policy.policy(servers, &dq);
+ selectedBackend = d_policy(servers, &dq);
}
else {
dnsdist_ffi_dnsquestion_t dnsq(&dq);
dnsdist_ffi_servers_list_t serversList(servers);
unsigned int selected = 0;
- {
+
+ if (!d_isPerThread) {
std::lock_guard<std::mutex> lock(g_luamutex);
- selected = policy.ffipolicy(&serversList, &dnsq);
+ selected = d_ffipolicy(&serversList, &dnsq);
+ }
+ else {
+ const auto& policy = getPerThreadPolicy();
+ selected = policy(&serversList, &dnsq);
}
+
selectedBackend = servers.at(selected).second;
}
}
else {
- selectedBackend = policy.policy(servers, &dq);
+ selectedBackend = d_policy(servers, &dq);
}
return selectedBackend;
#include "dolog.hh"
-void setupLuaBindingsDNSCrypt()
+void setupLuaBindingsDNSCrypt(LuaContext& luaCtx)
{
#ifdef HAVE_DNSCRYPT
/* DNSCryptContext bindings */
- g_lua.registerFunction<std::string(DNSCryptContext::*)()>("getProviderName", [](const DNSCryptContext& ctx) { return ctx.getProviderName().toStringNoDot(); });
- g_lua.registerFunction("markActive", &DNSCryptContext::markActive);
- g_lua.registerFunction("markInactive", &DNSCryptContext::markInactive);
- g_lua.registerFunction("removeInactiveCertificate", &DNSCryptContext::removeInactiveCertificate);
- g_lua.registerFunction<void(std::shared_ptr<DNSCryptContext>::*)(const std::string& certFile, const std::string& keyFile, boost::optional<bool> active)>("loadNewCertificate", [](std::shared_ptr<DNSCryptContext> ctx, const std::string& certFile, const std::string& keyFile, boost::optional<bool> active) {
+ luaCtx.registerFunction<std::string(DNSCryptContext::*)()>("getProviderName", [](const DNSCryptContext& ctx) { return ctx.getProviderName().toStringNoDot(); });
+ luaCtx.registerFunction("markActive", &DNSCryptContext::markActive);
+ luaCtx.registerFunction("markInactive", &DNSCryptContext::markInactive);
+ luaCtx.registerFunction("removeInactiveCertificate", &DNSCryptContext::removeInactiveCertificate);
+ luaCtx.registerFunction<void(std::shared_ptr<DNSCryptContext>::*)(const std::string& certFile, const std::string& keyFile, boost::optional<bool> active)>("loadNewCertificate", [](std::shared_ptr<DNSCryptContext> ctx, const std::string& certFile, const std::string& keyFile, boost::optional<bool> active) {
if (ctx == nullptr) {
throw std::runtime_error("DNSCryptContext::loadNewCertificate() called on a nil value");
ctx->loadNewCertificate(certFile, keyFile, active ? *active : true);
});
- g_lua.registerFunction<void(std::shared_ptr<DNSCryptContext>::*)(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, boost::optional<bool> active)>("addNewCertificate", [](std::shared_ptr<DNSCryptContext> ctx, const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, boost::optional<bool> active) {
+ luaCtx.registerFunction<void(std::shared_ptr<DNSCryptContext>::*)(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, boost::optional<bool> active)>("addNewCertificate", [](std::shared_ptr<DNSCryptContext> ctx, const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, boost::optional<bool> active) {
if (ctx == nullptr) {
throw std::runtime_error("DNSCryptContext::addNewCertificate() called on a nil value");
ctx->addNewCertificate(newCert, newKey, active ? *active : true);
});
- g_lua.registerFunction<std::map<int, std::shared_ptr<DNSCryptCertificatePair>>(std::shared_ptr<DNSCryptContext>::*)()>("getCertificatePairs", [](std::shared_ptr<DNSCryptContext> ctx) {
+ luaCtx.registerFunction<std::map<int, std::shared_ptr<DNSCryptCertificatePair>>(std::shared_ptr<DNSCryptContext>::*)()>("getCertificatePairs", [](std::shared_ptr<DNSCryptContext> ctx) {
std::map<int, std::shared_ptr<DNSCryptCertificatePair>> result;
if (ctx != nullptr) {
return result;
});
- g_lua.registerFunction<std::shared_ptr<DNSCryptCertificatePair>(std::shared_ptr<DNSCryptContext>::*)(size_t idx)>("getCertificatePair", [](std::shared_ptr<DNSCryptContext> ctx, size_t idx) {
+ luaCtx.registerFunction<std::shared_ptr<DNSCryptCertificatePair>(std::shared_ptr<DNSCryptContext>::*)(size_t idx)>("getCertificatePair", [](std::shared_ptr<DNSCryptContext> ctx, size_t idx) {
if (ctx == nullptr) {
throw std::runtime_error("DNSCryptContext::getCertificatePair() called on a nil value");
return result;
});
- g_lua.registerFunction<const DNSCryptCert(std::shared_ptr<DNSCryptContext>::*)(size_t idx)>("getCertificate", [](std::shared_ptr<DNSCryptContext> ctx, size_t idx) {
+ luaCtx.registerFunction<const DNSCryptCert(std::shared_ptr<DNSCryptContext>::*)(size_t idx)>("getCertificate", [](std::shared_ptr<DNSCryptContext> ctx, size_t idx) {
if (ctx == nullptr) {
throw std::runtime_error("DNSCryptContext::getCertificate() called on a nil value");
throw std::runtime_error("This DNSCrypt context has no certificate at index " + std::to_string(idx));
});
- g_lua.registerFunction<std::string(std::shared_ptr<DNSCryptContext>::*)()>("printCertificates", [](const std::shared_ptr<DNSCryptContext> ctx) {
+ luaCtx.registerFunction<std::string(std::shared_ptr<DNSCryptContext>::*)()>("printCertificates", [](const std::shared_ptr<DNSCryptContext> ctx) {
ostringstream ret;
if (ctx != nullptr) {
return ret.str();
});
- g_lua.registerFunction<void(DNSCryptContext::*)(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version)>("generateAndLoadInMemoryCertificate", [](DNSCryptContext& ctx, const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
+ luaCtx.registerFunction<void(DNSCryptContext::*)(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version)>("generateAndLoadInMemoryCertificate", [](DNSCryptContext& ctx, const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
DNSCryptPrivateKey privateKey;
DNSCryptCert cert;
});
/* DNSCryptCertificatePair */
- g_lua.registerFunction<const DNSCryptCert(std::shared_ptr<DNSCryptCertificatePair>::*)()>("getCertificate", [](const std::shared_ptr<DNSCryptCertificatePair> pair) {
+ luaCtx.registerFunction<const DNSCryptCert(std::shared_ptr<DNSCryptCertificatePair>::*)()>("getCertificate", [](const std::shared_ptr<DNSCryptCertificatePair> pair) {
if (pair == nullptr) {
throw std::runtime_error("DNSCryptCertificatePair::getCertificate() called on a nil value");
}
return pair->cert;
});
- g_lua.registerFunction<bool(std::shared_ptr<DNSCryptCertificatePair>::*)()>("isActive", [](const std::shared_ptr<DNSCryptCertificatePair> pair) {
+ luaCtx.registerFunction<bool(std::shared_ptr<DNSCryptCertificatePair>::*)()>("isActive", [](const std::shared_ptr<DNSCryptCertificatePair> pair) {
if (pair == nullptr) {
throw std::runtime_error("DNSCryptCertificatePair::isActive() called on a nil value");
}
});
/* DNSCryptCert */
- g_lua.registerFunction<std::string(DNSCryptCert::*)()>("getMagic", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.magic), sizeof(cert.magic)); });
- g_lua.registerFunction<std::string(DNSCryptCert::*)()>("getEsVersion", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.esVersion), sizeof(cert.esVersion)); });
- g_lua.registerFunction<std::string(DNSCryptCert::*)()>("getProtocolMinorVersion", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.protocolMinorVersion), sizeof(cert.protocolMinorVersion)); });
- g_lua.registerFunction<std::string(DNSCryptCert::*)()>("getSignature", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signature), sizeof(cert.signature)); });
- g_lua.registerFunction<std::string(DNSCryptCert::*)()>("getResolverPublicKey", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.resolverPK), sizeof(cert.signedData.resolverPK)); });
- g_lua.registerFunction<std::string(DNSCryptCert::*)()>("getClientMagic", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.clientMagic), sizeof(cert.signedData.clientMagic)); });
- g_lua.registerFunction<uint32_t(DNSCryptCert::*)()>("getSerial", [](const DNSCryptCert& cert) { return cert.getSerial(); });
- g_lua.registerFunction<uint32_t(DNSCryptCert::*)()>("getTSStart", [](const DNSCryptCert& cert) { return ntohl(cert.getTSStart()); });
- g_lua.registerFunction<uint32_t(DNSCryptCert::*)()>("getTSEnd", [](const DNSCryptCert& cert) { return ntohl(cert.getTSEnd()); });
+ luaCtx.registerFunction<std::string(DNSCryptCert::*)()>("getMagic", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.magic), sizeof(cert.magic)); });
+ luaCtx.registerFunction<std::string(DNSCryptCert::*)()>("getEsVersion", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.esVersion), sizeof(cert.esVersion)); });
+ luaCtx.registerFunction<std::string(DNSCryptCert::*)()>("getProtocolMinorVersion", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.protocolMinorVersion), sizeof(cert.protocolMinorVersion)); });
+ luaCtx.registerFunction<std::string(DNSCryptCert::*)()>("getSignature", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signature), sizeof(cert.signature)); });
+ luaCtx.registerFunction<std::string(DNSCryptCert::*)()>("getResolverPublicKey", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.resolverPK), sizeof(cert.signedData.resolverPK)); });
+ luaCtx.registerFunction<std::string(DNSCryptCert::*)()>("getClientMagic", [](const DNSCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.clientMagic), sizeof(cert.signedData.clientMagic)); });
+ luaCtx.registerFunction<uint32_t(DNSCryptCert::*)()>("getSerial", [](const DNSCryptCert& cert) { return cert.getSerial(); });
+ luaCtx.registerFunction<uint32_t(DNSCryptCert::*)()>("getTSStart", [](const DNSCryptCert& cert) { return ntohl(cert.getTSStart()); });
+ luaCtx.registerFunction<uint32_t(DNSCryptCert::*)()>("getTSEnd", [](const DNSCryptCert& cert) { return ntohl(cert.getTSEnd()); });
#endif
}
#include "dnsdist-kvs.hh"
#include "dnsdist-lua.hh"
-void setupLuaBindingsKVS(bool client)
+void setupLuaBindingsKVS(LuaContext& luaCtx, bool client)
{
/* Key Value Store objects */
- g_lua.writeFunction("KeyValueLookupKeySourceIP", []() {
- return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySourceIP());
+ luaCtx.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) {
+ luaCtx.writeFunction("KeyValueLookupKeyQName", [](boost::optional<bool> wireFormat) {
return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeyQName(wireFormat ? *wireFormat : true));
});
- g_lua.writeFunction("KeyValueLookupKeySuffix", [](boost::optional<size_t> minLabels, boost::optional<bool> wireFormat) {
+ luaCtx.writeFunction("KeyValueLookupKeySuffix", [](boost::optional<size_t> minLabels, boost::optional<bool> wireFormat) {
return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySuffix(minLabels ? *minLabels : 0, wireFormat ? *wireFormat : true));
});
- g_lua.writeFunction("KeyValueLookupKeyTag", [](const std::string& tag) {
+ luaCtx.writeFunction("KeyValueLookupKeyTag", [](const std::string& tag) {
return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeyTag(tag));
});
#ifdef HAVE_LMDB
- g_lua.writeFunction("newLMDBKVStore", [client](const std::string& fname, const std::string& dbName) {
+ luaCtx.writeFunction("newLMDBKVStore", [client](const std::string& fname, const std::string& dbName) {
if (client) {
return std::shared_ptr<KeyValueStore>(nullptr);
}
#endif /* HAVE_LMDB */
#ifdef HAVE_CDB
- g_lua.writeFunction("newCDBKVStore", [client](const std::string& fname, time_t refreshDelay) {
+ luaCtx.writeFunction("newCDBKVStore", [client](const std::string& fname, time_t refreshDelay) {
if (client) {
return std::shared_ptr<KeyValueStore>(nullptr);
}
});
#endif /* HAVE_CDB */
- g_lua.registerFunction<std::string(std::shared_ptr<KeyValueStore>::*)(const boost::variant<ComboAddress, DNSName, std::string>, boost::optional<bool> wireFormat)>("lookup", [](std::shared_ptr<KeyValueStore>& kvs, const boost::variant<ComboAddress, DNSName, std::string> keyVar, boost::optional<bool> wireFormat) {
+ luaCtx.registerFunction<std::string(std::shared_ptr<KeyValueStore>::*)(const boost::variant<ComboAddress, DNSName, std::string>, boost::optional<bool> wireFormat)>("lookup", [](std::shared_ptr<KeyValueStore>& kvs, const boost::variant<ComboAddress, DNSName, std::string> keyVar, boost::optional<bool> wireFormat) {
std::string result;
if (!kvs) {
return result;
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 result;
});
- g_lua.registerFunction<std::string(std::shared_ptr<KeyValueStore>::*)(const DNSName&, boost::optional<size_t> minLabels, boost::optional<bool> wireFormat)>("lookupSuffix", [](std::shared_ptr<KeyValueStore>& kvs, const DNSName& dn, boost::optional<size_t> minLabels, boost::optional<bool> wireFormat) {
+ luaCtx.registerFunction<std::string(std::shared_ptr<KeyValueStore>::*)(const DNSName&, boost::optional<size_t> minLabels, boost::optional<bool> wireFormat)>("lookupSuffix", [](std::shared_ptr<KeyValueStore>& kvs, const DNSName& dn, boost::optional<size_t> minLabels, boost::optional<bool> wireFormat) {
std::string result;
if (!kvs) {
return result;
return result;
});
- g_lua.registerFunction<bool(std::shared_ptr<KeyValueStore>::*)()>("reload", [](std::shared_ptr<KeyValueStore>& kvs) {
+ luaCtx.registerFunction<bool(std::shared_ptr<KeyValueStore>::*)()>("reload", [](std::shared_ptr<KeyValueStore>& kvs) {
if (!kvs) {
return false;
}
#include "dnsdist.hh"
#include "dnsdist-lua.hh"
-void setupLuaBindingsPacketCache()
+void setupLuaBindingsPacketCache(LuaContext& luaCtx)
{
/* PacketCache */
- g_lua.writeFunction("newPacketCache", [](size_t maxEntries, boost::optional<std::unordered_map<std::string, boost::variant<bool, size_t>>> vars) {
+ luaCtx.writeFunction("newPacketCache", [](size_t maxEntries, boost::optional<std::unordered_map<std::string, boost::variant<bool, size_t>>> vars) {
bool keepStaleData = false;
size_t maxTTL = 86400;
bool dontAge = false;
bool deferrableInsertLock = true;
bool ecsParsing = false;
+ bool cookieHashing = false;
if (vars) {
if (vars->count("temporaryFailureTTL")) {
tempFailTTL = boost::get<size_t>((*vars)["temporaryFailureTTL"]);
}
+
+ if (vars->count("cookieHashing")) {
+ cookieHashing = boost::get<bool>((*vars)["cookieHashing"]);
+ }
}
auto res = std::make_shared<DNSDistPacketCache>(maxEntries, maxTTL, minTTL, tempFailTTL, maxNegativeTTL, staleTTL, dontAge, numberOfShards, deferrableInsertLock, ecsParsing);
res->setKeepStaleData(keepStaleData);
+ res->setCookieHashing(cookieHashing);
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<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype, boost::optional<bool> suffixMatch)>("expungeByName", [](
- std::shared_ptr<DNSDistPacketCache> cache,
- const DNSName& dname,
+ luaCtx.registerFunction<std::string(std::shared_ptr<DNSDistPacketCache>::*)()>("toString", [](const std::shared_ptr<DNSDistPacketCache>& cache) {
+ if (cache) {
+ return cache->toString();
+ }
+ return std::string();
+ });
+ luaCtx.registerFunction<bool(std::shared_ptr<DNSDistPacketCache>::*)()>("isFull", [](const std::shared_ptr<DNSDistPacketCache>& cache) {
+ if (cache) {
+ return cache->isFull();
+ }
+ return false;
+ });
+ luaCtx.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);
+ });
+ luaCtx.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);
+ });
+ luaCtx.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const boost::variant<DNSName, string>& dname, boost::optional<uint16_t> qtype, boost::optional<bool> suffixMatch)>("expungeByName", [](
+ std::shared_ptr<DNSDistPacketCache>& cache,
+ const boost::variant<DNSName, string>& dname,
boost::optional<uint16_t> qtype,
boost::optional<bool> suffixMatch) {
+ DNSName qname;
+ if (dname.type() == typeid(DNSName)) {
+ qname = boost::get<DNSName>(dname);
+ }
+ if (dname.type() == typeid(string)) {
+ qname = DNSName(boost::get<string>(dname));
+ }
if (cache) {
- g_outputBuffer="Expunged " + std::to_string(cache->expungeByName(dname, qtype ? *qtype : QType(QType::ANY).getCode(), suffixMatch ? *suffixMatch : false)) + " records\n";
+ g_outputBuffer="Expunged " + std::to_string(cache->expungeByName(qname, 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) {
+ luaCtx.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) {
+ luaCtx.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) {
+ luaCtx.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);
#include "ipcipher.hh"
#endif /* HAVE_LIBCRYPTO */
-void setupLuaBindingsProtoBuf(bool client, bool configCheck)
+#ifdef HAVE_FSTRM
+static void parseFSTRMOptions(const boost::optional<std::unordered_map<std::string, unsigned int>>& params, std::unordered_map<string, unsigned int>& options)
+{
+ if (!params) {
+ return;
+ }
+
+ static std::vector<std::string> const potentialOptions = { "bufferHint", "flushTimeout", "inputQueueSize", "outputQueueSize", "queueNotifyThreshold", "reopenInterval" };
+
+ for (const auto& potentialOption : potentialOptions) {
+ if (params->count(potentialOption)) {
+ options[potentialOption] = boost::get<unsigned int>(params->at(potentialOption));
+ }
+ }
+}
+#endif /* HAVE_FSTRM */
+
+void setupLuaBindingsProtoBuf(LuaContext& luaCtx, bool client, bool configCheck)
{
#ifdef HAVE_LIBCRYPTO
- g_lua.registerFunction<ComboAddress(ComboAddress::*)(const std::string& key)>("ipencrypt", [](const ComboAddress& ca, const std::string& key) {
+ luaCtx.registerFunction<ComboAddress(ComboAddress::*)(const std::string& key)>("ipencrypt", [](const ComboAddress& ca, const std::string& key) {
return encryptCA(ca, key);
});
- g_lua.registerFunction<ComboAddress(ComboAddress::*)(const std::string& key)>("ipdecrypt", [](const ComboAddress& ca, const std::string& key) {
+ luaCtx.registerFunction<ComboAddress(ComboAddress::*)(const std::string& key)>("ipdecrypt", [](const ComboAddress& ca, const std::string& key) {
return decryptCA(ca, key);
});
- g_lua.writeFunction("makeIPCipherKey", [](const std::string& password) {
+ luaCtx.writeFunction("makeIPCipherKey", [](const std::string& password) {
return makeIPCipherKey(password);
});
#endif /* HAVE_LIBCRYPTO */
/* ProtobufMessage */
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(std::string)>("setTag", [](DNSDistProtoBufMessage& message, const std::string& strValue) {
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(std::string)>("setTag", [](DNSDistProtoBufMessage& message, const std::string& strValue) {
message.addTag(strValue);
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(vector<pair<int, string>>)>("setTagArray", [](DNSDistProtoBufMessage& message, const vector<pair<int, string>>&tags) {
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(vector<pair<int, string>>)>("setTagArray", [](DNSDistProtoBufMessage& message, const vector<pair<int, string>>&tags) {
for (const auto& tag : tags) {
message.addTag(tag.second);
}
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(boost::optional <time_t> sec, boost::optional <uint32_t> uSec)>("setProtobufResponseType",
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(boost::optional <time_t> sec, boost::optional <uint32_t> uSec)>("setProtobufResponseType",
[](DNSDistProtoBufMessage& message, boost::optional <time_t> sec, boost::optional <uint32_t> uSec) {
message.setType(DNSProtoBufMessage::Response);
message.setQueryTime(sec?*sec:0, uSec?*uSec:0);
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string& strQueryName, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& strBlob)>("addResponseRR", [](DNSDistProtoBufMessage& message,
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string& strQueryName, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& strBlob)>("addResponseRR", [](DNSDistProtoBufMessage& message,
const std::string& strQueryName, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& strBlob) {
message.addRR(DNSName(strQueryName), uType, uClass, uTTL, strBlob);
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const Netmask&)>("setEDNSSubnet", [](DNSDistProtoBufMessage& message, const Netmask& subnet) { message.setEDNSSubnet(subnet); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const DNSName&, uint16_t, uint16_t)>("setQuestion", [](DNSDistProtoBufMessage& message, const DNSName& qname, uint16_t qtype, uint16_t qclass) { message.setQuestion(qname, qtype, qclass); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(size_t)>("setBytes", [](DNSDistProtoBufMessage& message, size_t bytes) { message.setBytes(bytes); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setTime(sec, usec); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setQueryTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setQueryTime(sec, usec); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(uint8_t)>("setResponseCode", [](DNSDistProtoBufMessage& message, uint8_t rcode) { message.setResponseCode(rcode); });
- g_lua.registerFunction<std::string(DNSDistProtoBufMessage::*)()>("toDebugString", [](const DNSDistProtoBufMessage& message) { return message.toDebugString(); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&, boost::optional<uint16_t>)>("setRequestor", [](DNSDistProtoBufMessage& message, const ComboAddress& addr, boost::optional<uint16_t> port) {
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const Netmask&)>("setEDNSSubnet", [](DNSDistProtoBufMessage& message, const Netmask& subnet) { message.setEDNSSubnet(subnet); });
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const DNSName&, uint16_t, uint16_t)>("setQuestion", [](DNSDistProtoBufMessage& message, const DNSName& qname, uint16_t qtype, uint16_t qclass) { message.setQuestion(qname, qtype, qclass); });
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(size_t)>("setBytes", [](DNSDistProtoBufMessage& message, size_t bytes) { message.setBytes(bytes); });
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setTime(sec, usec); });
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setQueryTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setQueryTime(sec, usec); });
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(uint8_t)>("setResponseCode", [](DNSDistProtoBufMessage& message, uint8_t rcode) { message.setResponseCode(rcode); });
+ luaCtx.registerFunction<std::string(DNSDistProtoBufMessage::*)()>("toDebugString", [](const DNSDistProtoBufMessage& message) { return message.toDebugString(); });
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&, boost::optional<uint16_t>)>("setRequestor", [](DNSDistProtoBufMessage& message, const ComboAddress& addr, boost::optional<uint16_t> port) {
message.setRequestor(addr);
if (port) {
message.setRequestorPort(*port);
}
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&, boost::optional<uint16_t>)>("setRequestorFromString", [](DNSDistProtoBufMessage& message, const std::string& str, boost::optional<uint16_t> port) {
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&, boost::optional<uint16_t>)>("setRequestorFromString", [](DNSDistProtoBufMessage& message, const std::string& str, boost::optional<uint16_t> port) {
message.setRequestor(str);
if (port) {
message.setRequestorPort(*port);
}
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&, boost::optional<uint16_t>)>("setResponder", [](DNSDistProtoBufMessage& message, const ComboAddress& addr, boost::optional<uint16_t> port) {
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&, boost::optional<uint16_t>)>("setResponder", [](DNSDistProtoBufMessage& message, const ComboAddress& addr, boost::optional<uint16_t> port) {
message.setResponder(addr);
if (port) {
message.setResponderPort(*port);
}
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&, boost::optional<uint16_t>)>("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str, boost::optional<uint16_t> port) {
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&, boost::optional<uint16_t>)>("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str, boost::optional<uint16_t> port) {
message.setResponder(str);
if (port) {
message.setResponderPort(*port);
}
});
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setServerIdentity", [](DNSDistProtoBufMessage& message, const std::string& str) {
+ luaCtx.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setServerIdentity", [](DNSDistProtoBufMessage& message, const std::string& str) {
message.setServerIdentity(str);
});
- g_lua.registerFunction<std::string(DnstapMessage::*)()>("toDebugString", [](const DnstapMessage& message) { return message.toDebugString(); });
- g_lua.registerFunction<void(DnstapMessage::*)(const std::string&)>("setExtra", [](DnstapMessage& message, const std::string& str) {
+ luaCtx.registerFunction<std::string(DnstapMessage::*)()>("toDebugString", [](const DnstapMessage& message) { return message.toDebugString(); });
+ luaCtx.registerFunction<void(DnstapMessage::*)(const std::string&)>("setExtra", [](DnstapMessage& message, const std::string& str) {
message.setExtra(str);
});
/* RemoteLogger */
- g_lua.writeFunction("newRemoteLogger", [client,configCheck](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
+ luaCtx.writeFunction("newRemoteLogger", [client,configCheck](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
if (client || configCheck) {
return std::shared_ptr<RemoteLoggerInterface>(nullptr);
}
return std::shared_ptr<RemoteLoggerInterface>(new RemoteLogger(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? (*maxQueuedEntries*100) : 10000, reconnectWaitTime ? *reconnectWaitTime : 1, client));
});
- g_lua.writeFunction("newFrameStreamUnixLogger", [client,configCheck](const std::string& address) {
+ luaCtx.writeFunction("newFrameStreamUnixLogger", [client,configCheck](const std::string& address, boost::optional<std::unordered_map<std::string, unsigned int>> params) {
#ifdef HAVE_FSTRM
if (client || configCheck) {
return std::shared_ptr<RemoteLoggerInterface>(nullptr);
}
- return std::shared_ptr<RemoteLoggerInterface>(new FrameStreamLogger(AF_UNIX, address, !client));
+
+ std::unordered_map<string, unsigned int> options;
+ parseFSTRMOptions(params, options);
+ return std::shared_ptr<RemoteLoggerInterface>(new FrameStreamLogger(AF_UNIX, address, !client, options));
#else
throw std::runtime_error("fstrm support is required to build an AF_UNIX FrameStreamLogger");
#endif /* HAVE_FSTRM */
});
- g_lua.writeFunction("newFrameStreamTcpLogger", [client,configCheck](const std::string& address) {
+ luaCtx.writeFunction("newFrameStreamTcpLogger", [client,configCheck](const std::string& address, boost::optional<std::unordered_map<std::string, unsigned int>> params) {
#if defined(HAVE_FSTRM) && defined(HAVE_FSTRM_TCP_WRITER_INIT)
if (client || configCheck) {
return std::shared_ptr<RemoteLoggerInterface>(nullptr);
}
- return std::shared_ptr<RemoteLoggerInterface>(new FrameStreamLogger(AF_INET, address, !client));
+
+ std::unordered_map<string, unsigned int> options;
+ parseFSTRMOptions(params, options);
+ return std::shared_ptr<RemoteLoggerInterface>(new FrameStreamLogger(AF_INET, address, !client, options));
#else
throw std::runtime_error("fstrm with TCP support is required to build an AF_INET FrameStreamLogger");
#endif /* HAVE_FSTRM */
});
- g_lua.registerFunction<std::string(std::shared_ptr<RemoteLoggerInterface>::*)()>("toString", [](const std::shared_ptr<RemoteLoggerInterface>& logger) {
+ luaCtx.registerFunction<std::string(std::shared_ptr<RemoteLoggerInterface>::*)()>("toString", [](const std::shared_ptr<RemoteLoggerInterface>& logger) {
if (logger) {
return logger->toString();
}
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")));
*/
#include "dnsdist-lua-ffi.hh"
+#include "dnsdist-lua.hh"
#include "dnsdist-ecs.hh"
uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq)
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();
)FFICodeContent";
return code;
}
+
+void setupLuaLoadBalancingContext(LuaContext& luaCtx)
+{
+ setupLuaBindings(luaCtx, true);
+ setupLuaBindingsDNSQuestion(luaCtx);
+ setupLuaBindingsKVS(luaCtx, true);
+ setupLuaVars(luaCtx);
+
+#ifdef LUAJIT_VERSION
+ luaCtx.executeCode(getLuaFFIWrappers());
+#endif
+}
uint64_t dnsdist_ffi_stat_node_get_children_nxdomains_count(const dnsdist_ffi_stat_node_t* node) __attribute__ ((visibility ("default")));
uint64_t dnsdist_ffi_stat_node_get_children_servfails_count(const dnsdist_ffi_stat_node_t* node) __attribute__ ((visibility ("default")));
uint64_t dnsdist_ffi_stat_node_get_children_drops_count(const dnsdist_ffi_stat_node_t* node) __attribute__ ((visibility ("default")));
- uint64_t dnsdist_ffi_stat_node_get_children_bytes(const dnsdist_ffi_stat_node_t* node) __attribute__ ((visibility ("default")));
+ uint64_t dnsdist_ffi_stat_node_get_children_bytes_count(const dnsdist_ffi_stat_node_t* node) __attribute__ ((visibility ("default")));
}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dnsdist-proxy-protocol.hh"
+
+bool addProxyProtocol(DNSQuestion& dq)
+{
+ auto payload = makeProxyHeader(dq.tcp, *dq.remote, *dq.local, dq.proxyProtocolValues ? *dq.proxyProtocolValues : std::vector<ProxyProtocolValue>());
+ if ((dq.size - dq.len) < payload.size()) {
+ return false;
+ }
+
+ memmove(reinterpret_cast<char*>(dq.dh) + payload.size(), dq.dh, dq.len);
+ memcpy(dq.dh, payload.c_str(), payload.size());
+ dq.len += payload.size();
+
+ return true;
+}
+
+bool addProxyProtocol(std::vector<uint8_t>& buffer, bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values)
+{
+ 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());
+
+ return true;
+}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "dnsdist.hh"
+
+bool addProxyProtocol(DNSQuestion& dq);
+bool addProxyProtocol(std::vector<uint8_t>& buffer, bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values);
public:
TimedIPSetRule()
{
- pthread_rwlock_init(&d_lock4, 0);
- pthread_rwlock_init(&d_lock6, 0);
}
~TimedIPSetRule()
{
- pthread_rwlock_destroy(&d_lock4);
- pthread_rwlock_destroy(&d_lock6);
}
bool matches(const DNSQuestion* dq) const override
{
void cleanup()
{
- time_t now=time(0);
+ time_t now = time(nullptr);
{
WriteLock rl(&d_lock4);
};
std::unordered_map<IPv6, time_t, IPv6Hash> d_ip6s;
std::unordered_map<uint32_t, time_t> d_ip4s;
- mutable pthread_rwlock_t d_lock4;
- mutable pthread_rwlock_t d_lock6;
+ mutable ReadWriteLock d_lock4;
+ mutable ReadWriteLock d_lock6;
};
}
if (releaseVersion) {
- warnlog("Could not retrieve security status update for '%s' on %s", pkgv, queriedName);
+ warnlog("Failed to retrieve security status update for '%s' on %s", pkgv, queriedName);
}
else if (!g_secPollDone) {
infolog("Not validating response for security status update, this is a non-release version.");
--- /dev/null
+#pragma once
+
+struct WebserverConfig
+{
+ WebserverConfig()
+ {
+ acl.toMasks("127.0.0.1, ::1");
+ }
+
+ NetmaskGroup acl;
+ std::string password;
+ std::string apiKey;
+ boost::optional<std::map<std::string, std::string> > customHeaders;
+ std::mutex lock;
+};
+
+void setWebserverAPIKey(const boost::optional<std::string> apiKey);
+void setWebserverPassword(const std::string& password);
+void setWebserverACL(const std::string& acl);
+void setWebserverCustomHeaders(const boost::optional<std::map<std::string, std::string> > customHeaders);
+
+void dnsdistWebserverThread(int sock, const ComboAddress& local);
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
+# Setting PrivateUsers=true prevents us from opening our sockets
+ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
+ProtectHostname=true
+ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=full
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
+RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=~ @clock @debug @module @mount @raw-io @reboot @swap @cpu-emulation @obsolete
Modifying the ACL
-----------------
-ACLs can be modfied at runtime from the :ref:`Console`.
+ACLs can be modified at runtime from the :ref:`Console`.
To inspect the currently active :term:`ACL`, run :func:`showACL`.
To add a new network range to the existing ACL, use :func:`addACL`:
In order to provide the downstream server with the address of the real client, or at least the one talking to dnsdist, the ``useClientSubnet`` parameter can be used when creating a :func:`new server <newServer>`.
This parameter indicates whether an EDNS Client Subnet option should be added to the request.
-If the incoming request already contains an EDNS Client Subnet value, it will not be overriden unless :func:`setECSOverride` is set to ``true``.
+If the incoming request already contains an EDNS Client Subnet value, it will not be overridden unless :func:`setECSOverride` is set to ``true``.
The default source prefix-length is 24 for IPv4 and 56 for IPv6, meaning that for a query received from 192.0.2.42, the EDNS Client Subnet value sent to the backend will be 192.0.2.0.
This can be changed with :func:`setECSSourcePrefixV4` and :func:`setECSSourcePrefixV6`.
luaaction
timedipsetrule
ecs
+ xpf
+ proxyprotocol
qpslimits
ebpf
tuning
--- /dev/null
+Using the Proxy Protocol
+------------------------
+
+In order to provide the downstream server with the address of the real client, or at least the one talking to dnsdist, the ``useProxyProtocol`` parameter can be used when creating a :func:`new server <newServer>`.
+This parameter indicates whether a Proxy Protocol version 2 (binary) header should be prepended to the query before forwarding it to the backend, over UDP or TCP. This header contains the initial source and destination addresses and ports, and can also contain several custom values in a Type-Length-Value format. More information about the Proxy Protocol can be found at https://www.haproxy.org/download/2.2/doc/proxy-protocol.txt
+
+Custom values can be added to the header via :meth:`DNSQuestion:setProxyProtocolValues` and :func:`SetProxyProtocolValuesAction`.
+
+As of 1.5.0 only outgoing Proxy Protocol support has been implemented, although support for parsing incoming Proxy Protocol headers will likely be implemented in the future.
Most of the query processing is done in C++ for maximum performance, but some operations are executed in Lua for maximum flexibility:
* Rules added by :func:`addLuaAction`
- * Server selection policies defined via :func:`setServerPolicyLua`, :func:`setServerPolicyLuaFFI` or :func:`newServerPolicy`
+ * Server selection policies defined via :func:`setServerPolicyLua`, :func:`setServerPolicyLuaFFI`, :func:`setServerPolicyLuaFFIPerThread` or :func:`newServerPolicy`
While Lua is fast, its use should be restricted to the strict necessary in order to achieve maximum performance, it might be worth considering using LuaJIT instead of Lua.
When Lua inspection is needed, the best course of action is to restrict the queries sent to Lua inspection by using :func:`addLuaAction` with a selector.
--- /dev/null
+Using XPF
+---------
+
+In order to provide the downstream server with the address of the real client, or at least the one talking to dnsdist, the ``addXPF`` parameter can be used when creating a :func:`new server <newServer>`.
+This parameter indicates whether an experimental XPF record (from `draft-bellis-dnsop-xpf <https://datatracker.ietf.org/doc/draft-bellis-dnsop-xpf/>`_) shall be added to the query. Since that record is experimental, there is currently no option code assigned to it, and therefore one needs to be specified as an argument to the ``addXPF`` parameter.
+
+The XPF record is an alternative to the use of EDNS Client Subnet which has the advantages of preserving any existing EDNS Client Subnet value sent by the client, and of passing along the original destination address, as well as the initial source and destination ports.
+
+If the incoming request already contains a XPF record, it will not be overwritten. Instead a new one will be added to the query and the existing one will be preserved.
+That might be an issue by allowing clients to spoof their source address by adding a forged XPF record to their query. That can be prevented by using a rule to drop incoming queries containing a XPF record (in that example the 65280 option code has been assigned to XPF):
+
+ addAction(RecordsTypeCountRule(DNSSection.Additional, 65280, 1, 65535), DropAction())
+
Changelog
=========
+.. changelog::
+ :version: 1.5.0
+ :released: 30th of July 2020
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9231
+
+ Use explicit flag for the specific version of c++ we are targeting.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9320
+
+ Prevent a possible overflow via large Proxy Protocol values. (Valentei Sergey)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9348
+ :tickets: 9279
+
+ Avoid name clashes on Solaris derived systems.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9343
+
+ Resize hostname to final size in getCarbonHostname(). (Aki Tuomi)
+
+ .. change::
+ :tags: Bug Fixes, DNS over HTTPS
+ :pullreq: 9344
+
+ Fix compilation with h2o_socket_get_ssl_server_name().
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9346
+
+ Fix compilation on OpenBSD/amd64.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9356
+
+ Handle calling PacketCache methods on a nil object.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9360
+
+ Prevent a copy of a pool's backends when selecting a server.
+
+.. changelog::
+ :version: 1.5.0-rc4
+ :released: 7th of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9278
+
+ Prevent a race between the DoH handling threads
+
+.. changelog::
+ :version: 1.5.0-rc3
+ :released: 18th of June 2020
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9100
+
+ Less negatives in secpoll error messages improves readability.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9127
+ :tickets: 9125
+
+ Fix compilation on systems that do not define HOST_NAME_MAX
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9207
+
+ Use std::string_view when available (Rosen Penev)
+
+ .. change::
+ :tags: Bug Fixes, DNS over HTTPS
+ :pullreq: 9211
+ :tickets: 9206
+
+ Use non-blocking pipes to pass DoH queries/responses around
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9213
+
+ Do not use `using namespace std;`
+
+ .. change::
+ :tags: New Features
+ :pullreq: 9229
+
+ Implement an ACL in the internal web server
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9238
+ :tickets: 8038
+
+ Clean up dnsdistconf.lua as a default configuration file
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9244
+
+ Add optional masks to KeyValueLookupKeySourceIP
+
+.. changelog::
+ :version: 1.5.0-rc2
+ :released: 13th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9031
+ :tickets: 9025
+
+ Fix compilation of the ports event multiplexer
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9042
+
+ Avoid copies in for loops
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9054
+
+ Build with -Wmissing-declarations -Wredundant-decls
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9016
+ :tickets: 9004
+
+ Use std::shuffle instead of std::random_shuffle
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9053
+
+ Get rid of a naked pointer in the /dev/poll event multiplexer
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9059
+
+ A few warnings fixed, reported by clang on OpenBSD
+
+ .. change::
+ :tags: Bug Fixes, DNS over HTTPS
+ :pullreq: 9068
+
+ Fix duplicated HTTP/1 counter in 'showDOHFrontends()'
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9057
+
+ Gracefully handle a failure to remove FD on (re)-connection
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9067
+
+ Wrap pthread objects
+
+ .. change::
+ :tags: Improvements, Metrics
+ :pullreq: 9084
+
+ Add the unit to the help for latency buckets
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9078
+
+ NetmaskTree: do not test node for null, the loop guarantees node is not null.
+
+.. changelog::
+ :version: 1.5.0-rc1
+ :released: 16th of April 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8955
+
+ On OpenBSD string_view is both in boost and std
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8956
+
+ Expose SuffixMatchNode::remove in Lua
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8962
+
+ Remove a std::move() preventing Return-Value Optimization in lmdb-safe.cc
+
+ .. change::
+ :tags: Bug Fixes, DNSCrypt
+ :pullreq: 8974
+
+ Keep accepting fragmented UDP datagrams on DNSCrypt binds
+
+ .. change::
+ :tags: Bug Fixes, DNSCrypt
+ :pullreq: 8976
+ :tickets: 8974
+
+ Accept UDP datagrams larger than 1500 bytes for DNSCrypt
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8996
+
+ Drop responses with the QR bit set to 0
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8994
+ :tickets: 8986
+
+ Add an option to control the size of the TCP listen queue
+
+.. changelog::
+ :version: 1.5.0-alpha1
+ :released: 20th of March 2020
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 7820
+
+ Don't start as root within a systemd environment
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8115
+ :tickets: 8098
+
+ Fix ECS addition when the OPT record is not the last one
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8171
+ :tickets: 4747
+
+ Add SetNegativeAndSOAAction() and its Lua binding
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8274
+
+ Implement dynamic blocking on ratio of rcode/total responses
+
+ .. change::
+ :tags: Improvements, Performance
+ :pullreq: 8355
+
+ Rework NetmaskTree for better CPU and memory efficiency. (Stephan Bosch)
+
+ .. change::
+ :tags: Improvements, DNS over TLS
+ :pullreq: 8380
+
+ Switch the default DoT provider from GnuTLS to OpenSSL
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8456
+
+ Separate the check-config and client modes
+
+ .. change::
+ :tags: Improvements, Performance
+ :pullreq: 8491
+
+ Implement parallel health checks
+
+ .. change::
+ :tags: New Features, Performance
+ :pullreq: 8505
+ :tickets: 7617
+
+ Implement LuaFFIRule, LuaFFIAction and LuaFFIResponseAction
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8529
+
+ Add the number of received bytes to StatNode entries
+
+ .. change::
+ :tags: Improvements, Performance
+ :pullreq: 8538
+
+ Use move semantics when updating the content of the StateHolder
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8556
+ :tickets: 8534
+
+ Support setting the value of AA, AD and RA when self-generating answers
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8567
+ :tickets: 7387
+
+ Add bounded loads to the consistent hashing policy
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8580
+
+ pthread_rwlock_init() should be matched by pthread_rwlock_destroy()
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8591
+
+ Wait longer for the TLS ticket to arrive in our tests
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8604
+
+ Add missing exception message in KVS error
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8631
+
+ Replace include guard ifdef/define with pragma once (Chris Hofstaedtler)
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8654
+
+ Dnsdist: LogResponseAction (phonedph1)
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8657
+
+ Allow retrieving and deleting a backend via its UUID
+
+ .. change::
+ :tags: Bug Fixes, DNS over TLS
+ :pullreq: 8662
+
+ Display the correct DoT provider
+
+ .. change::
+ :tags: Improvements, Protobuf
+ :pullreq: 8702
+
+ Add the source and destination ports to the protobuf msg
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8722
+
+ Add spoofRawAction() to craft answers from raw bytes
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8733
+
+ Load an openssl configuration file, if any, during startup
+
+ .. change::
+ :tags: Improvements, DNS over HTTPS
+ :pullreq: 8760
+ :tickets: 8573
+
+ Don't accept sub-paths of configured DoH URLs
+
+ .. change::
+ :tags: Bug Fixes, DNS over TLS
+ :pullreq: 8761
+
+ Use ref counting for the DoT TLS context
+
+ .. change::
+ :tags: Improvements, DNS over HTTPS
+ :pullreq: 8762
+ :tickets: 8586
+
+ Implement Cache-Control headers in DoH
+
+ .. change::
+ :tags: Improvements, Metrics
+ :pullreq: 8772
+ :tickets: 8746
+
+ Add backend status to prometheus metrics
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8782
+
+ Add getTag()/setTag() Lua bindings for a DNSResponse
+
+ .. change::
+ :tags: Improvements, Metrics
+ :pullreq: 8783
+
+ Add 'IO wait' and 'steal' metrics on Linux
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8787
+ :tickets: 8442
+
+ Fix key logging for DNS over TLS
+
+ .. change::
+ :tags: Improvements, Performance
+ :pullreq: 8812
+
+ Keep a masked network in the Netmask class
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8874
+
+ Add support for Proxy Protocol between dnsdist and the recursor
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8848
+
+ Add get*BindCount() functions
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8855
+
+ Fix a typo in the help/completion for getDNSCryptBindCount
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8856
+
+ Implement rmACL() (swoga)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8879
+
+ Remove unused lambda capture reported by clang++
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8882
+
+ Add sessionTimeout setting for TLS session lifetime (Matti Hiljanen)
+
+ .. change::
+ :tags: Bug Fixes, Protobuf
+ :pullreq: 8883
+ :tickets: 8629
+
+ Add 'queue full' metrics for our remote logger, log at debug only
+
+ .. change::
+ :tags: Improvements, Protobuf
+ :pullreq: 8887
+
+ Better handling of reconnections in Remote Logger
+
+ .. change::
+ :tags: Improvements, DNS over HTTPS, DNS over TLS
+ :pullreq: 8899
+ :tickets: 8806
+
+ Document that the 'keyLogFile' option requires OpenSSL >= 1.1.1
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8900
+ :tickets: 8739
+
+ Detect {Libre,Open}SSL functions availability during configure
+
+ .. change::
+ :tags: Improvements, DNS over HTTPS
+ :pullreq: 8905
+ :tickets: 8819
+
+ Change the default DoH path from / to /dns-query
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8909
+
+ Implement bounded loads for the whashed and wrandom policies
+
+ .. change::
+ :tags: Improvements, DNSTAP, Performance
+ :pullreq: 8937
+
+ Make FrameStream IO parameters configurable
+
+ .. change::
+ :tags: Improvements, DNS over HTTPS
+ :pullreq: 8945
+ :tickets: 8661
+
+ Add support for the processing of X-Forwarded-For headers
+
+ .. change::
+ :tags: Bug Fixes, DNS over HTTPS
+ :pullreq: 8949
+
+ Set the DoH ticket rotation delay before loading tickets
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8950
+ :tickets: 8669
+
+ Warn on startup about low weight values with chashed
+
.. changelog::
:version: 1.4.0
:released: 20th of November 2019
:tags: Improvements
:pullreq: 8440
- Fix -WShadow warnings (Aki Tuomi)
+ Fix -Wshadow warnings (Aki Tuomi)
.. change::
:tags: Improvements
:tags: Bug Fixes
:pullreq: 7886
- SuffixMatchTree: fix root removal, partial match of non-leaf nodes
+ SuffixMatchTree: fix root removal, partial match of non-leaf nodes
.. change::
:tags: Improvements
:pullreq: 7585
:tickets: 7534
- Prevent 0-ttl cache hits
+ Prevent 0-ttl cache hits
.. change::
:tags: Improvements
:pullreq: 7015
:tickets: 7004, 6990
- Add support for exporting a server id in protobuf
+ Add support for exporting a server id in protobuf
.. change::
:tags: Improvements
:pullreq: 7064
:tickets: 7060
- Wrap GnuTLS and OpenSSL pointers in smart pointers
+ Wrap GnuTLS and OpenSSL pointers in smart pointers
.. change::
:tags: New Features
:pullreq: 6523
:tickets: 6430
- Tests: avoid failure on not-so-optimal distribution
+ Tests: avoid failure on not-so-optimal distribution
.. change::
:tags: New Features
:tags: Bug Fixes
:pullreq: 6672
- Fix reconnection handling
+ Fix reconnection handling
.. change::
:tags: Improvements
:tags: Improvements, Performance
:pullreq: 5185
- Add the possiblity to fill a :class:`NetmaskGroup` (using :meth:`NetmaskGroup:addMask`) from `exceeds*` results.
+ Add the possibility to fill a :class:`NetmaskGroup` (using :meth:`NetmaskGroup:addMask`) from `exceeds*` results.
.. change::
:tags: Improvements
changelog_render_changeset = "https://github.com/PowerDNS/pdns/commit/%s"
changelog_sections = ['New Features', 'Improvements', 'Bug Fixes', 'Removals']
-changelog_inner_tag_sort = ['Security', 'DNS over HTTPS', 'DNS over TLS', 'DNSCrypt', 'Protobuf', 'Performance', 'Webserver', 'Metrics']
+changelog_inner_tag_sort = ['Security', 'DNS over HTTPS', 'DNS over TLS', 'DNSCrypt', 'DNSTAP', 'Protobuf', 'Performance', 'Webserver', 'Metrics']
changelog_render_tags = False
Open Resolver
A recursive DNS server available for many hosts on the internet.
- Ususally without adequate rate-limiting, allowing it to be used in reflection attacks.
+ Usually without adequate rate-limiting, allowing it to be used in reflection attacks.
QPS
Queries Per Second
pc = newPacketCache(10000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false})
getPool(""):setCache(pc)
- + The first parameter (10000) is the maximum number of entries stored in the cache, and is the only one required. All the other parameters are optional and in seconds, except the last one which is a boolean.
++ The first parameter (10000) is the maximum number of entries stored in the cache, and is the only one required. All the other parameters are optional and in seconds, except the last one which is a boolean.
+ The second one (86400) is the maximum lifetime of an entry in the cache.
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::
addDOHLocal("127.0.0.1:8053")
- addDOHLocal("127.0.0.1:8053", nil, nil, "/", { reusePort=true })
\ No newline at end of file
+ addDOHLocal("127.0.0.1:8053", nil, nil, "/", { reusePort=true })
+
+A particular attention should be taken to the permissions of the certificate and key files. Many ACME clients used to get and renew certificates, like CertBot, set permissions assuming that services are started as root, which is no longer true for dnsdist as of 1.5.0. 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.
addTLSLocal('192.0.2.55', {'/etc/ssl/certs/example.com.rsa.pem', '/etc/ssl/certs/example.com.ecdsa.pem'}, {'/etc/ssl/private/example.com.rsa.key', '/etc/ssl/private/example.com.ecdsa.key'})
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 particular attention should be taken to the permissions of the certificate and key files. Many ACME clients used to get and renew certificates, like CertBot, set permissions assuming that services are started as root, which is no longer true for dnsdist as of 1.5.0. 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.
All exceed-functions are documented in the :ref:`Configuration Reference <exceedfuncs>`.
Dynamic blocks drop matched queries by default, but this behavior can be changed with :func:`setDynBlocksAction`.
-For example, to send a REFUSED code instead of droppping the query::
+For example, to send a REFUSED code instead of dropping the query::
setDynBlocksAction(DNSAction.Refused)
setServerPolicyLua("splitsetup", splitSetup)
+For performance reasons, 1.6.0 introduced per-thread Lua FFI policies that are run in a lock-free per-thread Lua context instead of the global one.
+This reduces contention between threads at the cost of preventing sharing data between threads for these policies. Since the policy needs to be recompiled
+in the context of each thread instead of the global one, Lua code that returns a function should be passed to the function as a string instead of directly
+passing the name of a function:
+
+.. code-block:: lua
+
+ setServerPolicyLuaFFIPerThread("luaffiroundrobin", [[
+ local ffi = require("ffi")
+ local C = ffi.C
+
+ local counter = 0
+ return function(servers_list, dq)
+ counter = counter + 1
+ return (counter % tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list)))
+ end
+ ]])
+
ServerPolicy Objects
--------------------
Whether this policy is a native (C++) policy or a Lua-based one.
+ .. attribute:: ServerPolicy.isPerThread
+
+ .. versionadded: 1.6.0
+
+ Whether a FFI Lua-based policy is executed in a lock-free per-thread context instead of running in the global Lua context.
+
.. attribute:: ServerPolicy.name
The name of the policy.
:param string name: name for this policy
:param string function: name of the FFI function
+.. function:: setServerPolicyLuaFFIPerThread(name, code)
+
+ .. versionadded:: 1.6.0
+
+ Set server selection policy to one named ``name`` and the Lua FFI function returned by the Lua code passed in ``code``.
+ The resulting policy will be executed in a lock-free per-thread context, instead of running in the global Lua context.
+
+ :param string name: name for this policy
+ :param string code: Lua FFI code returning the function to execute as a server selection policy
+
.. function:: setServFailWhenNoServer(value)
If set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query.
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
-------------------------
-The built-in webserver serves its content from inside the binary, this means it will not and connot read from disk.
+The built-in webserver serves its content from inside the binary, this means it will not and cannot read from disk.
By default, our web server sends some security-related headers::
.. 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.
Use the API, this key will need to be sent to dnsdist in the ``X-API-Key`` request header.
An HTTP 401 response is returned when a wrong or no API key is received.
A 404 response is generated is the requested endpoint does not exist.
-And a 405 response is returned when the HTTP methos is not allowed.
+And a 405 response is returned when the HTTP method is not allowed.
URL Endpoints
~~~~~~~~~~~~~
Allows you to update the ``allow-from`` :ref:`ACL <ACL>` with a list of netmasks.
Make sure you made the API writable using :func:`setAPIWritable`.
+ Changes to the ACL are directly applied, no restart is required.
**Example request**:
==================
dnsdist only runs on UNIX-like systems and there are several ways to install dnsdist.
-The fastest way is using packages, either from your own operating system vendor or suppied by the PowerDNS project.
+The fastest way is using packages, either from your own operating system vendor or supplied by the PowerDNS project.
Building from source is also supported.
If dnsdist is available in your operating system's software repositories, install it from there.
However, the version of dnsdist in the repositories might be an older version that might not have a feature that was added in a later version.
Or you might want to be brave and try a development snapshot from the master branch.
-PowerDNS provides software respositories for the most popular distributions.
+PowerDNS provides software repositories for the most popular distributions.
Visit https://repo.powerdns.com for more information and installation instructions.
Debian
.. versionchanged:: 1.4.0
Removed ``doTCP`` from the options. A listen socket on TCP is always created.
+ .. versionchanged:: 1.5.0
+ Added ``tcpListenQueueSize`` parameter.
+
Add to the list of listen addresses.
:param str address: The IP Address with an optional port to listen on.
* ``tcpFastOpenQueueSize=0``: int - Set the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0.
* ``interface=""``: str - Set the network interface to use.
* ``cpus={}``: table - Set the CPU affinity for this listener thread, asking the scheduler to run it on a single CPU id, or a set of CPU ids. This parameter is only available if the OS provides the pthread_setaffinity_np() function.
+ * ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
.. code-block:: lua
.. versionadded:: 1.4.0
.. versionchanged:: 1.5.0
- ``sendCacheControlHeaders``, ``sessionTimeout`` options added.
- ``url`` now defaults to ``/dns-query`` instead of ``/``
+ ``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.
If no certificate (or key) files are specified, listen for incoming DNS over HTTP connections instead.
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:
* ``sessionTimeout``: int - Set the TLS session lifetime in seconds, this is used both for TLS ticket lifetime and for sessions kept in memory.
* ``sessionTickets``: bool - Whether session resumption via session tickets is enabled. Default is true, meaning tickets are enabled.
* ``numberOfStoredSessions``: int - The maximum number of sessions kept in memory at the same time. Default is 20480. Setting this value to 0 disables stored session entirely.
- * ``preferServerCiphers``: bool - Whether to prefer the order of ciphers set by the server instead of the one set by the client. Default is true, meaning that the order of the server is used.
+ * ``preferServerCiphers``: bool - Whether to prefer the order of ciphers set by the server instead of the one set by the client. Default is true, meaning that the order of the server is used. For OpenSSL >= 1.1.1, setting this option also enables the temporary re-prioritization of the ChaCha20-Poly1305 cipher if the client prioritizes it.
* ``keyLogFile``: str - Write the TLS keys in the specified file so that an external program can decrypt TLS exchanges, in the format described in https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. Note that this feature requires OpenSSL >= 1.1.1.
* ``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])
.. versionchanged:: 1.4.0
``ciphersTLS13``, ``minTLSVersion``, ``ocspResponses``, ``preferServerCiphers``, ``keyLogFile`` options added.
.. versionchanged:: 1.5.0
- ``sessionTimeout`` option added.
+ ``sessionTimeout`` and ``tcpListenQueueSize`` options added.
Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate.
* ``interface=""``: str - Set the network interface to use.
* ``cpus={}``: table - Set the CPU affinity for this listener thread, asking the scheduler to run it on a single CPU id, or a set of CPU ids. This parameter is only available if the OS provides the pthread_setaffinity_np() function.
* ``provider``: str - The TLS library to use between GnuTLS and OpenSSL, if they were available and enabled at compilation time. Default is to use OpenSSL when available.
- * ``ciphers``: str - The TLS ciphers to use. The exact format depends on the provider used. When the OpenSSL provder is used, ciphers for TLS 1.3 must be specified via ``ciphersTLS13``.
+ * ``ciphers``: str - The TLS ciphers to use. The exact format depends on the provider used. When the OpenSSL provider is used, ciphers for TLS 1.3 must be specified via ``ciphersTLS13``.
* ``ciphersTLS13``: str - The ciphers to use for TLS 1.3, when the OpenSSL provider is used. When the GnuTLS provider is used, ``ciphers`` applies regardless of the TLS protocol and this setting is not used.
* ``numberOfTicketsKeys``: int - The maximum number of tickets keys to keep in memory at the same time, if the provider supports it (GnuTLS doesn't, OpenSSL does). Only one key is marked as active and used to encrypt new tickets while the remaining ones can still be used to decrypt existing tickets after a rotation. Default to 5.
* ``ticketKeyFile``: str - The path to a file from where TLS tickets keys should be loaded, to support RFC 5077. These keys should be rotated often and never written to persistent storage to preserve forward secrecy. The default is to generate a random key. The OpenSSL provider supports several tickets keys to be able to decrypt existing sessions after the rotation, while the GnuTLS provider only supports one key.
* ``numberOfStoredSessions``: int - The maximum number of sessions kept in memory at the same time. At this time this is only supported by the OpenSSL provider, as stored sessions are not supported with the GnuTLS one. Default is 20480. Setting this value to 0 disables stored session entirely.
* ``ocspResponses``: list - List of files containing OCSP responses, in the same order than the certificates and keys, that will be used to provide OCSP stapling responses.
* ``minTLSVersion``: str - Minimum version of the TLS protocol to support. Possible values are 'tls1.0', 'tls1.1', 'tls1.2' and 'tls1.3'. Default is to require at least TLS 1.0. Note that this value is ignored when the GnuTLS provider is in use, and the ``ciphers`` option should be set accordingly instead. For example, 'NORMAL:!VERS-TLS1.0:!VERS-TLS1.1' will disable TLS 1.0 and 1.1.
- * ``preferServerCiphers``: bool - Whether to prefer the order of ciphers set by the server instead of the one set by the client. Default is true, meaning that the order of the server is used.
+ * ``preferServerCiphers``: bool - Whether to prefer the order of ciphers set by the server instead of the one set by the client. Default is true, meaning that the order of the server is used. For OpenSSL >= 1.1.1, setting this option also enables the temporary re-prioritization of the ChaCha20-Poly1305 cipher if the client prioritizes it.
* ``keyLogFile``: str - Write the TLS keys in the specified file so that an external program can decrypt TLS exchanges, in the format described in https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. Note that this feature requires OpenSSL >= 1.1.1.
+ * ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
.. function:: setLocal(address[, options])
:param str netmask: A CIDR netmask, e.g. ``"192.0.2.0/24"``. Without a subnetmask, only the specific address is allowed.
+.. function:: clearConsoleHistory()
+
+ .. versionadded:: 1.6.0
+
+ Clear the internal (in-memory) buffers of console commands. These buffers are used to provide the :func:`delta` command and
+ console completion and history, and can end up being quite large when a lot of commands are issued via the console, consuming
+ a noticeable amount of memory.
+
.. function:: controlSocket(address)
Bind to ``addr`` and listen for a connection for the console. Since 1.3.0 only connections from local users are allowed
:param str address: An IP address with optional port. By default, the port is 5199.
+.. function:: delta()
+
+ Issuing `delta` on the console will print the changes to the configuration that have been made since startup.
+
.. function:: inClientStartup()
Returns true while the console client is parsing the configuration.
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
~~~~~~~~~~~~~~~~~~~~
.. versionchanged:: 1.4.0
Added ``checkInterval``, ``checkTimeout`` and ``rise`` to server_table.
+ .. versionchanged:: 1.5.0
+ Added ``useProxyProtocol`` to server_table.
+
Add a new backend server. Call this function with either a string::
newServer(
-- using the experimental XPF record from `draft-bellis-dnsop-xpf <https://datatracker.ietf.org/doc/draft-bellis-dnsop-xpf/>`_ and the specified option code. Default is disabled (0)
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
+ 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.
+ 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.
.. versionadded:: 1.4.0
+ .. versionchanged:: 1.6.0
+ ``cookieHashing`` parameter added.
+
Creates a new :class:`PacketCache` with the settings specified.
:param int maxEntries: The maximum number of entries in this cache
* ``parseECS=false``: bool - Whether any EDNS Client Subnet option present in the query should be extracted and stored to be able to detect hash collisions involving queries with the same qname, qtype and qclass but a different incoming ECS value. Enabling this option adds a parsing cost and only makes sense if at least one backend might send different responses based on the ECS value, so it's disabled by default. Enabling this option is required for the 'zero scope' option to work
* ``staleTTL=60``: int - When the backend servers are not reachable, and global configuration ``setStaleCacheEntriesTTL`` is set appropriately, TTL that will be used when a stale cache entry is returned.
* ``temporaryFailureTTL=60``: int - On a SERVFAIL or REFUSED from the backend, cache for this amount of seconds..
+ * ``cookieHashing=false``: bool - Whether EDNS Cookie values will be hashed, resulting in separate entries for different cookies in the packet cache. This is required if the backend is sending answers with EDNS Cookies, otherwise a client might receive an answer with the wrong cookie.
.. class:: PacketCache
.. versionchanged:: 1.2.0
``suffixMatch`` parameter added.
+ .. versionchanged:: 1.6.0
+ ``name`` can now also be a string
+
Remove entries matching ``name`` and type from the cache.
: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()
:param int action: The action to take when the dynamic block matches, see :ref:`here <DNSAction>`. (default to the one set with :func:`setDynBlocksAction`)
:param int warningRate: If set to a non-zero value, the rate above which a warning message will be issued and a no-op block inserted
+ .. method:: DynBlockRulesGroup:setSuffixMatchRule(seconds, reason, blockingTime, action , visitor)
+
+ .. versionadded:: 1.4.0
+
+ Set a Lua visitor function that will be called for each label of every domain seen in queries and responses. The function receives a `StatNode` object representing the stats of the parent, a second one with the stats of the current label and one with the stats of the current node plus all its children.
+ Note that this function will not be called if a FFI version has been set using :meth:`DynBlockRulesGroup:setSuffixMatchRuleFFI`
+ If the function returns true, the current label will be blocked according to the `seconds`, `reason`, `blockingTime` and `action` parameters.
+ Selected domains can be excluded from this processing using the :meth:`DynBlockRulesGroup:excludeDomains` method.
+
+ This replaces the existing :func:`addDynBlockSMT` function.
+
+ :param int seconds: Number of seconds the rate has been exceeded
+ :param string reason: The message to show next to the blocks
+ :param int blockingTime: The number of seconds this block to expire
+ :param int action: The action to take when the dynamic block matches, see :ref:`here <DNSAction>`. (default to the one set with :func:`setDynBlocksAction`)
+ :param function visitor: The Lua function to call.
+
+ .. method:: DynBlockRulesGroup:setSuffixMatchRuleFFI(seconds, reason, blockingTime, action , visitor)
+
+ .. versionadded:: 1.4.0
+
+ Set a Lua FFI visitor function that will be called for each label of every domain seen in queries and responses. The function receives a `dnsdist_ffi_stat_node_t` object containing the stats of the parent, a second one with the stats of the current label and one with the stats of the current node plus all its children.
+ If the function returns true, the current label will be blocked according to the `seconds`, `reason`, `blockingTime` and `action` parameters.
+ Selected domains can be excluded from this processing using the :meth:`DynBlockRulesGroup:excludeDomains` method.
+
+ :param int seconds: Number of seconds the rate has been exceeded
+ :param string reason: The message to show next to the blocks
+ :param int blockingTime: The number of seconds this block to expire
+ :param int action: The action to take when the dynamic block matches, see :ref:`here <DNSAction>`. (default to the one set with :func:`setDynBlocksAction`)
+ :param function visitor: The Lua FFI function to call.
+
.. method:: DynBlockRulesGroup:apply()
Walk the in-memory query and response ring buffers and apply the configured rate-limiting rules, adding dynamic blocks when the limits have been exceeded.
:param bool quiet: True means that insertions will not be logged, false that they will. Default is false.
+ .. method:: DynBlockRulesGroup:excludeDomains(domains)
+
+ .. versionadded:: 1.4.0
+
+ Exclude this domain, or list of domains, meaning that no dynamic block will ever be inserted for this domain via :meth:`DynBlockRulesGroup:setSuffixMatchRule` or :meth:`DynBlockRulesGroup:setSuffixMatchRuleFFI`. Default to empty, meaning rules are applied to all domains.
+
+ :param str domain: A domain, or list of domains, as strings, like for example "powerdns.com"
+
.. method:: DynBlockRulesGroup:excludeRange(netmasks)
.. versionadded:: 1.3.1
Exclude this range, or list of ranges, meaning that no dynamic block will ever be inserted for clients in that range. Default to empty, meaning rules are applied to all ranges. When used in combination with :meth:`DynBlockRulesGroup:includeRange`, the more specific entry wins.
- :param int netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
+ :param list netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
.. method:: DynBlockRulesGroup:includeRange(netmasks)
Include this range, or list of ranges, meaning that rules will be applied to this range. When used in combination with :meth:`DynBlockRulesGroup:excludeRange`, the more specific entry wins.
- :param int netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
+ :param list netmasks: A netmask, or list of netmasks, as strings, like for example "192.0.2.1/24"
.. method:: DynBlockRulesGroup:toString()
Return a string describing the rules and range exclusions of this DynBlockRulesGroup.
+StatNode
+~~~~~~~~
+
+.. class:: StatNode
+
+ Represent metrics about a given node, for the visitor functions used with :meth:`DynBlockRulesGroup:setSuffixMatchRul` and :meth:`DynBlockRulesGroup:setSuffixMatchRuleFFI`. Note that some nodes includes the metrics for their children as well as their own.
+
+ .. attribute:: StatNode.bytes
+
+ The number of bytes for all responses returned for that node.
+
+ .. attribute:: StatNode.drops
+
+ The number of drops for that node.
+
+ .. attribute:: StatNode.fullname
+
+ The complete name of that node, ie 'www.powerdns.com'.
+
+ .. attribute:: StatNode.labelsCount
+
+ The number of labels in that node, for example 3 for 'www.powerdns.com'.
+
+ .. attribute:: StatNode.noerrors
+
+ The number of No Error answers returned for that node.
+
+ .. attribute:: StatNode.nxdomains
+
+ The number of NXDomain answers returned for that node.
+
+ .. attribute:: StatNode.queries
+
+ The number of queries for that node.
+
+ .. attribute:: StatNode.servfails
+
+ The number of Server Failure answers returned for that node.
+
+ .. method:: StatNode:numChildren
+
+ The number of children of that node.
+
SuffixMatchNode
~~~~~~~~~~~~~~~
:param string name: The suffix to add to the set.
:param table name: The suffixes to add to the set. Elements of the table should be of the same type, either DNSName or string.
+ .. method:: SuffixMatchNode:remove(name)
+
+ .. versionadded:: 1.5.0
+
+ Remove a suffix from the current set.
+
+ :param DNSName name: The suffix to remove from the set.
+ :param string name: The suffix to remove from the set.
+ :param table name: The suffixes to remove from the set. Elements of the table should be of the same type, either DNSName or string.
+
.. method:: SuffixMatchNode:check(name) -> bool
Return true if the given name is a sub-domain of one of those in the set, and false otherwise.
.. 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
-overriden 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
~~~~~~~~~~~~~~~~
* ``DNSAction.Pool``: use the specified pool to forward this query
* ``DNSAction.Refused``: return a response with a Refused rcode
* ``DNSAction.ServFail``: return a response with a ServFail rcode
- * ``DNSAction.Spoof``: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value
+ * ``DNSAction.Spoof``: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value. TTL will be 60 seconds.
* ``DNSAction.SpoofRaw``: spoof the response using the supplied raw value as record data
* ``DNSAction.Truncate``: truncate the response
* ``DNSAction.NoRecurse``: set rd=0 on the query
DNSName objects
===============
-A :class:`DNSName` object represents a name in the DNS. It has serveral functions that can manipulate it without conversions to strings.
+A :class:`DNSName` object represents a name in the DNS. It has several functions that can manipulate it without conversions to strings.
Creating a ``DNSName`` is done with the :func:`newDNSName`::
myname = newDNSName("www.example.com")
:param DNSName name: The name to check against
+ .. method:: DNSName:toDNSString() -> string
+
+ Returns a wire format form of the DNSName, suitable for usage in :func:`SpoofRawAction`.
+
.. method:: DNSName:toString() -> string
DNSName:tostring() -> string
To use FrameStream transport, :program:`dnsdist` must have been built with `libfstrm`.
-.. function:: newFrameStreamUnixLogger(path)
+.. function:: newFrameStreamUnixLogger(path [, options])
+
+ .. versionchanged:: 1.5.0
+ Added the optional parameter ``options``.
Create a Frame Stream Logger object, to use with :func:`DnstapLogAction` and :func:`DnstapLogResponseAction`.
This version will log to a local AF_UNIX socket.
:param string path: A local AF_UNIX socket path. Note that most platforms have a rather short limit on the length.
+ :param table options: A table with key: value pairs with options.
+
+ The following options apply to the settings of the framestream library. Refer to the documentation of that
+ library for the default and allowed values for these options, as well as their exact descriptions.
+ For all these options, absence or a zero value has the effect of using the library-provided default value.
+
+ * ``bufferHint=0``: unsigned
+ * ``flushTimeout=0``: unsigned
+ * ``inputQueueSize=0``: unsigned
+ * ``outputQueueSize=0``: unsigned
+ * ``queueNotifyThreshold=0``: unsigned
+ * ``reopenInterval=0``: unsigned
+
+.. function:: newFrameStreamTcpLogger(address [, options])
-.. function:: newFrameStreamTcpLogger(address)
+ .. versionchanged:: 1.5.0
+ Added the optional parameter ``options``.
Create a Frame Stream Logger object, to use with :func:`DnstapLogAction` and :func:`DnstapLogResponseAction`.
This version will log to a possibly remote TCP socket.
Needs tcp_writer support in libfstrm.
:param string address: An IP:PORT combination where the logger will connect to.
+ :param table options: A table with key: value pairs with options.
+
+ The following options apply to the settings of the framestream library. Refer to the documentation of that
+ library for the default and allowed values for these options, as well as their exact descriptions.
+ For all these options, absence or a zero value has the effect of using the library-provided default value.
+
+ * ``bufferHint=0``: unsigned
+ * ``flushTimeout=0``: unsigned
+ * ``inputQueueSize=0``: unsigned
+ * ``outputQueueSize=0``: unsigned
+ * ``queueNotifyThreshold=0``: unsigned
+ * ``reopenInterval=0``: unsigned
.. class:: DnstapMessage
Return the HTTP scheme for a DoH query.
- :returns: The scheme of the DoH query, for example ''http'' or ''https''
+ :returns: The scheme of the DoH query, for example ``http`` or ``https``
.. method:: DNSQuestion:getServerNameIndication() -> string
.. versionadded:: 1.4.0
Set the HTTP status code and content to immediately send back to the client.
- For HTTP redirects (3xx), the string supplied in ''body'' should be the URL to redirect to.
- For 200 responses, the value of the content type header can be specified via the ''contentType'' parameter.
+ For HTTP redirects (3xx), the string supplied in ``body`` should be the URL to redirect to.
+ For 200 responses, the value of the content type header can be specified via the ``contentType`` parameter.
In order for the response to be sent, the QR bit should be set before returning and the function should return Action.HeaderModify.
:param int status: The HTTP status code to return
:param string body: The body of the HTTP response, or a URL if the status code is a redirect (3xx)
- :param string contentType: The HTTP Content-Type header to return for a 200 response, ignored otherwise. Default is ''application/dns-message''.
+ :param string contentType: The HTTP Content-Type header to return for a 200 response, ignored otherwise. Default is ``application/dns-message``.
.. method:: DNSQuestion:setNegativeAndAdditionalSOA(nxd, zone, ttl, mname, rname, serial, refresh, retry, expire, minimum)
.. versionadded:: 1.5.0
- Turn a question into a response, either a NXDOMAIN or a NODATA one based on ''nxd'', setting the QR bit to 1 and adding a SOA record in the additional section.
+ Turn a question into a response, either a NXDOMAIN or a NODATA one based on ``nxd``, setting the QR bit to 1 and adding a SOA record in the additional section.
:param bool nxd: Whether the answer is a NXDOMAIN (true) or a NODATA (false)
:param string zone: The owner name for the SOA record
:param int expire: The value of the expire field in the SOA record
:param int minimum: The value of the minimum field in the SOA record
+ .. method:: DNSQuestion:setProxyProtocolValues(values)
+
+ .. versionadded:: 1.5.0
+
+ Set the Proxy-Protocol Type-Length values to send to the backend along with this query.
+
+ :param table values: A table of types and values to send, for example: ``{ [0x00] = "foo", [0x42] = "bar" }``. Note that the type must be an integer. Try to avoid these values: 0x01 - 0x05, 0x20 - 0x25, 0x30 as those are predefined in https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt (search for `PP2_TYPE_ALPN`)
+
.. method:: DNSQuestion:setTag(key, value)
.. versionadded:: 1.2.0
snmp
tuning
kvs
+ logging
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
--- /dev/null
+Logging\r
+=======\r
+\r
+There are some functions to create log output.\r
+\r
+.. function:: errlog(line)\r
+\r
+ Writes a error line.\r
+\r
+ :param str line: The line to write.\r
+\r
+\r
+.. function:: warnlog(line)\r
+\r
+ Writes a warning line.\r
+\r
+ :param str line: The line to write.\r
+\r
+\r
+.. function:: infolog(line)\r
+\r
+ Writes an info line.\r
+\r
+ :param str line: The line to write.\r
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.
.. deprecated:: 1.2.0
Send at most ``limit`` queries/s for this pool, letting the subsequent rules apply otherwise.
- This function has been deprecated as of 1.2.0 and removed in 1.3.0, as it is only a convience function for the following syntax::
+ This function has been deprecated as of 1.2.0 and removed in 1.3.0, as it is only a convenience function for the following syntax::
addAction("192.0.2.0/24", QPSPoolAction(15, "myPool")
Return a pair of DNS Rule and DNS Action, to be used with :func:`setRules`.
- :param Rule rule: A `Rule <#traffic-matching>`_
- :param Action action: The `Action <#actions>`_ to apply to the matched traffic
+ :param Rule rule: A `Rule (see `Matching Packets (Selectors)`_)
+ :param Action action: The Action (see `Actions`_) to apply to the matched traffic
:param table options: A table with key: value pairs with options.
Options:
Move the last response rule to the first position.
-Functions for manipulating Cache Hit Respone Rules:
+Functions for manipulating Cache Hit Response Rules:
.. function:: addCacheHitResponseAction(DNSRule, action [, options])
.. 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`),
:param bool name: The name of the tag that has to be set
:param bool value: If set, the value the tag has to be set to. Default is unset
-.. function:: TCPRule([tcp])
+.. function:: TCPRule(tcp)
Matches question received over TCP if ``tcp`` is true, over UDP otherwise.
- :param bool tcp: Match TCP traffic. Default is true.
+ :param bool tcp: Match TCP traffic if true, UDP traffic if false.
.. function:: TrailingDataRule()
.. function:: DelayAction(milliseconds)
- Delay the response by the specified amount of milliseconds (UDP-only).
+ Delay the response by the specified amount of milliseconds (UDP-only). Note that the sending of the query to the backend, if needed,
+ is not delayed. Only the sending of the response to the client will be delayed.
Subsequent rules are processed after this action.
:param int milliseconds: The amount of milliseconds to delay the response
.. function:: DelayResponseAction(milliseconds)
Delay the response by the specified amount of milliseconds (UDP-only).
+ The only difference between this action and :func:`DelayAction` is that they can only be applied on, respectively, responses and queries.
Subsequent rules are processed after this action.
:param int milliseconds: The amount of milliseconds to delay the response
.. 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`).
* ``ad``: bool - Set the AD bit to this value (true means the bit is set, false means it's cleared). Default is to clear it.
* ``ra``: bool - Set the RA bit to this value (true means the bit is set, false means it's cleared). Default is to copy the value of the RD bit from the incoming query.
+.. function:: SetProxyProtocolValuesAction(values)
+
+ .. versionadded:: 1.5.0
+
+ Set the Proxy-Protocol Type-Length values to be sent to the server along with this query to ``values``.
+
+ :param table values: A table of types and values to send, for example: ``{ [0] = foo", [42] = "bar" }``
+
.. function:: SkipCacheAction()
Don't lookup the cache for this query, don't store the answer.
-- select reverse queries for '127.0.0.1' and answer with 'localhost'
addAction(AndRule({QNameRule('1.0.0.127.in-addr.arpa.'), QTypeRule(DNSQType.PTR)}), SpoofRawAction("\009localhost\000"))
+ :func:`DNSName:toDNSString` is convenient for converting names to wire format for passing to ``SpoofRawAction``.
+
:param string rawAnswer: The raw record data
:param table options: A table with key: value pairs with options.
Send copy of query to ``remote``, keep stats on responses.
If ``addECS`` is set to true, EDNS Client Subnet information will be added to the query.
- :param string remote: An IP:PORT conbination to send the copied queries to
+ :param string remote: An IP:PORT combination to send the copied queries to
:param bool addECS: Whether or not to add ECS information. Default false
.. function:: TempFailureCacheTTLAction(ttl)
dnsdist is designed to (re)start almost instantly.
But to prevent downtime when changing configuration, the console (see :ref:`Console`) can be used for live configuration.
-Issueing :func:`delta` on the console will print the changes to the configuration that have been made since startup::
+Issuing :func:`delta` on the console will print the changes to the configuration that have been made since startup::
> delta()
-- Wed Feb 22 2017 11:31:44 CET
Upgrade Guide
=============
-1.4.0 to 1.5.x
+1.5.x to 1.6.0
--------------
-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``.
+The packet cache no longer hashes EDNS Cookies by default, which means that two queries that are identical except for the content of their cookie will now be served the same answer. This only works if the backend is not returning any answer containing EDNS Cookies, otherwise the wrong cookie might be returned to a client. To prevent this, the ``cookieHashing=true`` parameter might be passed to :func:`newPacketCache` so that cookies are hashed, resulting in separate entries in the packet cache.
+
+1.4.x to 1.5.0
+--------------
+
+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.
Packages provided on `the PowerDNS Repository <https://repo.powerdns.com>`__ will ``chown`` directories created by them accordingly in the post-installation steps.
+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 "dns.hh"
#include "dolog.hh"
#include "dnsdist-ecs.hh"
+#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-rules.hh"
#include "dnsdist-xpf.hh"
#include "libssl.hh"
#include "threadname.hh"
-
-using namespace std;
+#include "views.hh"
/* 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,
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;
dh->id = idOffset;
+ if (ss->useProxyProtocol) {
+ addProxyProtocol(dq);
+ }
+
int fd = pickBackendSocketForSending(ss);
try {
/* you can't touch du after this line, because it might already have been freed */
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, pdns_string_view& value)
+{
+ bool found = false;
+ /* early versions of boost::string_ref didn't have the ability to compare to string */
+ pdns_string_view headerNameView(headerName);
+
+ for (size_t i = 0; i < req->headers.size; ++i) {
+ if (pdns_string_view(req->headers.entries[i].name->base, req->headers.entries[i].name->len) == headerNameView) {
+ value = pdns_string_view(req->headers.entries[i].value.base, req->headers.entries[i].value.len);
+ /* don't stop there, we might have more than one header with the same name, and we want the last one */
+ found = true;
+ }
+ }
+
+ 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";
+ pdns_string_view value;
+
+ if (getHTTPHeaderValue(req, headerName, value)) {
+ try {
+ auto pos = value.rfind(',');
+ if (pos != pdns_string_view::npos) {
+ ++pos;
+ for (; pos < value.size() && value[pos] == ' '; ++pos)
+ {
+ }
+
+ if (pos < value.size()) {
+ value = value.substr(pos);
+ }
+ }
+ auto newRemote = ComboAddress(std::string(value));
+ remote = newRemote;
+ }
+ catch (const std::exception& e) {
+ vinfolog("Invalid X-Forwarded-For header ('%s') received from %s : %s", std::string(value), remote.toStringWithPort(), e.what());
+ }
+ catch (const PDNSException& e) {
+ vinfolog("Invalid X-Forwarded-For header ('%s') received from %s : %s", std::string(value), remote.toStringWithPort(), e.reason);
+ }
+ }
+}
+
/*
- 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);
+ if (dsc->df->d_trustForwardedForHeader) {
+ processForwardedForHeader(req, remote);
+ }
+
auto& holders = dsc->holders;
if (!holders.acl->match(remote)) {
++g_stats.aclDrops;
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);
return;
}
- ComboAddress remote;
- h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote));
+ // ComboAddress remote;
+ // h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote));
// cout<<"New HTTP accept for client "<<remote.toStringWithPort()<<": "<< listener->data << endl;
sock->data = dsc;
h2o_ssl_register_alpn_protocols(ctx.get(), h2o_http2_alpn_protocols);
+ acceptCtx.d_ticketsKeyRotationDelay = tlsConfig.d_ticketsKeyRotationDelay;
if (tlsConfig.d_ticketKeyFile.empty()) {
acceptCtx.handleTicketsKeyRotation();
}
{
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 <atomic>
#include <fstream>
#include <cstring>
+#include <mutex>
#include <pthread.h>
#include <openssl/conf.h>
#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL)
/* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */
-static pthread_mutex_t *openssllocks{nullptr};
+
+#include "lock.hh"
+static std::vector<std::mutex> openssllocks;
extern "C" {
static void openssl_pthreads_locking_callback(int mode, int type, const char *file, int line)
{
if (mode & CRYPTO_LOCK) {
- pthread_mutex_lock(&(openssllocks[type]));
+ openssllocks.at(type).lock();
} else {
- pthread_mutex_unlock(&(openssllocks[type]));
+ openssllocks.at(type).unlock();
}
}
static void openssl_thread_setup()
{
- openssllocks = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
-
- for (int i = 0; i < CRYPTO_num_locks(); i++)
- pthread_mutex_init(&(openssllocks[i]), NULL);
-
- CRYPTO_set_id_callback(openssl_pthreads_id_callback);
- CRYPTO_set_locking_callback(openssl_pthreads_locking_callback);
+ openssllocks = std::vector<std::mutex>(CRYPTO_num_locks());
+ CRYPTO_set_id_callback(&openssl_pthreads_id_callback);
+ CRYPTO_set_locking_callback(&openssl_pthreads_locking_callback);
}
static void openssl_thread_cleanup()
{
- CRYPTO_set_locking_callback(NULL);
-
- for (int i=0; i<CRYPTO_num_locks(); i++) {
- pthread_mutex_destroy(&(openssllocks[i]));
- }
-
- OPENSSL_free(openssllocks);
+ CRYPTO_set_locking_callback(nullptr);
+ openssllocks.clear();
}
#endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL) */
OpenSSLTLSTicketKeysRing::OpenSSLTLSTicketKeysRing(size_t capacity)
{
- pthread_rwlock_init(&d_lock, nullptr);
d_ticketKeys.set_capacity(capacity);
}
OpenSSLTLSTicketKeysRing::~OpenSSLTLSTicketKeysRing()
{
- pthread_rwlock_destroy(&d_lock);
}
void OpenSSLTLSTicketKeysRing::addKey(std::shared_ptr<OpenSSLTLSTicketKey> newKey)
if (config.d_preferServerCiphers) {
sslOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+#ifdef SSL_OP_PRIORITIZE_CHACHA
+ sslOptions |= SSL_OP_PRIORITIZE_CHACHA;
+#endif /* SSL_OP_PRIORITIZE_CHACHA */
}
SSL_CTX_set_options(ctx.get(), sslOptions);
--- /dev/null
+../packetcache.hh
\ No newline at end of file
--- /dev/null
+../proxy-protocol.cc
\ No newline at end of file
--- /dev/null
+../proxy-protocol.hh
\ No newline at end of file
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
-void safe_memory_lock(void* data, size_t size)
+static void safe_memory_lock(void* data, size_t size)
{
#ifdef HAVE_LIBSODIUM
sodium_mlock(data, size);
#endif
}
-void safe_memory_release(void* data, size_t size)
+static void safe_memory_release(void* data, size_t size)
{
#ifdef HAVE_LIBSODIUM
sodium_munlock(data, size);
throw std::runtime_error("Error setting up TLS cipher preferences to '" + fe.d_tlsConfig.d_ciphers + "' (" + gnutls_strerror(rc) + ") on " + fe.d_addr.toStringWithPort());
}
- pthread_rwlock_init(&d_lock, nullptr);
-
try {
if (fe.d_tlsConfig.d_ticketKeyFile.empty()) {
handleTicketsKeyRotation(time(nullptr));
}
}
catch(const std::runtime_error& e) {
- pthread_rwlock_destroy(&d_lock);
throw std::runtime_error("Error generating tickets key for TLS context on " + fe.d_addr.toStringWithPort() + ": " + e.what());
}
}
virtual ~GnuTLSIOCtx() override
{
- pthread_rwlock_destroy(&d_lock);
-
d_creds.reset();
if (d_priorityCache) {
std::unique_ptr<gnutls_certificate_credentials_st, void(*)(gnutls_certificate_credentials_t)> d_creds;
gnutls_priority_t d_priorityCache{nullptr};
std::shared_ptr<GnuTLSTicketsKey> d_ticketsKey{nullptr};
- pthread_rwlock_t d_lock;
+ ReadWriteLock d_lock;
bool d_enableTickets{true};
};
int i;
for(int n=0; n < 100; ++n) {
- bool res=op.read(&i);
- BOOST_CHECK_EQUAL(res, true);
+ int res=op.readTimeout(&i, -1);
+ BOOST_CHECK_EQUAL(res, 1);
BOOST_CHECK_EQUAL(n, i);
}
op.close();
- BOOST_CHECK_EQUAL(op.read(&i), false);
+ BOOST_CHECK_EQUAL(op.readTimeout(&i, 1), 0);
};
#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.");
BOOST_CHECK_EQUAL(value, "this is the value for the qname");
}
}
+#endif // defined(HAVE_LMDB) || defined(HAVE_CDB)
BOOST_AUTO_TEST_SUITE(dnsdistkvs_cc)
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();
GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
#endif /* BENCH_POLICIES */
+GlobalStateHolder<pools_t> g_pools;
+std::vector<std::unique_ptr<ClientState>> g_frontends;
+
/* add stub implementations, we don't want to include the corresponding object files
and their dependencies */
return false;
}
+void setLuaNoSideEffect()
+{
+}
+
+string g_outputBuffer;
+
static DNSQuestion getDQ(const DNSName* providedName = nullptr)
{
static const DNSName qname("powerdns.com.");
for (size_t idx = 0; idx < 1000; idx++) {
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
}
}
cerr<<pol.name<<" took "<<std::to_string(sw.udiff())<<" us for "<<names.size()<<endl;
servers.push_back({ 1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")) });
/* servers start as 'down' */
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_CHECK(server == nullptr);
/* mark the server as 'up' */
servers.at(0).second->setUp();
- server = getSelectedBackendFromPolicy(pol, servers, dq);
+ server = pol.getSelectedBackend(servers, dq);
BOOST_CHECK(server != nullptr);
/* add a second server, we should still get the first one */
servers.push_back({ 2, std::make_shared<DownstreamState>(ComboAddress("192.0.2.2:53")) });
- server = getSelectedBackendFromPolicy(pol, servers, dq);
+ server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(server != nullptr);
BOOST_CHECK(server == servers.at(0).second);
/* mark the first server as 'down', second as 'up' */
servers.at(0).second->setDown();
servers.at(1).second->setUp();
- server = getSelectedBackendFromPolicy(pol, servers, dq);
+ server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(server != nullptr);
BOOST_CHECK(server == servers.at(1).second);
- std::vector<DNSName> names;
- names.reserve(1000);
+ benchPolicy(pol);
+}
+
+BOOST_AUTO_TEST_CASE(test_roundRobin) {
+ auto dq = getDQ();
+
+ ServerPolicy pol{"roundrobin", roundrobin, false};
+ ServerPolicy::NumberedServerVector servers;
+
+ /* selecting a server on an empty server list */
+ g_roundrobinFailOnNoServer = false;
+ auto server = pol.getSelectedBackend(servers, dq);
+ BOOST_CHECK(server == nullptr);
+
+ servers.push_back({ 1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")) });
+
+ /* servers start as 'down' but the RR policy returns a server unless g_roundrobinFailOnNoServer is set */
+ g_roundrobinFailOnNoServer = true;
+ server = pol.getSelectedBackend(servers, dq);
+ BOOST_CHECK(server == nullptr);
+ g_roundrobinFailOnNoServer = false;
+ server = pol.getSelectedBackend(servers, dq);
+ BOOST_CHECK(server != nullptr);
+
+ /* mark the server as 'up' */
+ servers.at(0).second->setUp();
+ server = pol.getSelectedBackend(servers, dq);
+ BOOST_CHECK(server != nullptr);
+
+ /* add a second server, we should get the first one then the second one */
+ servers.push_back({ 2, std::make_shared<DownstreamState>(ComboAddress("192.0.2.2:53")) });
+ servers.at(1).second->setUp();
+ server = pol.getSelectedBackend(servers, dq);
+ BOOST_REQUIRE(server != nullptr);
+ BOOST_CHECK(server == servers.at(0).second);
+ server = pol.getSelectedBackend(servers, dq);
+ BOOST_REQUIRE(server != nullptr);
+ BOOST_CHECK(server == servers.at(1).second);
+
+ /* mark the first server as 'down', second as 'up' */
+ servers.at(0).second->setDown();
+ servers.at(1).second->setUp();
+ server = pol.getSelectedBackend(servers, dq);
+ BOOST_REQUIRE(server != nullptr);
+ BOOST_CHECK(server == servers.at(1).second);
+
+ std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
+ /* mark all servers 'up' */
+ for (auto& s : servers) {
+ s.second->setUp();
+ serversMap[s.second] = 0;
+ }
+
for (size_t idx = 0; idx < 1000; idx++) {
- names.push_back(DNSName("powerdns-" + std::to_string(idx) + ".com."));
+ server = pol.getSelectedBackend(servers, dq);
+ BOOST_REQUIRE(serversMap.count(server) == 1);
+ ++serversMap[server];
}
- std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
- for (size_t idx = 1; idx <= 10; idx++) {
- servers.push_back({ idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")) });
- serversMap[servers.at(idx - 1).second] = 0;
- servers.at(idx - 1).second->setUp();
+ uint64_t total = 0;
+ for (const auto& entry : serversMap) {
+ BOOST_CHECK_EQUAL(entry.second, 1000 / servers.size());
+ total += entry.second;
}
+ BOOST_CHECK_EQUAL(total, 1000U);
benchPolicy(pol);
}
servers.push_back({ 1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")) });
/* servers start as 'down' */
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_CHECK(server == nullptr);
/* mark the server as 'up' */
servers.at(0).second->setUp();
- server = getSelectedBackendFromPolicy(pol, servers, dq);
+ server = pol.getSelectedBackend(servers, dq);
BOOST_CHECK(server != nullptr);
/* add a second server, we should still get the first one */
servers.push_back({ 2, std::make_shared<DownstreamState>(ComboAddress("192.0.2.2:53")) });
- server = getSelectedBackendFromPolicy(pol, servers, dq);
+ server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(server != nullptr);
BOOST_CHECK(server == servers.at(0).second);
/* mark the first server as 'down', second as 'up' */
servers.at(0).second->setDown();
servers.at(1).second->setUp();
- server = getSelectedBackendFromPolicy(pol, servers, dq);
+ server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(server != nullptr);
BOOST_CHECK(server == servers.at(1).second);
servers.at(0).second->setUp();
servers.at(0).second->outstanding = 42;
servers.at(1).second->setUp();
- server = getSelectedBackendFromPolicy(pol, servers, dq);
+ server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(server != nullptr);
BOOST_CHECK(server == servers.at(1).second);
benchPolicy(pol);
for (size_t idx = 0; idx < 1000; idx++) {
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (1000 / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (1000 / servers.size() * 2));
total += entry.second;
}
- BOOST_CHECK_EQUAL(total, 1000);
+ BOOST_CHECK_EQUAL(total, 1000U);
/* reset */
for (auto& entry : serversMap) {
servers.at(servers.size()-1).second->weight = 100;
for (size_t idx = 0; idx < 1000; idx++) {
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
total += entry.second;
totalW += entry.first->weight;
}
- BOOST_CHECK_EQUAL(total, 1000);
+ BOOST_CHECK_EQUAL(total, 1000U);
auto last = servers.at(servers.size()-1).second;
const auto got = serversMap[last];
float expected = (1000 * 1.0 * last->weight) / totalW;
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
total += entry.second;
/* request 1000 times the same name, we should go to the same server every time */
{
auto dq = getDQ(&names.at(0));
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
for (size_t idx = 0; idx < 1000; idx++) {
- BOOST_CHECK(getSelectedBackendFromPolicy(pol, servers, dq) == server);
+ BOOST_CHECK(pol.getSelectedBackend(servers, dq) == server);
}
}
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
total += entry.second;
/* request 1000 times the same name, we should go to the same server every time */
{
auto dq = getDQ(&names.at(0));
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
for (size_t idx = 0; idx < 1000; idx++) {
- BOOST_CHECK(getSelectedBackendFromPolicy(pol, servers, dq) == server);
+ BOOST_CHECK(pol.getSelectedBackend(servers, dq) == server);
}
}
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
serversMap[servers.at(idx - 1).second] = 0;
servers.at(idx - 1).second->setUp();
}
- BOOST_REQUIRE_EQUAL(servers.size(), 10);
+ BOOST_REQUIRE_EQUAL(servers.size(), 10U);
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
total += entry.second;
serversMap[servers.at(idx - 1).second] = 0;
servers.at(idx - 1).second->setUp();
}
- BOOST_REQUIRE_EQUAL(servers.size(), 10);
+ BOOST_REQUIRE_EQUAL(servers.size(), 10U);
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
total += entry.second;
serversMap[servers.at(idx - 1).second] = 0;
servers.at(idx - 1).second->setUp();
}
- BOOST_REQUIRE_EQUAL(servers.size(), 10);
+ BOOST_REQUIRE_EQUAL(servers.size(), 10U);
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
total += entry.second;
serversMap[servers.at(idx - 1).second] = 0;
servers.at(idx - 1).second->setUp();
}
- BOOST_REQUIRE_EQUAL(servers.size(), 10);
+ BOOST_REQUIRE_EQUAL(servers.size(), 10U);
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
total += entry.second;
/* make sure that the hashes have been computed */
servers.at(idx - 1).second->hash();
}
- BOOST_REQUIRE_EQUAL(servers.size(), 10);
+ BOOST_REQUIRE_EQUAL(servers.size(), 10U);
for (const auto& name : names) {
auto dq = getDQ(&name);
- auto server = getSelectedBackendFromPolicy(pol, servers, dq);
+ auto server = pol.getSelectedBackend(servers, dq);
BOOST_REQUIRE(serversMap.count(server) == 1);
++serversMap[server];
}
uint64_t total = 0;
for (const auto& entry : serversMap) {
- BOOST_CHECK_GT(entry.second, 0);
+ BOOST_CHECK_GT(entry.second, 0U);
BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
total += entry.second;
--- /dev/null
+../test-proxy_protocol_cc.cc
\ No newline at end of file
--- /dev/null
+../views.hh
\ No newline at end of file
StatBag S;
-struct tm* pdns_localtime_r(const uint32_t* then, struct tm* tm)
+static struct tm* pdns_localtime_r(const uint32_t* then, struct tm* tm)
{
time_t t = *then;
int32_t g_clientQuestions, g_clientResponses, g_serverQuestions, g_serverResponses, g_skipped;
struct pdns_timeval g_lastanswerTime, g_lastquestionTime;
-void makeReport(const struct pdns_timeval& tv)
+
+static void makeReport(const struct pdns_timeval& tv)
{
int64_t clientdiff = g_clientQuestions - g_clientResponses;
int64_t serverdiff = g_serverQuestions - g_serverResponses;
g_skipped=0;
}
-void usage() {
+static void usage() {
cerr<<"syntax: dnsgram INFILE..."<<endl;
}
#include <string>
#include "dnsname.hh"
#include "namespaces.hh"
+#include "dnswriter.hh"
namespace {
void appendSplit(vector<string>& ret, string& segment, char c)
optional uint32 queryTimeSec = 5; // Time of the corresponding query reception (seconds since epoch)
optional uint32 queryTimeUsec = 6; // Time of the corresponding query reception (additional micro-seconds)
optional PolicyType appliedPolicyType = 7; // Type of the filtering policy (RPZ or Lua) applied
+ optional string appliedPolicyTrigger = 8; // The RPZ trigger
+ optional string appliedPolicyHit = 9; // The value (qname or IP) that caused the hit
}
optional DNSResponse response = 13;
std::string DNSName::toDNSStringLC() const
{
- return toLower(toDNSString()); // label lengths are always < 'A'
+ auto result = toDNSString();
+ toLowerInPlace(result); // label lengths are always < 'A'
+ return result;
}
/**
{
DNSName ret(*this);
ret.makeUsRelative(zone);
- return ret.empty() ? zone : ret; // HACK FIXME400
+ return ret;
}
+
void DNSName::makeUsRelative(const DNSName& zone)
{
if (isPartOf(zone)) {
{
public:
DNSName() {} //!< Constructs an *empty* DNSName, NOT the root!
+ // Work around assertion in some boost versions that do not like self-assignment of boost::container::string
+ DNSName& operator=(const DNSName& rhs)
+ {
+ if (this != &rhs) {
+ d_storage = rhs.d_storage;
+ }
+ return *this;
+ }
+ DNSName& operator=(const DNSName&& rhs)
+ {
+ if (this != &rhs) {
+ d_storage = std::move(rhs.d_storage);
+ }
+ return *this;
+ }
+ DNSName(const DNSName& a) = default;
+ DNSName(DNSName&& a) = default;
explicit DNSName(const char* p): DNSName(p, std::strlen(p)) {} //!< Constructs from a human formatted, escaped presentation
explicit DNSName(const char* p, size_t len); //!< Constructs from a human formatted, escaped presentation
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 "dnssecinfra.hh"
#include "base64.hh"
#include "ednssubnet.hh"
-#include "gss_context.hh"
#include "dns_random.hh"
+#include "shuffle.hh"
bool DNSPacket::s_doEDNSSubnetProcessing;
uint16_t DNSPacket::s_udpTruncationThreshold;
static bool mustNotShuffle = ::arg().mustDo("no-shuffle");
if(!d_tcp && !mustNotShuffle) {
- shuffle(d_rrs);
+ pdns::shuffle(d_rrs);
}
d_wrapped=true;
tt.algo = DNSName("hmac-md5");
string secret64;
- if (tt.algo != DNSName("gss-tsig")) {
- if(!B->getTSIGKey(*keyname, &tt.algo, &secret64)) {
- g_log<<Logger::Error<<"Packet for domain '"<<this->qdomain<<"' denied: can't find TSIG key with name '"<<*keyname<<"' and algorithm '"<<tt.algo<<"'"<<endl;
- return false;
- }
- B64Decode(secret64, *secret);
- tt.secret = *secret;
+ if(!B->getTSIGKey(*keyname, &tt.algo, &secret64)) {
+ g_log<<Logger::Error<<"Packet for domain '"<<this->qdomain<<"' denied: can't find TSIG key with name '"<<*keyname<<"' and algorithm '"<<tt.algo<<"'"<<endl;
+ return false;
}
+ B64Decode(secret64, *secret);
+ tt.secret = *secret;
bool result;
// parse the input
vector<string> parts;
stringtok(parts, zone);
- if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) )
- throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+std::to_string(parts.size())+": "+zone );
- const string& relevant=(parts.size() > 2) ? parts[2] : "";
- unsigned int total=pdns_stou(parts[1]);
- if(relevant.size() % 2 || relevant.size() / 2 != total)
+ // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
+ if (parts.size() != 3 && !(parts.size() == 2 && equals(parts.at(1), "0"))) {
+ throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
+ }
+
+ if (parts.at(0) != "\\#") {
+ throw MOADNSException("Unknown record was stored incorrectly, first part should be '\\#', got '" + parts.at(0) + "'");
+ }
+
+ const string& relevant = (parts.size() > 2) ? parts.at(2) : "";
+ unsigned int total = pdns_stou(parts.at(1));
+ if (relevant.size() % 2 || (relevant.size() / 2) != total) {
throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
+ }
+
string out;
- out.reserve(total+1);
- for(unsigned int n=0; n < total; ++n) {
+ out.reserve(total + 1);
+
+ for (unsigned int n = 0; n < total; ++n) {
int c;
- sscanf(relevant.c_str()+2*n, "%02x", &c);
+ if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
+ throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
+ }
out.append(1, (char)c);
}
vector<uint8_t> d_record;
};
-shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const DNSName& qname, uint16_t qtype, const string& serialized)
+shared_ptr<DNSRecordContent> DNSRecordContent::deserialize(const DNSName& qname, uint16_t qtype, const string& serialized)
{
dnsheader dnsheader;
memset(&dnsheader, 0, sizeof(dnsheader));
/* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
- string encoded=qname.toDNSString();
+ const auto& encoded = qname.getStorage();
packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size());
uint16_t d_pos;
uint16_t d_startrecordpos; // needed for getBlob later on
uint16_t d_recordlen; // ditto
- uint16_t not_used; // Aligns the whole class on 8-byte boundries
+ uint16_t not_used; // Aligns the whole class on 8-byte boundaries
const std::string& d_content;
};
return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
}
- static shared_ptr<DNSRecordContent> unserialize(const DNSName& qname, uint16_t qtype, const string& serialized);
+ static shared_ptr<DNSRecordContent> deserialize(const DNSName& qname, uint16_t qtype, const string& serialized);
void doRecordCheck(const struct DNSRecord&){}
#include "namespaces.hh"
PcapPacketReader::PcapPacketReader(const string& fname) : d_fname(fname)
{
- d_fp=fopen(fname.c_str(),"r");
- if(!d_fp)
+ d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname.c_str(), "r"), fclose);
+ if (!d_fp) {
unixDie("Unable to open file " + fname);
-
- int flags=fcntl(fileno(d_fp),F_GETFL,0);
- fcntl(fileno(d_fp), F_SETFL,flags&(~O_NONBLOCK)); // bsd needs this in stdin (??)
-
+ }
+
+ int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
+ fcntl(fileno(d_fp.get()), F_SETFL, flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
+
checkedFread(&d_pfh);
-
- if(d_pfh.magic != 2712847316UL)
+
+ if (d_pfh.magic != 2712847316UL) {
throw runtime_error((format("PCAP file %s has bad magic %x, should be %x") % fname % d_pfh.magic % 2712847316UL).str());
-
+ }
+
if( d_pfh.linktype==1) {
d_skipMediaHeader=sizeof(struct ether_header);
}
+ else if( d_pfh.linktype==12) { // LOOP
+ d_skipMediaHeader=4;
+ }
else if(d_pfh.linktype==101) {
d_skipMediaHeader=0;
}
d_skipMediaHeader=16;
}
else throw runtime_error((format("Unsupported link type %d") % d_pfh.linktype).str());
-
- d_runts = d_oversized = d_correctpackets = d_nonetheripudp = 0;
-}
-PcapPacketReader::~PcapPacketReader()
-{
- fclose(d_fp);
+ d_runts = d_oversized = d_correctpackets = d_nonetheripudp = 0;
}
-
void PcapPacketReader::checkedFreadSize(void* ptr, size_t size)
{
- int ret=fread(ptr, 1, size, d_fp);
- if(ret < 0)
+ int ret = fread(ptr, 1, size, d_fp.get());
+ if (ret < 0) {
unixDie( (format("Error reading %d bytes from %s") % size % d_fname).str());
-
+ }
+
if(!ret)
throw EofException();
d_ether=reinterpret_cast<struct ether_header*>(d_buffer);
contentCode=ntohs(d_ether->ether_type);
}
+ else if(d_pfh.linktype == 12) { // LOOP
+ if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
+ d_runts++;
+ continue;
+ }
+ if(d_ip->ip_v == 4)
+ contentCode = 0x0800;
+ else
+ contentCode = 0x86dd;
+ }
else if(d_pfh.linktype==101) {
if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
d_runts++;
PcapPacketWriter::PcapPacketWriter(const string& fname) : d_fname(fname)
{
- d_fp=fopen(fname.c_str(),"w");
- if(!d_fp)
+ d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname.c_str(),"w"), fclose);
+
+ if (!d_fp) {
unixDie("Unable to open file");
-
- int flags=fcntl(fileno(d_fp),F_GETFL,0);
- fcntl(fileno(d_fp), F_SETFL,flags&(~O_NONBLOCK)); // bsd needs this in stdin (??)
+ }
+
+ int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
+ fcntl(fileno(d_fp.get()), F_SETFL,flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
}
void PcapPacketWriter::write()
}
if(d_first) {
- fwrite(&d_ppr->d_pfh, 1, sizeof(d_ppr->d_pfh), d_fp);
+ fwrite(&d_ppr->d_pfh, 1, sizeof(d_ppr->d_pfh), d_fp.get());
d_first=false;
}
- fwrite(&d_ppr->d_pheader, 1, sizeof(d_ppr->d_pheader), d_fp);
- fwrite(d_ppr->d_buffer, 1, d_ppr->d_pheader.caplen, d_fp);
-}
-
-PcapPacketWriter::~PcapPacketWriter()
-{
- fclose(d_fp);
+ fwrite(&d_ppr->d_pheader, 1, sizeof(d_ppr->d_pheader), d_fp.get());
+ fwrite(d_ppr->d_buffer, 1, d_ppr->d_pheader.caplen, d_fp.get());
}
PcapPacketReader(const string& fname);
- ~PcapPacketReader();
-
template<typename T>
void checkedFread(T* ptr)
{
unsigned int d_runts, d_oversized, d_correctpackets, d_nonetheripudp;
char d_buffer[32768];
private:
- FILE* d_fp;
+ std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
string d_fname;
unsigned int d_skipMediaHeader;
};
void write();
void setPPR(const PcapPacketReader& ppr) { d_ppr = &ppr; }
- ~PcapPacketWriter();
private:
string d_fname;
const PcapPacketReader* d_ppr{nullptr};
- FILE *d_fp;
+ std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
bool d_first{true};
};
PcapPacketReader pr(argv[1]);
- FILE* fp = fopen(argv[2], "w");
+ auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(argv[2], "w"), fclose);
if (!fp) {
cerr<<"Error opening output file "<<argv[2]<<": "<<stringerror()<<endl;
exit(EXIT_FAILURE);
message.serialize(str);
uint16_t mlen = htons(str.length());
- fwrite(&mlen, 1, sizeof(mlen), fp);
- fwrite(str.c_str(), 1, str.length(), fp);
+ fwrite(&mlen, 1, sizeof(mlen), fp.get());
+ fwrite(str.c_str(), 1, str.length(), fp.get());
}
}
catch (const std::exception& e) {
cerr<<"Error while parsing the PCAP file: "<<e.what()<<endl;
- fclose(fp);
exit(EXIT_FAILURE);
}
-
- fclose(fp);
}
catch(const std::exception& e) {
cerr<<"Error opening PCAP file: "<<e.what()<<endl;
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+
+#include <sys/types.h>
+#include <thread>
+
#include "packetcache.hh"
#include "utility.hh"
#include "dnsproxy.hh"
#include "pdnsexception.hh"
-#include <sys/types.h>
#include "dns.hh"
#include "logger.hh"
#include "statbag.hh"
DNSProxy::DNSProxy(const string &remote)
{
- pthread_mutex_init(&d_lock,0);
d_resanswers=S.getPointer("recursing-answers");
d_resquestions=S.getPointer("recursing-questions");
d_udpanswers=S.getPointer("udp-answers");
void DNSProxy::go()
{
- pthread_t tid;
- pthread_create(&tid,0,&launchhelper,this);
+ std::thread t(std::bind(&DNSProxy::mainloop, this));
+ t.detach();
}
//! look up qname target with r->qtype, plonk it in the answer section of 'r' with name aname
uint16_t id;
uint16_t qtype = r->qtype.getCode();
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
id=getID_locked();
ConntrackEntry ce;
dnsheader d;
memcpy(&d,buffer,sizeof(d));
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
#if BYTE_ORDER == BIG_ENDIAN
// this is needed because spoof ID down below does not respect the native byteorder
d.id = ( 256 * (uint16_t)buffer[1] ) + (uint16_t)buffer[0];
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
-#include <pthread.h>
#include <map>
+#include <mutex>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
bool completePacket(std::unique_ptr<DNSPacket>& r, const DNSName& target,const DNSName& aname, const uint8_t scopeMask);
void mainloop(); //!< this is the main loop that receives reply packets and sends them out again
- static void *launchhelper(void *p)
- {
- static_cast<DNSProxy *>(p)->mainloop();
- return 0;
- }
bool recurseFor(DNSPacket* p);
private:
struct ConntrackEntry
AtomicCounter* d_resanswers;
AtomicCounter* d_udpanswers;
AtomicCounter* d_resquestions;
- pthread_mutex_t d_lock;
+ std::mutex d_lock;
map_t d_conntrack;
int d_sock;
int getID_locked();
/* EUI64 end */
+/* APL start */
+/* https://tools.ietf.org/html/rfc3123 */
+void APLRecordContent::report(void)
+{
+ regist(1, QType::APL, &make, &make, "APL");
+}
+
+// Parse incoming packets (e.g. nsupdate)
+std::shared_ptr<DNSRecordContent> APLRecordContent::make(const DNSRecord &dr, PacketReader& pr) {
+ uint8_t temp;
+ APLRDataElement ard;
+ size_t processed = 0;
+
+ auto ret=std::make_shared<APLRecordContent>();
+
+ while (processed<dr.d_clen) {
+ pr.xfr16BitInt(ard.d_family);
+ pr.xfr8BitInt(ard.d_prefix);
+ pr.xfr8BitInt(temp);
+ ard.d_n = (temp & 128) >> 7;
+ ard.d_afdlength = temp & 127;
+
+ if (ard.d_family == APL_FAMILY_IPV4) {
+ if (ard.d_afdlength > 4) {
+ throw MOADNSException("Invalid IP length for IPv4 APL");
+ }
+ memset(ard.d_ip.d_ip4, 0, sizeof(ard.d_ip.d_ip4));
+ for (u_int i=0; i < ard.d_afdlength; i++)
+ pr.xfr8BitInt(ard.d_ip.d_ip4[i]);
+ } else if (ard.d_family == APL_FAMILY_IPV6) {
+ if (ard.d_afdlength > 16) {
+ throw MOADNSException("Invalid IP length for IPv6 APL");
+ }
+ memset(ard.d_ip.d_ip6, 0, sizeof(ard.d_ip.d_ip6));
+ for (u_int i=0; i < ard.d_afdlength; i++)
+ pr.xfr8BitInt(ard.d_ip.d_ip6[i]);
+ } else
+ throw MOADNSException("Unknown family for APL record");
+
+ processed += 4 + ard.d_afdlength;
+
+ ret->aplrdata.push_back(ard);
+ }
+
+ return ret;
+}
+
+// Parse a single APL <apitem>
+APLRDataElement APLRecordContent::parseAPLElement(const string& element) {
+ string record;
+ Netmask nm;
+ unsigned int bytes;
+ bool done_trimming;
+ APLRDataElement ard;
+
+ // Parse the optional leading ! (negate)
+ if (element.at(0) == '!') {
+ ard.d_n = true;
+ record = element.substr(1, element.length()-1);
+ } else {
+ ard.d_n = false;
+ record = element;
+ }
+
+ if (record.find("/") == string::npos) { // Required by RFC section 5
+ throw MOADNSException("Asked to decode '"+element+"' as an APL record, but missing subnet mask");
+ }
+
+
+ if (record.find("1:", 0) == 0) { // IPv4
+ uint32_t v4ip;
+
+ ard.d_family = APL_FAMILY_IPV4;
+
+ // Ensure that a mask is provided
+
+ // Read IPv4 string into a Netmask object
+ nm = Netmask(record.substr(2, record.length() - 2));
+ ard.d_prefix = nm.getBits();
+
+ if (nm.getNetwork().isIPv4() == 0)
+ throw MOADNSException("Asked to decode '"+element+"' as an APL v4 record");
+
+ // Section 4.1 of RFC 3123 (don't send trailing "0" bytes)
+ // Copy data; using array of bytes since we might end up truncating them in the packet
+ v4ip = ntohl(nm.getNetwork().sin4.sin_addr.s_addr);
+ memset(ard.d_ip.d_ip4, 0, sizeof(ard.d_ip.d_ip4));
+ bytes = 4; // Start by assuming we'll send 4 bytes
+ done_trimming = false;
+ for (int i=0; i<4; i++) {
+ ard.d_ip.d_ip4[3-i] = (v4ip & 255);
+ // Remove trailing "0" bytes from packet and update length
+ if ((v4ip & 255) == 0 and !done_trimming) {
+ bytes--;
+ } else {
+ done_trimming = true;
+ }
+ v4ip = v4ip >> 8;
+ }
+ ard.d_afdlength = bytes;
+
+ } else if (record.find("2:", 0) == 0) { // IPv6
+ ard.d_family = APL_FAMILY_IPV6;
+
+ // Parse IPv6 string into a Netmask object
+ nm = Netmask(record.substr(2, record.length() - 2));
+ ard.d_prefix = nm.getBits();
+
+ if (nm.getNetwork().isIPv6() == 0)
+ throw MOADNSException("Asked to decode '"+element+"' as an APL v6 record");
+
+ // Section 4.2 of RFC 3123 (don't send trailing "0" bytes)
+ // Remove trailing "0" bytes from packet and reduce length
+ memset(ard.d_ip.d_ip6, 0, sizeof(ard.d_ip.d_ip6));
+ bytes = 16; // Start by assuming we'll send 16 bytes
+ done_trimming = false;
+ for (int i=0; i<16; i++) {
+ ard.d_ip.d_ip6[15-i] = nm.getNetwork().sin6.sin6_addr.s6_addr[15-i];
+ if (nm.getNetwork().sin6.sin6_addr.s6_addr[15-i] == 0 and !done_trimming) {
+ // trailing 0 byte, update length
+ bytes--;
+ } else {
+ done_trimming = true;
+ }
+ }
+ ard.d_afdlength = bytes;
+
+ } else {
+ throw MOADNSException("Asked to encode '"+element+"' as an IPv6 APL record but got unknown Address Family");
+ }
+ return ard;
+
+}
+
+// Parse backend record (0, 1 or more <apitem>)
+std::shared_ptr<DNSRecordContent> APLRecordContent::make(const string& zone) {
+ APLRDataElement ard;
+ vector<string> elements;
+
+ auto ret=std::make_shared<APLRecordContent>();
+
+ boost::split(elements, zone, boost::is_any_of(" "));
+ for (std::vector<std::string>::iterator elem = elements.begin() ; elem != elements.end(); ++elem) {
+ if (!elem->empty()) {
+ ard = ret->parseAPLElement(*elem);
+ ret->aplrdata.push_back(ard);
+ }
+ }
+ return ret;
+}
+
+
+// DNSRecord to Packet conversion
+void APLRecordContent::toPacket(DNSPacketWriter& pw) {
+ for (std::vector<APLRDataElement>::iterator ard = aplrdata.begin() ; ard != aplrdata.end(); ++ard) {
+ pw.xfr16BitInt(ard->d_family);
+ pw.xfr8BitInt(ard->d_prefix);
+ pw.xfr8BitInt((ard->d_n << 7) + ard->d_afdlength);
+ if (ard->d_family == APL_FAMILY_IPV4) {
+ for (int i=0; i<ard->d_afdlength; i++) {
+ pw.xfr8BitInt(ard->d_ip.d_ip4[i]);
+ }
+ } else if (ard->d_family == APL_FAMILY_IPV6) {
+ for (int i=0; i<ard->d_afdlength; i++) {
+ pw.xfr8BitInt(ard->d_ip.d_ip6[i]);
+ }
+ }
+ }
+}
+
+// Decode record into string
+string APLRecordContent::getZoneRepresentation(bool noDot) const {
+ string s_n, s_family, output;
+ ComboAddress ca;
+ Netmask nm;
+
+ output = "";
+
+ for (std::vector<APLRDataElement>::const_iterator ard = aplrdata.begin() ; ard != aplrdata.end(); ++ard) {
+
+ // Negation flag
+ if (ard->d_n) {
+ s_n = "!";
+ } else {
+ s_n = "";
+ }
+
+ if (ard->d_family == APL_FAMILY_IPV4) { // IPv4
+ s_family = std::to_string(APL_FAMILY_IPV4);
+ ca = ComboAddress();
+ for (int i=0; i < 4; i++) {
+ ca.sin4.sin_addr.s_addr |= ard->d_ip.d_ip4[i] << (i*8);
+ }
+ } else if (ard->d_family == APL_FAMILY_IPV6) { // IPv6
+ s_family = std::to_string(APL_FAMILY_IPV6);
+ ca = ComboAddress();
+ ca.sin4.sin_family = AF_INET6;
+ for (int i=0; i < 16; i++) {
+ if (i < ard->d_afdlength) {
+ ca.sin6.sin6_addr.s6_addr[i] = ard->d_ip.d_ip6[i];
+ } else {
+ ca.sin6.sin6_addr.s6_addr[i] = 0;
+ }
+ }
+ } else {
+ throw MOADNSException("Asked to decode APL record but got unknown Address Family");
+ }
+
+ nm = Netmask(ca, ard->d_prefix);
+
+ output += s_n + s_family + ":" + nm.toString();
+ if (std::next(ard) != aplrdata.end())
+ output += " ";
+ }
+ return output;
+}
+
+/* APL end */
+
boilerplate_conv(TKEY, QType::TKEY,
conv.xfrName(d_algo);
conv.xfr32BitInt(d_inception);
MINFORecordContent::report();
URIRecordContent::report();
CAARecordContent::report();
+ APLRecordContent::report();
}
void reportAllTypes()
uint8_t d_eui64[8];
};
+#define APL_FAMILY_IPV4 1
+#define APL_FAMILY_IPV6 2
+typedef struct s_APLRDataElement {
+ uint16_t d_family;
+ uint8_t d_prefix;
+ bool d_n : 1;
+ unsigned int d_afdlength : 7;
+ union u_d_ip {
+ uint8_t d_ip4[4];
+ uint8_t d_ip6[16];
+ } d_ip;
+} APLRDataElement;
+class APLRecordContent : public DNSRecordContent
+{
+public:
+ APLRecordContent() {};
+ includeboilerplate(APL)
+private:
+ std::vector<APLRDataElement> aplrdata;
+ APLRDataElement parseAPLElement(const string &element);
+};
+
+
class TKEYRecordContent : public DNSRecordContent
{
public:
po::variables_map g_vm;
-const struct timeval operator*(float fact, const struct timeval& rhs)
+static const struct timeval operator*(float fact, const struct timeval& rhs)
{
// cout<<"In: "<<rhs.tv_sec<<" + "<<rhs.tv_usec<<"\n";
struct timeval ret;
}
bool g_pleaseQuit;
-void pleaseQuitHandler(int)
+static void pleaseQuitHandler(int)
{
g_pleaseQuit=true;
}
} s_idmanager;
-void setSocketBuffer(int fd, int optname, uint32_t size)
+static void setSocketBuffer(int fd, int optname, uint32_t size)
{
uint32_t psize=0;
socklen_t len=sizeof(psize);
-void WeOrigSlowQueriesDelta(int& weOutstanding, int& origOutstanding, int& weSlow, int& origSlow)
+static void WeOrigSlowQueriesDelta(int& weOutstanding, int& origOutstanding, int& weSlow, int& origSlow)
{
struct timeval now;
gettimeofday(&now, 0);
}
}
-void compactAnswerSet(MOADNSParser::answers_t orig, set<DNSRecord>& compacted)
+static void compactAnswerSet(MOADNSParser::answers_t orig, set<DNSRecord>& compacted)
{
for(MOADNSParser::answers_t::const_iterator i=orig.begin(); i != orig.end(); ++i)
if(i->first.d_place==DNSResourceRecord::ANSWER)
compacted.insert(i->first);
}
-bool isRcodeOk(int rcode)
+static bool isRcodeOk(int rcode)
{
return rcode==0 || rcode==3;
}
set<pair<DNSName,uint16_t> > s_origbetterset;
-bool isRootReferral(const MOADNSParser::answers_t& answers)
+static bool isRootReferral(const MOADNSParser::answers_t& answers)
{
if(answers.empty())
return false;
}
vector<uint32_t> flightTimes;
-void accountFlightTime(qids_t::const_iterator iter)
+static void accountFlightTime(qids_t::const_iterator iter)
{
if(flightTimes.empty())
flightTimes.resize(2050);
flightTimes[mdiff]++;
}
-uint64_t countLessThan(unsigned int msec)
+static uint64_t countLessThan(unsigned int msec)
{
uint64_t ret=0;
for(unsigned int i = 0 ; i < msec && i < flightTimes.size() ; ++i) {
return ret;
}
-void emitFlightTimes()
+static void emitFlightTimes()
{
uint64_t totals = countLessThan(flightTimes.size());
unsigned int limits[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes.size()};
}
}
-void measureResultAndClean(qids_t::const_iterator iter)
+static void measureResultAndClean(qids_t::const_iterator iter)
{
const QuestionData& qd=*iter;
accountFlightTime(iter);
std::unique_ptr<Socket> s_socket = nullptr;
-void receiveFromReference()
+static void receiveFromReference()
try
{
string packet;
exit(1);
}
-void pruneQids()
+static void pruneQids()
{
struct timeval now;
gettimeofday(&now, 0);
}
}
-void printStats(uint64_t origWaitingFor=0, uint64_t weWaitingFor=0)
+static void printStats(uint64_t origWaitingFor=0, uint64_t weWaitingFor=0)
{
format headerfmt ("%|9t|Questions - Pend. - Drop = Answers = (On time + Late) = (Err + Ok)\n");
format datafmt("%s%|9t|%d %|21t|%d %|29t|%d %|36t|%d %|47t|%d %|57t|%d %|66t|%d %|72t|%d\n");
}
-void houseKeeping()
+static void houseKeeping()
{
static timeval last;
return sent;
}
-void usage(po::options_description &desc) {
+static void usage(po::options_description &desc) {
cerr << "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl;
cerr << desc << "\n";
}
#include "namespaces.hh"
StatBag S;
-void usage() {
+static void usage() {
cerr<<"syntax: dnsscan INFILE ..."<<endl;
}
typedef map<QuestionIdentifier, QuestionData> statmap_t;
statmap_t statmap;
-unsigned int liveQuestions()
+static unsigned int liveQuestions()
{
unsigned int ret=0;
for(statmap_t::value_type& val : statmap) {
}
};
-void visitor(const StatNode* node, const StatNode::Stat& selfstat, const StatNode::Stat& childstat)
+static void visitor(const StatNode* node, const StatNode::Stat& selfstat, const StatNode::Stat& childstat)
{
// 20% servfails, >100 children, on average less than 2 copies of a query
// >100 different subqueries
}
}
-const struct timeval operator-(const struct pdns_timeval& lhs, const struct pdns_timeval& rhs)
+static const struct timeval operator-(const struct pdns_timeval& lhs, const struct pdns_timeval& rhs)
{
struct timeval a{lhs.tv_sec, static_cast<suseconds_t>(lhs.tv_usec)}, b{rhs.tv_sec, static_cast<suseconds_t>(rhs.tv_usec)};
return operator-(a,b);
#ifdef HAVE_P11KIT1
#include "pkcs11signers.hh"
#endif
-#include "gss_context.hh"
#include "misc.hh"
using namespace boost::assign;
std::string DNSCryptoKeyEngine::convertToISC() const
{
- storvector_t stormap = this->convertToISCVector();
+ storvector_t storvector = this->convertToISCVector();
ostringstream ret;
ret<<"Private-key-format: v1.2\n";
- for(const stormap_t::value_type& value : stormap) {
+ for(const storvector_t::value_type& value : storvector) {
if(value.first != "Algorithm" && value.first != "PIN" &&
value.first != "Slot" && value.first != "Engine" &&
value.first != "Label" && value.first != "PubLabel")
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;
string toSign = makeTSIGPayload(tsigprevious, reinterpret_cast<const char*>(pw.getContent().data()), pw.getContent().size(), tsigkeyname, trc, timersonly);
if (algo == TSIG_GSS) {
- if (!gss_add_signature(tsigkeyname, toSign, trc.d_mac)) {
- throw PDNSException(string("Could not add TSIG signature with algorithm 'gss-tsig' and key name '")+tsigkeyname.toLogString()+string("'"));
- }
+ throw PDNSException(string("Unsupported TSIG GSS algorithm ") + trc.d_algoName.toLogString());
} else {
trc.d_mac = calculateHMAC(tsigsecret, toSign, algo);
// trc.d_mac[0]++; // sabotage
tsigMsg = makeTSIGMessageFromTSIGPacket(packet, sigPos, tt.name, trc, previousMAC, timersOnly, dnsHeaderOffset);
if (algo == TSIG_GSS) {
- GssContext gssctx(tt.name);
- if (!gss_verify_signature(tt.name, tsigMsg, theirMAC)) {
- throw std::runtime_error("Signature with TSIG key '"+tt.name.toLogString()+"' failed to validate");
- }
+ throw std::runtime_error("Unsupported TSIG GSS algorithm " + trc.d_algoName.toLogString());
} else {
string ourMac = calculateHMAC(tt.secret, tsigMsg, algo);
#include "dnssecinfra.hh"
#include "dnsrecords.hh"
#include "ueberbackend.hh"
+#include "lock.hh"
using namespace ::boost::multi_index;
static uint64_t dbdnssecCacheSizes(const std::string& str);
static void clearAllCaches();
+ static bool clearKeyCache(const DNSName& name);
+ static bool clearMetaCache(const DNSName& name);
static void clearCaches(const DNSName& name);
bool doesDNSSEC();
static void setMaxEntries(size_t maxEntries);
+ typedef std::map<std::string, std::vector<std::string> > METAValues;
private:
-
+ int64_t d_metaCacheCleanAction{0};
struct KeyCacheEntry
{
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;
static keycache_t s_keycache;
static metacache_t s_metacache;
- static pthread_rwlock_t s_metacachelock;
- static pthread_rwlock_t s_keycachelock;
+ static int64_t s_metaCacheCleanActions;
+ static ReadWriteLock s_metacachelock;
+ static ReadWriteLock s_keycachelock;
static AtomicCounter s_ops;
static time_t s_last_prune;
static size_t s_maxEntries;
#include "statbag.hh"
extern StatBag S;
-static pthread_rwlock_t g_signatures_lock = PTHREAD_RWLOCK_INITIALIZER;
+static ReadWriteLock g_signatures_lock;
typedef map<pair<string, string>, string> signaturecache_t;
static signaturecache_t g_signatures;
static int g_cacheweekno;
enum Type {
// AUTH_QUERY is a DNS query message received from a resolver by an
- // authoritative name server, from the perspective of the authorative
+ // authoritative name server, from the perspective of the authoritative
// name server.
AUTH_QUERY = 1;
#include <boost/accumulators/statistics.hpp>
+#include <thread>
+
#include "dnsparser.hh"
#include "sstuff.hh"
#include "misc.hh"
AtomicCounter g_networkErrors, g_otherErrors, g_OK, g_truncates, g_authAnswers, g_timeOuts;
ComboAddress g_dest;
-unsigned int makeUsec(const struct timeval& tv)
+static unsigned int makeUsec(const struct timeval& tv)
{
return 1000000*tv.tv_sec + tv.tv_usec;
}
time_t answerSecond;
};
-void doQuery(BenchQuery* q)
+static void doQuery(BenchQuery* q)
try
{
vector<uint8_t> packet;
vector<BenchQuery> g_queries;
-static void* worker(void*)
+static void worker()
{
setThreadName("dnstcpb/worker");
for(;;) {
doQuery(&g_queries[pos]); // this is safe as long as nobody *inserts* to g_queries
}
- return 0;
}
-void usage(po::options_description &desc) {
+static void usage(po::options_description &desc) {
cerr<<"Syntax: dnstcpbench REMOTE [PORT] < QUERIES"<<endl;
cerr<<"Where QUERIES is one query per line, format: qname qtype, just 1 space"<<endl;
cerr<<desc<<endl;
}
- std::vector<pthread_t> workers(numworkers);
+ 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);
-
- for(unsigned int n = 0; n < numworkers; ++n) {
- pthread_create(&workers[n], 0, worker, 0);
+ fp.reset();
+
+ for (unsigned int n = 0; n < numworkers; ++n) {
+ workers.push_back(std::thread(worker));
}
- for(unsigned int n = 0; n < numworkers; ++n) {
- void* status;
- pthread_join(workers[n], &status);
+ for (auto& w : workers) {
+ w.join();
}
using namespace boost::accumulators;
};
-void usage() {
+static void usage() {
cerr<<"Syntax: dnswasher INFILE1 [INFILE2..] OUTFILE"<<endl;
}
private:
uint16_t lookupName(const DNSName& name, uint16_t* matchlen);
vector<uint16_t> d_namepositions;
- // We declare 1 uint_16 in the public section, these 3 align on a 8-byte boundry
+ // We declare 1 uint_16 in the public section, these 3 align on a 8-byte boundary
uint16_t d_sor;
uint16_t d_rollbackmarker; // start of last complete packet, for rollback
HTTPVersionStats d_http1Stats;
HTTPVersionStats d_http2Stats;
+ uint32_t d_internalPipeBufferSize{0};
bool d_sendCacheControlHeaders{true};
+ bool d_trustForwardedForHeader{false};
time_t getTicketsKeyRotationDelay() const
{
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
static void tcpAcceptor(const ComboAddress local)
{
Socket tcpSocket(local.sin4.sin_family, SOCK_STREAM);
-#ifdef SO_REUSEPORT
- int one=1;
- if(setsockopt(tcpSocket.getHandle(), SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
- unixDie("setsockopt for REUSEPORT");
-#endif
-
+ setReusePort(tcpSocket.getHandle());
tcpSocket.bind(local);
tcpSocket.listen(1024);
}
Socket s(local.sin4.sin_family, SOCK_DGRAM);
-#ifdef SO_REUSEPORT
- int one=1;
- if(setsockopt(s.getHandle(), SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
- unixDie("setsockopt for REUSEPORT");
-#endif
-
+ setReusePort(s.getHandle());
s.bind(local);
cout<<"Bound to UDP "<<local.toStringWithPort()<<endl;
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)";
- random_shuffle(di.masters.begin(), di.masters.end());
- Communicator.addSuckRequest(domain, di.masters.front());
- return "Added retrieval request for '"+domain.toString()+"' from master "+di.masters.front().toLogString();
+ shuffle(di.masters.begin(), di.masters.end(), pdns::dns_random_engine());
+ 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)
string DLCurrentConfigHandler(const vector<string>&parts, Utility::pid_t ppid);
string DLListZones(const vector<string>&parts, Utility::pid_t ppid);
string DLTokenLogin(const vector<string>&parts, Utility::pid_t ppid);
-uint64_t udpErrorStats(const std::string& str);
#include <sys/types.h>
#include <sys/un.h>
#include <dlfcn.h>
-#include <pthread.h>
#include <unistd.h>
#include <boost/algorithm/string.hpp>
#include <fcntl.h>
#include <unistd.h>
#include <boost/algorithm/string.hpp>
+#include <thread>
+
#include "misc.hh"
#include "dns.hh"
#include "arguments.hh"
void DynListener::go()
{
d_ppid=getpid();
- pthread_create(&d_tid,0,&DynListener::theListenerHelper,this);
-}
-
-void *DynListener::theListenerHelper(void *p)
-{
- setThreadName("pdns/ctrlListen");
- DynListener *us=static_cast<DynListener *>(p);
- us->theListener();
- g_log<<Logger::Error<<"Control listener aborted, please file a bug!"<<endl;
- return 0;
+ std::thread listener(std::bind(&DynListener::theListener,this));
+ listener.detach();
}
string DynListener::getLine()
void DynListener::theListener()
{
+ setThreadName("pdns/ctrlListen");
+
try {
signal(SIGPIPE,SIG_IGN);
#pragma once
#include <string>
#include <vector>
-#include <pthread.h>
#include <sys/types.h>
#include <errno.h>
#include <iostream>
~DynListener();
void go();
void theListener();
- static void *theListenerHelper(void *p);
typedef string g_funk_t(const vector<string> &parts, Utility::pid_t ppid); // guido!
typedef struct { g_funk_t *func; string args; string usage; } g_funkwithusage_t;
NetmaskGroup d_tcprange;
int d_s{-1};
int d_client{-1};
- pthread_t d_tid{0};
bool d_nonlocal;
bool d_tcp{false};
pid_t d_ppid{0};
#include "ednsoptions.hh"
#include "iputils.hh"
+bool getNextEDNSOption(const char* data, size_t dataLen, uint16_t& optionCode, uint16_t& optionLen)
+{
+ if (data == nullptr || dataLen < (sizeof(uint16_t) + sizeof(uint16_t))) {
+ return false;
+ }
+
+ size_t pos = 0;
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+
+ optionCode = (static_cast<uint16_t>(p[pos]) * 256) + p[pos + 1];
+ pos += EDNS_OPTION_CODE_SIZE;
+
+ optionLen = (static_cast<uint16_t>(p[pos]) * 256) + p[pos + 1];
+ pos += EDNS_OPTION_LENGTH_SIZE;
+
+ return true;
+}
+
/* extract a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
int getEDNSOption(char* optRR, const size_t len, uint16_t wantedOption, char ** optionValue, size_t * optionValueSize)
{
while(len >= (pos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) &&
rdLen >= (rdPos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) {
- const uint16_t optionCode = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
+ uint16_t optionCode;
+ uint16_t optionLen;
+ if (!getNextEDNSOption(optRR + pos, len-pos, optionCode, optionLen)) {
+ break;
+ }
+
pos += EDNS_OPTION_CODE_SIZE;
rdPos += EDNS_OPTION_CODE_SIZE;
- const uint16_t optionLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
pos += EDNS_OPTION_LENGTH_SIZE;
rdPos += EDNS_OPTION_LENGTH_SIZE;
- if (optionLen > (rdLen - rdPos) || optionLen > (len - pos))
+
+ if (optionLen > (rdLen - rdPos) || optionLen > (len - pos)) {
return EINVAL;
+ }
if (optionCode == wantedOption) {
*optionValue = optRR + pos - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE);
int getEDNSOptions(const char* optRR, size_t len, EDNSOptionViewMap& options);
/* extract all EDNS0 options from the content (so after rdLen) of the OPT RR */
bool getEDNSOptionsFromContent(const std::string& content, std::vector<std::pair<uint16_t, std::string>>& options);
+/* parse the next EDNS option and the return the code and length. data should point to the beginning of the option code, dataLen should be maximum length of the data (minimum of remaining size in packet and remaining size in rdata) */
+bool getNextEDNSOption(const char* data, size_t dataLen, uint16_t& optionCode, uint16_t& optionLen);
void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res);
#include "namespaces.hh"
#include "dnsrecords.hh"
+// Names below are RPZ Actions and end with a dot (except "Local Data")
+static const std::string rpzDropName("rpz-drop."),
+ rpzTruncateName("rpz-tcp-only."),
+ rpzNoActionName("rpz-passthru."),
+ rpzCustomName("Local Data");
+
+// Names below are (part) of RPZ Trigger names and do NOT end with a dot
+static const std::string rpzClientIPName("rpz-client-ip"),
+ rpzIPName("rpz-ip"),
+ rpzNSDnameName("rpz-nsdname"),
+ rpzNSIPName("rpz-nsip");
+
DNSFilterEngine::DNSFilterEngine()
{
}
iter = polmap.find(g_wildcarddnsname+s);
if(iter != polmap.end()) {
pol=iter->second;
+ pol.d_trigger = iter->first;
+ pol.d_hit = qname.toStringNoDot();
return true;
}
}
const auto& it = polmap.find(qname);
if (it != polmap.end()) {
pol = it->second;
+ pol.d_trigger = qname;
+ pol.d_hit = qname.toStringNoDot();
return true;
}
bool allEmpty = true;
for (const auto& z : d_zones) {
bool enabled = true;
- const auto zoneName = z->getName();
- if (z->getPriority() >= pol.d_priority) {
+ const auto& zoneName = z->getName();
+ if (z->getPriority() >= pol.getPriority()) {
enabled = false;
}
- else if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+ else if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
enabled = false;
}
else {
}
if (z->findExactNSPolicy(qname, pol)) {
// cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
+ pol.d_trigger = qname;
+ pol.d_trigger.appendRawLabel(rpzNSDnameName);
+ pol.d_hit = qname.toStringNoDot();
return true;
}
for (const auto& wc : wcNames) {
if (z->findExactNSPolicy(wc, pol)) {
// cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
+ pol.d_trigger = wc;
+ pol.d_trigger.appendRawLabel(rpzNSDnameName);
+ pol.d_hit = qname.toStringNoDot();
return true;
}
}
{
// cout<<"Got question for nameserver IP "<<address.toString()<<endl;
for(const auto& z : d_zones) {
- if (z->getPriority() >= pol.d_priority) {
+ if (z->getPriority() >= pol.getPriority()) {
break;
}
- const auto zoneName = z->getName();
- if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+ const auto& zoneName = z->getName();
+ if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
continue;
}
if(z->findNSIPPolicy(address, pol)) {
// cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
+ // XXX should use ns RPZ
+ pol.d_trigger = Zone::maskToRPZ(address);
+ pol.d_trigger.appendRawLabel(rpzNSIPName);
+ pol.d_hit = address.toString();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DNSFilterEngine::getClientPolicy(const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
+{
+ // cout<<"Got question from "<<ca.toString()<<endl;
+ for (const auto& z : d_zones) {
+ if (z->getPriority() >= pol.getPriority()) {
+ break;
+ }
+ const auto& zoneName = z->getName();
+ if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
+ continue;
+ }
+
+ if (z->findClientPolicy(ca, pol)) {
+ // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
return true;
}
}
return false;
}
-bool DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
+bool DNSFilterEngine::getQueryPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
{
- // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
+ //cerr<<"Got question for "<<qname<<' '<< pol.getPriority()<< endl;
std::vector<bool> zoneEnabled(d_zones.size());
size_t count = 0;
bool allEmpty = true;
for (const auto& z : d_zones) {
bool enabled = true;
- if (z->getPriority() >= pol.d_priority) {
+ if (z->getPriority() >= pol.getPriority()) {
enabled = false;
} else {
- const auto zoneName = z->getName();
- if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
+ const auto& zoneName = z->getName();
+ if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
enabled = false;
}
else {
- if (z->hasQNamePolicies() || z->hasClientPolicies()) {
+ if (z->hasQNamePolicies()) {
allEmpty = false;
}
else {
continue;
}
- if (z->findClientPolicy(ca, pol)) {
- // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
- return true;
- }
-
if (z->findExactQNamePolicy(qname, pol)) {
// cerr<<"Had a hit on the name of the query"<<endl;
+ pol.d_trigger = qname;
+ pol.d_hit = qname.toStringNoDot();
return true;
}
for (const auto& wc : wcNames) {
if (z->findExactQNamePolicy(wc, pol)) {
// cerr<<"Had a hit on the name of the query"<<endl;
+ pol.d_trigger = wc;
+ pol.d_hit = qname.toStringNoDot();
return true;
}
}
}
bool DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
+{
+ for (const auto& record : records) {
+ if (getPostPolicy(record, discardedPolicies, pol)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool DNSFilterEngine::getPostPolicy(const DNSRecord& record, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
{
ComboAddress ca;
- for (const auto& r : records) {
- if (r.d_place != DNSResourceRecord::ANSWER)
- continue;
- if (r.d_type == QType::A) {
- if (auto rec = getRR<ARecordContent>(r)) {
- ca = rec->getCA();
- }
+ if (record.d_place != DNSResourceRecord::ANSWER) {
+ return false;
+ }
+
+ if (record.d_type == QType::A) {
+ if (auto rec = getRR<ARecordContent>(record)) {
+ ca = rec->getCA();
}
- else if(r.d_type == QType::AAAA) {
- if (auto rec = getRR<AAAARecordContent>(r)) {
- ca = rec->getCA();
- }
+ }
+ else if(record.d_type == QType::AAAA) {
+ if (auto rec = getRR<AAAARecordContent>(record)) {
+ ca = rec->getCA();
}
- else
- continue;
+ }
+ else {
+ return false;
+ }
- for (const auto& z : d_zones) {
- if (z->getPriority() >= pol.d_priority) {
- break;
- }
- const auto zoneName = z->getName();
- if (zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
- continue;
- }
+ for (const auto& z : d_zones) {
+ if (z->getPriority() >= pol.getPriority()) {
+ break;
+ }
+ const auto& zoneName = z->getName();
+ if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
+ return false;
+ }
- if (z->findResponsePolicy(ca, pol)) {
- return true;
- }
+ if (z->findResponsePolicy(ca, pol)) {
+ pol.d_trigger = Zone::maskToRPZ(ca);
+ pol.d_trigger.appendRawLabel(rpzIPName);
+ pol.d_hit = ca.toString();
+ return true;
}
}
+
return false;
}
d_zones.resize(zone+1);
}
-void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol)
+void DNSFilterEngine::Zone::addNameTrigger(std::unordered_map<DNSName,Policy>& map, const DNSName& n, Policy&& pol, bool ignoreDuplicate, PolicyType ptype)
{
- pol.d_name = d_name;
- pol.d_type = PolicyType::ClientIP;
- d_qpolAddr.insert(nm).second=std::move(pol);
-}
+ auto it = map.find(n);
-void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol)
-{
- pol.d_name = d_name;
- pol.d_type = PolicyType::ResponseIP;
- d_postpolAddr.insert(nm).second=std::move(pol);
-}
-
-void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool ignoreDuplicate)
-{
- auto it = d_qpolName.find(n);
-
- if (it != d_qpolName.end()) {
+ if (it != map.end()) {
auto& existingPol = it->second;
if (pol.d_kind != PolicyKind::Custom && !ignoreDuplicate) {
- throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(pol.d_kind) + " but a policy of kind " + getKindToString(existingPol.d_kind) + " already exists for the following QName: " + n.toLogString());
+ throw std::runtime_error("Adding a " + getTypeToString(ptype) + "-based filter policy of kind " + getKindToString(pol.d_kind) + " but a policy of kind " + getKindToString(existingPol.d_kind) + " already exists for the following name: " + n.toLogString());
}
if (existingPol.d_kind != PolicyKind::Custom && ignoreDuplicate) {
- throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(existingPol.d_kind) + " but there was already an existing policy for the following QName: " + n.toLogString());
+ throw std::runtime_error("Adding a " + getTypeToString(ptype) + "-based filter policy of kind " + getKindToString(existingPol.d_kind) + " but there was already an existing policy for the following name: " + n.toLogString());
}
existingPol.d_custom.reserve(existingPol.d_custom.size() + pol.d_custom.size());
std::move(pol.d_custom.begin(), pol.d_custom.end(), std::back_inserter(existingPol.d_custom));
}
else {
- auto& qpol = d_qpolName.insert({n, std::move(pol)}).first->second;
- qpol.d_name = d_name;
- qpol.d_type = PolicyType::QName;
+ auto& qpol = map.insert({n, std::move(pol)}).first->second;
+ qpol.d_zoneData = d_zoneData;
+ qpol.d_type = ptype;
}
}
-void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol)
+void DNSFilterEngine::Zone::addNetmaskTrigger(NetmaskTree<Policy>& nmt, const Netmask& nm, Policy&& pol, bool ignoreDuplicate, PolicyType ptype)
{
- pol.d_name = d_name;
- pol.d_type = PolicyType::NSDName;
- d_propolName.insert({n, std::move(pol)});
-}
+ bool exists = nmt.has_key(nm);
-void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol)
-{
- pol.d_name = d_name;
- pol.d_type = PolicyType::NSIP;
- d_propolNSAddr.insert(nm).second = std::move(pol);
-}
+ if (exists) {
+ // XXX NetMaskTree's node_type has a non-const second, but lookup() returns a const node_type *, so we cannot modify second
+ // Should look into making lookup) return a non-const node_type *...
+ auto& existingPol = const_cast<Policy&>(nmt.lookup(nm)->second);
-bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, const Policy& pol)
-{
- d_qpolAddr.erase(nm);
- return true;
-}
+ if (pol.d_kind != PolicyKind::Custom && !ignoreDuplicate) {
+ throw std::runtime_error("Adding a " + getTypeToString(ptype) + "-based filter policy of kind " + getKindToString(pol.d_kind) + " but a policy of kind " + getKindToString(existingPol.d_kind) + " already exists for the following netmask: " + nm.toString());
+ }
-bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, const Policy& pol)
-{
- d_postpolAddr.erase(nm);
- return true;
+ if (existingPol.d_kind != PolicyKind::Custom && ignoreDuplicate) {
+ throw std::runtime_error("Adding a " + getTypeToString(ptype) + "-based filter policy of kind " + getKindToString(existingPol.d_kind) + " but there was already an existing policy for the following netmask: " + nm.toString());
+ }
+
+ existingPol.d_custom.reserve(existingPol.d_custom.size() + pol.d_custom.size());
+
+ std::move(pol.d_custom.begin(), pol.d_custom.end(), std::back_inserter(existingPol.d_custom));
+ }
+ else {
+ pol.d_zoneData = d_zoneData;
+ pol.d_type = ptype;
+ nmt.insert(nm).second = std::move(pol);
+ }
}
-bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, const Policy& pol)
+bool DNSFilterEngine::Zone::rmNameTrigger(std::unordered_map<DNSName,Policy>& map, const DNSName& n, const Policy& pol)
{
- auto found = d_qpolName.find(n);
- if (found == d_qpolName.end()) {
+ auto found = map.find(n);
+ if (found == map.end()) {
return false;
}
auto& existing = found->second;
if (existing.d_kind != DNSFilterEngine::PolicyKind::Custom) {
- d_qpolName.erase(found);
+ map.erase(found);
return true;
}
/* for custom types, we might have more than one type,
and then we need to remove only the right ones. */
- if (existing.d_custom.size() <= 1) {
- d_qpolName.erase(found);
+ bool result = false;
+ for (auto& toRemove : pol.d_custom) {
+ for (auto it = existing.d_custom.begin(); it != existing.d_custom.end(); ++it) {
+ if (**it == *toRemove) {
+ existing.d_custom.erase(it);
+ result = true;
+ break;
+ }
+ }
+ }
+
+ // No records left for this trigger?
+ if (existing.d_custom.size() == 0) {
+ map.erase(found);
return true;
}
+ return result;
+}
+
+bool DNSFilterEngine::Zone::rmNetmaskTrigger(NetmaskTree<Policy>& nmt, const Netmask& nm, const Policy& pol)
+{
+ bool found = nmt.has_key(nm);
+ if (!found) {
+ return false;
+ }
+
+ // XXX NetMaskTree's node_type has a non-const second, but lookup() returns a const node_type *, so we cannot modify second
+ // Should look into making lookup) return a non-const node_type *...
+ auto& existing = const_cast<Policy&>(nmt.lookup(nm)->second);
+ if (existing.d_kind != DNSFilterEngine::PolicyKind::Custom) {
+ nmt.erase(nm);
+ return true;
+ }
+
+ /* for custom types, we might have more than one type,
+ and then we need to remove only the right ones. */
+
bool result = false;
for (auto& toRemove : pol.d_custom) {
for (auto it = existing.d_custom.begin(); it != existing.d_custom.end(); ++it) {
}
}
+ // No records left for this trigger?
+ if (existing.d_custom.size() == 0) {
+ nmt.erase(nm);
+ return true;
+ }
+
return result;
}
+void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol, bool ignoreDuplicate)
+{
+ addNetmaskTrigger(d_qpolAddr, nm, std::move(pol), ignoreDuplicate, PolicyType::ClientIP);
+}
+
+void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol, bool ignoreDuplicate)
+{
+ addNetmaskTrigger(d_postpolAddr, nm, std::move(pol), ignoreDuplicate, PolicyType::ResponseIP);
+}
+
+void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool ignoreDuplicate)
+{
+ addNameTrigger(d_qpolName, n, std::move(pol), ignoreDuplicate, PolicyType::QName);
+}
+
+void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol, bool ignoreDuplicate)
+{
+ addNameTrigger(d_propolName, n, std::move(pol), ignoreDuplicate, PolicyType::NSDName);
+}
+
+void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol, bool ignoreDuplicate)
+{
+ addNetmaskTrigger(d_propolNSAddr, nm, std::move(pol), ignoreDuplicate, PolicyType::NSIP);
+}
+
+bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, const Policy& pol)
+{
+ return rmNetmaskTrigger(d_qpolAddr, nm, pol);
+}
+
+bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, const Policy& pol)
+{
+ return rmNetmaskTrigger(d_postpolAddr, nm, pol);
+}
+
+bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, const Policy& pol)
+{
+ return rmNameTrigger(d_qpolName, n, pol);
+}
+
bool DNSFilterEngine::Zone::rmNSTrigger(const DNSName& n, const Policy& pol)
{
- d_propolName.erase(n); // XXX verify policy matched? =pol;
- return true;
+ return rmNameTrigger(d_propolName, n, pol);
}
bool DNSFilterEngine::Zone::rmNSIPTrigger(const Netmask& nm, const Policy& pol)
{
- d_propolNSAddr.erase(nm);
- return true;
+ return rmNetmaskTrigger(d_propolNSAddr, nm, pol);
+}
+
+std::string DNSFilterEngine::Policy::getLogString() const {
+ return ": RPZ Hit; PolicyName=" + getName() + "; Trigger=" + d_trigger.toLogString() + "; Hit=" + d_hit + "; Type=" + getTypeToString(d_type) + "; Kind=" + getKindToString(d_kind);
}
DNSRecord DNSFilterEngine::Policy::getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const
std::string DNSFilterEngine::getKindToString(DNSFilterEngine::PolicyKind kind)
{
- static const DNSName drop("rpz-drop."), truncate("rpz-tcp-only."), noaction("rpz-passthru.");
- static const DNSName rpzClientIP("rpz-client-ip"), rpzIP("rpz-ip"),
- rpzNSDname("rpz-nsdname"), rpzNSIP("rpz-nsip.");
- static const std::string rpzPrefix("rpz-");
+ //static const std::string rpzPrefix("rpz-");
switch(kind) {
case DNSFilterEngine::PolicyKind::NoAction:
- return noaction.toString();
+ return rpzNoActionName;
case DNSFilterEngine::PolicyKind::Drop:
- return drop.toString();
+ return rpzDropName;
case DNSFilterEngine::PolicyKind::NXDOMAIN:
return g_rootdnsname.toString();
case PolicyKind::NODATA:
return g_wildcarddnsname.toString();
case DNSFilterEngine::PolicyKind::Truncate:
- return truncate.toString();
+ return rpzTruncateName;
+ case DNSFilterEngine::PolicyKind::Custom:
+ return rpzCustomName;
default:
throw std::runtime_error("Unexpected DNSFilterEngine::Policy kind");
}
}
for (const auto& pair : d_propolName) {
- dumpNamedPolicy(fp, pair.first + DNSName("rpz-nsdname.") + d_domain, pair.second);
+ dumpNamedPolicy(fp, pair.first + DNSName(rpzNSDnameName) + d_domain, pair.second);
}
- for (const auto pair : d_qpolAddr) {
- dumpAddrPolicy(fp, pair.first, DNSName("rpz-client-ip.") + d_domain, pair.second);
+ for (const auto& pair : d_qpolAddr) {
+ dumpAddrPolicy(fp, pair.first, DNSName(rpzClientIPName) + d_domain, pair.second);
}
- for (const auto pair : d_propolNSAddr) {
- dumpAddrPolicy(fp, pair.first, DNSName("rpz-nsip.") + d_domain, pair.second);
+ for (const auto& pair : d_propolNSAddr) {
+ dumpAddrPolicy(fp, pair.first, DNSName(rpzNSIPName) + d_domain, pair.second);
}
- for (const auto pair : d_postpolAddr) {
- dumpAddrPolicy(fp, pair.first, DNSName("rpz-ip.") + d_domain, pair.second);
+ for (const auto& pair : d_postpolAddr) {
+ dumpAddrPolicy(fp, pair.first, DNSName(rpzIPName) + d_domain, pair.second);
+ }
+}
+
+void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags)
+{
+ for (const auto& tag : newTags) {
+ tags.insert(tag);
}
}
static std::string getKindToString(PolicyKind kind);
static std::string getTypeToString(PolicyType type);
+ struct PolicyZoneData
+ {
+ /* shared by all the policies from a single zone */
+ std::unordered_set<std::string> d_tags;
+ std::string d_name;
+ Priority d_priority{maximumPriority};
+ bool d_policyOverridesGettag{true};
+ };
+
struct Policy
{
- Policy(): d_name(nullptr), d_ttl(0), d_priority(maximumPriority), d_kind(PolicyKind::NoAction), d_type(PolicyType::None)
+ Policy(): d_ttl(0), d_kind(PolicyKind::NoAction), d_type(PolicyType::None)
{
}
- Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr<std::string> name=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& custom={}): d_custom(custom), d_name(name), d_ttl(ttl), d_priority(maximumPriority), d_kind(kind), d_type(type)
+ Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr<PolicyZoneData> data=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& custom={}): d_custom(custom), d_zoneData(data), d_ttl(ttl), d_kind(kind), d_type(type)
{
}
{
return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom;
}
+
+ const std::string& getName() const
+ {
+ static const std::string notSet;
+ if (d_zoneData) {
+ return d_zoneData->d_name;
+ }
+ return notSet;
+ }
+
+ void setName(const std::string& name)
+ {
+ /* until now the PolicyZoneData was shared,
+ we now need to copy it, then write to it */
+ std::shared_ptr<PolicyZoneData> newZoneData;
+ if (d_zoneData) {
+ newZoneData = std::make_shared<PolicyZoneData>(*d_zoneData);
+ }
+ else {
+ newZoneData = std::make_shared<PolicyZoneData>();
+ }
+ newZoneData->d_name = name;
+ d_zoneData = newZoneData;
+ }
+
+ const std::unordered_set<std::string>& getTags() const
+ {
+ static const std::unordered_set<std::string> notSet;
+ if (d_zoneData) {
+ return d_zoneData->d_tags;
+ }
+ return notSet;
+ }
+
+ Priority getPriority() const
+ {
+ static Priority notSet = maximumPriority;
+ if (d_zoneData) {
+ return d_zoneData->d_priority;
+ }
+ return notSet;
+ }
+
+ bool policyOverridesGettag() const {
+ if (d_zoneData) {
+ return d_zoneData->d_policyOverridesGettag;
+ }
+ return true;
+ }
+
+ bool wasHit() const
+ {
+ return (d_type != DNSFilterEngine::PolicyType::None && d_kind != DNSFilterEngine::PolicyKind::NoAction);
+ }
+
+ std::string getLogString() const;
std::vector<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const;
std::vector<DNSRecord> getRecords(const DNSName& qname) const;
std::vector<std::shared_ptr<DNSRecordContent>> d_custom;
- std::shared_ptr<std::string> d_name; // the name of the policy
+ std::shared_ptr<PolicyZoneData> d_zoneData{nullptr};
+ DNSName d_trigger;
+ string d_hit;
/* Yup, we are currently using the same TTL for every record for a given name */
int32_t d_ttl;
- Priority d_priority;
PolicyKind d_kind;
PolicyType d_type;
class Zone {
public:
+ Zone(): d_zoneData(std::make_shared<PolicyZoneData>())
+ {
+ }
+
void clear()
{
d_qpolAddr.clear();
}
void setName(const std::string& name)
{
- d_name = std::make_shared<std::string>(name);
+ d_zoneData->d_name = name;
}
void setDomain(const DNSName& domain)
{
{
d_refresh = refresh;
}
- const std::shared_ptr<std::string> getName() const
+ void setTags(std::unordered_set<std::string>&& tags)
{
- return d_name;
+ 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;
}
DNSName getDomain() const
void dump(FILE * fp) const;
- void addClientTrigger(const Netmask& nm, Policy&& pol);
- void addQNameTrigger(const DNSName& nm, Policy&& pol, bool ignoreDuplicate=false);
- void addNSTrigger(const DNSName& dn, Policy&& pol);
- void addNSIPTrigger(const Netmask& nm, Policy&& pol);
- void addResponseTrigger(const Netmask& nm, Policy&& pol);
+ void addClientTrigger(const Netmask& nm, Policy&& pol, bool ignoreDuplicate = false);
+ void addQNameTrigger(const DNSName& nm, Policy&& pol, bool ignoreDuplicate = false);
+ void addNSTrigger(const DNSName& dn, Policy&& pol, bool ignoreDuplicate = false);
+ void addNSIPTrigger(const Netmask& nm, Policy&& pol, bool ignoreDuplicate = false);
+ void addResponseTrigger(const Netmask& nm, Policy&& pol, bool ignoreDuplicate = false);
bool rmClientTrigger(const Netmask& nm, const Policy& pol);
bool rmQNameTrigger(const DNSName& nm, const Policy& pol);
return !d_postpolAddr.empty();
}
Priority getPriority() const {
- return d_priority;
+ return d_zoneData->d_priority;
}
void setPriority(Priority p) {
- d_priority = p;
- for (auto& pair : d_qpolName) {
- pair.second.d_priority = p;
- }
- for (auto& pair : d_propolName) {
- pair.second.d_priority = p;
- }
- for (auto& pair : d_qpolAddr) {
- pair.second.d_priority = p;
- }
- for (auto& pair : d_propolNSAddr) {
- pair.second.d_priority = p;
- }
- for (auto& pair : d_postpolAddr) {
- pair.second.d_priority = p;
- }
+ d_zoneData->d_priority = p;
}
- private:
+
static DNSName maskToRPZ(const Netmask& nm);
+
+ private:
+ void addNameTrigger(std::unordered_map<DNSName,Policy>& map, const DNSName& n, Policy&& pol, bool ignoreDuplicate, PolicyType ptype);
+ void addNetmaskTrigger(NetmaskTree<Policy>& nmt, const Netmask& nm, Policy&& pol, bool ignoreDuplicate, PolicyType ptype);
+ bool rmNameTrigger(std::unordered_map<DNSName,Policy>& map, const DNSName& n, const Policy& pol);
+ bool rmNetmaskTrigger(NetmaskTree<Policy>& nmt, const Netmask& nm, const Policy& pol);
+
+ private:
static bool findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
static bool findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
static void dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol);
NetmaskTree<Policy> d_propolNSAddr; // NSIP (RPZ)
NetmaskTree<Policy> d_postpolAddr; // IP trigger (RPZ)
DNSName d_domain;
- std::shared_ptr<std::string> d_name;
+ std::shared_ptr<PolicyZoneData> d_zoneData{nullptr};
uint32_t d_serial{0};
uint32_t d_refresh{0};
- Priority d_priority{0};
};
DNSFilterEngine();
}
const std::shared_ptr<Zone> getZone(const std::string& name) const
{
- for (const auto zone : d_zones) {
+ for (const auto& zone : d_zones) {
const auto& zName = zone->getName();
- if (zName && *zName == name) {
+ if (zName == name) {
return zone;
}
}
}
}
- bool getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
+ bool getQueryPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
+ bool getClientPolicy(const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
bool getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
bool getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
bool getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
+ bool getPostPolicy(const DNSRecord& record, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const;
// A few convenience methods for the unit test code
- Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
+ Policy getQueryPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
+ Policy policy;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
+ getQueryPolicy(qname, discardedPolicies, policy);
+ return policy;
+ }
+
+ Policy getClientPolicy(const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
- getQueryPolicy(qname, nm, discardedPolicies, policy);
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
+ getClientPolicy(ca, discardedPolicies, policy);
return policy;
}
Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
getProcessingPolicy(qname, discardedPolicies, policy);
return policy;
}
Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
getProcessingPolicy(address, discardedPolicies, policy);
return policy;
}
Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const {
Policy policy;
- policy.d_priority = p;
+ policy.d_zoneData = std::make_shared<PolicyZoneData>();
+ policy.d_zoneData->d_priority = p;
getPostPolicy(records, discardedPolicies, policy);
return policy;
}
void assureZones(size_t zone);
vector<std::shared_ptr<Zone>> d_zones;
};
+
+void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags);
#include "dnsdist-cache.hh"
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size > std::numeric_limits<uint16_t>::max()) {
}
/* dnsdist's version */
+ DNSDistPacketCache pcSkipCookies(10000);
+ pcSkipCookies.setECSParsingEnabled(true);
+ pcSkipCookies.setCookieHashing(false);
+
+ DNSDistPacketCache pcHashCookies(10000);
+ pcHashCookies.setECSParsingEnabled(true);
+ pcHashCookies.setCookieHashing(true);
+
try {
uint16_t qtype;
uint16_t qclass;
unsigned int consumed;
- DNSName qname(reinterpret_cast<const char*>(data), size, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
- DNSDistPacketCache::getKey(qname.toString(), consumed, data, size, false);
+ const DNSName qname(reinterpret_cast<const char*>(data), size, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
+ pcSkipCookies.getKey(qname.getStorage(), consumed, data, size, false);
+ pcHashCookies.getKey(qname.getStorage(), consumed, data, size, false);
boost::optional<Netmask> subnet;
DNSDistPacketCache::getClientSubnet(reinterpret_cast<const char*>(data), consumed, size, subnet);
}
reportAllTypes();
}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static bool initialized = false;
StatBag S;
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size > std::numeric_limits<uint16_t>::max()) {
+ if (size > std::numeric_limits<uint16_t>::max() || size < sizeof(dnsheader)) {
return 0;
}
/* auth's version */
try {
- PacketCache::canHashPacket(input);
+ static const std::unordered_set<uint16_t> optionsToIgnore{ EDNSOptionCode::COOKIE };
+
+ PacketCache::canHashPacket(input, false);
+ DNSName qname(input.data(), input.size(), sizeof(dnsheader), false);
+ PacketCache::queryMatches(input, input, qname, optionsToIgnore);
}
catch(const std::exception& e) {
}
/* recursor's version */
try {
- uint16_t ecsBegin = 0;
- uint16_t ecsEnd = 0;
- PacketCache::canHashPacket(input, &ecsBegin, &ecsEnd);
+ static const std::unordered_set<uint16_t> optionsToIgnore{ EDNSOptionCode::COOKIE, EDNSOptionCode::ECS };
+
+ PacketCache::canHashPacket(input, true);
+ DNSName qname(input.data(), input.size(), sizeof(dnsheader), false);
+ PacketCache::queryMatches(input, input, qname, optionsToIgnore);
}
catch(const std::exception& e) {
}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "proxy-protocol.hh"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+
+ std::vector<ProxyProtocolValue> values;
+ ComboAddress source;
+ ComboAddress destination;
+ bool proxy = false;
+ bool tcp = false;
+
+ try {
+ parseProxyHeader(std::string(reinterpret_cast<const char*>(data), size), proxy, source, destination, tcp, values);
+ }
+ catch(const std::exception& e) {
+ }
+ catch(const PDNSException& e) {
+ }
+
+ return 0;
+}
reportAllTypes();
}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static bool initialized = false;
+++ /dev/null
-/*
- * This file is part of PowerDNS or dnsdist.
- * Copyright -- PowerDNS.COM B.V. and its contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * In addition, for the avoidance of any doubt, permission is granted to
- * link this program with OpenSSL and to (re)distribute the binaries
- * produced as the result of such linking.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include <map>
-#include <string>
-#include "namespaces.hh"
-#include "dns.hh"
-#include "dnsparser.hh"
-#include "dnspacket.hh"
-#include "dnsrecords.hh"
-#include "logger.hh"
-#include "lock.hh"
-#include "arguments.hh"
-
-#include <boost/shared_ptr.hpp>
-#include <boost/make_shared.hpp>
-#include "gss_context.hh"
-
-#ifndef ENABLE_GSS_TSIG
-
-bool GssContext::supported() { return false; }
-GssContext::GssContext() : d_error(GSS_CONTEXT_UNSUPPORTED), d_type(GSS_CONTEXT_NONE) {}
-GssContext::GssContext(const DNSName& label) : d_error(GSS_CONTEXT_UNSUPPORTED), d_type(GSS_CONTEXT_NONE) {}
-void GssContext::setLocalPrincipal(const std::string& name) {}
-bool GssContext::getLocalPrincipal(std::string& name) { return false; }
-void GssContext::setPeerPrincipal(const std::string& name) {}
-bool GssContext::getPeerPrincipal(std::string& name) { return false; }
-void GssContext::generateLabel(const std::string& suffix) {}
-void GssContext::setLabel(const DNSName& label) {}
-bool GssContext::init(const std::string &input, std::string& output) { return false; }
-bool GssContext::accept(const std::string &input, std::string& output) { return false; }
-bool GssContext::destroy() { return false; }
-bool GssContext::expired() { return false; }
-bool GssContext::valid() { return false; }
-bool GssContext::sign(const std::string &input, std::string& output) { return false; }
-bool GssContext::verify(const std::string &input, const std::string &signature) { return false; }
-GssContextError GssContext::getError() { return GSS_CONTEXT_UNSUPPORTED; }
-
-#else
-
-class GssCredential : boost::noncopyable {
-public:
- GssCredential(const std::string& name, const gss_cred_usage_t usage) :
- d_valid(false), d_nameS(name), d_name(GSS_C_NO_NAME), d_cred(GSS_C_NO_CREDENTIAL), d_usage(usage) {
- gss_buffer_desc buffer;
-
- if (name.empty() == false) {
- buffer.length = name.size();
- buffer.value = (void*)name.c_str();
- d_maj = gss_import_name(&d_min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &d_name);
- if (d_maj != GSS_S_COMPLETE) {
- d_valid = false;
- return;
- }
- }
-
- renew();
- };
-
- ~GssCredential() {
- OM_uint32 tmp_maj __attribute__((unused)), tmp_min __attribute__((unused));
- if (d_cred != GSS_C_NO_CREDENTIAL)
- tmp_maj = gss_release_cred(&tmp_min, &d_cred);
- if (d_name != GSS_C_NO_NAME)
- tmp_maj = gss_release_name(&tmp_min, &d_name);
- };
-
- bool expired() const {
- if (d_expires == -1) return false;
- return time((time_t*)NULL)>d_expires;
- }
-
- bool renew() {
- OM_uint32 time_rec, tmp_maj __attribute__((unused)), tmp_min __attribute__((unused));
- d_maj = gss_acquire_cred(&d_min, d_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, d_usage, &d_cred, NULL, &time_rec);
-
- if (d_maj != GSS_S_COMPLETE) {
- d_valid = false;
- tmp_maj = gss_release_name(&tmp_min, &d_name);
- d_name = GSS_C_NO_NAME;
- return false;
- }
-
- d_valid = true;
-
- if (time_rec > GSS_C_INDEFINITE) {
- d_expires = time((time_t*)NULL)+time_rec;
- } else {
- d_expires = -1;
- }
-
- return true;
- }
-
- bool valid() {
- return d_valid && !expired();
- }
-
- OM_uint32 d_maj,d_min;
-
- bool d_valid;
- int64_t d_expires;
- std::string d_nameS;
- gss_name_t d_name;
- gss_cred_id_t d_cred;
- gss_cred_usage_t d_usage;
-};
-
-std::map<std::string, boost::shared_ptr<GssCredential> > s_gss_accept_creds;
-std::map<std::string, boost::shared_ptr<GssCredential> > s_gss_init_creds;
-
-class GssSecContext : boost::noncopyable {
-public:
- GssSecContext(boost::shared_ptr<GssCredential> cred) {
- if (cred->valid() == false) throw PDNSException("Invalid credential " + cred->d_nameS);
- d_cred = cred;
- d_state = GssStateInitial;
- d_ctx = GSS_C_NO_CONTEXT;
- d_expires = 0;
- d_maj = d_min = 0;
- d_peer_name = GSS_C_NO_NAME;
- d_type = GSS_CONTEXT_NONE;
- }
-
- ~GssSecContext() {
- OM_uint32 tmp_maj __attribute__((unused)), tmp_min __attribute__((unused));
- if (d_ctx != GSS_C_NO_CONTEXT) {
- tmp_maj = gss_delete_sec_context(&tmp_min, &d_ctx, GSS_C_NO_BUFFER);
- }
- if (d_peer_name != GSS_C_NO_NAME) {
- tmp_maj = gss_release_name(&tmp_min, &(d_peer_name));
- }
- }
-
- GssContextType d_type;
- gss_ctx_id_t d_ctx;
- gss_name_t d_peer_name;
- int64_t d_expires;
- boost::shared_ptr<GssCredential> d_cred;
- OM_uint32 d_maj,d_min;
-
- enum {
- GssStateInitial,
- GssStateNegotiate,
- GssStateComplete,
- GssStateError
- } d_state;
-
-};
-
-std::map<DNSName, boost::shared_ptr<GssSecContext> > s_gss_sec_context;
-
-bool GssContext::supported() { return true; }
-
-void GssContext::initialize() {
- d_peerPrincipal = "";
- d_localPrincipal = "";
- d_error = GSS_CONTEXT_NO_ERROR;
- d_type = GSS_CONTEXT_NONE;
-}
-
-GssContext::GssContext() {
- initialize();
- generateLabel("pdns.tsig.");
-}
-
-GssContext::GssContext(const DNSName& label) {
- initialize();
- setLabel(label);
-}
-
-void GssContext::generateLabel(const std::string& suffix) {
- std::ostringstream oss;
- oss << std::hex << time((time_t*)NULL) << "." << suffix;
- setLabel(DNSName(oss.str()));
-}
-
-void GssContext::setLabel(const DNSName& label) {
- d_label = label;
- if (s_gss_sec_context.find(d_label) != s_gss_sec_context.end()) {
- d_ctx = s_gss_sec_context[d_label];
- d_type = d_ctx->d_type;
- }
-}
-
-bool GssContext::expired() {
- return (!d_ctx || (d_ctx->d_expires > -1 && d_ctx->d_expires < time((time_t*)NULL)));
-}
-
-bool GssContext::valid() {
- return (d_ctx && !expired() && d_ctx->d_state == GssSecContext::GssStateComplete);
-}
-
-bool GssContext::init(const std::string &input, std::string& output) {
- OM_uint32 tmp_maj __attribute__((unused)), tmp_min __attribute__((unused));
- OM_uint32 maj,min;
- gss_buffer_desc recv_tok, send_tok, buffer;
- OM_uint32 flags;
- OM_uint32 expires;
-
- boost::shared_ptr<GssCredential> cred;
- if (d_label.empty()) {
- d_error = GSS_CONTEXT_INVALID;
- return false;
- }
-
- d_type = GSS_CONTEXT_INIT;
-
- if (s_gss_init_creds.find(d_localPrincipal) != s_gss_init_creds.end()) {
- cred = s_gss_init_creds[d_localPrincipal];
- } else {
- s_gss_init_creds[d_localPrincipal] = boost::make_shared<GssCredential>(d_localPrincipal, GSS_C_INITIATE);
- cred = s_gss_init_creds[d_localPrincipal];
- }
-
- // see if we can find a context in non-completed state
- if (d_ctx) {
- if (d_ctx->d_state != GssSecContext::GssStateNegotiate) {
- d_error = GSS_CONTEXT_INVALID;
- return false;
- }
- } else {
- // make context
- s_gss_sec_context[d_label] = boost::make_shared<GssSecContext>(cred);
- s_gss_sec_context[d_label]->d_type = d_type;
- d_ctx = s_gss_sec_context[d_label];
- d_ctx->d_state = GssSecContext::GssStateNegotiate;
- }
-
- recv_tok.length = input.size();
- recv_tok.value = (void*)input.c_str();
-
- if (d_peerPrincipal.empty() == false) {
- buffer.value = (void*)d_peerPrincipal.c_str();
- buffer.length = d_peerPrincipal.size();
- maj = gss_import_name(&min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &(d_ctx->d_peer_name));
- if (maj != GSS_S_COMPLETE) {
- processError("gss_import_name", maj, min);
- return false;
- }
- }
-
- maj = gss_init_sec_context(&min, cred->d_cred, &(d_ctx->d_ctx), d_ctx->d_peer_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &recv_tok, NULL, &send_tok, &flags, &expires);
-
- if (send_tok.length>0) {
- output.assign((const char*)send_tok.value, send_tok.length);
- tmp_maj = gss_release_buffer(&tmp_min, &send_tok);
- }
-
- if (maj == GSS_S_COMPLETE) {
- if (expires > GSS_C_INDEFINITE) {
- d_ctx->d_expires = time((time_t*)NULL) + expires;
- } else {
- d_ctx->d_expires = -1;
- }
- d_ctx->d_state = GssSecContext::GssStateComplete;
- return true;
- } else if (maj != GSS_S_CONTINUE_NEEDED) {
- processError("gss_init_sec_context", maj,min);
- }
-
- return (maj == GSS_S_CONTINUE_NEEDED);
-}
-
-bool GssContext::accept(const std::string &input, std::string& output) {
- OM_uint32 tmp_maj __attribute__((unused)), tmp_min __attribute__((unused));
- OM_uint32 maj,min;
- gss_buffer_desc recv_tok, send_tok;
- OM_uint32 flags;
- OM_uint32 expires;
-
- boost::shared_ptr<GssCredential> cred;
- if (d_label.empty()) {
- d_error = GSS_CONTEXT_INVALID;
- return false;
- }
-
- d_type = GSS_CONTEXT_ACCEPT;
-
- if (s_gss_accept_creds.find(d_localPrincipal) != s_gss_accept_creds.end()) {
- cred = s_gss_accept_creds[d_localPrincipal];
- } else {
- s_gss_accept_creds[d_localPrincipal] = boost::make_shared<GssCredential>(d_localPrincipal, GSS_C_ACCEPT);
- cred = s_gss_accept_creds[d_localPrincipal];
- }
-
- // see if we can find a context in non-completed state
- if (d_ctx) {
- if (d_ctx->d_state != GssSecContext::GssStateNegotiate) {
- d_error = GSS_CONTEXT_INVALID;
- return false;
- }
- } else {
- // make context
- s_gss_sec_context[d_label] = boost::make_shared<GssSecContext>(cred);
- s_gss_sec_context[d_label]->d_type = d_type;
- d_ctx = s_gss_sec_context[d_label];
- d_ctx->d_state = GssSecContext::GssStateNegotiate;
- }
-
- recv_tok.length = input.size();
- recv_tok.value = (void*)input.c_str();
-
- maj = gss_accept_sec_context(&min, &(d_ctx->d_ctx), cred->d_cred, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &(d_ctx->d_peer_name), NULL, &send_tok, &flags, &expires, NULL);
-
- if (send_tok.length>0) {
- output.assign((const char*)send_tok.value, send_tok.length);
- tmp_maj = gss_release_buffer(&tmp_min, &send_tok);
- }
-
- if (maj == GSS_S_COMPLETE) {
- if (expires > GSS_C_INDEFINITE) {
- d_ctx->d_expires = time((time_t*)NULL) + expires;
- } else {
- d_ctx->d_expires = -1;
- }
- d_ctx->d_state = GssSecContext::GssStateComplete;
- return true;
- } else if (maj != GSS_S_CONTINUE_NEEDED) {
- processError("gss_accept_sec_context", maj,min);
- }
- return (maj == GSS_S_CONTINUE_NEEDED);
-};
-
-bool GssContext::sign(const std::string& input, std::string& output) {
- OM_uint32 tmp_maj __attribute__((unused)), tmp_min __attribute__((unused));
- OM_uint32 maj,min;
-
- gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
- gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
-
- recv_tok.length = input.size();
- recv_tok.value = (void*)input.c_str();
-
- maj = gss_get_mic(&min, d_ctx->d_ctx, GSS_C_QOP_DEFAULT, &recv_tok, &send_tok);
-
- if (send_tok.length>0) {
- output.assign((const char*)send_tok.value, send_tok.length);
- tmp_maj = gss_release_buffer(&tmp_min, &send_tok);
- }
-
- if (maj != GSS_S_COMPLETE) {
- processError("gss_get_mic", maj,min);
- }
-
- return (maj == GSS_S_COMPLETE);
-}
-
-bool GssContext::verify(const std::string& input, const std::string& signature) {
- OM_uint32 maj,min;
-
- gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
- gss_buffer_desc sign_tok = GSS_C_EMPTY_BUFFER;
-
- recv_tok.length = input.size();
- recv_tok.value = (void*)input.c_str();
- sign_tok.length = signature.size();
- sign_tok.value = (void*)signature.c_str();
-
- maj = gss_verify_mic(&min, d_ctx->d_ctx, &recv_tok, &sign_tok, NULL);
-
- if (maj != GSS_S_COMPLETE) {
- processError("gss_get_mic", maj,min);
- }
-
- return (maj == GSS_S_COMPLETE);
-}
-
-bool GssContext::destroy() {
- return false;
-}
-
-void GssContext::setLocalPrincipal(const std::string& name) {
- d_localPrincipal = name;
-}
-
-bool GssContext::getLocalPrincipal(std::string& name) {
- name = d_localPrincipal;
- return name.size()>0;
-}
-
-void GssContext::setPeerPrincipal(const std::string& name) {
- d_peerPrincipal = name;
-}
-
-bool GssContext::getPeerPrincipal(std::string& name) {
- gss_buffer_desc value;
- OM_uint32 maj,min;
-
- if (d_ctx->d_peer_name != GSS_C_NO_NAME) {
- maj = gss_display_name(&min, d_ctx->d_peer_name, &value, NULL);
- if (maj == GSS_S_COMPLETE && value.length > 0) {
- name.assign((const char*)value.value, value.length);
- maj = gss_release_buffer(&min, &value);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
-}
-
-void GssContext::processError(const std::string& method, OM_uint32 maj, OM_uint32 min) {
- OM_uint32 tmp_min;
- gss_buffer_desc msg;
- OM_uint32 msg_ctx;
-
- msg_ctx = 0;
- while (1) {
- ostringstream oss;
- gss_display_status(&tmp_min, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &msg);
- oss << method << ": " << (char*)msg.value;
- d_gss_errors.push_back(oss.str());
- if (!msg_ctx) break;
- }
- msg_ctx = 0;
- while (1) {
- ostringstream oss;
- gss_display_status(&tmp_min, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &msg);
- oss << method << ": " << (char*)msg.value;
- d_gss_errors.push_back(oss.str());
- if (!msg_ctx) break;
- }
-}
-
-#endif
-
-bool gss_add_signature(const DNSName& context, const std::string& message, std::string& mac) {
- string tmp_mac;
- GssContext gssctx(context);
- if (!gssctx.valid()) {
- g_log<<Logger::Error<<"GSS context '"<<context<<"' is not valid"<<endl;
- for(const string& error : gssctx.getErrorStrings()) {
- g_log<<Logger::Error<<"GSS error: "<<error<<endl;;
- }
- return false;
- }
-
- if (!gssctx.sign(message, tmp_mac)) {
- g_log<<Logger::Error<<"Could not sign message using GSS context '"<<context<<"'"<<endl;
- for(const string& error : gssctx.getErrorStrings()) {
- g_log<<Logger::Error<<"GSS error: "<<error<<endl;;
- }
- return false;
- }
- mac = tmp_mac;
- return true;
-}
-
-bool gss_verify_signature(const DNSName& context, const std::string& message, const std::string& mac) {
- GssContext gssctx(context);
- if (!gssctx.valid()) {
- g_log<<Logger::Error<<"GSS context '"<<context<<"' is not valid"<<endl;
- for(const string& error : gssctx.getErrorStrings()) {
- g_log<<Logger::Error<<"GSS error: "<<error<<endl;;
- }
- return false;
- }
-
- if (!gssctx.verify(message, mac)) {
- g_log<<Logger::Error<<"Could not verify message using GSS context '"<<context<<"'"<<endl;
- for(const string& error : gssctx.getErrorStrings()) {
- g_log<<Logger::Error<<"GSS error: "<<error<<endl;;
- }
- return false;
- }
- return true;
-}
+++ /dev/null
-/*
- * This file is part of PowerDNS or dnsdist.
- * Copyright -- PowerDNS.COM B.V. and its contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * In addition, for the avoidance of any doubt, permission is granted to
- * link this program with OpenSSL and to (re)distribute the binaries
- * produced as the result of such linking.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-#pragma once
-
-#ifdef ENABLE_GSS_TSIG
-#include <gssapi/gssapi.h>
-#include <gssapi/gssapi_krb5.h>
-#include <gssapi/gssapi_ext.h>
-#endif
-
-//! Generic errors
-enum GssContextError {
- GSS_CONTEXT_NO_ERROR,
- GSS_CONTEXT_UNSUPPORTED,
- GSS_CONTEXT_NOT_FOUND,
- GSS_CONTEXT_NOT_INITIALIZED,
- GSS_CONTEXT_INVALID,
- GSS_CONTEXT_EXPIRED,
- GSS_CONTEXT_ALREADY_INITIALIZED
-};
-
-//! GSS context types
-enum GssContextType {
- GSS_CONTEXT_NONE,
- GSS_CONTEXT_INIT,
- GSS_CONTEXT_ACCEPT
-};
-
-class GssSecContext;
-
-/*! Class for representing GSS names, such as host/host.domain.com@REALM.
-*/
-class GssName {
-public:
- //! Initialize to empty name
- GssName() {
- setName("");
- };
-
- //! Initialize using specific name
- GssName(const std::string& name) {
- setName(name);
- };
-
- //! Parse name into native representation
- bool setName(const std::string& name) {
-#ifdef ENABLE_GSS_TSIG
- gss_buffer_desc buffer;
- d_name = GSS_C_NO_NAME;
-
- if (!name.empty()) {
- buffer.length = name.size();
- buffer.value = (void*)name.c_str();
- d_maj = gss_import_name(&d_min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &d_name);
- return d_maj == GSS_S_COMPLETE;
- }
-
- return true;
-#endif
- return false;
- };
-
- ~GssName() {
-#ifdef ENABLE_GSS_TSIG
- if (d_name != GSS_C_NO_NAME)
- gss_release_name(&d_min, &d_name);
-#endif
- };
-
- //! Compare two Gss Names, if no gss support is compiled in, returns false always
- //! This is not necessarily same as string comparison between two non-parsed names
- bool operator==(const GssName& rhs) {
-#ifdef ENABLE_GSS_TSIG
- OM_uint32 maj,min;
- int result;
- maj = gss_compare_name(&min, d_name, rhs.d_name, &result);
- return (maj == GSS_S_COMPLETE && result != 0);
-#endif
- return false;
- }
-
- //! Compare two Gss Names, if no gss support is compiled in, returns false always
- //! This is not necessarily same as string comparison between two non-parsed names
- bool match(const std::string& name) {
-#ifdef ENABLE_GSS_TSIG
- OM_uint32 maj,min;
- int result;
- gss_name_t comp;
- gss_buffer_desc buffer;
- buffer.length = name.size();
- buffer.value = (void*)name.c_str();
- maj = gss_import_name(&min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &comp);
- if (maj != GSS_S_COMPLETE)
- throw PDNSException("Could not import " + name + ": " + std::to_string(maj) + string(",") + std::to_string(min));
- // do comparison
- maj = gss_compare_name(&min, d_name, comp, &result);
- gss_release_name(&min, &comp);
- return (maj == GSS_S_COMPLETE && result != 0);
-#else
- return false;
-#endif
- };
-
- //! Check if GSS name was parsed successfully.
- bool valid() {
-#ifdef ENABLE_GSS_TSIG
- return d_maj == GSS_S_COMPLETE;
-#else
- return false;
-#endif
- }
-private:
-#ifdef ENABLE_GSS_TSIG
- OM_uint32 d_maj,d_min;
- gss_name_t d_name;
-#endif
-};
-
-class GssContext {
-public:
- static bool supported(); //<! Returns true if GSS is supported in the first place
- GssContext(); //<! Construct new GSS context with random name
- GssContext(const DNSName& label); //<! Create or open existing named context
-
- void setLocalPrincipal(const std::string& name); //<! Set our gss name
- bool getLocalPrincipal(std::string& name); //<! Get our name
- void setPeerPrincipal(const std::string& name); //<! Set remote name (do not use after negotiation)
- bool getPeerPrincipal(std::string &name); //<! Return remote name, returns actual name after negotiation
-
- void generateLabel(const std::string& suffix); //<! Generate random context name using suffix (such as mydomain.com)
- void setLabel(const DNSName& label); //<! Set context name to this label
- const DNSName& getLabel() { return d_label; } //<! Return context name
-
- bool init(const std::string &input, std::string& output); //<! Perform GSS Initiate Security Context handshake
- bool accept(const std::string &input, std::string& output); //<! Perform GSS Accept Security Context handshake
- bool destroy(); //<! Release the cached context
- bool expired(); //<! Check if context is expired
- bool valid(); //<! Check if context is valid
-
- bool sign(const std::string &input, std::string& output); //<! Sign something using gss
- bool verify(const std::string &input, const std::string &signature); //<! Validate gss signature with something
-
- GssContextError getError(); //<! Get error
- const std::vector<std::string> getErrorStrings() { return d_gss_errors; } //<! Get native error texts
- private:
- void release(); //<! Release context
- void initialize(); //<! Initialize context
-#ifdef ENABLE_GSS_TSIG
- void processError(const string& method, OM_uint32 maj, OM_uint32 min); //<! Process and fill error text vector
-#endif
- DNSName d_label; //<! Context name
- std::string d_peerPrincipal; //<! Remote name
- std::string d_localPrincipal; //<! Our name
- GssContextError d_error; //<! Context error
- GssContextType d_type; //<! Context type
- std::vector<std::string> d_gss_errors; //<! Native error string(s)
- boost::shared_ptr<GssSecContext> d_ctx; //<! Attached security context
-};
-
-bool gss_add_signature(const DNSName& context, const std::string& message, std::string& mac); //<! Create signature
-bool gss_verify_signature(const DNSName& context, const std::string& message, const std::string& mac); //<! Validate signature
#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
}
+bool setReusePort(int sockfd)
+{
+#if defined(SO_REUSEPORT_LB)
+ try {
+ SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
+ return true;
+ }
+ catch (const std::exception& e) {
+ return false;
+ }
+#elif defined(SO_REUSEPORT)
+ try {
+ SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, 1);
+ return true;
+ }
+ catch (const std::exception& e) {
+ return false;
+ }
+#endif
+ return false;
+}
+
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv)
{
#ifdef SO_TIMESTAMP
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;
}
// we turn left on 0 and right on 1
int bits = 0;
- for(; node && bits < key.getBits(); bits++) {
+ for(; bits < key.getBits(); bits++) {
bool vall = key.getBit(-1-bits);
if (bits >= node->d_bits) {
int SListen(int sockfd, int limit);
int SSetsockopt(int sockfd, int level, int opname, int value);
void setSocketIgnorePMTU(int sockfd);
+bool setReusePort(int sockfd);
#if defined(IP_PKTINFO)
#define GEN_IP_PKTINFO IP_PKTINFO
return ret;
};
};
+
+extern string doGetStats();
#include "iputils.hh"
#include "ixfrdist-stats.hh"
-string doGetStats();
-
IXFRDistWebServer::IXFRDistWebServer(const ComboAddress &listenAddress, const NetmaskGroup &acl, const string &loglevel) :
d_ws(std::unique_ptr<WebServer>(new WebServer(listenAddress.toString(), listenAddress.getPort())))
{
d_ws->setACL(acl);
d_ws->setLogLevel(loglevel);
- d_ws->registerWebHandler("/metrics", boost::bind(&IXFRDistWebServer::getMetrics, this, _1, _2));
+ d_ws->registerWebHandler("/metrics", std::bind(&IXFRDistWebServer::getMetrics, this, std::placeholders::_1, std::placeholders::_2));
d_ws->bind();
}
#include <condition_variable>
#include "ixfr.hh"
#include "ixfrutils.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "dns_random.hh"
#include "sstuff.hh"
#include "mplexer.hh"
// FIXME: also report zone size?
}
-void updateThread(const string& workdir, const uint16_t& keep, const uint16_t& axfrTimeout, const uint16_t& soaRetry, const uint32_t axfrMaxRecords) {
+static void updateThread(const string& workdir, const uint16_t& keep, const uint16_t& axfrTimeout, const uint16_t& soaRetry, const uint32_t axfrMaxRecords) {
setThreadName("ixfrdist/update");
std::map<DNSName, time_t> lastCheck;
Restart=on-failure
RestartSec=1
StartLimitInterval=0
-PrivateTmp=true
-PrivateDevices=true
+
+# Sandboxing
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID
+LockPersonality=true
NoNewPrivileges=true
-ProtectSystem=full
+PrivateDevices=true
+PrivateTmp=true
+# Setting PrivateUsers=true prevents us from opening our sockets
+ProtectClock=true
+ProtectControlGroups=true
ProtectHome=true
+ProtectHostname=true
+ProtectKernelLogs=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectSystem=full
RestrictAddressFamilies=AF_INET AF_INET6
+RestrictNamespaces=true
+RestrictRealtime=true
+RestrictSUIDSGID=true
+SystemCallArchitectures=native
+SystemCallFilter=~ @clock @debug @module @mount @raw-io @reboot @swap @cpu-emulation @obsolete
[Install]
WantedBy=multi-user.target
DNSRecord soa;
auto serial = getSerialFromRecords(records, soa);
string fname=directory +"/"+std::to_string(serial);
- FILE* fp=fopen((fname+".partial").c_str(), "w");
- if(!fp)
+ auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen((fname+".partial").c_str(), "w"), fclose);
+ if (!fp) {
throw runtime_error("Unable to open file '"+fname+".partial' for writing: "+stringerror());
+ }
records_t soarecord;
soarecord.insert(soa);
- if(fprintf(fp, "$ORIGIN %s\n", zone.toString().c_str()) < 0) {
+ if (fprintf(fp.get(), "$ORIGIN %s\n", zone.toString().c_str()) < 0) {
string error = "Error writing to zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + stringerror();
- fclose(fp);
+ fp.reset();
unlink((fname+".partial").c_str());
throw std::runtime_error(error);
}
try {
- writeRecords(fp, soarecord);
- writeRecords(fp, records);
- writeRecords(fp, soarecord);
+ writeRecords(fp.get(), soarecord);
+ writeRecords(fp.get(), records);
+ writeRecords(fp.get(), soarecord);
} catch (runtime_error &e) {
- fclose(fp);
+ fp.reset();
unlink((fname+".partial").c_str());
throw runtime_error("Error closing zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + e.what());
}
- if(fclose(fp) != 0) {
+ if (fclose(fp.release()) != 0) {
string error = "Error closing zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + stringerror();
unlink((fname+".partial").c_str());
throw std::runtime_error(error);
#include "dnssecinfra.hh"
#include "dns_random.hh"
-#include "gss_context.hh"
#include <boost/multi_index_container.hpp>
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include <fstream>
#include "ixfr.hh"
#include "ixfrutils.hh"
return theArg;
}
-void usage() {
+static void usage() {
cerr<<"Syntax: ixplore diff ZONE BEFORE_FILE AFTER_FILE"<<endl;
cerr<<"Syntax: ixplore track IP-ADDRESS PORT ZONE DIRECTORY [TSIGKEY TSIGALGO TSIGSECRET]"<<endl;
}
private:
boost::circular_buffer<std::shared_ptr<OpenSSLTLSTicketKey> > d_ticketKeys;
- pthread_rwlock_t d_lock;
+ ReadWriteLock d_lock;
};
void* libssl_get_ticket_key_callback_data(SSL* s);
#include "misc.hh"
#include "pdnsexception.hh"
-extern bool g_singleThreaded;
-
-class Lock
+class ReadWriteLock
{
- pthread_mutex_t *d_lock;
public:
- Lock(const Lock& rhs) = delete;
- Lock& operator=(const Lock& rhs) = delete;
-
- Lock(pthread_mutex_t *lock) : d_lock(lock)
+ ReadWriteLock()
{
- if(g_singleThreaded)
- return;
-
- int err;
- if((err = pthread_mutex_lock(d_lock))) {
- errno = err;
- throw PDNSException("error acquiring lock: "+stringerror());
+ if (pthread_rwlock_init(&d_lock, nullptr) != 0) {
+ throw std::runtime_error("Error creating a read-write lock: " + stringerror());
}
}
- ~Lock()
- {
- if(g_singleThreaded)
- return;
- pthread_mutex_unlock(d_lock);
+ ~ReadWriteLock() {
+ /* might have been moved */
+ pthread_rwlock_destroy(&d_lock);
}
+
+ ReadWriteLock(const ReadWriteLock& rhs) = delete;
+ ReadWriteLock& operator=(const ReadWriteLock& rhs) = delete;
+
+ pthread_rwlock_t* getLock()
+ {
+ return &d_lock;
+ }
+
+private:
+ pthread_rwlock_t d_lock;
};
-class WriteLock
+class ReadLock
{
- pthread_rwlock_t *d_lock;
public:
+ ReadLock(ReadWriteLock& lock): ReadLock(lock.getLock())
+ {
+ }
- WriteLock(pthread_rwlock_t *lock) : d_lock(lock)
+ ReadLock(ReadWriteLock* lock): ReadLock(lock->getLock())
{
- if(g_singleThreaded)
- return;
+ }
+ ~ReadLock()
+ {
+ if(d_lock) // may have been moved
+ pthread_rwlock_unlock(d_lock);
+ }
+
+ ReadLock(ReadLock&& rhs)
+ {
+ d_lock = rhs.d_lock;
+ rhs.d_lock = nullptr;
+ }
+ ReadLock(const ReadLock& rhs) = delete;
+ ReadLock& operator=(const ReadLock& rhs) = delete;
+
+private:
+ ReadLock(pthread_rwlock_t *lock) : d_lock(lock)
+ {
int err;
- if((err = pthread_rwlock_wrlock(d_lock))) {
- throw PDNSException("error acquiring rwlock wrlock: "+stringerror(err));
+ if((err = pthread_rwlock_rdlock(d_lock))) {
+ throw PDNSException("error acquiring rwlock readlock: "+stringerror(err));
}
}
- ~WriteLock()
+
+ pthread_rwlock_t *d_lock;
+};
+
+class WriteLock
+{
+public:
+ WriteLock(ReadWriteLock& lock): WriteLock(lock.getLock())
+ {
+ }
+
+ WriteLock(ReadWriteLock* lock): WriteLock(lock->getLock())
{
- if(g_singleThreaded)
- return;
- if(d_lock) // might have been moved
- pthread_rwlock_unlock(d_lock);
}
WriteLock(WriteLock&& rhs)
d_lock = rhs.d_lock;
rhs.d_lock=0;
}
+
+ ~WriteLock()
+ {
+ if(d_lock) // might have been moved
+ pthread_rwlock_unlock(d_lock);
+ }
+
WriteLock(const WriteLock& rhs) = delete;
WriteLock& operator=(const WriteLock& rhs) = delete;
+private:
+ WriteLock(pthread_rwlock_t *lock) : d_lock(lock)
+ {
+ int err;
+ if((err = pthread_rwlock_wrlock(d_lock))) {
+ throw PDNSException("error acquiring rwlock wrlock: "+stringerror(err));
+ }
+ }
+
+ pthread_rwlock_t *d_lock;
};
-class TryWriteLock
+class TryReadLock
{
- pthread_rwlock_t *d_lock;
- bool d_havelock;
public:
- TryWriteLock(const TryWriteLock& rhs) = delete;
- TryWriteLock& operator=(const TryWriteLock& rhs) = delete;
-
- TryWriteLock(pthread_rwlock_t *lock) : d_lock(lock)
+ TryReadLock(ReadWriteLock& lock): TryReadLock(lock.getLock())
{
- if(g_singleThreaded) {
- d_havelock=true;
- return;
- }
+ }
- d_havelock=false;
- int err;
- if((err = pthread_rwlock_trywrlock(d_lock)) && err!=EBUSY) {
- throw PDNSException("error acquiring rwlock tryrwlock: "+stringerror(err));
- }
- d_havelock=(err==0);
+ TryReadLock(ReadWriteLock* lock): TryReadLock(lock->getLock())
+ {
}
- TryWriteLock(TryWriteLock&& rhs)
+ TryReadLock(TryReadLock&& rhs)
{
d_lock = rhs.d_lock;
rhs.d_lock = nullptr;
rhs.d_havelock = false;
}
-
- ~TryWriteLock()
+ ~TryReadLock()
{
- if(g_singleThreaded)
- return;
-
- if(d_havelock && d_lock) // we might be moved
+ if(d_havelock && d_lock)
pthread_rwlock_unlock(d_lock);
}
+
+ TryReadLock(const TryReadLock& rhs) = delete;
+ TryReadLock& operator=(const TryReadLock& rhs) = delete;
+
bool gotIt()
{
- if(g_singleThreaded)
- return true;
-
return d_havelock;
}
-};
-
-class TryReadLock
-{
- pthread_rwlock_t *d_lock;
- bool d_havelock;
-public:
- TryReadLock(const TryReadLock& rhs) = delete;
- TryReadLock& operator=(const TryReadLock& rhs) = delete;
+private:
TryReadLock(pthread_rwlock_t *lock) : d_lock(lock)
{
- if(g_singleThreaded) {
- d_havelock=true;
- return;
- }
-
int err;
if((err = pthread_rwlock_tryrdlock(d_lock)) && err!=EBUSY) {
throw PDNSException("error acquiring rwlock tryrdlock: "+stringerror(err));
}
d_havelock=(err==0);
}
- TryReadLock(TryReadLock&& rhs)
+
+ pthread_rwlock_t *d_lock;
+ bool d_havelock;
+};
+
+class TryWriteLock
+{
+public:
+ TryWriteLock(ReadWriteLock& lock): TryWriteLock(lock.getLock())
+ {
+ }
+
+ TryWriteLock(ReadWriteLock* lock): TryWriteLock(lock->getLock())
+ {
+ }
+
+ TryWriteLock(TryWriteLock&& rhs)
{
d_lock = rhs.d_lock;
rhs.d_lock = nullptr;
rhs.d_havelock = false;
}
- ~TryReadLock()
+ ~TryWriteLock()
{
- if(g_singleThreaded)
- return;
-
- if(d_havelock && d_lock)
+ if(d_havelock && d_lock) // we might be moved
pthread_rwlock_unlock(d_lock);
}
+
+ TryWriteLock(const TryWriteLock& rhs) = delete;
+ TryWriteLock& operator=(const TryWriteLock& rhs) = delete;
+
bool gotIt()
{
- if(g_singleThreaded)
- return true;
-
return d_havelock;
}
-};
-
-class ReadLock
-{
- pthread_rwlock_t *d_lock;
-public:
-
- ReadLock(pthread_rwlock_t *lock) : d_lock(lock)
+private:
+ TryWriteLock(pthread_rwlock_t *lock) : d_lock(lock)
{
- if(g_singleThreaded)
- return;
-
+ d_havelock=false;
int err;
- if((err = pthread_rwlock_rdlock(d_lock))) {
- throw PDNSException("error acquiring rwlock readlock: "+stringerror(err));
+ if((err = pthread_rwlock_trywrlock(d_lock)) && err!=EBUSY) {
+ throw PDNSException("error acquiring rwlock tryrwlock: "+stringerror(err));
}
- }
- ~ReadLock()
- {
- if(g_singleThreaded)
- return;
- if(d_lock) // may have been moved
- pthread_rwlock_unlock(d_lock);
+ d_havelock=(err==0);
}
- ReadLock(ReadLock&& rhs)
- {
- d_lock = rhs.d_lock;
- rhs.d_lock=0;
- }
- ReadLock(const ReadLock& rhs) = delete;
- ReadLock& operator=(const ReadLock& rhs) = delete;
+ pthread_rwlock_t *d_lock;
+ bool d_havelock;
};
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+
+#include <mutex>
+
#include "logger.hh"
#include "misc.hh"
#ifndef RECURSOR
#include "statbag.hh"
extern StatBag S;
#endif
-#include "lock.hh"
#include "namespaces.hh"
thread_local Logger::PerThread Logger::t_perThread;
return log;
}
-void Logger::log(const string &msg, Urgency u)
+void Logger::log(const string &msg, Urgency u) noexcept
{
#ifndef RECURSOR
bool mustAccount(false);
}
}
- static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
- Lock l(&m); // the C++-2011 spec says we need this, and OSX actually does
+ static std::mutex m;
+ std::lock_guard<std::mutex> l(m); // the C++-2011 spec says we need this, and OSX actually does
clog << string(buffer) + prefix + msg <<endl;
#ifndef RECURSOR
mustAccount=true;
}
#ifndef RECURSOR
- if(mustAccount)
- S.ringAccount("logmessages",msg);
+ if(mustAccount) {
+ try {
+ S.ringAccount("logmessages",msg);
+ }
+ catch (const runtime_error& e) {
+ cerr << e.what() << endl;
+ }
+ }
#endif
}
\param msg Message you wish to log
\param u Urgency of the message you wish to log
*/
- void log(const string &msg, Urgency u=Notice);
+ void log(const string &msg, Urgency u=Notice) noexcept;
void setFacility(int f){d_facility=f;open();} //!< Choose logging facility
void setFlag(int f){flags|=f;open();} //!< set a syslog flag
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);
std::atomic<time_t> lastAccess{0};
};
- pthread_rwlock_t d_lock;
+ ReadWriteLock d_lock;
public:
IsUpOracle()
{
- pthread_rwlock_init(&d_lock, nullptr);
}
~IsUpOracle()
{
- pthread_rwlock_destroy(&d_lock);
}
bool isUp(const ComboAddress& remote, const opts_t& opts);
bool isUp(const ComboAddress& remote, const std::string& url, const opts_t& opts);
}
-std::string getGeo(const std::string& ip, GeoIPInterface::GeoIPQueryAttribute qa)
+static std::string getGeo(const std::string& ip, GeoIPInterface::GeoIPQueryAttribute qa)
{
static bool initialized;
extern std::function<std::string(const std::string& ip, int)> g_getGeo;
sum += i.first;
pick.push_back({sum, i.second});
}
+ if (sum == 0) {
+ /* we should not have any weight of zero, but better safe than sorry */
+ return ComboAddress();
+ }
ComboAddress::addressOnlyHash aoh;
int r = aoh(bestwho) % sum;
auto p = upper_bound(pick.begin(), pick.end(), r, [](int rarg, const decltype(pick)::value_type& a) { return rarg < a.first; });
double latsec, lonsec;
char lathem='X', lonhem='X';
- double lat, lon;
+ double lat = 0, lon = 0;
if(!getLatLon(ip, lat, lon))
return false;
static thread_local unique_ptr<lua_record_ctx_t> s_lua_record_ctx;
-void setupLuaRecords()
+static void setupLuaRecords()
{
LuaContext& lua = *s_LUA->getLua();
lua.writeFunction("latlon", []() {
- double lat, lon;
+ double lat = 0, lon = 0;
getLatLon(s_lua_record_ctx->bestwho.toString(), lat, lon);
return std::to_string(lat)+" "+std::to_string(lon);
});
auto labels= s_lua_record_ctx->qname.getRawLabels();
if(labels.size()<4)
return std::string("unknown");
- double lat, lon;
+ double lat = 0, lon = 0;
getLatLon(labels[3]+"."+labels[2]+"."+labels[1]+"."+labels[0], lat, lon);
return std::to_string(lat)+" "+std::to_string(lon);
});
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();
}
* Simplistic test to see if an IP address listens on a certain port
* Will return a single IP address from the set of available IP addresses. If
* no IP address is available, will return a random element of the set of
- * addresses suppplied for testing.
+ * addresses supplied for testing.
*
* @example ifportup(443, { '1.2.3.4', '5.4.3.2' })"
*/
const void* data;
} pdns_ednsoption_t;
+ typedef struct pdns_proxyprotocol_value {
+ uint8_t type;
+ uint16_t len;
+ const void* data;
+ } pdns_proxyprotocol_value_t;
+
typedef enum
{
answer = 1,
size_t pdns_ffi_param_get_edns_options(pdns_ffi_param_t* ref, const pdns_ednsoption_t** out) __attribute__ ((visibility ("default")));
size_t pdns_ffi_param_get_edns_options_by_code(pdns_ffi_param_t* ref, uint16_t optionCode, const pdns_ednsoption_t** out) __attribute__ ((visibility ("default")));
+ // returns the length of the resulting 'out' array. 'out' is not set if the length is 0
+ size_t pdns_ffi_param_get_proxy_protocol_values(pdns_ffi_param_t* ref, const pdns_proxyprotocol_value_t** out) __attribute__ ((visibility ("default")));
+
void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag) __attribute__ ((visibility ("default")));
void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name) __attribute__ ((visibility ("default")));
void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name) __attribute__ ((visibility ("default")));
void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name) __attribute__ ((visibility ("default")));
void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name) __attribute__ ((visibility ("default")));
+ void pdns_ffi_param_set_routingtag(pdns_ffi_param_t* ref, const char* name) __attribute__ ((visibility ("default")));
+
void pdns_ffi_param_set_variable(pdns_ffi_param_t* ref, bool variable) __attribute__ ((visibility ("default")));
void pdns_ffi_param_set_ttl_cap(pdns_ffi_param_t* ref, uint32_t ttl) __attribute__ ((visibility ("default")));
void pdns_ffi_param_set_log_query(pdns_ffi_param_t* ref, bool logQuery) __attribute__ ((visibility ("default")));
RecursorLua4::RecursorLua4() { prepareContext(); }
-static int getFakeAAAARecords(const DNSName& qname, const std::string& prefix, vector<DNSRecord>& ret)
-{
- int rcode=directResolve(qname, QType(QType::A), 1, ret);
-
- ComboAddress prefixAddress(prefix);
-
- // Remove double CNAME records
- std::set<DNSName> seenCNAMEs;
- ret.erase(std::remove_if(
- ret.begin(),
- ret.end(),
- [&seenCNAMEs](DNSRecord& rr) {
- if (rr.d_type == QType::CNAME) {
- auto target = getRR<CNAMERecordContent>(rr);
- if (target == nullptr) {
- return false;
- }
- if (seenCNAMEs.count(target->getTarget()) > 0) {
- // We've had this CNAME before, remove it
- return true;
- }
- seenCNAMEs.insert(target->getTarget());
- }
- return false;
- }),
- ret.end());
-
- bool seenA = false;
- for(DNSRecord& rr : ret)
- {
- if(rr.d_type == QType::A && rr.d_place==DNSResourceRecord::ANSWER) {
- if(auto rec = getRR<ARecordContent>(rr)) {
- ComboAddress ipv4(rec->getCA());
- uint32_t tmp;
- memcpy((void*)&tmp, &ipv4.sin4.sin_addr.s_addr, 4);
- // tmp=htonl(tmp);
- memcpy(((char*)&prefixAddress.sin6.sin6_addr.s6_addr)+12, &tmp, 4);
- rr.d_content = std::make_shared<AAAARecordContent>(prefixAddress);
- rr.d_type = QType::AAAA;
- }
- seenA = true;
- }
- }
-
- if (seenA) {
- // We've seen an A in the ANSWER section, so there is no need to keep any
- // SOA in the AUTHORITY section as this is not a NODATA response.
- ret.erase(std::remove_if(
- ret.begin(),
- ret.end(),
- [](DNSRecord& rr) {
- return (rr.d_type == QType::SOA && rr.d_place==DNSResourceRecord::AUTHORITY);
- }),
- ret.end());
- }
- return rcode;
-}
-
-static int getFakePTRRecords(const DNSName& qname, const std::string& prefix, vector<DNSRecord>& ret)
-{
- /* qname has a reverse ordered IPv6 address, need to extract the underlying IPv4 address from it
- and turn it into an IPv4 in-addr.arpa query */
- ret.clear();
- vector<string> parts = qname.getRawLabels();
-
- if(parts.size() < 8)
- return -1;
-
- string newquery;
- for(int n = 0; n < 4; ++n) {
- newquery +=
- std::to_string(stoll(parts[n*2], 0, 16) + 16*stoll(parts[n*2+1], 0, 16));
- newquery.append(1,'.');
- }
- newquery += "in-addr.arpa.";
-
-
- DNSRecord rr;
- rr.d_name = qname;
- rr.d_type = QType::CNAME;
- rr.d_content = std::make_shared<CNAMERecordContent>(newquery);
- ret.push_back(rr);
-
- int rcode = directResolve(DNSName(newquery), QType(QType::PTR), 1, ret);
-
- return rcode;
-
-}
-
boost::optional<dnsheader> RecursorLua4::DNSQuestion::getDH() const
{
if (dh)
return boost::optional<Netmask>();
}
+std::vector<std::pair<int, ProxyProtocolValue>> RecursorLua4::DNSQuestion::getProxyProtocolValues() const
+{
+ std::vector<std::pair<int, ProxyProtocolValue>> result;
+ if (proxyProtocolValues) {
+ result.reserve(proxyProtocolValues->size());
+
+ int idx = 1;
+ for (const auto& value: *proxyProtocolValues) {
+ result.push_back({ idx++, value });
+ }
+ }
+
+ return result;
+}
vector<pair<int, DNSRecord> > RecursorLua4::DNSQuestion::getRecords() const
{
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->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy);
d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyName",
[](const DNSFilterEngine::Policy& pol) -> std::string {
- if(pol.d_name)
- return *pol.d_name;
- return std::string();
+ return pol.getName();
},
[](DNSFilterEngine::Policy& pol, const std::string& name) {
- pol.d_name = std::make_shared<std::string>(name);
+ pol.setName(name);
});
d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind);
d_lw->registerMember("policyType", &DNSFilterEngine::Policy::d_type);
d_lw->registerMember("policyTTL", &DNSFilterEngine::Policy::d_ttl);
+ d_lw->registerMember("policyTrigger", &DNSFilterEngine::Policy::d_trigger);
+ d_lw->registerMember("policyHit", &DNSFilterEngine::Policy::d_hit);
d_lw->registerMember<DNSFilterEngine::Policy, std::string>("policyCustom",
[](const DNSFilterEngine::Policy& pol) -> std::string {
std::string result;
d_lw->registerFunction("getEDNSOptions", &DNSQuestion::getEDNSOptions);
d_lw->registerFunction("getEDNSOption", &DNSQuestion::getEDNSOption);
d_lw->registerFunction("getEDNSSubnet", &DNSQuestion::getEDNSSubnet);
+ d_lw->registerFunction("getProxyProtocolValues", &DNSQuestion::getProxyProtocolValues);
d_lw->registerFunction("getEDNSFlags", &DNSQuestion::getEDNSFlags);
d_lw->registerFunction("getEDNSFlag", &DNSQuestion::getEDNSFlag);
d_lw->registerMember("name", &DNSRecord::d_name);
return ret;
});
+ 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_lw->registerFunction("setRecords", &DNSQuestion::setRecords);
- d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->push_back(tag); } });
+ d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->insert(tag); } });
d_lw->registerFunction<void(DNSQuestion::*)(const std::vector<std::pair<int, std::string> >&)>("setPolicyTags", [](DNSQuestion& dq, const std::vector<std::pair<int, std::string> >& tags) {
if (dq.policyTags) {
dq.policyTags->clear();
+ dq.policyTags->reserve(tags.size());
for (const auto& tag : tags) {
- dq.policyTags->push_back(tag.second);
+ dq.policyTags->insert(tag.second);
}
}
});
std::vector<std::pair<int, std::string> > ret;
if (dq.policyTags) {
int count = 1;
+ ret.reserve(dq.policyTags->size());
for (const auto& tag : *dq.policyTags) {
ret.push_back({count++, tag});
}
{"Custom", (int)DNSFilterEngine::PolicyKind::Custom }
}});
+ d_pd.push_back({"policytypes", in_t {
+ {"None", (int)DNSFilterEngine::PolicyType::None },
+ {"QName", (int)DNSFilterEngine::PolicyType::QName },
+ {"ClientIP", (int)DNSFilterEngine::PolicyType::ClientIP },
+ {"ResponseIP", (int)DNSFilterEngine::PolicyType::ResponseIP },
+ {"NSDName", (int)DNSFilterEngine::PolicyType::NSDName },
+ {"NSIP", (int)DNSFilterEngine::PolicyType::NSIP }
+ }});
+
for(const auto& n : QType::names)
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});
d_lw->writeFunction("getregisteredname", [](const DNSName &dname) {
return getRegisteredName(dname);
});
+
+ d_lw->registerMember<const DNSName (PolicyEvent::*)>("qname", [](const PolicyEvent& event) -> const DNSName& { return event.qname; }, [](PolicyEvent& event, const DNSName& newName) { (void) newName; });
+ d_lw->registerMember<uint16_t (PolicyEvent::*)>("qtype", [](const PolicyEvent& event) -> uint16_t { return event.qtype.getCode(); }, [](PolicyEvent& event, uint16_t newType) { (void) newType; });
+ d_lw->registerMember<bool (PolicyEvent::*)>("isTcp", [](const PolicyEvent& event) -> bool { return event.isTcp; }, [](PolicyEvent& event, bool newTcp) { (void) newTcp; });
+ d_lw->registerMember<const ComboAddress (PolicyEvent::*)>("remote", [](const PolicyEvent& event) -> const ComboAddress& { return event.remote; }, [](PolicyEvent& event, const ComboAddress& newRemote) { (void) newRemote; });
+ d_lw->registerMember("appliedPolicy", &PolicyEvent::appliedPolicy);
+ d_lw->registerFunction<void(PolicyEvent::*)(const std::string&)>("addPolicyTag", [](PolicyEvent& event, const std::string& tag) { if (event.policyTags) { event.policyTags->insert(tag); } });
+ d_lw->registerFunction<void(PolicyEvent::*)(const std::vector<std::pair<int, std::string> >&)>("setPolicyTags", [](PolicyEvent& event, const std::vector<std::pair<int, std::string> >& tags) {
+ if (event.policyTags) {
+ event.policyTags->clear();
+ event.policyTags->reserve(tags.size());
+ for (const auto& tag : tags) {
+ event.policyTags->insert(tag.second);
+ }
+ }
+ });
+ d_lw->registerFunction<std::vector<std::pair<int, std::string> >(PolicyEvent::*)()>("getPolicyTags", [](const PolicyEvent& event) {
+ std::vector<std::pair<int, std::string> > ret;
+ if (event.policyTags) {
+ int count = 1;
+ ret.reserve(event.policyTags->size());
+ for (const auto& tag : *event.policyTags) {
+ ret.push_back({count++, tag});
+ }
+ }
+ return ret;
+ });
+ d_lw->registerFunction<void(PolicyEvent::*)(const std::string&)>("discardPolicy", [](PolicyEvent& event, const std::string& policy) {
+ if (event.discardedPolicies) {
+ (*event.discardedPolicies)[policy] = true;
+ }
+ });
}
void RecursorLua4::postLoad() {
d_ipfilter = d_lw->readVariable<boost::optional<ipfilter_t>>("ipfilter").get_value_or(0);
d_gettag = d_lw->readVariable<boost::optional<gettag_t>>("gettag").get_value_or(0);
d_gettag_ffi = d_lw->readVariable<boost::optional<gettag_ffi_t>>("gettag_ffi").get_value_or(0);
+
+ d_policyHitEventFilter = d_lw->readVariable<boost::optional<policyEventFilter_t>>("policyEventFilter").get_value_or(0);
}
void RecursorLua4::getFeatures(Features & features) {
// Add key-values pairs below.
- // Make sure you add string values explicity converted to string.
+ // Make sure you add string values explicitly converted to string.
// e.g. features.push_back(make_pair("somekey", string("stringvalue"));
// Both int and double end up as a lua number type.
features.push_back(make_pair("PR8001_devicename", true));
return false; // don't block
}
-unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName) const
+bool RecursorLua4::policyHitEventFilter(const ComboAddress& remote, const DNSName& qname, const QType& qtype, bool tcp, DNSFilterEngine::Policy& policy, std::unordered_set<std::string>& tags, std::unordered_map<std::string, bool>& dicardedPolicies) const
+{
+ if (!d_policyHitEventFilter) {
+ return false;
+ }
+
+ PolicyEvent event(remote, qname, qtype, tcp);
+ event.appliedPolicy = &policy;
+ event.policyTags = &tags;
+ event.discardedPolicies = &dicardedPolicies;
+
+ if (d_policyHitEventFilter(event)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const
{
if(d_gettag) {
- auto ret = d_gettag(remote, ednssubnet, local, qname, qtype, ednsOptions, tcp);
+ std::vector<std::pair<int, const ProxyProtocolValue*>> proxyProtocolValuesMap;
+ proxyProtocolValuesMap.reserve(proxyProtocolValues.size());
+ int num = 1;
+ for (const auto& value : proxyProtocolValues) {
+ proxyProtocolValuesMap.emplace_back(num++, &value);
+ }
+
+ auto ret = d_gettag(remote, ednssubnet, local, qname, qtype, ednsOptions, tcp, proxyProtocolValuesMap);
if (policyTags) {
const auto& tags = std::get<1>(ret);
if (tags) {
+ policyTags->reserve(policyTags->size() + tags->size());
for (const auto& tag : *tags) {
- policyTags->push_back(tag.second);
+ policyTags->insert(tag.second);
}
}
}
if (deviceNameret) {
deviceName = *deviceNameret;
}
+
+ const auto routingTarget = std::get<6>(ret);
+ if (routingTarget) {
+ routingTag = *routingTarget;
+ }
+
return std::get<0>(ret);
}
return 0;
struct pdns_ffi_param
{
public:
- pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::vector<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, boost::optional<int>& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), rcode(rcode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_)
+ pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::unordered_set<std::string>& policyTags_, std::vector<DNSRecord>& records_, const EDNSOptionViewMap& ednsOptions_, const std::vector<ProxyProtocolValue>& proxyProtocolValues_, std::string& requestorId_, std::string& deviceId_, std::string& deviceName_, std::string& routingTag_, boost::optional<int>& rcode_, uint32_t& ttlCap_, bool& variable_, bool tcp_, bool& logQuery_, bool& logResponse_, bool& followCNAMERecords_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), records(records_), ednsOptions(ednsOptions_), proxyProtocolValues(proxyProtocolValues_), requestorId(requestorId_), deviceId(deviceId_), deviceName(deviceName_), routingTag(routingTag_), rcode(rcode_), ttlCap(ttlCap_), variable(variable_), logQuery(logQuery_), logResponse(logResponse_), followCNAMERecords(followCNAMERecords_), qtype(qtype_), tcp(tcp_)
{
}
std::unique_ptr<std::string> remoteStr{nullptr};
std::unique_ptr<std::string> ednssubnetStr{nullptr};
std::vector<pdns_ednsoption_t> ednsOptionsVect;
+ std::vector<pdns_proxyprotocol_value_t> proxyProtocolValuesVect;
const DNSName& qname;
const ComboAddress& local;
const ComboAddress& remote;
const Netmask& ednssubnet;
- std::vector<std::string>& policyTags;
+ std::unordered_set<std::string>& policyTags;
std::vector<DNSRecord>& records;
const EDNSOptionViewMap& ednsOptions;
+ const std::vector<ProxyProtocolValue>& proxyProtocolValues;
std::string& requestorId;
std::string& deviceId;
std::string& deviceName;
+ std::string& routingTag;
boost::optional<int>& rcode;
uint32_t& ttlCap;
bool& variable;
bool tcp;
};
-unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const
+unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const
{
if (d_gettag_ffi) {
- pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, records, ednsOptions, requestorId, deviceId, deviceName, rcode, ttlCap, variable, tcp, logQuery, logResponse, followCNAMERecords);
+ pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, tcp, logQuery, logResponse, followCNAMERecords);
auto ret = d_gettag_ffi(¶m);
if (ret) {
ret = followCNAMERecords(dq.records, QType(dq.qtype));
}
else if(dq.followupFunction=="getFakeAAAARecords") {
- ret=getFakeAAAARecords(dq.followupName, dq.followupPrefix, dq.records);
+ ret=getFakeAAAARecords(dq.followupName, ComboAddress(dq.followupPrefix), dq.records);
}
else if(dq.followupFunction=="getFakePTRRecords") {
- ret=getFakePTRRecords(dq.followupName, dq.followupPrefix, dq.records);
+ ret=getFakePTRRecords(dq.followupName, dq.records);
}
else if(dq.followupFunction=="udpQueryResponse") {
dq.udpAnswer = GenUDPQueryResponse(dq.udpQueryDest, dq.udpQuery);
return pos;
}
+size_t pdns_ffi_param_get_proxy_protocol_values(pdns_ffi_param_t* ref, const pdns_proxyprotocol_value_t** out)
+{
+ if (ref->proxyProtocolValues.empty()) {
+ return 0;
+ }
+
+ ref->proxyProtocolValuesVect.resize(ref->proxyProtocolValues.size());
+
+ size_t pos = 0;
+ for (const auto& value : ref->proxyProtocolValues) {
+ auto& dest = ref->proxyProtocolValuesVect.at(pos);
+ dest.type = value.type;
+ dest.len = value.content.size();
+ if (dest.len > 0) {
+ dest.data = value.content.data();
+ }
+ pos++;
+ }
+
+ *out = ref->proxyProtocolValuesVect.data();
+
+ return ref->proxyProtocolValuesVect.size();
+}
+
void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag)
{
ref->tag = tag;
void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name)
{
- ref->policyTags.push_back(std::string(name));
+ ref->policyTags.insert(std::string(name));
}
void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name)
ref->deviceId = std::string(reinterpret_cast<const char*>(name), len);
}
+void pdns_ffi_param_set_routingtag(pdns_ffi_param_t* ref, const char* rtag)
+{
+ ref->routingTag = std::string(rtag);
+}
+
void pdns_ffi_param_set_variable(pdns_ffi_param_t* ref, bool variable)
{
ref->variable = variable;
#include "ednsoptions.hh"
#include "validate.hh"
#include "lua-base4.hh"
+#include "proxy-protocol.hh"
+
#include <unordered_map>
#include "lua-recursor4-ffi.hh"
const uint16_t* ednsFlags{nullptr};
vector<DNSRecord>* currentRecords{nullptr};
DNSFilterEngine::Policy* appliedPolicy{nullptr};
- std::vector<std::string>* policyTags{nullptr};
- std::unordered_map<std::string,bool>* discardedPolicies{nullptr};
+ std::unordered_set<std::string>* policyTags{nullptr};
+ const std::vector<ProxyProtocolValue>* proxyProtocolValues{nullptr};
+ std::unordered_map<std::string, bool>* discardedPolicies{nullptr};
std::string requestorId;
std::string deviceId;
std::string deviceName;
- vState validationState{Indeterminate};
+ vState validationState{vState::Indeterminate};
bool& variable;
bool& wantsRPZ;
bool& logResponse;
vector<pair<uint16_t, string> > getEDNSOptions() const;
boost::optional<string> getEDNSOption(uint16_t code) const;
boost::optional<Netmask> getEDNSSubnet() const;
+ std::vector<std::pair<int, ProxyProtocolValue>> getProxyProtocolValues() const;
vector<string> getEDNSFlags() const;
bool getEDNSFlag(string flag) const;
void setRecords(const vector<pair<int,DNSRecord> >& records);
DNSName followupName;
};
- unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName) const;
- unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const;
+ struct PolicyEvent
+ {
+ PolicyEvent(const ComboAddress& rem, const DNSName& name, const QType& type, bool tcp): qname(name), qtype(type), remote(rem), isTcp(tcp)
+ {
+ }
+ const DNSName& qname;
+ const QType qtype;
+ const ComboAddress& remote;
+ const bool isTcp;
+ DNSFilterEngine::Policy* appliedPolicy{nullptr};
+ std::unordered_set<std::string>* policyTags{nullptr};
+ std::unordered_map<std::string, bool>* discardedPolicies{nullptr};
+ };
+
+ unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const;
+ unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const;
void maintenance() const;
bool prerpz(DNSQuestion& dq, int& ret) const;
bool preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret) const;
bool ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader&) const;
+ bool policyHitEventFilter(const ComboAddress& remote, const DNSName& qname, const QType& qtype, bool tcp, DNSFilterEngine::Policy& policy, std::unordered_set<std::string>& tags, std::unordered_map<std::string, bool>& dicardedPolicies) const;
+
bool needDQ() const
{
return (d_prerpz ||
d_postresolve);
}
- typedef std::function<std::tuple<unsigned int,boost::optional<std::unordered_map<int,string> >,boost::optional<LuaContext::LuaObject>,boost::optional<std::string>,boost::optional<std::string>,boost::optional<std::string> >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t, const EDNSOptionViewMap&, bool)> gettag_t;
+ typedef std::function<std::tuple<unsigned int,boost::optional<std::unordered_map<int, string> >,boost::optional<LuaContext::LuaObject>,boost::optional<std::string>,boost::optional<std::string>,boost::optional<std::string>,boost::optional<string> >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t, const EDNSOptionViewMap&, bool, const std::vector<std::pair<int, const ProxyProtocolValue*>>&)> gettag_t;
gettag_t d_gettag; // public so you can query if we have this hooked
typedef std::function<boost::optional<LuaContext::LuaObject>(pdns_ffi_param_t*)> gettag_ffi_t;
gettag_ffi_t d_gettag_ffi;
bool genhook(const luacall_t& func, DNSQuestion& dq, int& ret) const;
typedef std::function<bool(ComboAddress,ComboAddress, struct dnsheader)> ipfilter_t;
ipfilter_t d_ipfilter;
+ typedef std::function<bool(PolicyEvent&)> policyEventFilter_t;
+ policyEventFilter_t d_policyHitEventFilter;
};
#include <boost/algorithm/string.hpp>
#include "validate-recursor.hh"
#include "ednssubnet.hh"
+#include "query-local-address.hh"
#ifdef HAVE_PROTOBUF
#ifdef HAVE_FSTRM
#include "rec-dnstap.hh"
#include "fstrm_logger.hh"
+
+
bool g_syslog;
static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
Socket s(ip.sin4.sin_family, SOCK_STREAM);
s.setNonBlocking();
- ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
+ ComboAddress local = pdns::getQueryLocalAddress(ip.sin4.sin_family, 0);
s.bind(local);
int asendto(const char *data, size_t len, int flags, const ComboAddress& ip, uint16_t id,
const DNSName& domain, uint16_t qtype, int* fd);
int arecvfrom(std::string& packet, int flags, const ComboAddress& ip, size_t *d_len, uint16_t id,
- const DNSName& domain, uint16_t qtype, int fd, struct timeval* now);
+ const DNSName& domain, uint16_t qtype, int fd, const struct timeval* now);
class LWResException : public PDNSException
{
#include "packetcache.hh"
#include "base64.hh"
#include "namespaces.hh"
+#include "query-local-address.hh"
void CommunicatorClass::queueNotifyDomain(const DomainInfo& di, UeberBackend* B)
ComboAddress remote(ip, 53); // default to 53
if((d_nsock6 < 0 && remote.sin4.sin_family == AF_INET6) ||
(d_nsock4 < 0 && remote.sin4.sin_family == AF_INET)) {
- g_log<<Logger::Warning<<"Unable to notify "<<remote.toStringWithPort()<<" for domain '"<<domain<<"', address family is disabled. Is query-local-address"<<(remote.sin4.sin_family == AF_INET ? "" : "6")<<" unset?"<<endl;
+ g_log<<Logger::Warning<<"Unable to notify "<<remote.toStringWithPort()<<" for domain '"<<domain<<"', address family is disabled. Is an IPv"<<(remote.sin4.sin_family == AF_INET ? "4" : "6")<<" address set in query-local-address?"<<endl;
d_nq.removeIf(remote.toStringWithPort(), id, domain); // Remove, we'll never be able to notify
continue; // don't try to notify what we can't!
}
void CommunicatorClass::drillHole(const DNSName &domain, const string &ip)
{
- Lock l(&d_holelock);
+ std::lock_guard<std::mutex> l(d_holelock);
d_holes[make_pair(domain,ip)]=time(0);
}
bool CommunicatorClass::justNotified(const DNSName &domain, const string &ip)
{
- Lock l(&d_holelock);
+ std::lock_guard<std::mutex> l(d_holelock);
if(d_holes.find(make_pair(domain,ip))==d_holes.end()) // no hole
return false;
void CommunicatorClass::makeNotifySockets()
{
- if(!::arg()["query-local-address"].empty()) {
- d_nsock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+ d_nsock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
} else {
d_nsock4 = -1;
}
- if(!::arg()["query-local-address6"].empty()) {
- d_nsock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+ d_nsock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
} else {
d_nsock6 = -1;
}
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
+#include <limits.h>
#ifdef __FreeBSD__
# include <pthread_np.h>
#endif
# include <sched.h>
#endif
-bool g_singleThreaded;
-
size_t writen2(int fd, const void *buf, size_t count)
{
const char *ptr = (char*)buf;
return ret.str();
}
-DTime::DTime()
-{
-// set(); // saves lots of gettimeofday calls
- d_set.tv_sec=d_set.tv_usec=0;
-}
-
-time_t DTime::time()
-{
- return d_set.tv_sec;
-}
-
const string unquotify(const string &item)
{
if(item.size()<2)
return ret;
}
-// shuffle, maintaining some semblance of order
-void shuffle(vector<DNSZoneRecord>& rrs)
-{
- vector<DNSZoneRecord>::iterator first, second;
- for(first=rrs.begin();first!=rrs.end();++first)
- if(first->dr.d_place==DNSResourceRecord::ANSWER && first->dr.d_type != QType::CNAME) // CNAME must come first
- break;
- for(second=first;second!=rrs.end();++second)
- if(second->dr.d_place!=DNSResourceRecord::ANSWER)
- break;
-
- if(second-first > 1)
- random_shuffle(first,second);
-
- // now shuffle the additional records
- for(first=second;first!=rrs.end();++first)
- if(first->dr.d_place==DNSResourceRecord::ADDITIONAL && first->dr.d_type != QType::CNAME) // CNAME must come first
- break;
- for(second=first;second!=rrs.end();++second)
- if(second->dr.d_place!=DNSResourceRecord::ADDITIONAL)
- break;
-
- if(second-first>1)
- random_shuffle(first,second);
-
- // we don't shuffle the rest
-}
-
-
-// shuffle, maintaining some semblance of order
-void shuffle(vector<DNSRecord>& rrs)
-{
- vector<DNSRecord>::iterator first, second;
- for(first=rrs.begin();first!=rrs.end();++first)
- if(first->d_place==DNSResourceRecord::ANSWER && first->d_type != QType::CNAME) // CNAME must come first
- break;
- for(second=first;second!=rrs.end();++second)
- if(second->d_place!=DNSResourceRecord::ANSWER || second->d_type == QType::RRSIG) // leave RRSIGs at the end
- break;
-
- if(second-first>1)
- random_shuffle(first,second);
-
- // now shuffle the additional records
- for(first=second;first!=rrs.end();++first)
- if(first->d_place==DNSResourceRecord::ADDITIONAL && first->d_type != QType::CNAME) // CNAME must come first
- break;
- for(second=first; second!=rrs.end(); ++second)
- if(second->d_place!=DNSResourceRecord::ADDITIONAL)
- break;
-
- if(second-first>1)
- random_shuffle(first,second);
-
- // we don't shuffle the rest
-}
-
-static uint16_t mapTypesToOrder(uint16_t type)
-{
- if(type == QType::CNAME)
- return 0;
- if(type == QType::RRSIG)
- return 65535;
- else
- return 1;
-}
-
-// make sure rrs is sorted in d_place order to avoid surprises later
-// then shuffle the parts that desire shuffling
-void orderAndShuffle(vector<DNSRecord>& rrs)
-{
- std::stable_sort(rrs.begin(), rrs.end(), [](const DNSRecord&a, const DNSRecord& b) {
- return std::make_tuple(a.d_place, mapTypesToOrder(a.d_type)) < std::make_tuple(b.d_place, mapTypesToOrder(b.d_type));
- });
- shuffle(rrs);
-}
-
void normalizeTV(struct timeval& tv)
{
if(tv.tv_usec > 1000000) {
hints.ai_family = AF_INET6;
hints.ai_flags = AI_NUMERICHOST;
- int error;
// getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
- if((error=getaddrinfo(ourAddr.c_str(), 0, &hints, &res))) {
+ if (getaddrinfo(ourAddr.c_str(), 0, &hints, &res) != 0) {
return -1;
}
return false;
#endif /* F_SETPIPE_SZ */
}
+
+DNSName reverseNameFromIP(const ComboAddress& ip)
+{
+ if (ip.isIPv4()) {
+ std::string result("in-addr.arpa.");
+ auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin4.sin_addr.s_addr);
+ for (size_t idx = 0; idx < sizeof(ip.sin4.sin_addr.s_addr); idx++) {
+ result = std::to_string(ptr[idx]) + "." + result;
+ }
+ return DNSName(result);
+ }
+ else if (ip.isIPv6()) {
+ std::string result("ip6.arpa.");
+ auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin6.sin6_addr.s6_addr[0]);
+ for (size_t idx = 0; idx < sizeof(ip.sin6.sin6_addr.s6_addr); idx++) {
+ std::stringstream stream;
+ stream << std::hex << (ptr[idx] & 0x0F);
+ stream << '.';
+ stream << std::hex << (((ptr[idx]) >> 4) & 0x0F);
+ stream << '.';
+ result = stream.str() + result;
+ }
+ return DNSName(result);
+ }
+
+ 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;
+}
size_t readn2WithTimeout(int fd, void* buffer, size_t len, int idleTimeout, int totalTimeout=0);
size_t writen2WithTimeout(int fd, const void * buffer, size_t len, int timeout);
+void toLowerInPlace(string& str);
const string toLower(const string &upper);
const string toLowerCanonic(const string &upper);
bool IpToU32(const string &str, uint32_t *ip);
class DTime
{
public:
- DTime(); //!< Does not set the timer for you! Saves lots of gettimeofday() calls
+ //!< Does not set the timer for you! Saves lots of gettimeofday() calls
+ DTime() = default;
DTime(const DTime &dt) = default;
DTime & operator=(const DTime &dt) = default;
- time_t time();
+ inline time_t time() const;
inline void set(); //!< Reset the timer
- inline int udiff(); //!< Return the number of microseconds since the timer was last set.
- inline int udiffNoReset(); //!< Return the number of microseconds since the timer was last set.
+ inline int udiff(bool reset = true); //!< Return the number of microseconds since the timer was last set.
+
+ int udiffNoReset() //!< Return the number of microseconds since the timer was last set.
+ {
+ return udiff(false);
+ }
void setTimeval(const struct timeval& tv)
{
d_set=tv;
}
- struct timeval getTimeval()
+ struct timeval getTimeval() const
{
return d_set;
}
private:
- struct timeval d_set;
+struct timeval d_set{0, 0};
};
-inline void DTime::set()
+inline time_t DTime::time() const
{
- gettimeofday(&d_set,0);
+ return d_set.tv_sec;
}
-inline int DTime::udiff()
+inline void DTime::set()
{
- int res=udiffNoReset();
- gettimeofday(&d_set,0);
- return res;
+ gettimeofday(&d_set, nullptr);
}
-inline int DTime::udiffNoReset()
+inline int DTime::udiff(bool reset)
{
struct timeval now;
+ gettimeofday(&now, nullptr);
- gettimeofday(&now,0);
int ret=1000000*(now.tv_sec-d_set.tv_sec)+(now.tv_usec-d_set.tv_usec);
+
+ if (reset) {
+ d_set = now;
+ }
+
return ret;
}
-inline const string toLower(const string &upper)
+inline void toLowerInPlace(string& str)
{
- string reply(upper);
- const size_t length = reply.length();
+ const size_t length = str.length();
char c;
- for(unsigned int i = 0; i < length; ++i) {
- c = dns_tolower(upper[i]);
- if( c != upper[i])
- reply[i] = c;
+ for (size_t i = 0; i < length; ++i) {
+ c = dns_tolower(str[i]);
+ if (c != str[i]) {
+ str[i] = c;
+ }
}
+}
+
+inline const string toLower(const string &upper)
+{
+ string reply(upper);
+
+ toLowerInPlace(reply);
+
return reply;
}
}
string makeHexDump(const string& str);
-struct DNSRecord;
-struct DNSZoneRecord;
-void shuffle(vector<DNSRecord>& rrs);
-void shuffle(vector<DNSZoneRecord>& rrs);
-
-void orderAndShuffle(vector<DNSRecord>& rrs);
void normalizeTV(struct timeval& tv);
const struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs);
int mapThreadToCPUList(pthread_t tid, const std::set<int>& cpus);
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath);
+
+DNSName reverseNameFromIP(const ComboAddress& ip);
+
+std::string getCarbonHostName();
\return returns -1 in case of error, 0 in case of timeout, 1 in case of an answer
*/
-template<class EventKey, class EventVal>int MTasker<EventKey,EventVal>::waitEvent(EventKey &key, EventVal *val, unsigned int timeoutMsec, struct timeval* now)
+template<class EventKey, class EventVal>int MTasker<EventKey,EventVal>::waitEvent(EventKey &key, EventVal *val, unsigned int timeoutMsec, const struct timeval* now)
{
if(d_waiters.count(key)) { // there was already an exact same waiter
return -1;
\return Returns if there is more work scheduled and recalling schedule now would be useful
*/
-template<class Key, class Val>bool MTasker<Key,Val>::schedule(struct timeval* now)
+template<class Key, class Val>bool MTasker<Key,Val>::schedule(const struct timeval* now)
{
if(!d_runQueue.empty()) {
d_tid=d_runQueue.front();
}
typedef void tfunc_t(void *); //!< type of the pointer that starts a thread
- int waitEvent(EventKey &key, EventVal *val=0, unsigned int timeoutMsec=0, struct timeval* now=0);
+ int waitEvent(EventKey &key, EventVal *val=nullptr, unsigned int timeoutMsec=0, const struct timeval* now=nullptr);
void yield();
- int sendEvent(const EventKey& key, const EventVal* val=0);
+ int sendEvent(const EventKey& key, const EventVal* val=nullptr);
void getEvents(std::vector<EventKey>& events);
void makeThread(tfunc_t *start, void* val);
- bool schedule(struct timeval* now=0);
+ bool schedule(const struct timeval* now=nullptr);
bool noProcesses() const;
unsigned int numProcesses() const;
int getTid() const;
}
}
-#ifdef SO_REUSEPORT
- if( d_can_reuseport )
- if( setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) )
- d_can_reuseport = false;
-#endif
+ if (d_can_reuseport) {
+ if (!setReusePort(s)) {
+ d_can_reuseport = false;
+ }
+ }
if( ::arg().mustDo("non-local-bind") )
Utility::setBindAny(locala.sin4.sin_family, s);
UDPNameserver::UDPNameserver( bool additional_socket )
{
-#ifdef SO_REUSEPORT
d_can_reuseport = ::arg().mustDo("reuseport");
-#endif
// Are we the main socket (false) or a rebinding using SO_REUSEPORT ?
d_additional_socket = additional_socket;
bool receive(DNSPacket& packet, std::string& buffer); //!< call this in a while or for(;;) loop to get packets
void send(DNSPacket&); //!< send a DNSPacket. Will call DNSPacket::truncate() if over 512 bytes
inline bool canReusePort() {
-#ifdef SO_REUSEPORT
return d_can_reuseport;
-#else
- return false;
-#endif
};
private:
bool d_additional_socket;
-#ifdef SO_REUSEPORT
- bool d_can_reuseport;
-#endif
+ bool d_can_reuseport{false};
vector<int> d_sockets;
void bindAddresses();
vector<pollfd> d_rfds;
return arg;
}
-void usage() {
+static void usage() {
cerr<<"Syntax: pdns_notify IP_ADDRESS/HOSTNAME[:PORT] DOMAIN"<<endl;
}
typedef map<uint16_t, NotificationInFlight> nifs_t;
nifs_t g_nifs;
-void syslogFmt(const boost::format& fmt)
+static void syslogFmt(const boost::format& fmt)
{
cerr<<"nproxy: "<<fmt<<endl;
syslog(LOG_WARNING, "%s", str(fmt).c_str());
}
-void handleOutsideUDPPacket(int fd, boost::any&)
+static void handleOutsideUDPPacket(int fd, boost::any&)
try
{
char buffer[1500];
}
-void handleInsideUDPPacket(int fd, boost::any&)
+static void handleInsideUDPPacket(int fd, boost::any&)
try
{
char buffer[1500];
syslogFmt(boost::format("Error parsing packet from internal nameserver: %s") % e.what());
}
-void expireOldNotifications()
+static void expireOldNotifications()
{
time_t limit = time(0) - 10;
for(nifs_t::iterator iter = g_nifs.begin(); iter != g_nifs.end(); ) {
}
}
-void daemonize(int null_fd);
+static void daemonize(int null_fd)
+{
+ if(fork())
+ exit(0); // bye bye
-void usage(po::options_description &desc) {
+ setsid();
+
+ dup2(null_fd,0); /* stdin */
+ dup2(null_fd,1); /* stderr */
+ dup2(null_fd,2); /* stderr */
+}
+
+static void usage(po::options_description &desc) {
cerr<<"nproxy"<<endl;
cerr<<desc<<endl;
}
{
syslogFmt(boost::format("Fatal: %s") % e.reason);
}
-
-void daemonize(int null_fd)
-{
- if(fork())
- exit(0); // bye bye
-
- setsid();
-
- dup2(null_fd,0); /* stdin */
- dup2(null_fd,1); /* stderr */
- dup2(null_fd,2); /* stderr */
-}
typedef std::pair<string,string> nsec3;
typedef set<nsec3> nsec3set;
-string nsec3Hash(const DNSName &qname, const string &salt, unsigned int iters)
+static string nsec3Hash(const DNSName &qname, const string &salt, unsigned int iters)
{
NSEC3PARAMRecordContent ns3prc;
ns3prc.d_iterations = iters;
return toBase32Hex(hashQNameWithSalt(ns3prc, qname));
}
-void proveOrDeny(const nsec3set &nsec3s, const DNSName &qname, const string &salt, unsigned int iters, set<DNSName> &proven, set<DNSName> &denied)
+static void proveOrDeny(const nsec3set &nsec3s, const DNSName &qname, const string &salt, unsigned int iters, set<DNSName> &proven, set<DNSName> &denied)
{
string hashed = nsec3Hash(qname, salt, iters);
}
}
-void usage() {
+static void usage() {
cerr<<"nsec3dig"<<endl;
cerr<<"Syntax: nsec3dig IP-ADDRESS PORT QUESTION QUESTION-TYPE [recurse]\n";
}
#if defined(HAVE_LIBCRYPTO_ED25519) || defined(HAVE_LIBCRYPTO_ED448)
#include <openssl/evp.h>
#endif
+#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL)
/* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */
-static pthread_mutex_t *openssllocks;
+
+#include "lock.hh"
+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) {
- pthread_mutex_lock(&(openssllocks[type]));
+ openssllocks.at(type).lock();
- }else {
- pthread_mutex_unlock(&(openssllocks[type]));
+ } else {
+ openssllocks.at(type).unlock();
}
}
-unsigned long openssl_pthreads_id_callback()
+static unsigned long openssl_pthreads_id_callback(void)
{
return (unsigned long)pthread_self();
}
void openssl_thread_setup()
{
- openssllocks = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
-
- for (int i = 0; i < CRYPTO_num_locks(); i++)
- pthread_mutex_init(&(openssllocks[i]), NULL);
-
- CRYPTO_set_id_callback(openssl_pthreads_id_callback);
- CRYPTO_set_locking_callback(openssl_pthreads_locking_callback);
+ openssllocks = std::vector<std::mutex>(CRYPTO_num_locks());
+ CRYPTO_set_id_callback(&openssl_pthreads_id_callback);
+ CRYPTO_set_locking_callback(&openssl_pthreads_locking_callback);
}
void openssl_thread_cleanup()
{
- CRYPTO_set_locking_callback(NULL);
-
- for (int i=0; i<CRYPTO_num_locks(); i++) {
- pthread_mutex_destroy(&(openssllocks[i]));
- }
-
- OPENSSL_free(openssllocks);
+ CRYPTO_set_locking_callback(nullptr);
+ openssllocks.clear();
}
#ifndef HAVE_RSA_GET0_KEY
#include "ednsoptions.hh"
#include "misc.hh"
#include "iputils.hh"
+#include "views.hh"
class PacketCache : public boost::noncopyable
{
public:
- static uint32_t canHashPacket(const std::string& packet, uint16_t* ecsBegin, uint16_t* ecsEnd)
+
+ /* hash the packet from the provided position, which should point right after tje qname. This skips:
+ - the query ID ;
+ - EDNS Cookie options, if any ;
+ - EDNS Client Subnet options, if any and skipECS is true.
+ */
+ static uint32_t hashAfterQname(const pdns_string_view& packet, uint32_t currentHash, size_t pos, bool skipECS)
{
- uint32_t ret = 0;
- ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
- size_t packetSize = packet.size();
- size_t pos = sizeof(dnsheader);
- const char* end = packet.c_str() + packetSize;
- const char* p = packet.c_str() + pos;
-
- for(; p < end && *p; ++p, ++pos) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
- const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
- ret=burtle(&l, 1, ret);
- } // XXX the embedded 0 in the qname will break the subnet stripping
-
- const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(packet.c_str());
- const char* skipBegin = p;
- const char* skipEnd = p;
- if (ecsBegin != nullptr && ecsEnd != nullptr) {
- *ecsBegin = 0;
- *ecsEnd = 0;
- }
- /* we need at least 1 (final empty label) + 2 (QTYPE) + 2 (QCLASS)
+ const size_t packetSize = packet.size();
+ assert(packetSize >= sizeof(dnsheader));
+
+ /* we need at least 2 (QTYPE) + 2 (QCLASS)
+
+ OPT root label (1), type (2), class (2) and ttl (4)
+ the OPT RR rdlen (2)
- = 16
+ = 15
*/
- if(ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
- char* optionBegin = nullptr;
- size_t optionLen = 0;
- /* skip the final empty label (1), the qtype (2), qclass (2) */
- /* root label (1), type (2), class (2) and ttl (4) */
- int res = getEDNSOption(const_cast<char*>(reinterpret_cast<const char*>(p)) + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
- if (res == 0) {
- skipBegin = optionBegin;
- skipEnd = optionBegin + optionLen;
- if (ecsBegin != nullptr && ecsEnd != nullptr) {
- *ecsBegin = optionBegin - packet.c_str();
- *ecsEnd = *ecsBegin + optionLen;
+ const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(packet.data());
+ if (ntohs(dh->qdcount) != 1 || ntohs(dh->ancount) != 0 || ntohs(dh->nscount) != 0 || ntohs(dh->arcount) != 1 || (pos + 15) >= packetSize) {
+ if (packetSize > pos) {
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(pos)), packetSize - pos, currentHash);
+ }
+ return currentHash;
+ }
+
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(pos)), 15, currentHash);
+ /* skip the qtype (2), qclass (2) */
+ /* root label (1), type (2), class (2) and ttl (4) */
+ /* already hashed above */
+ pos += 13;
+
+ const uint16_t rdLen = ((static_cast<uint16_t>(packet.at(pos)) * 256) + static_cast<uint16_t>(packet.at(pos + 1)));
+ /* skip the rd length */
+ /* already hashed above */
+ pos += 2;
+
+ if (rdLen > (packetSize - pos)) {
+ if (pos < packetSize) {
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(pos)), packetSize - pos, currentHash);
+ }
+ return currentHash;
+ }
+
+ uint16_t rdataRead = 0;
+ uint16_t optionCode;
+ uint16_t optionLen;
+
+ while (pos < packetSize && rdataRead < rdLen && getNextEDNSOption(&packet.at(pos), rdLen - rdataRead, optionCode, optionLen)) {
+ if (optionLen > (rdLen - rdataRead - 4)) {
+ if (packetSize > pos) {
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(pos)), packetSize - pos, currentHash);
}
+ return currentHash;
}
+
+ bool skip = false;
+ if (optionCode == EDNSOptionCode::COOKIE) {
+ skip = true;
+ }
+ else if (optionCode == EDNSOptionCode::ECS) {
+ if (skipECS) {
+ skip = true;
+ }
+ }
+
+ if (!skip) {
+ /* hash the option code, length and content */
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(pos)), 4 + optionLen, currentHash);
+ }
+ else {
+ /* hash the option code and length */
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(pos)), 4, currentHash);
+ }
+
+ pos += 4 + optionLen;
+ rdataRead += 4 + optionLen;
}
- if (skipBegin > p) {
- ret = burtle(reinterpret_cast<const unsigned char*>(p), skipBegin-p, ret);
+
+ if (pos < packetSize) {
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(pos)), packetSize - pos, currentHash);
}
- if (skipEnd < end) {
- ret = burtle(reinterpret_cast<const unsigned char*>(skipEnd), end-skipEnd, ret);
+
+ return currentHash;
+ }
+
+ static uint32_t hashHeaderAndQName(const std::string& packet, size_t& pos)
+ {
+ uint32_t currentHash = 0;
+ const size_t packetSize = packet.size();
+ assert(packetSize >= sizeof(dnsheader));
+ currentHash = burtle(reinterpret_cast<const unsigned char*>(&packet.at(2)), sizeof(dnsheader) - 2, currentHash); // rest of dnsheader, skip id
+ pos = sizeof(dnsheader);
+
+ for (; pos < packetSize; ) {
+ const unsigned char labelLen = static_cast<unsigned char>(packet.at(pos));
+ currentHash = burtle(&labelLen, 1, currentHash);
+ ++pos;
+ if (labelLen == 0) {
+ break;
+ }
+
+ for (size_t idx = 0; idx < labelLen && pos < packetSize; ++idx, ++pos) {
+ const unsigned char l = dns_tolower(packet.at(pos));
+ currentHash = burtle(&l, 1, currentHash);
+ }
}
- return ret;
+ return currentHash;
}
- static uint32_t canHashPacket(const std::string& packet)
+ /* hash the packet from the beginning, including the qname. This skips:
+ - the query ID ;
+ - EDNS Cookie options, if any ;
+ - EDNS Client Subnet options, if any and skipECS is true.
+ */
+ static uint32_t canHashPacket(const std::string& packet, bool skipECS)
{
- uint32_t ret = 0;
- ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
+ size_t pos = 0;
+ uint32_t currentHash = hashHeaderAndQName(packet, pos);
size_t packetSize = packet.size();
- size_t pos = sizeof(dnsheader);
- const char* end = packet.c_str() + packetSize;
- const char* p = packet.c_str() + pos;
-
- for(; p < end && *p; ++p) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
- const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
- ret=burtle(&l, 1, ret);
- } // XXX the embedded 0 in the qname will break the subnet stripping
- if (p < end) {
- ret = burtle(reinterpret_cast<const unsigned char*>(p), end-p, ret);
+ if (pos >= packetSize) {
+ return currentHash;
}
- return ret;
+ return hashAfterQname(packet, currentHash, pos, skipECS);
}
static bool queryHeaderMatches(const std::string& cachedQuery, const std::string& query)
return (cachedQuery.compare(/* skip the ID */ 2, sizeof(dnsheader) - 2, query, 2, sizeof(dnsheader) - 2) == 0);
}
- static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname)
+ static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname, const std::unordered_set<uint16_t>& optionsToIgnore)
{
+ const size_t querySize = query.size();
+ const size_t cachedQuerySize = cachedQuery.size();
+ if (querySize != cachedQuerySize) {
+ return false;
+ }
+
if (!queryHeaderMatches(cachedQuery, query)) {
return false;
}
size_t pos = sizeof(dnsheader) + qname.wirelength();
- return (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) == 0);
- }
+ /* we need at least 2 (QTYPE) + 2 (QCLASS)
+ + OPT root label (1), type (2), class (2) and ttl (4)
+ + the OPT RR rdlen (2)
+ = 15
+ */
+ const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(query.data());
+ if (ntohs(dh->qdcount) != 1 || ntohs(dh->ancount) != 0 || ntohs(dh->nscount) != 0 || ntohs(dh->arcount) != 1 || (pos + 15) >= querySize || optionsToIgnore.empty()) {
+ return cachedQuery.compare(pos, cachedQuerySize - pos, query, pos, querySize - pos) == 0;
+ }
- static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname, uint16_t ecsBegin, uint16_t ecsEnd)
- {
- if (!queryHeaderMatches(cachedQuery, query)) {
+ /* compare up to the first option, if any */
+ if (cachedQuery.compare(pos, 15, query, pos, 15) != 0) {
return false;
}
- size_t pos = sizeof(dnsheader) + qname.wirelength();
+ /* skip the qtype (2), qclass (2) */
+ /* root label (1), type (2), class (2) and ttl (4) */
+ /* already compared above */
+ pos += 13;
- if (ecsBegin != 0 && ecsBegin >= pos && ecsEnd > ecsBegin) {
- if (cachedQuery.compare(pos, ecsBegin - pos, query, pos, ecsBegin - pos) != 0) {
- return false;
+ const uint16_t rdLen = ((static_cast<unsigned char>(query.at(pos)) * 256) + static_cast<unsigned char>(query.at(pos + 1)));
+ /* skip the rd length */
+ /* already compared above */
+ pos += sizeof(uint16_t);
+
+ if (rdLen > (querySize - pos)) {
+ /* something is wrong, let's just compare everything */
+ return cachedQuery.compare(pos, cachedQuerySize - pos, query, pos, querySize - pos) == 0;
+ }
+
+ uint16_t rdataRead = 0;
+ uint16_t optionCode;
+ uint16_t optionLen;
+
+ while (pos < querySize && rdataRead < rdLen && getNextEDNSOption(&query.at(pos), rdLen - rdataRead, optionCode, optionLen)) {
+ if (optionLen > (rdLen - rdataRead)) {
+ return cachedQuery.compare(pos, cachedQuerySize - pos, query, pos, querySize - pos) == 0;
}
- if (cachedQuery.compare(ecsEnd, cachedQuery.size() - ecsEnd, query, ecsEnd, query.size() - ecsEnd) != 0) {
+ /* compare the option code and length */
+ if (cachedQuery.compare(pos, 4, query, pos, 4) != 0) {
return false;
}
- }
- else {
- if (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) != 0) {
- return false;
+ pos += 4;
+ rdataRead += 4;
+
+ if (optionLen > 0 && optionsToIgnore.count(optionCode) == 0) {
+ if (cachedQuery.compare(pos, optionLen, query, pos, optionLen) != 0) {
+ return false;
+ }
}
+ pos += optionLen;
+ rdataRead += optionLen;
+ }
+
+ if (pos >= querySize) {
+ return true;
}
- return true;
+ return cachedQuery.compare(pos, cachedQuerySize - pos, query, pos, querySize - pos) == 0;
}
};
d_doDNAME=::arg().mustDo("dname-processing");
d_doExpandALIAS = ::arg().mustDo("expand-alias");
d_logDNSDetails= ::arg().mustDo("log-dns-details");
- d_doIPv6AdditionalProcessing = ::arg().mustDo("do-ipv6-additional-processing");
string fname= ::arg()["lua-prequery-script"];
if(fname.empty())
{
return haveSomething;
}
-/** dangling is declared true if we were unable to resolve everything */
-int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SOAData& soadata, bool retargeted)
-{
- DNSZoneRecord rr;
- SOAData sd;
- sd.db=0;
-
- if(p.qtype.getCode()!=QType::AXFR) { // this packet needs additional processing
- // we now have a copy, push_back on packet might reallocate!
- auto& records = r->getRRS();
- vector<DNSZoneRecord> toAdd;
-
- for(auto i = records.cbegin() ; i!= records.cend(); ++i) {
- if(i->dr.d_place==DNSResourceRecord::ADDITIONAL ||
- !(i->dr.d_type==QType::MX || i->dr.d_type==QType::NS || i->dr.d_type==QType::SRV))
- continue;
-
- if(r->d.aa && i->dr.d_name.countLabels() && i->dr.d_type==QType::NS && !B.getSOA(i->dr.d_name,sd) && !retargeted) { // drop AA in case of non-SOA-level NS answer, except for root referral
- r->setA(false);
- // i->d_place=DNSResourceRecord::AUTHORITY; // XXX FIXME
- }
-
- DNSName lookup;
-
- if(i->dr.d_type == QType::MX)
- lookup = getRR<MXRecordContent>(i->dr)->d_mxname;
- else if(i->dr.d_type == QType::SRV)
- lookup = getRR<SRVRecordContent>(i->dr)->d_target;
- else if(i->dr.d_type == QType::NS)
- lookup = getRR<NSRecordContent>(i->dr)->getNS();
- else
- continue;
-
- B.lookup(QType(d_doIPv6AdditionalProcessing ? QType::ANY : QType::A), lookup, soadata.domain_id, &p);
- while(B.get(rr)) {
- if(rr.dr.d_type != QType::A && rr.dr.d_type!=QType::AAAA)
- continue;
- if(!rr.dr.d_name.isPartOf(soadata.qname)) {
- // FIXME we might still pass on the record if it is occluded and the
- // backend uses a single id for all zones
+void PacketHandler::doAdditionalProcessing(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SOAData& soadata)
+{
+ DNSName content;
+ std::unordered_set<DNSName> lookup;
+ const auto& rrs = r->getRRS();
+
+ lookup.reserve(rrs.size());
+ for(auto& rr : rrs) {
+ if(rr.dr.d_place != DNSResourceRecord::ADDITIONAL) {
+ switch(rr.dr.d_type) {
+ case QType::NS:
+ content=std::move(getRR<NSRecordContent>(rr.dr)->getNS());
+ break;
+ case QType::MX:
+ content=std::move(getRR<MXRecordContent>(rr.dr)->d_mxname);
+ break;
+ case QType::SRV:
+ content=std::move(getRR<SRVRecordContent>(rr.dr)->d_target);
+ break;
+ default:
continue;
- }
- rr.dr.d_place=DNSResourceRecord::ADDITIONAL;
- toAdd.push_back(rr);
+ }
+ if(content.isPartOf(soadata.qname)) {
+ lookup.emplace(std::move(content));
}
}
+ }
- for(auto& rec : toAdd) {
- r->addRecord(std::move(rec));
+ DNSZoneRecord dzr;
+ for(const auto& name : lookup) {
+ B.lookup(QType(QType::ANY), name, soadata.domain_id, &p);
+ while(B.get(dzr)) {
+ if(dzr.dr.d_type == QType::A || dzr.dr.d_type == QType::AAAA) {
+ dzr.dr.d_place=DNSResourceRecord::ADDITIONAL;
+ r->addRecord(std::move(dzr));
+ }
}
-
- //records.insert(records.end(), toAdd.cbegin(), toAdd.cend()); // would be faster, but no dedup
}
- return 1;
}
if(sd.qname == name) {
nrc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
auto keyset = d_dk.getKeys(name);
- if (!keyset.empty()) {
- nrc.set(QType::DNSKEY);
- string publishCDNSKEY;
- d_dk.getPublishCDNSKEY(name, publishCDNSKEY);
- if (publishCDNSKEY == "1")
- nrc.set(QType::CDNSKEY);
- string publishCDS;
- d_dk.getPublishCDS(name, publishCDS);
- if (! publishCDS.empty())
- nrc.set(QType::CDS);
+ for(const auto& value: keyset) {
+ if (value.second.published) {
+ nrc.set(QType::DNSKEY);
+ string publishCDNSKEY;
+ d_dk.getPublishCDNSKEY(name, publishCDNSKEY);
+ if (publishCDNSKEY == "1")
+ nrc.set(QType::CDNSKEY);
+ string publishCDS;
+ d_dk.getPublishCDS(name, publishCDS);
+ if (! publishCDS.empty())
+ nrc.set(QType::CDS);
+ break;
+ }
}
}
n3rc.set(QType::SOA); // 1dfd8ad SOA can live outside the records table
n3rc.set(QType::NSEC3PARAM);
auto keyset = d_dk.getKeys(name);
- if (!keyset.empty()) {
- n3rc.set(QType::DNSKEY);
- string publishCDNSKEY;
- d_dk.getPublishCDNSKEY(name, publishCDNSKEY);
- if (publishCDNSKEY == "1")
- n3rc.set(QType::CDNSKEY);
- string publishCDS;
- d_dk.getPublishCDS(name, publishCDS);
- if (! publishCDS.empty())
- n3rc.set(QType::CDS);
+ for(const auto& value: keyset) {
+ if (value.second.published) {
+ n3rc.set(QType::DNSKEY);
+ string publishCDNSKEY;
+ d_dk.getPublishCDNSKEY(name, publishCDNSKEY);
+ if (publishCDNSKEY == "1")
+ n3rc.set(QType::CDNSKEY);
+ string publishCDS;
+ d_dk.getPublishCDS(name, publishCDS);
+ if (! publishCDS.empty())
+ n3rc.set(QType::CDS);
+ break;
+ }
}
}
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());
int PacketHandler::processNotify(const DNSPacket& p)
{
- /* now what?
+ /* now what?
was this notification from an approved address?
was this notification approved by TSIG?
We determine our internal SOA id (via UeberBackend)
}
if(::arg().mustDo("slave")) {
- g_log<<Logger::Debug<<"Queueing slave check for "<<p.qdomain<<endl;
+ g_log<<Logger::Notice<<"Received NOTIFY for "<<p.qdomain<<" from "<<p.getRemote()<<" - queueing check"<<endl;
Communicator.addSlaveCheckRequest(di, p.d_remote);
}
return 0;
return r;
} else {
getTSIGHashEnum(trc.d_algoName, p.d_tsig_algo);
- if (p.d_tsig_algo == TSIG_GSS) {
- GssContext gssctx(keyname);
- if (!gssctx.getPeerPrincipal(p.d_peer_principal)) {
- g_log<<Logger::Warning<<"Failed to extract peer principal from GSS context with keyname '"<<keyname<<"'"<<endl;
- }
- }
}
p.setTSIGDetails(trc, keyname, secret, trc.d_mac); // this will get copied by replyPacket()
noCache=true;
bool doReferral = true;
if(d_dk.doesDNSSEC()) {
for(auto& loopRR: rrset) {
- // In a dnssec capable backend auth=true means, there is no delagation at
+ // In a dnssec capable backend auth=true means, there is no delegation at
// or above this qname in this zone (for DS queries). Without a delegation,
// at or above this level, it is pointless to search for refferals.
if(loopRR.auth) {
}
sendit:;
- if(doAdditionalProcessingAndDropAA(p, r, sd, retargetcount)<0) {
- return 0;
- }
+ doAdditionalProcessing(p, r, sd);
for(const auto& loopRR: r->getRRS()) {
if(loopRR.scopeMask) {
#include "packetcache.hh"
#include "dnsseckeeper.hh"
#include "lua-auth4.hh"
-#include "gss_context.hh"
#include "namespaces.hh"
bool addCDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SOAData& sd);
bool addCDS(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SOAData& sd);
bool addNSEC3PARAM(const DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SOAData& sd);
- int doAdditionalProcessingAndDropAA(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SOAData& sd, bool retargeted);
+ void doAdditionalProcessing(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SOAData& sd);
void addNSECX(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName &target, const DNSName &wildcard, const DNSName &auth, int mode);
void addNSEC(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName &target, const DNSName &wildcard, const DNSName& auth, int mode);
void addNSEC3(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName &target, const DNSName &wildcard, const DNSName& auth, const NSEC3PARAMRecordContent& nsec3param, bool narrow, int mode);
uint performUpdate(const string &msgPrefix, const DNSRecord *rr, DomainInfo *di, bool isPresigned, bool* narrow, bool* haveNSEC3, NSEC3PARAMRecordContent *ns3pr, bool *updatedSerial);
int checkUpdatePrescan(const DNSRecord *rr);
int checkUpdatePrerequisites(const DNSRecord *rr, DomainInfo *di);
- void increaseSerial(const string &msgPrefix, const DomainInfo *di, bool haveNSEC3, bool narrow, const NSEC3PARAMRecordContent *ns3pr);
+ void increaseSerial(const string &msgPrefix, const DomainInfo *di, const string& soaEditSetting, bool haveNSEC3, bool narrow, const NSEC3PARAMRecordContent *ns3pr);
void makeNXDomain(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, const SOAData& sd);
void makeNOError(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const DNSName& target, const DNSName& wildcard, const SOAData& sd, int mode);
void tkeyHandler(const DNSPacket& p, std::unique_ptr<DNSPacket>& r); //<! process TKEY record, and adds TKEY record to (r)eply, or error code.
static AtomicCounter s_count;
- static pthread_mutex_t s_rfc2136lock;
+ static std::mutex s_rfc2136lock;
bool d_logDNSDetails;
- bool d_doIPv6AdditionalProcessing;
bool d_doDNAME;
bool d_doExpandALIAS;
bool d_dnssec;
DNSSECKeeper d_dk; // B is shared with DNSSECKeeper
};
-std::shared_ptr<DNSRecordContent> makeSOAContent(const SOAData& sd);
[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
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
+# Setting PrivateUsers=true prevents us from opening our sockets
+ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
+ProtectHostname=true
+ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
# ProtectSystem=full will disallow write access to /etc and /usr, possibly
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
+RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=~ @clock @debug @module @mount @raw-io @reboot @swap @cpu-emulation @obsolete
#include "rec-lua-conf.hh"
#include "ednsoptions.hh"
#include "gettime.hh"
+#include "proxy-protocol.hh"
#include "pubsuffix.hh"
+#include "shuffle.hh"
#ifdef NOD_ENABLED
#include "nod.hh"
#endif /* NOD_ENABLED */
+#include "query-local-address.hh"
#include "rec-protobuf.hh"
#include "rec-snmp.hh"
#endif /* HAVE_FSTRM */
thread_local std::unique_ptr<MT_t> MT; // the big MTasker
-std::unique_ptr<MemRecursorCache> s_RC;
-
+std::unique_ptr<MemRecursorCache> g_recCache;
+std::unique_ptr<NegCache> g_negCache;
thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
thread_local FDMultiplexer* t_fdm{nullptr};
std::thread thread;
MT_t* mt{nullptr};
uint64_t numberOfDistributedQueries{0};
+ int exitCode{0};
/* handle the web server, carbon, statistics and the control channel */
bool isHandler{false};
/* accept incoming queries (and distributes them to the workers if pdns-distributes-queries is set) */
typedef vector<int> tcpListenSockets_t;
typedef map<int, ComboAddress> listenSocketsAddresses_t; // is shared across all threads right now
-static const ComboAddress g_local4("0.0.0.0"), g_local6("::");
static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
static set<int> g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism
-static vector<ComboAddress> g_localQueryAddresses4, g_localQueryAddresses6;
static AtomicCounter counter;
static std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
static std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
static NetmaskGroup g_XPFAcl;
+static NetmaskGroup g_proxyProtocolACL;
+static boost::optional<ComboAddress> g_dns64Prefix{boost::none};
+static DNSName g_dns64PrefixReverse;
+static size_t g_proxyProtocolMaximumSize;
static size_t g_tcpMaxQueriesPerConn;
static size_t s_maxUDPQueriesPerRound;
static uint64_t g_latencyStatSize;
{
}
- DNSComboWriter(const std::string& query, const struct timeval& now, std::vector<std::string>&& policyTags, LuaContext::LuaObject&& data, std::vector<DNSRecord>&& records): d_mdp(true, query), d_now(now), d_query(query), d_policyTags(std::move(policyTags)), d_records(std::move(records)), d_data(std::move(data))
+ DNSComboWriter(const std::string& query, const struct timeval& now, std::unordered_set<std::string>&& policyTags, LuaContext::LuaObject&& data, std::vector<DNSRecord>&& records): d_mdp(true, query), d_now(now), d_query(query), d_policyTags(std::move(policyTags)), d_records(std::move(records)), d_data(std::move(data))
{
}
return d_source.toStringWithPort() + " (proxied by " + d_remote.toStringWithPort() + ")";
}
+ std::vector<ProxyProtocolValue> d_proxyProtocolValues;
MOADNSParser d_mdp;
struct timeval d_now;
/* Remote client, might differ from d_source
struct timeval d_kernelTimestamp{0,0};
#endif
std::string d_query;
- std::vector<std::string> d_policyTags;
+ std::unordered_set<std::string> d_policyTags;
+ std::string d_routingTag;
std::vector<DNSRecord> d_records;
LuaContext::LuaObject d_data;
EDNSSubnetOpts d_ednssubnet;
unsigned int d_tag{0};
uint32_t d_qhash{0};
uint32_t d_ttlCap{std::numeric_limits<uint32_t>::max()};
- uint16_t d_ecsBegin{0};
- uint16_t d_ecsEnd{0};
bool d_variable{false};
bool d_ecsFound{false};
bool d_ecsParsed{false};
return t_id;
}
-int getMTaskerTID()
-{
- return MT->getTid();
-}
-
static bool isDistributorThread()
{
if (t_id == 0) {
{
Socket s(dest.sin4.sin_family, SOCK_DGRAM);
s.setNonBlocking();
- ComboAddress local = getQueryLocalAddress(dest.sin4.sin_family, 0);
+ ComboAddress local = pdns::getQueryLocalAddress(dest.sin4.sin_family, 0);
s.bind(local);
s.connect(dest);
return data;
}
-//! pick a random query local address
-ComboAddress getQueryLocalAddress(int family, uint16_t port)
-{
- ComboAddress ret;
- if(family==AF_INET) {
- if(g_localQueryAddresses4.empty())
- ret = g_local4;
- else
- ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())];
- ret.sin4.sin_port = htons(port);
- }
- else {
- if(g_localQueryAddresses6.empty())
- ret = g_local6;
- else
- ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())];
-
- ret.sin6.sin6_port = htons(port);
- }
- return ret;
-}
-
static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t&);
static void setSocketBuffer(int fd, int optname, uint32_t size)
while (s_avoidUdpSourcePorts.count(port));
}
- sin=getQueryLocalAddress(family, port); // does htons for us
+ sin=pdns::getQueryLocalAddress(family, port); // does htons for us
if (::bind(ret, (struct sockaddr *)&sin, sin.getSocklen()) >= 0)
break;
// -1 is error, 0 is timeout, 1 is success
int arecvfrom(std::string& packet, int flags, const ComboAddress& fromaddr, size_t *d_len,
- uint16_t id, const DNSName& domain, uint16_t qtype, int fd, struct timeval* now)
+ uint16_t id, const DNSName& domain, uint16_t qtype, int fd, const struct timeval* now)
{
static optional<unsigned int> nearMissLimit;
if(!nearMissLimit)
}
#ifdef HAVE_PROTOBUF
-static void protobufLogQuery(uint8_t maskV4, uint8_t maskV6, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::vector<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName)
+static void protobufLogQuery(uint8_t maskV4, uint8_t maskV6, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::unordered_set<std::string>& policyTags, const std::string& requestorId, const std::string& deviceId, const std::string& deviceName)
{
if (!t_protobufServers) {
return;
return true;
}
+enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop };
+
+static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& dc, SyncRes& sr, int& res, vector<DNSRecord>& ret, DNSPacketWriter& pw)
+{
+ /* don't account truncate actions for TCP queries, since they are not applied */
+ if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !dc->d_tcp) {
+ ++g_stats.policyResults[appliedPolicy.d_kind];
+ }
+
+ if (sr.doLog() && appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
+ g_log << Logger::Warning << dc->d_mdp.d_qname << "|" << QType(dc->d_mdp.d_qtype).getName() << appliedPolicy.getLogString() << endl;
+ }
+
+ switch (appliedPolicy.d_kind) {
+
+ case DNSFilterEngine::PolicyKind::NoAction:
+ return PolicyResult::NoAction;
+
+ case DNSFilterEngine::PolicyKind::Drop:
+ ++g_stats.policyDrops;
+ return PolicyResult::Drop;
+
+ case DNSFilterEngine::PolicyKind::NXDOMAIN:
+ ret.clear();
+ res = RCode::NXDomain;
+ return PolicyResult::HaveAnswer;
+
+ case DNSFilterEngine::PolicyKind::NODATA:
+ ret.clear();
+ res = RCode::NoError;
+ return PolicyResult::HaveAnswer;
+
+ case DNSFilterEngine::PolicyKind::Truncate:
+ if (!dc->d_tcp) {
+ ret.clear();
+ res = RCode::NoError;
+ pw.getHeader()->tc = 1;
+ return PolicyResult::HaveAnswer;
+ }
+ return PolicyResult::NoAction;
+
+ case DNSFilterEngine::PolicyKind::Custom:
+ res = RCode::NoError;
+ {
+ auto spoofed = appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
+ for (auto& dr : spoofed) {
+ ret.push_back(dr);
+ 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;
+ }
+ }
+
+ return PolicyResult::NoAction;
+}
+
#ifdef HAVE_PROTOBUF
static std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config)
{
#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)) {
- // Now check the NODDB (note this is probablistic so can have FNs/FPs)
+ // Now check the NODDB (note this is probabilistic so can have FNs/FPs)
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;
}
return rcode;
}
+int getFakeAAAARecords(const DNSName& qname, ComboAddress prefix, vector<DNSRecord>& ret)
+{
+ int rcode = directResolve(qname, QType(QType::A), QClass::IN, ret);
+
+ // Remove double CNAME records
+ std::set<DNSName> seenCNAMEs;
+ ret.erase(std::remove_if(
+ ret.begin(),
+ ret.end(),
+ [&seenCNAMEs](DNSRecord& rr) {
+ if (rr.d_type == QType::CNAME) {
+ auto target = getRR<CNAMERecordContent>(rr);
+ if (target == nullptr) {
+ return false;
+ }
+ if (seenCNAMEs.count(target->getTarget()) > 0) {
+ // We've had this CNAME before, remove it
+ return true;
+ }
+ seenCNAMEs.insert(target->getTarget());
+ }
+ return false;
+ }),
+ ret.end());
+
+ bool seenA = false;
+ for (DNSRecord& rr : ret) {
+ if (rr.d_type == QType::A && rr.d_place == DNSResourceRecord::ANSWER) {
+ if (auto rec = getRR<ARecordContent>(rr)) {
+ ComboAddress ipv4(rec->getCA());
+ memcpy(&prefix.sin6.sin6_addr.s6_addr[12], &ipv4.sin4.sin_addr.s_addr, sizeof(ipv4.sin4.sin_addr.s_addr));
+ rr.d_content = std::make_shared<AAAARecordContent>(prefix);
+ rr.d_type = QType::AAAA;
+ }
+ seenA = true;
+ }
+ }
+
+ if (seenA) {
+ // We've seen an A in the ANSWER section, so there is no need to keep any
+ // SOA in the AUTHORITY section as this is not a NODATA response.
+ ret.erase(std::remove_if(
+ ret.begin(),
+ ret.end(),
+ [](DNSRecord& rr) {
+ return (rr.d_type == QType::SOA && rr.d_place == DNSResourceRecord::AUTHORITY);
+ }),
+ ret.end());
+ }
+ return rcode;
+}
+
+int getFakePTRRecords(const DNSName& qname, vector<DNSRecord>& ret)
+{
+ /* qname has a reverse ordered IPv6 address, need to extract the underlying IPv4 address from it
+ and turn it into an IPv4 in-addr.arpa query */
+ ret.clear();
+ vector<string> parts = qname.getRawLabels();
+
+ if (parts.size() < 8) {
+ return -1;
+ }
+
+ string newquery;
+ for (int n = 0; n < 4; ++n) {
+ newquery +=
+ std::to_string(stoll(parts[n*2], 0, 16) + 16*stoll(parts[n*2+1], 0, 16));
+ newquery.append(1, '.');
+ }
+ newquery += "in-addr.arpa.";
+
+ DNSRecord rr;
+ rr.d_name = qname;
+ rr.d_type = QType::CNAME;
+ rr.d_content = std::make_shared<CNAMERecordContent>(newquery);
+ ret.push_back(rr);
+
+ int rcode = directResolve(DNSName(newquery), QType(QType::PTR), QClass::IN, ret);
+
+ return rcode;
+}
+
static void startDoResolve(void *p)
{
auto dc=std::unique_ptr<DNSComboWriter>(reinterpret_cast<DNSComboWriter*>(p));
sr.setFrameStreamServers(t_frameStreamServers);
#endif
sr.setQuerySource(dc->d_remote, g_useIncomingECS && !dc->d_ednssubnet.source.empty() ? boost::optional<const EDNSSubnetOpts&>(dc->d_ednssubnet) : boost::none);
+ sr.setQueryReceivedOverTCP(dc->d_tcp);
bool tracedQuery=false; // we could consider letting Lua know about this too
bool shouldNotValidate = false;
int res = RCode::NoError;
DNSFilterEngine::Policy appliedPolicy;
- std::vector<DNSRecord> spoofed;
RecursorLua4::DNSQuestion dq(dc->d_source, dc->d_destination, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_tcp, variableAnswer, wantsRPZ, dc->d_logResponse);
dq.ednsFlags = &edo.d_extFlags;
dq.ednsOptions = &ednsOpts;
dq.deviceId = dc->d_deviceId;
dq.deviceName = dc->d_deviceName;
#endif
+ dq.proxyProtocolValues = &dc->d_proxyProtocolValues;
if(ednsExtRCode != 0) {
goto sendit;
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);
}
- // Check if the query has a policy attached to it
- if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
- luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies, appliedPolicy);
+ // Check if the client has a policy attached to it
+ if (wantsRPZ && !appliedPolicy.wasHit()) {
+
+ if (luaconfsLocal->dfe.getClientPolicy(dc->d_source, sr.d_discardedPolicies, appliedPolicy)) {
+ mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags());
+ }
+ }
+
+ /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
+ should be applied to it */
+ if (dc->d_rcode != boost::none) {
+
+ bool policyOverride = false;
+ /* Unless we already matched on the client IP, time to check the qname.
+ We normally check it in beginResolve() but it will be bypassed since we already have an answer */
+ if (wantsRPZ && appliedPolicy.policyOverridesGettag()) {
+ if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
+ // Client IP already matched
+ }
+ else {
+ // no match on the client IP, check the qname
+ if (luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, sr.d_discardedPolicies, appliedPolicy)) {
+ // got a match
+ mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags());
+ }
+ }
+
+ if (appliedPolicy.wasHit()) {
+ policyOverride = true;
+ }
+ }
+
+ if (policyOverride) {
+ /* No RPZ or gettag overrides it anyway */
+ 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 || !t_pdl->preresolve(dq, res)) {
+
+ if (!g_dns64PrefixReverse.empty() && dq.qtype == QType::PTR && dq.qname.isPartOf(g_dns64PrefixReverse)) {
+ res = getFakePTRRecords(dq.qname, ret);
+ goto haveAnswer;
+ }
sr.setWantsRPZ(wantsRPZ);
- if(wantsRPZ) {
- switch(appliedPolicy.d_kind) {
- case DNSFilterEngine::PolicyKind::NoAction:
- break;
- case DNSFilterEngine::PolicyKind::Drop:
- g_stats.policyDrops++;
- g_stats.policyResults[appliedPolicy.d_kind]++;
- return;
- case DNSFilterEngine::PolicyKind::NXDOMAIN:
- g_stats.policyResults[appliedPolicy.d_kind]++;
- res=RCode::NXDomain;
- goto haveAnswer;
- case DNSFilterEngine::PolicyKind::NODATA:
- g_stats.policyResults[appliedPolicy.d_kind]++;
- res=RCode::NoError;
- goto haveAnswer;
- case DNSFilterEngine::PolicyKind::Custom:
- g_stats.policyResults[appliedPolicy.d_kind]++;
- res=RCode::NoError;
- spoofed=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
- for (const auto& dr : spoofed) {
- ret.push_back(dr);
- handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
- }
+
+ if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
+
+ if (t_pdl && t_pdl->policyHitEventFilter(dc->d_remote, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, appliedPolicy, dc->d_policyTags, sr.d_discardedPolicies)) {
+ /* reset to no match */
+ appliedPolicy = DNSFilterEngine::Policy();
+ }
+ else {
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ if (policyResult == PolicyResult::HaveAnswer) {
goto haveAnswer;
- case DNSFilterEngine::PolicyKind::Truncate:
- if(!dc->d_tcp) {
- g_stats.policyResults[appliedPolicy.d_kind]++;
- res=RCode::NoError;
- pw.getHeader()->tc=1;
- goto haveAnswer;
- }
- break;
+ }
+ else if (policyResult == PolicyResult::Drop) {
+ return;
+ }
}
}
- // Query got not handled for QNAME Policy reasons, now actually go out to find an answer
+ // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
try {
sr.d_appliedPolicy = appliedPolicy;
+ sr.d_policyTags = std::move(dc->d_policyTags);
+
+ if (!dc->d_routingTag.empty()) {
+ sr.d_routingTag = dc->d_routingTag;
+ }
+
+ ret.clear(); // policy might have filled it with custom records but we decided not to use them
res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
shouldNotValidate = sr.wasOutOfBand();
}
- catch(const ImmediateServFailException &e) {
+ catch (const ImmediateQueryDropException& e) {
+ // XXX We need to export a protobuf message (and do a NOD lookup) if requested!
+ g_stats.policyDrops++;
+ g_log<<Logger::Debug<<"Dropping query because of a filtering policy "<<makeLoginfo(dc)<<endl;
+ return;
+ }
+ catch (const ImmediateServFailException &e) {
if(g_logCommonErrors) {
g_log<<Logger::Notice<<"Sending SERVFAIL to "<<dc->getRemote()<<" during resolve of '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
}
res = RCode::ServFail;
}
- catch(const PolicyHitException& e) {
+ catch (const SendTruncatedAnswerException& e) {
+ ret.clear();
+ res = RCode::NoError;
+ pw.getHeader()->tc = 1;
+ }
+ catch (const PolicyHitException& e) {
res = -2;
}
dq.validationState = sr.getValidationState();
appliedPolicy = sr.d_appliedPolicy;
+ dc->d_policyTags = std::move(sr.d_policyTags);
// During lookup, an NSDNAME or NSIP trigger was hit in RPZ
if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve.
- appliedPolicy = sr.d_appliedPolicy;
- g_stats.policyResults[appliedPolicy.d_kind]++;
- switch(appliedPolicy.d_kind) {
- case DNSFilterEngine::PolicyKind::NoAction: // This can never happen
- throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
- case DNSFilterEngine::PolicyKind::Drop:
- g_stats.policyDrops++;
- return;
- case DNSFilterEngine::PolicyKind::NXDOMAIN:
- ret.clear();
- res=RCode::NXDomain;
- goto haveAnswer;
-
- case DNSFilterEngine::PolicyKind::NODATA:
- ret.clear();
- res=RCode::NoError;
- goto haveAnswer;
-
- case DNSFilterEngine::PolicyKind::Truncate:
- if(!dc->d_tcp) {
- ret.clear();
- res=RCode::NoError;
- pw.getHeader()->tc=1;
- goto haveAnswer;
- }
- break;
-
- case DNSFilterEngine::PolicyKind::Custom:
- ret.clear();
- res=RCode::NoError;
- spoofed=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
- for (const auto& dr : spoofed) {
- ret.push_back(dr);
- handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
- }
- goto haveAnswer;
+ if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) {
+ throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
+ }
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw);
+ if (policyResult == PolicyResult::HaveAnswer) {
+ goto haveAnswer;
+ }
+ else if (policyResult == PolicyResult::Drop) {
+ return;
}
}
- if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
- luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies, appliedPolicy);
- }
+ 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 (i->d_type == dc->d_mdp.d_qtype && i->d_place == DNSResourceRecord::ANSWER) {
+ break;
+ }
+ }
- if(t_pdl) {
- if(res == RCode::NoError) {
- auto i=ret.cbegin();
- for(; i!= ret.cend(); ++i)
- if(i->d_type == dc->d_mdp.d_qtype && i->d_place == DNSResourceRecord::ANSWER)
- break;
- if(i == ret.cend() && t_pdl->nodata(dq, res))
- shouldNotValidate = true;
+ if (i == ret.cend()) {
+ /* no record in the answer section, NODATA */
+ if (t_pdl && t_pdl->nodata(dq, res)) {
+ shouldNotValidate = true;
+ }
+ else if (g_dns64Prefix && dq.qtype == QType::AAAA && dq.validationState != vState::Bogus) {
+ res = getFakeAAAARecords(dq.qname, *g_dns64Prefix, ret);
+ shouldNotValidate = true;
+ }
+ }
}
- else if(res == RCode::NXDomain && t_pdl->nxdomain(dq, res))
+ else if(res == RCode::NXDomain && t_pdl && t_pdl->nxdomain(dq, res)) {
shouldNotValidate = true;
+ }
- if(t_pdl->postresolve(dq, res))
+ if (t_pdl && t_pdl->postresolve(dq, res)) {
shouldNotValidate = true;
- }
-
- if (wantsRPZ) { //XXX This block is repeated, see above
- g_stats.policyResults[appliedPolicy.d_kind]++;
- switch(appliedPolicy.d_kind) {
- case DNSFilterEngine::PolicyKind::NoAction:
- break;
- case DNSFilterEngine::PolicyKind::Drop:
- g_stats.policyDrops++;
- return;
- case DNSFilterEngine::PolicyKind::NXDOMAIN:
- ret.clear();
- res=RCode::NXDomain;
- goto haveAnswer;
-
- case DNSFilterEngine::PolicyKind::NODATA:
- ret.clear();
- res=RCode::NoError;
- goto haveAnswer;
-
- case DNSFilterEngine::PolicyKind::Truncate:
- if(!dc->d_tcp) {
- ret.clear();
- res=RCode::NoError;
- pw.getHeader()->tc=1;
- goto haveAnswer;
- }
- break;
-
- case DNSFilterEngine::PolicyKind::Custom:
- ret.clear();
- res=RCode::NoError;
- spoofed=appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
- for (const auto& dr : spoofed) {
- ret.push_back(dr);
- handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
- }
- goto haveAnswer;
}
}
}
+
haveAnswer:;
- if(res == PolicyDecision::DROP) {
- g_stats.policyDrops++;
- return;
- }
if(tracedQuery || res == -1 || res == RCode::ServFail || pw.getHeader()->rcode == RCode::ServFail)
{
string trace(sr.getTrace());
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)
}
if(ret.size()) {
- orderAndShuffle(ret);
+ pdns::orderAndShuffle(ret);
if(auto sl = luaconfsLocal->sortlist.getOrderCmp(dc->d_source)) {
stable_sort(ret.begin(), ret.end(), *sl);
variableAnswer=true;
#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
- if (t_protobufServers && !(luaconfsLocal->protobufExportConfig.taggedOnly && (!appliedPolicy.d_name || appliedPolicy.d_name->empty()) && dc->d_policyTags.empty())) {
+ if (t_protobufServers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && dc->d_policyTags.empty())) {
pbMessage->setBytes(packet.size());
pbMessage->setResponseCode(pw.getHeader()->rcode);
- if (appliedPolicy.d_name) {
- pbMessage->setAppliedPolicy(*appliedPolicy.d_name);
+ if (!appliedPolicy.getName().empty()) {
+ pbMessage->setAppliedPolicy(appliedPolicy.getName());
pbMessage->setAppliedPolicyType(appliedPolicy.d_type);
+ pbMessage->setAppliedPolicyTrigger(appliedPolicy.d_trigger);
+ pbMessage->setAppliedPolicyHit(appliedPolicy.d_hit);
}
pbMessage->setPolicyTags(dc->d_policyTags);
if (g_useKernelTimestamp && dc->d_kernelTimestamp.tv_sec) {
pw.getHeader()->rcode == RCode::ServFail ? SyncRes::s_packetcacheservfailttl :
min(minTTL,SyncRes::s_packetcachettl),
dq.validationState,
- dc->d_ecsBegin,
- dc->d_ecsEnd,
std::move(pbMessage));
}
// else cerr<<"Not putting in packet cache: "<<sr.wasVariable()<<endl;
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--;
ttd.tv_sec += g_tcpTimeout;
t_fdm->addReadFD(dc->d_socket, handleRunningTCPQuestion, dc->d_tcpConnection, &ttd);
} else {
- // fd might have been removed by read error code, so expect an exception
+ // fd might have been removed by read error code, or a read timeout, so expect an exception
try {
t_fdm->setReadTTD(dc->d_socket, ttd, g_tcpTimeout);
}
- catch (FDMultiplexerException &) {
+ catch (const FDMultiplexerException &) {
+ // but if the FD was removed because of a timeout while we were sending a response,
+ // we need to re-arm it. If it was an error it will error again.
+ ttd.tv_sec += g_tcpTimeout;
+ t_fdm->addReadFD(dc->d_socket, handleRunningTCPQuestion, dc->d_tcpConnection, &ttd);
}
}
}
}
}
+
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) {
- s_RC->cacheMisses++;
+ g_recCache->cacheMisses++;
}
else {
- s_RC->cacheHits++;
+ g_recCache->cacheHits++;
}
if(spent < 0.001)
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) {
+ catch (const PDNSException &ae) {
g_log<<Logger::Error<<"startDoResolve problem "<<makeLoginfo(dc)<<": "<<ae.reason<<endl;
}
- catch(const MOADNSException &mde) {
+ catch (const MOADNSException &mde) {
g_log<<Logger::Error<<"DNS parser error "<<makeLoginfo(dc) <<": "<<dc->d_mdp.d_qname<<", "<<mde.what()<<endl;
}
- catch(std::exception& e) {
+ catch (const std::exception& e) {
g_log<<Logger::Error<<"STL error "<< makeLoginfo(dc)<<": "<<e.what();
// Luawrapper nests the exception from Lua, so we unnest it here
}
}
+static bool handleTCPReadResult(int fd, ssize_t bytes)
+{
+ if (bytes == 0) {
+ /* EOF */
+ t_fdm->removeReadFD(fd);
+ return false;
+ }
+ else if (bytes < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ t_fdm->removeReadFD(fd);
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
{
shared_ptr<TCPConnection> conn=any_cast<shared_ptr<TCPConnection> >(var);
- if(conn->state==TCPConnection::BYTE0) {
+ if (conn->state == TCPConnection::PROXYPROTOCOLHEADER) {
+ ssize_t bytes = recv(conn->getFD(), &conn->data.at(conn->proxyProtocolGot), conn->proxyProtocolNeed, 0);
+ if (bytes <= 0) {
+ handleTCPReadResult(fd, bytes);
+ return;
+ }
+
+ conn->proxyProtocolGot += bytes;
+ conn->data.resize(conn->proxyProtocolGot);
+ ssize_t remaining = isProxyHeaderComplete(conn->data);
+ if (remaining == 0) {
+ if (g_logCommonErrors) {
+ g_log<<Logger::Error<<"Unable to consume proxy protocol header in packet from TCP client "<< conn->d_remote.toStringWithPort() <<endl;
+ }
+ ++g_stats.proxyProtocolInvalidCount;
+ t_fdm->removeReadFD(fd);
+ return;
+ }
+ else if (remaining < 0) {
+ conn->proxyProtocolNeed = -remaining;
+ conn->data.resize(conn->proxyProtocolGot + conn->proxyProtocolNeed);
+ return;
+ }
+ else {
+ /* proxy header received */
+ /* we ignore the TCP field for now, but we could properly set whether
+ the connection was received over UDP or TCP if needed */
+ bool tcp;
+ bool proxy = false;
+ size_t used = parseProxyHeader(conn->data, proxy, conn->d_source, conn->d_destination, tcp, conn->proxyProtocolValues);
+ if (used <= 0) {
+ if (g_logCommonErrors) {
+ g_log<<Logger::Error<<"Unable to parse proxy protocol header in packet from TCP client "<< conn->d_remote.toStringWithPort() <<endl;
+ }
+ ++g_stats.proxyProtocolInvalidCount;
+ t_fdm->removeReadFD(fd);
+ return;
+ }
+ else if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
+ if (g_logCommonErrors) {
+ g_log<<Logger::Error<<"Proxy protocol header in packet from TCP client "<< conn->d_remote.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used << "), dropping"<< endl;
+ }
+ ++g_stats.proxyProtocolInvalidCount;
+ t_fdm->removeReadFD(fd);
+ return;
+ }
+
+ /* Now that we have retrieved the address of the client, as advertised by the proxy
+ via the proxy protocol header, check that it is allowed by our ACL */
+ /* note that if the proxy header used a 'LOCAL' command, the original source and destination are untouched so everything should be fine */
+ if (t_allowFrom && !t_allowFrom->match(&conn->d_source)) {
+ if (!g_quiet) {
+ g_log<<Logger::Error<<"["<<MT->getTid()<<"] dropping TCP query from "<<conn->d_source.toString()<<", address not matched by allow-from"<<endl;
+ }
+
+ ++g_stats.unauthorizedTCP;
+ t_fdm->removeReadFD(fd);
+ return;
+ }
+
+ conn->data.resize(2);
+ conn->state = TCPConnection::BYTE0;
+ }
+ }
+
+ if (conn->state==TCPConnection::BYTE0) {
ssize_t bytes=recv(conn->getFD(), &conn->data[0], 2, 0);
if(bytes==1)
conn->state=TCPConnection::BYTE1;
conn->bytesread=0;
conn->state=TCPConnection::GETQUESTION;
}
- if(!bytes || bytes < 0) {
- t_fdm->removeReadFD(fd);
+ if (bytes <= 0) {
+ handleTCPReadResult(fd, bytes);
return;
}
}
- else if(conn->state==TCPConnection::BYTE1) {
+
+ if (conn->state==TCPConnection::BYTE1) {
ssize_t bytes=recv(conn->getFD(), &conn->data[1], 1, 0);
if(bytes==1) {
conn->state=TCPConnection::GETQUESTION;
conn->data.resize(conn->qlen);
conn->bytesread=0;
}
- if(!bytes || bytes < 0) {
- if(g_logCommonErrors)
- g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected after first byte"<<endl;
- t_fdm->removeReadFD(fd);
+ if (bytes <= 0) {
+ if (!handleTCPReadResult(fd, bytes)) {
+ if(g_logCommonErrors) {
+ g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected after first byte"<<endl;
+ }
+ }
return;
}
}
- else if(conn->state==TCPConnection::GETQUESTION) {
+
+ if(conn->state==TCPConnection::GETQUESTION) {
ssize_t bytes=recv(conn->getFD(), &conn->data[conn->bytesread], conn->qlen - conn->bytesread, 0);
- if(!bytes || bytes < 0 || bytes > std::numeric_limits<std::uint16_t>::max()) {
+ if (bytes <= 0) {
+ if (!handleTCPReadResult(fd, bytes)) {
+ if(g_logCommonErrors) {
+ g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected while reading question body"<<endl;
+ }
+ }
+ return;
+ }
+ else if (bytes > std::numeric_limits<std::uint16_t>::max()) {
if(g_logCommonErrors) {
- g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" disconnected while reading question body"<<endl;
+ g_log<<Logger::Error<<"TCP client "<< conn->d_remote.toStringWithPort() <<" sent an invalid question size while reading question body"<<endl;
}
t_fdm->removeReadFD(fd);
return;
dc->setSocket(conn->getFD()); // this is the only time a copy is made of the actual fd
dc->d_tcp=true;
dc->setRemote(conn->d_remote);
- dc->setSource(conn->d_remote);
+ dc->setSource(conn->d_source);
ComboAddress dest;
dest.reset();
dest.sin4.sin_family = conn->d_remote.sin4.sin_family;
socklen_t len = dest.getSocklen();
getsockname(conn->getFD(), (sockaddr*)&dest, &len); // if this fails, we're ok with it
dc->setLocal(dest);
- dc->setDestination(dest);
+ dc->setDestination(conn->d_destination);
+ /* we can't move this if we want to be able to access the values in
+ all queries sent over this connection */
+ dc->d_proxyProtocolValues = conn->proxyProtocolValues;
DNSName qname;
uint16_t qtype=0;
uint16_t qclass=0;
if(t_pdl) {
try {
if (t_pdl->d_gettag_ffi) {
- dc->d_tag = t_pdl->gettag_ffi(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_records, dc->d_data, ednsOptions, true, requestorId, deviceId, deviceName, dc->d_rcode, dc->d_ttlCap, dc->d_variable, logQuery, dc->d_logResponse, dc->d_followCNAMERecords);
+ dc->d_tag = t_pdl->gettag_ffi(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_records, dc->d_data, ednsOptions, true, dc->d_proxyProtocolValues, requestorId, deviceId, deviceName, dc->d_routingTag, dc->d_rcode, dc->d_ttlCap, dc->d_variable, logQuery, dc->d_logResponse, dc->d_followCNAMERecords);
}
else if (t_pdl->d_gettag) {
- dc->d_tag = t_pdl->gettag(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId, deviceId, deviceName);
+ dc->d_tag = t_pdl->gettag(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId, deviceId, deviceName, dc->d_routingTag, dc->d_proxyProtocolValues);
}
}
catch(const std::exception& e) {
}
}
+static bool expectProxyProtocol(const ComboAddress& from)
+{
+ return g_proxyProtocolACL.match(from);
+}
+
//! Handle new incoming TCP connection
static void handleNewTCPQuestion(int fd, FDMultiplexer::funcparam_t& )
{
return;
}
- if(t_remotes)
+ if(t_remotes) {
t_remotes->push_back(addr);
- if(t_allowFrom && !t_allowFrom->match(&addr)) {
+ }
+
+ bool fromProxyProtocolSource = expectProxyProtocol(addr);
+ if(t_allowFrom && !t_allowFrom->match(&addr) && !fromProxyProtocolSource) {
if(!g_quiet)
- g_log<<Logger::Error<<"["<<MT->getTid()<<"] dropping TCP query from "<<addr.toString()<<", address not matched by allow-from"<<endl;
+ g_log<<Logger::Error<<"["<<MT->getTid()<<"] dropping TCP query from "<<addr.toString()<<", address neither matched by allow-from nor proxy-protocol-from"<<endl;
g_stats.unauthorizedTCP++;
try {
}
return;
}
+
if(g_maxTCPPerClient && t_tcpClientCounts->count(addr) && (*t_tcpClientCounts)[addr] >= g_maxTCPPerClient) {
g_stats.tcpClientOverflow++;
try {
setNonBlocking(newsock);
std::shared_ptr<TCPConnection> tc = std::make_shared<TCPConnection>(newsock, addr);
- tc->state=TCPConnection::BYTE0;
+ tc->d_source = addr;
+ tc->d_destination.reset();
+ tc->d_destination.sin4.sin_family = addr.sin4.sin_family;
+ socklen_t len = tc->d_destination.getSocklen();
+ getsockname(tc->getFD(), reinterpret_cast<sockaddr*>(&tc->d_destination), &len); // if this fails, we're ok with it
+
+ if (fromProxyProtocolSource) {
+ tc->proxyProtocolNeed = s_proxyProtocolMinimumHeaderSize;
+ tc->data.resize(tc->proxyProtocolNeed);
+ tc->state = TCPConnection::PROXYPROTOCOLHEADER;
+ }
+ else {
+ tc->state = TCPConnection::BYTE0;
+ }
struct timeval ttd;
Utility::gettimeofday(&ttd, 0);
}
}
-static string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fromaddr, const ComboAddress& destaddr, struct timeval tv, int fd)
+static string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fromaddr, const ComboAddress& destaddr, ComboAddress source, ComboAddress destination, struct timeval tv, int fd, std::vector<ProxyProtocolValue>& proxyProtocolValues)
{
gettimeofday(&g_now, 0);
if (tv.tv_sec) {
uint32_t qhash = 0;
bool needECS = false;
bool needXPF = g_XPFAcl.match(fromaddr);
- std::vector<std::string> policyTags;
+ std::unordered_set<std::string> policyTags;
LuaContext::LuaObject data;
- ComboAddress source = fromaddr;
- ComboAddress destination = destaddr;
string requestorId;
string deviceId;
string deviceName;
+ string routingTag;
bool logQuery = false;
bool logResponse = false;
#ifdef HAVE_PROTOBUF
EDNSSubnetOpts ednssubnet;
bool ecsFound = false;
bool ecsParsed = false;
- uint16_t ecsBegin = 0;
- uint16_t ecsEnd = 0;
std::vector<DNSRecord> records;
boost::optional<int> rcode = boost::none;
uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
if(t_pdl) {
try {
if (t_pdl->d_gettag_ffi) {
- ctag = t_pdl->gettag_ffi(source, ednssubnet.source, destination, qname, qtype, &policyTags, records, data, ednsOptions, false, requestorId, deviceId, deviceName, rcode, ttlCap, variable, logQuery, logResponse, followCNAMEs);
+ ctag = t_pdl->gettag_ffi(source, ednssubnet.source, destination, qname, qtype, &policyTags, records, data, ednsOptions, false, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, logQuery, logResponse, followCNAMEs);
}
else if (t_pdl->d_gettag) {
- ctag = t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName);
+ ctag = t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
}
}
catch(const std::exception& e) {
as cacheable we would cache it with a wrong tag, so better safe than sorry. */
vState valState;
if (qnameParsed) {
- cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, qtype, qclass, g_now.tv_sec, &response, &age, &valState, &qhash, &ecsBegin, &ecsEnd, pbMessage ? &(*pbMessage) : nullptr));
+ cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, qtype, qclass, g_now.tv_sec, &response, &age, &valState, &qhash, pbMessage ? &(*pbMessage) : nullptr));
}
else {
- cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, &qtype, &qclass, g_now.tv_sec, &response, &age, &valState, &qhash, &ecsBegin, &ecsEnd, pbMessage ? &(*pbMessage) : nullptr));
+ cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, &qtype, &qclass, g_now.tv_sec, &response, &age, &valState, &qhash, pbMessage ? &(*pbMessage) : nullptr));
}
if (cacheHit) {
- if(valState == Bogus) {
+ if(valState == vState::Bogus) {
if(t_bogusremotes)
t_bogusremotes->push_back(source);
if(t_bogusqueryring)
dc->d_tcp=false;
dc->d_ecsFound = ecsFound;
dc->d_ecsParsed = ecsParsed;
- dc->d_ecsBegin = ecsBegin;
- dc->d_ecsEnd = ecsEnd;
dc->d_ednssubnet = ednssubnet;
dc->d_ttlCap = ttlCap;
dc->d_variable = variable;
dc->d_deviceName = deviceName;
dc->d_kernelTimestamp = tv;
#endif
+ dc->d_proxyProtocolValues = std::move(proxyProtocolValues);
+ dc->d_routingTag = std::move(routingTag);
MT->makeThread(startDoResolve, (void*) dc.release()); // deletes dc
return 0;
static void handleNewUDPQuestion(int fd, FDMultiplexer::funcparam_t& var)
{
ssize_t len;
- static const size_t maxIncomingQuerySize = 512;
+ static const size_t maxIncomingQuerySize = g_proxyProtocolACL.empty() ? 512 : (512 + g_proxyProtocolMaximumSize);
static thread_local std::string data;
ComboAddress fromaddr;
+ ComboAddress source;
+ ComboAddress destination;
struct msghdr msgh;
struct iovec iov;
cmsgbuf_aligned cbuf;
bool firstQuery = true;
+ std::vector<ProxyProtocolValue> proxyProtocolValues;
for(size_t queriesCounter = 0; queriesCounter < s_maxUDPQueriesPerRound; queriesCounter++) {
+ bool proxyProto = false;
data.resize(maxIncomingQuerySize);
fromaddr.sin6.sin6_family=AF_INET6; // this makes sure fromaddr is big enough
fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), &data[0], data.size(), &fromaddr);
firstQuery = false;
- if (static_cast<size_t>(len) < sizeof(dnsheader)) {
- g_stats.ignoredCount++;
+ if (msgh.msg_flags & MSG_TRUNC) {
+ g_stats.truncatedDrops++;
if (!g_quiet) {
- g_log<<Logger::Error<<"Ignoring too-short ("<<std::to_string(len)<<") query from "<<fromaddr.toString()<<endl;
+ g_log<<Logger::Error<<"Ignoring truncated query from "<<fromaddr.toString()<<endl;
}
return;
}
- if (msgh.msg_flags & MSG_TRUNC) {
+ data.resize(static_cast<size_t>(len));
+
+ if (expectProxyProtocol(fromaddr)) {
+ bool tcp;
+ ssize_t used = parseProxyHeader(data, proxyProto, source, destination, tcp, proxyProtocolValues);
+ if (used <= 0) {
+ ++g_stats.proxyProtocolInvalidCount;
+ if (!g_quiet) {
+ g_log<<Logger::Error<<"Ignoring invalid proxy protocol ("<<std::to_string(len)<<", "<<std::to_string(used)<<") query from "<<fromaddr.toStringWithPort()<<endl;
+ }
+ return;
+ }
+ else if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
+ if (g_quiet) {
+ g_log<<Logger::Error<<"Proxy protocol header in UDP packet from "<< fromaddr.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used << "), dropping"<< endl;
+ }
+ ++g_stats.proxyProtocolInvalidCount;
+ return;
+ }
+
+ data.erase(0, used);
+ }
+ else if (len > 512) {
+ /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
g_stats.truncatedDrops++;
if (!g_quiet) {
- g_log<<Logger::Error<<"Ignoring truncated query from "<<fromaddr.toString()<<endl;
+ g_log<<Logger::Error<<"Ignoring truncated query from "<<fromaddr.toStringWithPort()<<endl;
+ }
+ return;
+ }
+
+ if (data.size() < sizeof(dnsheader)) {
+ g_stats.ignoredCount++;
+ if (!g_quiet) {
+ g_log<<Logger::Error<<"Ignoring too-short ("<<std::to_string(data.size())<<") query from "<<fromaddr.toString()<<endl;
}
return;
}
+ if (!proxyProto) {
+ source = fromaddr;
+ }
+
if(t_remotes) {
t_remotes->push_back(fromaddr);
}
- if(t_allowFrom && !t_allowFrom->match(&fromaddr)) {
+ if(t_allowFrom && !t_allowFrom->match(&source)) {
if(!g_quiet) {
- g_log<<Logger::Error<<"["<<MT->getTid()<<"] dropping UDP query from "<<fromaddr.toString()<<", address not matched by allow-from"<<endl;
+ g_log<<Logger::Error<<"["<<MT->getTid()<<"] dropping UDP query from "<<source.toString()<<", address not matched by allow-from"<<endl;
}
g_stats.unauthorizedUDP++;
return;
}
+
BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
if(!fromaddr.sin4.sin_port) { // also works for IPv6
if(!g_quiet) {
}
try {
- data.resize(static_cast<size_t>(len));
dnsheader* dh=(dnsheader*)&data[0];
if(dh->qr) {
getsockname(fd, (sockaddr*)&dest, &slen); // if this fails, we're ok with it
}
}
+ if (!proxyProto) {
+ destination = dest;
+ }
if(g_weDistributeQueries) {
- distributeAsyncFunction(data, boost::bind(doProcessUDPQuestion, data, fromaddr, dest, tv, fd));
+ std::string localdata = data;
+ distributeAsyncFunction(data, [localdata, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues]() mutable
+ { return doProcessUDPQuestion(localdata, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues); });
}
else {
++s_threadInfos[t_id].numberOfDistributedQueries;
- doProcessUDPQuestion(data, fromaddr, dest, tv, fd);
+ doProcessUDPQuestion(data, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues);
}
}
}
if( ::arg().mustDo("non-local-bind") )
Utility::setBindAny(AF_INET, fd);
-#ifdef SO_REUSEPORT
- if(g_reusePort) {
- if(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &tmp, sizeof(tmp)) < 0)
- throw PDNSException("SO_REUSEPORT: "+stringerror());
- }
+ if (g_reusePort) {
+#if defined(SO_REUSEPORT_LB)
+ try {
+ SSetsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
+ }
+ catch (const std::exception& e) {
+ throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
+ }
+#elif defined(SO_REUSEPORT)
+ try {
+ SSetsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1);
+ }
+ catch (const std::exception& e) {
+ throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
+ }
#endif
+ }
if (::arg().asNum("tcp-fast-open") > 0) {
#ifdef TCP_FASTOPEN
sin.sin4.sin_port = htons(st.port);
-#ifdef SO_REUSEPORT
- if(g_reusePort) {
- if(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
- throw PDNSException("SO_REUSEPORT: "+stringerror());
- }
+ if (g_reusePort) {
+#if defined(SO_REUSEPORT_LB)
+ try {
+ SSetsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
+ }
+ catch (const std::exception& e) {
+ throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
+ }
+#elif defined(SO_REUSEPORT)
+ try {
+ SSetsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1);
+ }
+ catch (const std::exception& e) {
+ throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
+ }
#endif
+ }
if (sin.isIPv4()) {
try {
static time_t lastOutputTime;
static uint64_t lastQueryCount;
- uint64_t cacheHits = s_RC->cacheHits;
- uint64_t cacheMisses = s_RC->cacheMisses;
- uint64_t cacheSize = s_RC->size();
- auto rc_stats = s_RC->stats();
+ uint64_t cacheHits = g_recCache->cacheHits;
+ uint64_t cacheMisses = g_recCache->cacheMisses;
+ uint64_t cacheSize = g_recCache->size();
+ auto rc_stats = g_recCache->stats();
double r = rc_stats.second == 0 ? 0.0 : (100.0 * rc_stats.first / rc_stats.second);
if(g_stats.qcounter && (cacheHits + cacheMisses) && SyncRes::s_queries && SyncRes::s_outqueries) {
past.tv_sec -= 5;
if (last_prune < past) {
t_packetCache->doPruneTo(g_maxPacketCacheEntries / g_numWorkerThreads);
- SyncRes::pruneNegCache(g_maxCacheEntries / (g_numWorkerThreads * 10));
time_t limit;
if(!((cleanCounter++)%40)) { // this is a full scan!
if(isHandlerThread()) {
if (now.tv_sec - last_RC_prune > 5) {
- s_RC->doPrune(g_maxCacheEntries);
+ g_recCache->doPrune(g_maxCacheEntries);
+ g_negCache->prune(g_maxCacheEntries / 10);
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()
return func();
}
-vector<ComboAddress>& operator+=(vector<ComboAddress>&a, const vector<ComboAddress>& b)
-{
- a.insert(a.end(), b.begin(), b.end());
- return a;
-}
-
-vector<pair<string, uint16_t> >& operator+=(vector<pair<string, uint16_t> >&a, const vector<pair<string, uint16_t> >& b)
+static vector<ComboAddress>& operator+=(vector<ComboAddress>&a, const vector<ComboAddress>& b)
{
a.insert(a.end(), b.begin(), b.end());
return a;
}
-vector<pair<DNSName, uint16_t> >& operator+=(vector<pair<DNSName, uint16_t> >&a, const vector<pair<DNSName, uint16_t> >& b)
+static vector<pair<DNSName, uint16_t> >& operator+=(vector<pair<DNSName, uint16_t> >&a, const vector<pair<DNSName, uint16_t> >& b)
{
a.insert(a.end(), b.begin(), b.end());
return a;
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)) {
RecursorControlParser rcp;
RecursorControlParser::func_t* command;
+ g_log << Logger::Notice << "Received rec_control command '" << msg << "' from control socket" << endl;
string answer=rcp.getAnswer(msg, &command);
// If we are inside a chroot, we need to strip
}
}
-FDMultiplexer* getMultiplexer()
+static FDMultiplexer* getMultiplexer()
{
FDMultiplexer* ret;
for(const auto& i : FDMultiplexer::getMultiplexerMap()) {
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;
}
}
-void parseNODWhitelist(const std::string& wlist)
+static void parseNODWhitelist(const std::string& wlist)
{
vector<string> parts;
stringtok(parts, wlist, ",; ");
static int serviceMain(int argc, char*argv[])
{
+ int ret = EXIT_SUCCESS;
+
g_log.setName(s_programname);
g_log.disableSyslog(::arg().mustDo("disable-syslog"));
g_log.setTimestamps(::arg().mustDo("log-timestamp"));
checkLinuxIPv6Limits();
try {
- vector<string> addrs;
- if(!::arg()["query-local-address6"].empty()) {
- SyncRes::s_doIPv6=true;
- g_log<<Logger::Warning<<"Enabling IPv6 transport for outgoing queries"<<endl;
-
- stringtok(addrs, ::arg()["query-local-address6"], ", ;");
- for(const string& addr : addrs) {
- g_localQueryAddresses6.push_back(ComboAddress(addr));
- }
- }
- else {
- g_log<<Logger::Warning<<"NOT using IPv6 for outgoing queries - set 'query-local-address6=::' to enable"<<endl;
- }
- addrs.clear();
- stringtok(addrs, ::arg()["query-local-address"], ", ;");
- for(const string& addr : addrs) {
- g_localQueryAddresses4.push_back(ComboAddress(addr));
+ pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
+ if (!::arg()["query-local-address6"].empty()) {
+ // TODO remove in 4.5.0
+ g_log<<Logger::Warning<<"query-local-address6 is deprecated and will be removed in a future version. Please use query-local-address for IPv6 addresses as well"<<endl;
+ pdns::parseQueryLocalAddress(::arg()["query-local-address6"]);
}
}
catch(std::exception& e) {
exit(99);
}
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+ SyncRes::s_doIPv4=true;
+ g_log<<Logger::Warning<<"Enabling IPv4 transport for outgoing queries"<<endl;
+ }
+ else {
+ g_log<<Logger::Warning<<"NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable"<<endl;
+ }
+
+
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+ SyncRes::s_doIPv6=true;
+ g_log<<Logger::Warning<<"Enabling IPv6 transport for outgoing queries"<<endl;
+ }
+ else {
+ g_log<<Logger::Warning<<"NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"<<endl;
+ }
+
+ if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
+ g_log<<Logger::Error<<"No outgoing addresses configured! Can not continue"<<endl;
+ exit(99);
+ }
+
// keep this ABOVE loadRecursorLuaConfig!
if(::arg()["dnssec"]=="off")
g_dnssecmode=DNSSECMode::Off;
SyncRes::s_serverdownthrottletime=::arg().asNum("server-down-throttle-time");
SyncRes::s_serverID=::arg()["server-id"];
SyncRes::s_maxqperq=::arg().asNum("max-qperq");
+ SyncRes::s_maxnsaddressqperq=::arg().asNum("max-ns-address-qperq");
SyncRes::s_maxtotusec=1000*::arg().asNum("max-total-msec");
SyncRes::s_maxdepth=::arg().asNum("max-recursion-depth");
SyncRes::s_rootNXTrust = ::arg().mustDo( "root-nx-trust");
SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
}
else {
- bool found = false;
- for (const auto& addr : g_localQueryAddresses4) {
- if (!IsAnyAddress(addr)) {
- SyncRes::setECSScopeZeroAddress(Netmask(addr, 32));
- found = true;
- break;
+ Netmask nm;
+ bool done = false;
+
+ auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
+ if (addr.sin4.sin_family != 0) {
+ nm = Netmask(addr, 32);
+ done = true;
+ }
+ if (!done) {
+ addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
+ if (addr.sin4.sin_family != 0) {
+ nm = Netmask(addr, 128);
+ done = true;
}
}
- if (!found) {
- for (const auto& addr : g_localQueryAddresses6) {
- if (!IsAnyAddress(addr)) {
- SyncRes::setECSScopeZeroAddress(Netmask(addr, 128));
- found = true;
- break;
- }
- }
- if (!found) {
- SyncRes::setECSScopeZeroAddress(Netmask("127.0.0.1/32"));
- }
+ if (!done) {
+ nm = Netmask(ComboAddress("127.0.0.1"), 32);
}
+ SyncRes::setECSScopeZeroAddress(nm);
}
SyncRes::parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
g_XPFAcl.toMasks(::arg()["xpf-allow-from"]);
g_xpfRRCode = ::arg().asNum("xpf-rr-code");
+ g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
+ g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
+
+ if (!::arg()["dns64-prefix"].empty()) {
+ try {
+ auto dns64Prefix = Netmask(::arg()["dns64-prefix"]);
+ if (dns64Prefix.getBits() != 96) {
+ g_log << Logger::Error << "Invalid prefix for 'dns64-prefix', the current implementation only supports /96 prefixes: " << ::arg()["dns64-prefix"] << endl;
+ exit(1);
+ }
+ g_dns64Prefix = dns64Prefix.getNetwork();
+ g_dns64PrefixReverse = reverseNameFromIP(*g_dns64Prefix);
+ /* /96 is 24 nibbles + 2 for "ip6.arpa." */
+ while (g_dns64PrefixReverse.countLabels() > 26) {
+ g_dns64PrefixReverse.chopOff();
+ }
+ }
+ catch (const NetmaskException& ne) {
+ g_log << Logger::Error << "Invalid prefix '" << ::arg()["dns64-prefix"] << "' for 'dns64-prefix': " << ne.reason << endl;
+ exit(1);
+ }
+ }
+
g_networkTimeoutMsec = ::arg().asNum("network-timeout");
g_initialDomainMap = parseAuthAndForwards();
}
g_dontThrottleNames.setState(std::move(dontThrottleNames));
+ parts.clear();
NetmaskGroup dontThrottleNetmasks;
stringtok(parts, ::arg()["dont-throttle-netmasks"], " ,");
for (const auto &p : parts) {
For years, this was a safe assumption, but containers change that: in
most (all?) container implementations, the application itself is running
as pid 1. This means that sending signals to those applications, will not
- be handled by default. Results might be "your container not responsing
+ be handled by default. Results might be "your container not responding
when asking it to stop", or "ctrl-c not working even when the app is
running in the foreground inside a container".
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)
t_bogusqueryring = std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > >(new boost::circular_buffer<pair<DNSName, uint16_t> >());
t_bogusqueryring->set_capacity(ringsize);
}
-
MT=std::unique_ptr<MTasker<PacketID,string> >(new MTasker<PacketID,string>(::arg().asNum("stack-size")));
threadInfo.mt = MT.get();
if (threadInfo.isListener) {
if (g_reusePort) {
/* then every listener has its own FDs */
- for(const auto deferred : threadInfo.deferredAdds) {
+ for(const auto& deferred : threadInfo.deferredAdds) {
t_fdm->addReadFD(deferred.first, deferred.second);
}
}
else {
/* otherwise all listeners are listening on the same ones */
- for(const auto deferred : g_deferredAdds) {
+ for(const auto& deferred : g_deferredAdds) {
t_fdm->addReadFD(deferred.first, deferred.second);
}
}
while (!RecursorControlChannel::stop) {
while(MT->schedule(&g_now)); // MTasker letting the mthreads do their thing
- if(!(counter%500)) {
+ // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
+ // We want to call handler thread often, it gets scheduled about 2 times per second
+ if ((threadInfo.isHandler && counter % 11 == 0) || counter % 499 == 0) {
MT->makeThread(houseKeeping, 0);
}
::arg().set("socket-group","Group of socket")="";
::arg().set("socket-mode", "Permissions for socket")="";
- ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns-recursor when unset and not chrooted" )="";
+ ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns-recursor when unset and not chrooted"
+#ifdef HAVE_SYSTEMD
+ + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")="";
+ auto runtimeDir = getenv("RUNTIME_DIRECTORY");
+ if (runtimeDir != nullptr) {
+ ::arg().set("socket-dir") = runtimeDir;
+ }
+#else
+ )="";
+#endif
::arg().set("delegation-only","Which domains we only accept delegations from")="";
::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
- ::arg().set("query-local-address6","Source IPv6 address for sending queries. IF UNSET, IPv6 WILL NOT BE USED FOR OUTGOING QUERIES")="";
+ ::arg().set("query-local-address6","DEPRECATED: Use query-local-address for IPv6 as well. Source IPv6 address for sending queries. IF UNSET, IPv6 WILL NOT BE USED FOR OUTGOING QUERIES")="";
::arg().set("client-tcp-timeout","Timeout in seconds when talking to TCP clients")="2";
::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads")="2048";
::arg().set("max-tcp-clients","Maximum number of simultaneous TCP clients")="128";
::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size")="1232";
::arg().set("minimum-ttl-override", "Set under adverse conditions, a minimum TTL")="0";
::arg().set("max-qperq", "Maximum outgoing queries per query")="60";
+ ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query")="10";
::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited")="7000";
::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited")="40";
::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing")="10000";
::arg().set("xpf-allow-from","XPF information is only processed from these subnets")="";
::arg().set("xpf-rr-code","XPF option code to use")="0";
+ ::arg().set("proxy-protocol-from", "A Proxy Protocol header is only allowed from these subnets")="";
+ ::arg().set("proxy-protocol-maximum-size", "The maximum size of a proxy protocol payload, including the TLV values")="512";
+
+ ::arg().set("dns64-prefix", "DNS64 prefix")="";
+
::arg().set("udp-source-port-min", "Minimum UDP port to bind on")="1024";
::arg().set("udp-source-port-max", "Maximum UDP port to bind on")="65535";
::arg().set("udp-source-port-avoid", "List of comma separated UDP port number to avoid")="11211";
::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";
}
cerr<<" (";
bool first = true;
- for (auto const c : ::arg().getCommands()) {
+ for (const auto& c : ::arg().getCommands()) {
if (!first) {
cerr<<", ";
}
exit(0);
}
- s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache(::arg().asNum("cache-shards")));
+ g_recCache = std::unique_ptr<MemRecursorCache>(new MemRecursorCache(::arg().asNum("record-cache-shards")));
+ g_negCache = std::unique_ptr<NegCache>(new NegCache(::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;
}
-void loadMainConfig(const std::string& configdir)
+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;
::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
UeberBackend::go();
}
-bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone, bool quiet = false, bool rectifyTransaction = true)
+static bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone, bool quiet = false, bool rectifyTransaction = true)
{
string output;
string error;
return ret;
}
-void dbBench(const std::string& fname)
+static void dbBench(const std::string& fname)
{
::arg().set("query-cache-ttl")="0";
::arg().set("negquery-cache-ttl")="0";
cout<<"Packet cache reports: "<<S.read("query-cache-hit")<<" hits (should be 0) and "<<S.read("query-cache-miss") <<" misses"<<endl;
}
-bool rectifyAllZones(DNSSECKeeper &dk, bool quiet = false)
+static bool rectifyAllZones(DNSSECKeeper &dk, bool quiet = false)
{
UeberBackend B("default");
vector<DomainInfo> domainInfo;
return result;
}
-int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone, const vector<DNSResourceRecord>* suppliedrecords=0)
+static int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone, const vector<DNSResourceRecord>* suppliedrecords=0)
{
uint64_t numerrors=0, numwarnings=0;
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) {
return EXIT_FAILURE;
}
-int checkAllZones(DNSSECKeeper &dk, bool exitOnError)
+static int checkAllZones(DNSSECKeeper &dk, bool exitOnError)
{
UeberBackend B("default");
vector<DomainInfo> domainInfo;
return EXIT_FAILURE;
}
-int increaseSerial(const DNSName& zone, DNSSECKeeper &dk)
+static int increaseSerial(const DNSName& zone, DNSSECKeeper &dk)
{
UeberBackend B("default");
SOAData sd;
return 0;
}
-int deleteZone(const DNSName &zone) {
+static int deleteZone(const DNSName &zone) {
UeberBackend B;
DomainInfo di;
if (! B.getDomainInfo(zone, di)) {
return EXIT_FAILURE;
}
-void listKey(DomainInfo const &di, DNSSECKeeper& dk, bool printHeader = true) {
+static void listKey(DomainInfo const &di, DNSSECKeeper& dk, bool printHeader = true) {
if (printHeader) {
cout<<"Zone Type Size Algorithm ID Location Keytag"<<endl;
cout<<"----------------------------------------------------------------------------------"<<endl;
}
}
-int listKeys(const string &zname, DNSSECKeeper& dk){
+static int listKeys(const string &zname, DNSSECKeeper& dk){
UeberBackend B("default");
if (zname != "all") {
vector<DomainInfo> domainInfo;
B.getAllDomains(&domainInfo);
bool printHeader = true;
- for (auto const di : domainInfo) {
+ for (const auto& di : domainInfo) {
listKey(di, dk, printHeader);
printHeader = false;
}
return EXIT_SUCCESS;
}
-int listZone(const DNSName &zone) {
+static int listZone(const DNSName &zone) {
UeberBackend B;
DomainInfo di;
}
// lovingly copied from http://stackoverflow.com/questions/1798511/how-to-avoid-press-enter-with-any-getchar
-int read1char(){
+static int read1char(){
int c;
static struct termios oldt, newt;
return c;
}
-int clearZone(DNSSECKeeper& dk, const DNSName &zone) {
+static int clearZone(DNSSECKeeper& dk, const DNSName &zone) {
UeberBackend B;
DomainInfo di;
return EXIT_SUCCESS;
}
-int editZone(const DNSName &zone) {
+static int editZone(const DNSName &zone) {
UeberBackend B;
DomainInfo di;
DNSSECKeeper dk(&B);
}
-int loadZone(DNSName zone, const string& fname) {
+static int loadZone(DNSName zone, const string& fname) {
UeberBackend B;
DomainInfo di;
}
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_SUCCESS;
}
-int createZone(const DNSName &zone, const DNSName& nsname) {
+static int createZone(const DNSName &zone, const DNSName& nsname) {
UeberBackend B;
DomainInfo di;
if (B.getDomainInfo(zone, di)) {
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;
return EXIT_SUCCESS;
}
-int createSlaveZone(const vector<string>& cmds) {
+static int createSlaveZone(const vector<string>& cmds) {
UeberBackend B;
DomainInfo di;
DNSName zone(cmds[1]);
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;
}
-int changeSlaveZoneMaster(const vector<string>& cmds) {
+static int changeSlaveZoneMaster(const vector<string>& cmds) {
UeberBackend B;
DomainInfo di;
DNSName zone(cmds[1]);
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) {
}
// add-record ZONE name type [ttl] "content" ["content"]
-int addOrReplaceRecord(bool addOrReplace, const vector<string>& cmds) {
+static int addOrReplaceRecord(bool addOrReplace, const vector<string>& cmds) {
DNSResourceRecord rr;
vector<DNSResourceRecord> newrrs;
DNSName zone(cmds[1]);
return EXIT_SUCCESS;
}
+// addSuperMaster add anew super master
+static 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
-int deleteRRSet(const std::string& zone_, const std::string& name_, const std::string& type_)
+static int deleteRRSet(const std::string& zone_, const std::string& name_, const std::string& type_)
{
UeberBackend B;
DomainInfo di;
return EXIT_SUCCESS;
}
-int listAllZones(const string &type="") {
+static int listAllZones(const string &type="") {
int kindFilter = -1;
if (type.size()) {
return 0;
}
-bool testAlgorithm(int algo)
+static bool testAlgorithm(int algo)
{
return DNSCryptoKeyEngine::testOne(algo);
}
-bool testAlgorithms()
+static bool testAlgorithms()
{
return DNSCryptoKeyEngine::testAll();
}
-void testSpeed(DNSSECKeeper& dk, const DNSName& zone, const string& remote, int cores)
+static void testSpeed(DNSSECKeeper& dk, const DNSName& zone, const string& remote, int cores)
{
DNSResourceRecord rr;
rr.qname=DNSName("blah")+zone;
cerr<<"Net speed: "<<csp.d_signed/ (dt.udiff()/1000000.0) << " sigs/s"<<endl;
}
-void verifyCrypto(const string& zone)
+static void verifyCrypto(const string& zone)
{
ZoneParserTNG zpt(zone);
zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
cerr<<"Original DS: "<<apex.toString()<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
}
}
-bool disableDNSSECOnZone(DNSSECKeeper& dk, const DNSName& zone)
+
+static bool disableDNSSECOnZone(DNSSECKeeper& dk, const DNSName& zone)
{
UeberBackend B("default");
DomainInfo di;
return ret;
}
-int setZoneAccount(const DNSName& zone, const string &account)
+static int setZoneAccount(const DNSName& zone, const string &account)
{
UeberBackend B("default");
DomainInfo di;
return EXIT_SUCCESS;
}
-int setZoneKind(const DNSName& zone, const DomainInfo::DomainKind kind)
+static int setZoneKind(const DNSName& zone, const DomainInfo::DomainKind kind)
{
UeberBackend B("default");
DomainInfo di;
return EXIT_SUCCESS;
}
-bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false)
+static bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false)
{
UeberBackend B("default");
DomainInfo di;
cout<<endl;
for(const auto& m : metamap) {
- for(const auto i : m.second)
+ for(const auto& i : m.second)
cout << '\t' << m.first<<'\t' << i <<endl;
}
}
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;
}
-bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
+static bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
{
// parse attribute
int k_size;
return true;
}
-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;
}
-int addOrSetMeta(const DNSName& zone, const string& kind, const vector<string>& values, bool clobber) {
+static int addOrSetMeta(const DNSName& zone, const string& kind, const vector<string>& values, bool clobber) {
UeberBackend B("default");
DomainInfo di;
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)
cout<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
cout<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK"<<endl;
cout<<" [active|inactive] [ksk|zsk] [published|unpublished] Defaults to KSK, active and published"<<endl;
- cout<<"ipdecrypt IP passphrase/key [key] Encrypt IP address using passphrase or base64 key"<<endl;
+ cout<<"ipdecrypt IP passphrase/key [key] Decrypt IP address using passphrase or base64 key"<<endl;
cout<<"ipencrypt IP passphrase/key [key] Encrypt IP address using passphrase or base64 key"<<endl;
cout<<"load-zone ZONE FILE Load ZONE from FILE, possibly creating zone or atomically"<<endl;
cout<<" replacing contents"<<endl;
cout<<"DNSKEY algorithms supported by this installation of PowerDNS:"<<endl;
auto algosWithBackend = DNSCryptoKeyEngine::listAllAlgosWithBackend();
- for (auto const algoWithBackend : algosWithBackend){
+ for (const auto& algoWithBackend : algosWithBackend){
string algoName = DNSSECKeeper::algorithm2name(algoWithBackend.first);
cout<<std::to_string(algoWithBackend.first)<<" - "<<algoName;
if (cmds.size() == 2 && cmds[1] == "with-backend")
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]);
if (cmds.size() > 2) {
keys.assign(cmds.begin() + 2, cmds.end());
std::cout << "Metadata for '" << zone << "'" << endl;
- for(const string kind : keys) {
+ for(const auto& kind : keys) {
vector<string> meta;
meta.clear();
if (B.getDomainMetadata(zone, kind, meta)) {
DNSName zone(cmds[1]);
string kind = cmds[2];
static vector<string> multiMetaWhitelist = {"ALLOW-AXFR-FROM", "ALLOW-DNSUPDATE-FROM",
- "ALSO-NOTIFY", "TSIG-ALLOW-AXFR", "TSIG-ALLOW-DNSUPDATE", "GSS-ALLOW-AXFR-PRINCIPAL",
+ "ALSO-NOTIFY", "TSIG-ALLOW-AXFR", "TSIG-ALLOW-DNSUPDATE",
"PUBLISH-CDS"};
bool clobber = true;
if (cmds[0] == "add-meta") {
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;
#include <boost/format.hpp>
#include <p11-kit/p11-kit.h>
+#include <mutex>
+
#include "pdns/dnssecinfra.hh"
#include "pdns/logger.hh"
#include "pdns/pdnsexception.hh"
#include "pdns/lock.hh"
#ifdef HAVE_LIBCRYPTO_ECDSA
+#include <openssl/bn.h>
#include <openssl/ec.h>
#endif
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
CK_SESSION_HANDLE d_session;
CK_SLOT_ID d_slot;
CK_RV d_err;
- pthread_mutex_t d_m;
+ std::mutex d_m;
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;
}
}
d_err(0)
{
CK_TOKEN_INFO tokenInfo;
- pthread_mutex_init(&(this->d_m), NULL);
- Lock l(&d_m);
+ std::lock_guard<std::mutex> l(d_m);
if ((d_err = d_functions->C_OpenSession(this->d_slot, CKF_SERIAL_SESSION|CKF_RW_SESSION, 0, 0, &(this->d_session)))) {
logError("C_OpenSession");
CK_FUNCTION_LIST* f() { return d_functions; }
- pthread_mutex_t *m() { return &d_m; }
+ std::mutex& m() { return d_m; }
static std::shared_ptr<Pkcs11Slot> GetSlot(const std::string& module, const string& tokenId);
static CK_RV HuntSlot(const string& tokenId, CK_SLOT_ID &slotId, _CK_SLOT_INFO* info, CK_FUNCTION_LIST* functions);
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
}
}
void LoadAttributes() {
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
std::vector<P11KitAttribute> attr;
std::vector<CK_OBJECT_HANDLE> key;
attr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
int GenerateKeyPair(CK_MECHANISM_PTR mechanism, std::vector<P11KitAttribute>& pubAttributes, std::vector<P11KitAttribute>& privAttributes, CK_OBJECT_HANDLE_PTR pubKey, CK_OBJECT_HANDLE_PTR privKey) {
{
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
size_t k;
std::unique_ptr<CK_ATTRIBUTE[]> pubAttr(new CK_ATTRIBUTE[pubAttributes.size()]);
}
int Sign(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) {
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
CK_BYTE buffer[1024];
CK_ULONG buflen = sizeof buffer; // should be enough for most signatures.
}
int Verify(const std::string& data, const std::string& signature, CK_MECHANISM_PTR mechanism) {
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
if ((d_err = this->d_slot->f()->C_VerifyInit(d_slot->Session(), mechanism, d_public_key))) { logError("C_VerifyInit"); return d_err; }
d_err = this->d_slot->f()->C_Verify(d_slot->Session(), (unsigned char*)data.c_str(), data.size(), (unsigned char*)signature.c_str(), signature.size());
}
int Digest(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) {
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
CK_BYTE buffer[1024];
CK_ULONG buflen = sizeof buffer; // should be enough for most digests
}
int DigestKey(std::string& result) {
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
CK_MECHANISM mech;
mech.mechanism = CKM_SHA_1;
}
int FindObjects(const std::vector<P11KitAttribute>& attributes, std::vector<CK_OBJECT_HANDLE>& objects, int maxobjects) {
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
return FindObjects2(attributes, objects, maxobjects);
}
int GetAttributeValue(const CK_OBJECT_HANDLE& object, std::vector<P11KitAttribute>& attributes)
{
- Lock l(d_slot->m());
+ std::lock_guard<std::mutex> l(d_slot->m());
return GetAttributeValue2(object, attributes);
}
std::vector<P11KitAttribute> privAttr;
CK_MECHANISM mech;
CK_OBJECT_HANDLE pubKey, privKey;
- CK_RV rv;
std::shared_ptr<Pkcs11Token> d_slot;
d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
if (d_slot->LoggedIn() == false)
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 ((rv = d_slot->GenerateKeyPair(&mech, pubAttr, privAttr, &pubKey, &privKey))) {
+ 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");
}
};
close(d_portfd);
}
- virtual int run(struct timeval* tv, int timeout=500);
+ virtual int run(struct timeval* tv, int timeout=500) override;
+ virtual void getAvailableFDs(std::vector<int>& fds, int timeout) override;
- virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const boost::any& parameter, const struct timeval* ttd=nullptr);
- virtual void removeFD(callbackmap_t& cbmap, int fd);
- string getName()
+ virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const boost::any& parameter, const struct timeval* ttd=nullptr) override;
+ virtual void removeFD(callbackmap_t& cbmap, int fd) override;
+ string getName() const override
{
return "solaris completion ports";
}
throw FDMultiplexerException("Removing fd from port set: "+stringerror());
}
+void PortsFDMultiplexer::getAvailableFDs(std::vector<int>& fds, int timeout)
+{
+ struct timespec timeoutspec;
+ timeoutspec.tv_sec = timeout / 1000;
+ timeoutspec.tv_nsec = (timeout % 1000) * 1000000;
+ unsigned int numevents = 1;
+ int ret = port_getn(d_portfd, d_pevents.get(), min(PORT_MAX_LIST, s_maxevents), &numevents, &timeoutspec);
+
+ /* port_getn has an unusual API - (ret == -1, errno == ETIME) can
+ mean partial success; you must check (*numevents) in this case
+ and process anything in there, otherwise you'll never see any
+ events from that object again. We don't care about pure timeouts
+ (ret == -1, errno == ETIME, *numevents == 0) so we don't bother
+ with that case. */
+ if (ret == -1 && errno != ETIME) {
+ if (errno != EINTR) {
+ throw FDMultiplexerException("completion port_getn returned error: " + stringerror());
+ }
+
+ // EINTR is not really an error
+ return;
+ }
+
+ if (numevents == 0) {
+ // nothing
+ return;
+ }
+
+ fds.reserve(numevents);
+
+ for (unsigned int n = 0; n < numevents; ++n) {
+ const auto fd = d_pevents[n].portev_object;
+
+ /* we need to re-associate the FD */
+ if (d_readCallbacks.count(fd)) {
+ if (port_associate(d_portfd, PORT_SOURCE_FD, fd, POLLIN, 0) < 0) {
+ throw FDMultiplexerException("Unable to add fd back to ports (read): " + stringerror());
+ }
+ }
+ else if (d_writeCallbacks.count(fd)) {
+ if (port_associate(d_portfd, PORT_SOURCE_FD, fd, POLLOUT, 0) < 0) {
+ throw FDMultiplexerException("Unable to add fd back to ports (write): " + stringerror());
+ }
+ } else {
+ /* not registered, this is unexpected */
+ continue;
+ }
+
+ fds.push_back(fd);
+ }
+}
+
int PortsFDMultiplexer::run(struct timeval* now, int timeout)
{
if(d_inrun) {
}
struct timespec timeoutspec;
- timeoutspec.tv_sec = time / 1000;
- timeoutspec.tv_nsec = (time % 1000) * 1000000;
+ timeoutspec.tv_sec = timeout / 1000;
+ timeoutspec.tv_nsec = (timeout % 1000) * 1000000;
unsigned int numevents=1;
int ret= port_getn(d_portfd, d_pevents.get(), min(PORT_MAX_LIST, s_maxevents), &numevents, &timeoutspec);
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "proxy-protocol.hh"
+
+// TODO: maybe use structs instead of explicitly working byte by byte, like https://github.com/dovecot/core/blob/master/src/lib-master/master-service-haproxy.c
+
+#define PROXYMAGIC "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
+#define PROXYMAGICLEN sizeof(PROXYMAGIC)-1
+
+static string proxymagic(PROXYMAGIC, PROXYMAGICLEN);
+
+static std::string makeSimpleHeader(uint8_t command, uint8_t protocol, uint16_t contentLen)
+{
+ std::string ret;
+ const uint8_t versioncommand = (0x20 | command);
+
+ ret.reserve(proxymagic.size() + sizeof(versioncommand) + sizeof(protocol) + sizeof(contentLen) + contentLen);
+
+ ret.append(proxymagic);
+
+ ret.append(reinterpret_cast<const char*>(&versioncommand), sizeof(versioncommand));
+ ret.append(reinterpret_cast<const char*>(&protocol), sizeof(protocol));
+
+ ret.append(reinterpret_cast<const char*>(&contentLen), sizeof(contentLen));
+
+ return ret;
+}
+
+std::string makeLocalProxyHeader()
+{
+ return makeSimpleHeader(0x00, 0, 0);
+}
+
+std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values)
+{
+ if (source.sin4.sin_family != destination.sin4.sin_family) {
+ throw std::runtime_error("The PROXY destination and source addresses must be of the same family");
+ }
+
+ const uint8_t command = 0x01;
+ const uint8_t protocol = (source.isIPv4() ? 0x10 : 0x20) | (tcp ? 0x01 : 0x02);
+ const size_t addrSize = source.isIPv4() ? sizeof(source.sin4.sin_addr.s_addr) : sizeof(source.sin6.sin6_addr.s6_addr);
+ const uint16_t sourcePort = source.sin4.sin_port;
+ const uint16_t destinationPort = destination.sin4.sin_port;
+
+ size_t valuesSize = 0;
+ for (const auto& value : values) {
+ if (value.content.size() > std::numeric_limits<uint16_t>::max()) {
+ 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 (total > std::numeric_limits<uint16_t>::max()) {
+ throw std::runtime_error("The size of a proxy protocol header is limited to " + std::to_string(std::numeric_limits<uint16_t>::max()) + ", trying to send one of size " + std::to_string(total));
+ }
+
+ const uint16_t contentlen = htons(static_cast<uint16_t>(total));
+ std::string ret = makeSimpleHeader(command, protocol, contentlen);
+
+ // We already established source and destination sin_family equivalence
+ if (source.isIPv4()) {
+ assert(addrSize == sizeof(source.sin4.sin_addr.s_addr));
+ ret.append(reinterpret_cast<const char*>(&source.sin4.sin_addr.s_addr), addrSize);
+ assert(addrSize == sizeof(destination.sin4.sin_addr.s_addr));
+ ret.append(reinterpret_cast<const char*>(&destination.sin4.sin_addr.s_addr), addrSize);
+ }
+ else {
+ assert(addrSize == sizeof(source.sin6.sin6_addr.s6_addr));
+ ret.append(reinterpret_cast<const char*>(&source.sin6.sin6_addr.s6_addr), addrSize);
+ assert(addrSize == sizeof(destination.sin6.sin6_addr.s6_addr));
+ ret.append(reinterpret_cast<const char*>(&destination.sin6.sin6_addr.s6_addr), addrSize);
+ }
+
+ ret.append(reinterpret_cast<const char*>(&sourcePort), sizeof(sourcePort));
+ ret.append(reinterpret_cast<const char*>(&destinationPort), sizeof(destinationPort));
+
+ for (const auto& value : values) {
+ uint16_t contentSize = htons(static_cast<uint16_t>(value.content.size()));
+ ret.append(reinterpret_cast<const char*>(&value.type), sizeof(value.type));
+ ret.append(reinterpret_cast<const char*>(&contentSize), sizeof(contentSize));
+ ret.append(reinterpret_cast<const char*>(value.content.data()), value.content.size());
+ }
+
+ return ret;
+}
+
+/* returns: number of bytes consumed (positive) after successful parse
+ or number of bytes missing (negative)
+ or unfixable parse error (0)*/
+ssize_t isProxyHeaderComplete(const std::string& header, bool* proxy, bool* tcp, size_t* addrSizeOut, uint8_t* protocolOut)
+{
+ static const size_t addr4Size = sizeof(ComboAddress::sin4.sin_addr.s_addr);
+ static const size_t addr6Size = sizeof(ComboAddress::sin6.sin6_addr.s6_addr);
+ size_t addrSize = 0;
+ uint8_t versioncommand;
+ uint8_t protocol;
+
+ if (header.size() < s_proxyProtocolMinimumHeaderSize) {
+ // this is too short to be a complete proxy header
+ return -(s_proxyProtocolMinimumHeaderSize - header.size());
+ }
+
+ if (header.compare(0, proxymagic.size(), proxymagic) != 0) {
+ // wrong magic, can not be a proxy header
+ return 0;
+ }
+
+ versioncommand = header.at(12);
+ /* check version */
+ if (!(versioncommand & 0x20)) {
+ return 0;
+ }
+
+ /* remove the version to get the command */
+ uint8_t command = versioncommand & ~0x20;
+
+ if (command == 0x01) {
+ protocol = header.at(13);
+ if ((protocol & 0xf) == 1) {
+ if (tcp) {
+ *tcp = true;
+ }
+ } else if ((protocol & 0xf) == 2) {
+ if (tcp) {
+ *tcp = false;
+ }
+ } else {
+ return 0;
+ }
+
+ protocol = protocol >> 4;
+
+ if (protocol == 1) {
+ if (protocolOut) {
+ *protocolOut = 4;
+ }
+ addrSize = addr4Size; // IPv4
+ } else if (protocol == 2) {
+ if (protocolOut) {
+ *protocolOut = 6;
+ }
+ addrSize = addr6Size; // IPv6
+ } else {
+ // invalid protocol
+ return 0;
+ }
+
+ if (addrSizeOut) {
+ *addrSizeOut = addrSize;
+ }
+
+ if (proxy) {
+ *proxy = true;
+ }
+ }
+ else if (command == 0x00) {
+ if (proxy) {
+ *proxy = false;
+ }
+ }
+ else {
+ /* unsupported command */
+ return 0;
+ }
+
+ uint16_t contentlen = (static_cast<uint8_t>(header.at(14)) << 8) + static_cast<uint8_t>(header.at(15));
+ uint16_t expectedlen = 0;
+ if (command != 0x00) {
+ expectedlen = (addrSize * 2) + sizeof(ComboAddress::sin4.sin_port) + sizeof(ComboAddress::sin4.sin_port);
+ }
+
+ if (contentlen < expectedlen) {
+ return 0;
+ }
+
+ if (header.size() < s_proxyProtocolMinimumHeaderSize + contentlen) {
+ return -((s_proxyProtocolMinimumHeaderSize + contentlen) - header.size());
+ }
+
+ return s_proxyProtocolMinimumHeaderSize + contentlen;
+}
+
+/* returns: number of bytes consumed (positive) after successful parse
+ or number of bytes missing (negative)
+ or unfixable parse error (0)*/
+ssize_t parseProxyHeader(const std::string& header, bool& proxy, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector<ProxyProtocolValue>& values)
+{
+ size_t addrSize = 0;
+ uint8_t protocol = 0;
+ ssize_t got = isProxyHeaderComplete(header, &proxy, &tcp, &addrSize, &protocol);
+ if (got <= 0) {
+ return got;
+ }
+
+ size_t pos = s_proxyProtocolMinimumHeaderSize;
+
+ if (proxy) {
+ source = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize);
+ pos = pos + addrSize;
+ destination = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize);
+ pos = pos + addrSize;
+ source.setPort((static_cast<uint8_t>(header.at(pos)) << 8) + static_cast<uint8_t>(header.at(pos+1)));
+ pos = pos + sizeof(uint16_t);
+ destination.setPort((static_cast<uint8_t>(header.at(pos)) << 8) + static_cast<uint8_t>(header.at(pos+1)));
+ pos = pos + sizeof(uint16_t);
+ }
+
+ size_t remaining = got - pos;
+ while (remaining >= (sizeof(uint8_t) + sizeof(uint16_t))) {
+ /* we still have TLV values to parse */
+ uint8_t type = static_cast<uint8_t>(header.at(pos));
+ pos += sizeof(uint8_t);
+ uint16_t len = (static_cast<uint8_t>(header.at(pos)) << 8) + static_cast<uint8_t>(header.at(pos + 1));
+ pos += sizeof(uint16_t);
+
+ if (len > 0) {
+ if (len > (got - pos)) {
+ return 0;
+ }
+
+ values.push_back({ std::string(&header.at(pos), len), type });
+ pos += len;
+ }
+ else {
+ values.push_back({ std::string(), type });
+ }
+
+ remaining = got - pos;
+ }
+
+ return pos;
+}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <iputils.hh>
+
+struct ProxyProtocolValue
+{
+ std::string content;
+ uint8_t type;
+};
+
+static const size_t s_proxyProtocolMinimumHeaderSize = 16;
+
+std::string makeLocalProxyHeader();
+std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values);
+
+/* returns: number of bytes consumed (positive) after successful parse
+ or number of bytes missing (negative)
+ or unfixable parse error (0)*/
+ssize_t isProxyHeaderComplete(const std::string& header, bool* proxy=nullptr, bool* tcp=nullptr, size_t* addrSizeOut=nullptr, uint8_t* protocolOut=nullptr);
+
+/* returns: number of bytes consumed (positive) after successful parse
+ or number of bytes missing (negative)
+ or unfixable parse error (0)*/
+ssize_t parseProxyHeader(const std::string& payload, bool& proxy, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector<ProxyProtocolValue>& values);
A6=38,
DNAME=39,
OPT=41,
+ APL=42,
DS=43,
SSHFP=44,
IPSECKEY=45,
qtype_insert("A6", 38);
qtype_insert("DNAME", 39);
qtype_insert("OPT", 41);
+ qtype_insert("APL", 42);
qtype_insert("DS", 43);
qtype_insert("SSHFP", 44);
qtype_insert("IPSECKEY", 45);
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "query-local-address.hh"
+#include "iputils.hh"
+#include "dns_random.hh"
+
+namespace pdns {
+ static const ComboAddress local4("0.0.0.0");
+ static const ComboAddress local6("::");
+
+ static vector<ComboAddress> g_localQueryAddresses4;
+ static vector<ComboAddress> g_localQueryAddresses6;
+
+ ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port) {
+ ComboAddress ret;
+ if (family==AF_INET) {
+ if (g_localQueryAddresses4.empty()) {
+ ret = local4;
+ } else if (g_localQueryAddresses4.size() == 1) {
+ ret = g_localQueryAddresses4.at(0);
+ } else {
+ ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())];
+ }
+ ret.sin4.sin_port = htons(port);
+ }
+ else {
+ if (g_localQueryAddresses6.empty()) {
+ ret = local6;
+ } else if (g_localQueryAddresses6.size() == 1) {
+ ret = g_localQueryAddresses6.at(0);
+ } else {
+ ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())];
+ }
+ ret.sin6.sin6_port = htons(port);
+ }
+ return ret;
+ }
+
+ ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family) {
+ if (family == AF_INET) {
+ for (const auto& addr : pdns::g_localQueryAddresses4) {
+ if (!IsAnyAddress(addr)) {
+ return addr;
+ }
+ }
+ }
+ if (family == AF_INET6) {
+ for (const auto& addr : pdns::g_localQueryAddresses6) {
+ if (!IsAnyAddress(addr)) {
+ return addr;
+ }
+ }
+ }
+ ComboAddress ret("0.0.0.0");
+ ret.reset(); // Ensure all is zero, even the addr family
+ return ret;
+ }
+
+ void parseQueryLocalAddress(const std::string &qla) {
+ vector<string> addrs;
+ stringtok(addrs, qla, ", ;");
+ for(const string& addr : addrs) {
+ ComboAddress tmp(addr);
+ if (tmp.isIPv4()) {
+ g_localQueryAddresses4.push_back(tmp);
+ continue;
+ }
+ g_localQueryAddresses6.push_back(tmp);
+ }
+ }
+
+ bool isQueryLocalAddressFamilyEnabled(const sa_family_t family) {
+ if (family == AF_INET) {
+ return !g_localQueryAddresses4.empty();
+ }
+ if (family == AF_INET6) {
+ return !g_localQueryAddresses6.empty();
+ }
+ return false;
+ }
+} // namespace pdns
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "iputils.hh"
+
+namespace pdns {
+ /*! pick a random query local address for family
+ *
+ * Will always return a ComboAddress.
+ *
+ * @param family Address Family, only AF_INET and AF_INET6 are supported
+ * @param port Port to set in the returned ComboAddress
+ */
+ ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port);
+
+ /*! Returns a non-Any address QLA, or an empty QLA when the QLA is any
+ *
+ * @param family Address Family
+ */
+ ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family);
+
+ /*! Populate the query local address vectors
+ *
+ * Will throw when an address can't be parsed
+ *
+ * @param qla A string of one or more ip addresses, separated by
+ * spaces, semi-colons or commas
+ */
+ void parseQueryLocalAddress(const std::string &qla);
+
+ /*! Is the address family explicitly enabled
+ *
+ * i.e. was there an address parsed by parseQueryLocalAddress belonging
+ * to this family
+ *
+ * @param family Address Family, only AF_INET and AF_INET6 are supported
+ */
+ bool isQueryLocalAddressFamilyEnabled(const sa_family_t family);
+} // namespace pdns
}
-void HEXDecode(const char* begin, const char* end, string& out)
+static void HEXDecode(const char* begin, const char* end, string& out)
{
if(end - begin == 1 && *begin=='-') {
out.clear();
vector<string> carbonServers;
{
- Lock l(&g_carbon_config_lock);
+ std::lock_guard<std::mutex> l(g_carbon_config_lock);
stringtok(carbonServers, arg()["carbon-server"], ", ");
namespace_name=arg()["carbon-namespace"];
hostname=arg()["carbon-ourname"];
if(namespace_name.empty()) {
namespace_name="pdns";
}
- if(hostname.empty()) {
- char tmp[80];
- memset(tmp, 0, sizeof(tmp));
- gethostname(tmp, sizeof(tmp));
- char *p = strchr(tmp, '.');
- if(p) *p=0;
-
- hostname=tmp;
- boost::replace_all(hostname, ".", "_");
+ if (hostname.empty()) {
+ try {
+ hostname = getCarbonHostName();
+ }
+ catch(const std::exception& e) {
+ throw std::runtime_error(std::string("The 'carbon-ourname' setting has not been set and we are unable to determine the system's hostname: ") + e.what());
+ }
}
if(instance_name.empty()) {
instance_name="recursor";
return iter->second;
}
-typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string > > rpzOptions_t;
+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)
+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"]);
if(have.count("defpol")) {
defpol=DNSFilterEngine::Policy();
defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get<uint32_t>(have["defpol"]);
- defpol->d_name = std::make_shared<std::string>(polName);
+ defpol->setName(polName);
if(defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
defpol->d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN,
boost::get<string>(have["defcontent"])));
if(have.count("zoneSizeHint")) {
zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(have["zoneSizeHint"]));
}
+ if (have.count("tags")) {
+ const auto tagsTable = boost::get<std::vector<std::pair<int, std::string>>>(have["tags"]);
+ for (const auto& tag : tagsTable) {
+ 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;
- parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint);
+ std::unordered_set<std::string> tags;
+ parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags, overridesGettag);
if (zoneSizeHint > 0) {
zone->reserve(zoneSizeHint);
}
+ zone->setTags(std::move(tags));
}
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;
if (options) {
auto& have = *options;
size_t zoneSizeHint = 0;
- parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint);
+ std::unordered_set<std::string> tags;
+ bool overridesGettag = true;
+ parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags, overridesGettag);
if (zoneSizeHint > 0) {
zone->reserve(zoneSizeHint);
}
+ zone->setTags(std::move(tags));
+ zone->setPolicyOverridesGettag(overridesGettag);
if(have.count("tsigname")) {
tt.name=DNSName(toLower(boost::get<string>(have["tsigname"])));
#endif /* HAVE_PROTOBUF */
}
-void RecProtoBufMessage::setPolicyTags(const std::vector<std::string>& policyTags)
+void RecProtoBufMessage::setAppliedPolicyTrigger(const DNSName& trigger)
+{
+#ifdef HAVE_PROTOBUF
+ PBDNSMessage_DNSResponse* response = d_message.mutable_response();
+ if (response && !trigger.empty()) {
+ response->set_appliedpolicytrigger(trigger.toString());
+ }
+#endif /* HAVE_PROTOBUF */
+}
+
+void RecProtoBufMessage::setAppliedPolicyHit(const string& hit)
+{
+#ifdef HAVE_PROTOBUF
+ PBDNSMessage_DNSResponse* response = d_message.mutable_response();
+ if (response && !hit.empty()) {
+ response->set_appliedpolicyhit(hit);
+ }
+#endif /* HAVE_PROTOBUF */
+}
+
+void RecProtoBufMessage::setPolicyTags(const std::unordered_set<std::string>& policyTags)
{
#ifdef HAVE_PROTOBUF
PBDNSMessage_DNSResponse* response = d_message.mutable_response();
#endif /* NOD_ENABLED */
void setAppliedPolicy(const std::string& policy);
void setAppliedPolicyType(const DNSFilterEngine::PolicyType& policyType);
- void setPolicyTags(const std::vector<std::string>& policyTags);
+ void setAppliedPolicyTrigger(const DNSName& trigger);
+ void setAppliedPolicyHit(const string& hit);
+ void setPolicyTags(const std::unordered_set<std::string>& policyTags);
void addPolicyTag(const std::string& policyTag);
void removePolicyTag(const std::string& policyTag);
std::string getAppliedPolicy() const;
static const oid unreachablesOID[] = { RECURSOR_STATS_OID, 63 };
static const oid chainResendsOID[] = { RECURSOR_STATS_OID, 64 };
static const oid tcpClientsOID[] = { RECURSOR_STATS_OID, 65 };
+#ifdef __linux__
static const oid udpRecvbufErrorsOID[] = { RECURSOR_STATS_OID, 66 };
static const oid udpSndbufErrorsOID[] = { RECURSOR_STATS_OID, 67 };
static const oid udpNoportErrorsOID[] = { RECURSOR_STATS_OID, 68 };
static const oid udpinErrorsOID[] = { RECURSOR_STATS_OID, 69 };
+#endif /* __linux__ */
static const oid ednsPingMatchesOID[] = { RECURSOR_STATS_OID, 70 };
static const oid ednsPingMismatchesOID[] = { RECURSOR_STATS_OID, 71 };
static const oid dnssecQueriesOID[] = { RECURSOR_STATS_OID, 72 };
static const oid specialMemoryUsageOID[] = { RECURSOR_STATS_OID, 98 };
static const oid rebalancedQueriesOID[] = { RECURSOR_STATS_OID, 99 };
static const oid qnameMinFallbackSuccessOID[] = { RECURSOR_STATS_OID, 100 };
+static const oid proxyProtocolInvalidOID[] = { RECURSOR_STATS_OID, 101 };
+static const oid recordCacheContendedOID[] = { RECURSOR_STATS_OID, 102 };
+static const oid recordCacheAcquiredOID[] = { RECURSOR_STATS_OID, 103 };
static std::unordered_map<oid, std::string> s_statsMap;
registerCounter64Stat("policy-result-custom", policyResultCustomOID, OID_LENGTH(policyResultCustomOID));
registerCounter64Stat("special-memory-usage", specialMemoryUsageOID, OID_LENGTH(specialMemoryUsageOID));
registerCounter64Stat("rebalanced-queries", rebalancedQueriesOID, OID_LENGTH(rebalancedQueriesOID));
+ registerCounter64Stat("proxy-protocol-invalid", proxyProtocolInvalidOID, OID_LENGTH(proxyProtocolInvalidOID));
+ registerCounter64Stat("record-cache-contended", recordCacheContendedOID, OID_LENGTH(recordCacheContendedOID));
+ registerCounter64Stat("record-cache-acquired", recordCacheAcquiredOID, OID_LENGTH(recordCacheAcquiredOID));
#endif /* HAVE_NET_SNMP */
}
enum class StatComponent { API, Carbon, RecControl, SNMP };
std::map<std::string, std::string> getAllStatsMap(StatComponent component);
-extern pthread_mutex_t g_carbon_config_lock;
+extern std::mutex g_carbon_config_lock;
std::vector<std::pair<DNSName, uint16_t> >* pleaseGetQueryRing();
std::vector<std::pair<DNSName, uint16_t> >* pleaseGetServfailQueryRing();
std::vector<std::pair<DNSName, uint16_t> >* pleaseGetBogusQueryRing();
#include "secpoll-recursor.hh"
#include "pubsuffix.hh"
#include "namespaces.hh"
-pthread_mutex_t g_carbon_config_lock=PTHREAD_MUTEX_INITIALIZER;
+std::mutex g_carbon_config_lock;
static map<string, const uint32_t*> d_get32bitpointers;
static map<string, const std::atomic<uint64_t>*> d_getatomics;
static map<string, function< uint64_t() > > d_get64bitmembers;
-static pthread_mutex_t d_dynmetricslock = PTHREAD_MUTEX_INITIALIZER;
+static std::mutex d_dynmetricslock;
static map<string, std::atomic<unsigned long>* > d_dynmetrics;
static std::map<StatComponent, std::set<std::string>> s_blacklistedStats;
std::atomic<unsigned long>* getDynMetric(const std::string& str)
{
- Lock l(&d_dynmetricslock);
+ std::lock_guard<std::mutex> l(d_dynmetricslock);
auto f = d_dynmetrics.find(str);
if(f != d_dynmetrics.end())
return f->second;
if(d_get64bitmembers.count(name))
return d_get64bitmembers.find(name)->second();
- Lock l(&d_dynmetricslock);
+ std::lock_guard<std::mutex> l(d_dynmetricslock);
auto f =rplookup(d_dynmetrics, name);
if(f)
return (*f)->load();
}
{
- Lock l(&d_dynmetricslock);
+ std::lock_guard<std::mutex> l(d_dynmetricslock);
for(const auto& a : d_dynmetrics) {
if (blacklistMap.count(a.first) == 0) {
ret.insert({a.first, std::to_string(*a.second)});
}
-static uint64_t dumpNegCache(NegCache& negcache, int fd)
+static uint64_t dumpNegCache(int fd)
{
- auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(dup(fd), "w"), fclose);
- if(!fp) { // dup probably failed
+ int newfd = dup(fd);
+ if (newfd == -1) {
return 0;
}
- uint64_t ret;
- fprintf(fp.get(), "; negcache dump from thread follows\n;\n");
- ret = negcache.dumpToFile(fp.get());
- return ret;
+ auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(newfd, "w"), fclose);
+ if (!fp) {
+ return 0;
+ }
+ fprintf(fp.get(), "; negcache dump follows\n;\n");
+ return g_negCache->dumpToFile(fp.get());
}
static uint64_t* pleaseDump(int fd)
{
- return new uint64_t(dumpNegCache(SyncRes::t_sstorage.negcache, fd) + t_packetCache->doDump(fd));
+ return new uint64_t(t_packetCache->doDump(fd));
}
static uint64_t* pleaseDumpEDNSMap(int fd)
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 = g_recCache->doDump(fd) + dumpNegCache(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(...){}
uint64_t* pleaseWipeCache(const DNSName& canon, bool subtree, uint16_t qtype)
{
- return new uint64_t(s_RC->doWipeCache(canon, subtree));
+ return new uint64_t(g_recCache->doWipeCache(canon, subtree, qtype));
}
uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree, uint16_t qtype)
uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree)
{
- uint64_t ret = SyncRes::wipeNegCache(canon, subtree);
+ uint64_t ret = g_negCache->wipe(canon, subtree);
return new uint64_t(ret);
}
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));
+ try {
+ 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);});
+ }
+ catch (const std::exception& e) {
+ g_log<<Logger::Warning<<", failed: "<<e.what()<<endl;
+ }
}
return "wiped "+std::to_string(count)+" records, "+std::to_string(countNeg)+" negative records, "+std::to_string(pcount)+" packets\n";
template<typename T>
static string doSetCarbonServer(T begin, T end)
{
- Lock l(&g_carbon_config_lock);
+ std::lock_guard<std::mutex> l(g_carbon_config_lock);
if(begin==end) {
::arg().set("carbon-server").clear();
return "cleared carbon-server setting\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));
+ try {
+ broadcastAccFunction<uint64_t>([=]{return pleaseWipeCache(who, true, 0xffff);});
+ broadcastAccFunction<uint64_t>([=]{return pleaseWipePacketCache(who, true, 0xffff);});
+ broadcastAccFunction<uint64_t>([=]{return pleaseWipeAndCountNegCache(who, true);});
+ }
+ catch (std::exception& e) {
+ g_log<<Logger::Warning<<", failed: "<<e.what()<<endl;
+ return "Unable to clear caches while adding Negative Trust Anchor for " + who.toStringRootDot() + ": " + e.what() + "\n";
+ }
return "Added Negative Trust Anchor for " + who.toLogString() + " with reason '" + why + "'\n";
}
string removed("");
bool first(true);
- for (auto const &entry : toRemove) {
- g_log<<Logger::Warning<<"Clearing Negative Trust Anchor for "<<entry<<", requested via control channel"<<endl;
- 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));
- if (!first) {
- first = false;
- removed += ",";
+ try {
+ for (auto const &entry : toRemove) {
+ g_log<<Logger::Warning<<"Clearing Negative Trust Anchor for "<<entry<<", requested via control channel"<<endl;
+ g_luaconfs.modify([entry](LuaConfigItems& lci) {
+ lci.negAnchors.erase(entry);
+ });
+ 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 += ",";
+ }
+ removed += " " + entry.toStringRootDot();
}
- removed += " " + entry.toStringRootDot();
}
+ catch(std::exception &e) {
+ g_log<<Logger::Warning<<", failed: "<<e.what()<<endl;
+ return "Unable to clear caches while clearing Negative Trust Anchor for " + who.toStringRootDot() + ": " + e.what() + "\n";
+ }
+
return "Removed Negative Trust Anchors for " + removed + "\n";
}
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";
}
string removed("");
bool first(true);
- for (auto const &entry : toRemove) {
- g_log<<Logger::Warning<<"Removing Trust Anchor for "<<entry<<", requested via control channel"<<endl;
- 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));
- if (!first) {
- first = false;
- removed += ",";
+ try {
+ for (auto const &entry : toRemove) {
+ g_log<<Logger::Warning<<"Removing Trust Anchor for "<<entry<<", requested via control channel"<<endl;
+ g_luaconfs.modify([entry](LuaConfigItems& lci) {
+ lci.dsAnchors.erase(entry);
+ });
+ 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 += ",";
+ }
+ removed += " " + entry.toStringRootDot();
}
- removed += " " + entry.toStringRootDot();
}
+ catch (std::exception& e) {
+ g_log<<Logger::Warning<<", failed: "<<e.what()<<endl;
+ return "Unable to clear caches while clearing Trust Anchor for " + who.toStringRootDot() + ": " + e.what() + "\n";
+ }
+
return "Removed Trust Anchor(s) for" + removed + "\n";
}
uint64_t* pleaseGetNegCacheSize()
{
- uint64_t tmp=(SyncRes::getNegCacheSize());
+ uint64_t tmp = g_negCache->size();
return new uint64_t(tmp);
}
static uint64_t doGetCacheSize()
{
- return s_RC->size();
+ return g_recCache->size();
}
static uint64_t doGetAvgLatencyUsec()
static uint64_t doGetCacheBytes()
{
- return s_RC->bytes();
+ return g_recCache->bytes();
}
static uint64_t doGetCacheHits()
{
- return s_RC->cacheHits;
+ return g_recCache->cacheHits;
}
static uint64_t doGetCacheMisses()
{
- return s_RC->cacheMisses;
+ return g_recCache->cacheMisses;
}
uint64_t* pleaseGetPacketCacheSize()
addGetStat("max-cache-entries", []() { return g_maxCacheEntries.load(); });
addGetStat("max-packetcache-entries", []() { return g_maxPacketCacheEntries.load();});
addGetStat("cache-bytes", doGetCacheBytes);
+ addGetStat("record-cache-contended", []() { return g_recCache->stats().first;});
+ addGetStat("record-cache-acquired", []() { return g_recCache->stats().second;});
addGetStat("packetcache-hits", doGetPacketCacheHits);
addGetStat("packetcache-misses", doGetPacketCacheMisses);
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]);
addGetStat("rebalanced-queries", &g_stats.rebalancedQueries);
+ addGetStat("proxy-protocol-invalid", &g_stats.proxyProtocolInvalidCount);
+
/* make sure that the ECS stats are properly initialized */
SyncRes::clearECSStats();
for (size_t idx = 0; idx < SyncRes::s_ecsResponsesBySubnetSize4.size(); idx++) {
return ret;
}
-string doGenericTopRemotes(pleaseremotefunc_t func)
+static string doGenericTopRemotes(pleaseremotefunc_t func)
{
typedef map<ComboAddress, int, ComboAddress::addressOnlyLessThan> counts_t;
counts_t counts;
return name;
}
-string doGenericTopQueries(pleasequeryfunc_t func, boost::function<DNSName(const DNSName&)> filter=nopFilter)
+static string doGenericTopQueries(pleasequeryfunc_t func, boost::function<DNSName(const DNSName&)> filter=nopFilter)
{
typedef pair<DNSName,uint16_t> query_t;
typedef map<query_t, int> counts_t;
This file is where it all happens - main is here, as are the two pivotal threads qthread() and athread()
*/
-void daemonize(void)
+static void daemonize(void)
{
if(fork())
exit(0); // bye bye
int g_fd1[2], g_fd2[2];
FILE *g_fp;
-pthread_mutex_t g_guardian_lock = PTHREAD_MUTEX_INITIALIZER;
+std::mutex g_guardian_lock;
// The next two methods are not in dynhandler.cc because they use a few items declared in this file.
static string DLCycleHandler(const vector<string>&parts, pid_t ppid)
}
line.append(1,'\n');
- Lock l(&g_guardian_lock);
+ std::lock_guard<std::mutex> l(g_guardian_lock);
try {
writen2(g_fd1[1],line.c_str(),line.size()+1);
bool first=true;
cpid=0;
- pthread_mutex_lock(&g_guardian_lock);
+ g_guardian_lock.lock();
for(;;) {
int pid;
writePid();
}
- pthread_mutex_unlock(&g_guardian_lock);
+ g_guardian_lock.unlock();
int status;
cpid=pid;
for(;;) {
}
}
- pthread_mutex_lock(&g_guardian_lock);
+ g_guardian_lock.lock();
close(g_fd1[1]);
fclose(g_fp);
g_fp=0;
}
cerr<<" (";
bool first = true;
- for (auto const c : ::arg().getCommands()) {
+ for (const auto& c : ::arg().getCommands()) {
if (!first) {
cerr<<", ";
}
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>");
exit(1);
}
- declareStats();
+ try {
+ declareStats();
+ }
+ catch(PDNSException &PE) {
+ g_log<<Logger::Error<<"Exiting because: "<<PE.reason<<endl;
+ exit(1);
+ }
S.blacklist("special-memory-usage");
DLOG(g_log<<Logger::Warning<<"Verbose logging in effect"<<endl);
return count;
}
-bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t ecsBegin, uint16_t ecsEnd)
+bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass)
{
// this ignores checking on the EDNS subnet flags!
if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
return false;
}
- if (iter->d_ecsBegin != ecsBegin || iter->d_ecsEnd != ecsEnd) {
- return false;
- }
-
- return queryMatches(iter->d_query, queryPacket, qname, ecsBegin, ecsEnd);
+ static const std::unordered_set<uint16_t> optionsToSkip{ EDNSOptionCode::COOKIE, EDNSOptionCode::ECS };
+ return queryMatches(iter->d_query, queryPacket, qname, optionsToSkip);
}
-bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage, uint16_t ecsBegin, uint16_t ecsEnd)
+bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage)
{
for(auto iter = range.first ; iter != range.second ; ++iter) {
// the possibility is VERY real that we get hits that are not right - birthday paradox
- if (!qrMatch(iter, queryPacket, qname, qtype, qclass, ecsBegin, ecsEnd)) {
+ if (!qrMatch(iter, queryPacket, qname, qtype, qclass)) {
continue;
}
}
}
#endif
-
return true;
}
else {
{
DNSName qname;
uint16_t qtype, qclass;
- uint16_t ecsBegin;
- uint16_t ecsEnd;
vState valState;
- return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, &valState, qhash, &ecsBegin, &ecsEnd, nullptr);
+ return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, &valState, qhash, nullptr);
}
bool RecursorPacketCache::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, uint32_t* qhash)
{
vState valState;
- uint16_t ecsBegin;
- uint16_t ecsEnd;
- return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, &valState, qhash, &ecsBegin, &ecsEnd, nullptr);
+ return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, &valState, qhash, nullptr);
}
bool RecursorPacketCache::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)
+ std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
{
- *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
+ *qhash = canHashPacket(queryPacket, true);
const auto& idx = d_packetCache.get<HashTag>();
auto range = idx.equal_range(tie(tag,*qhash));
return false;
}
- return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, protobufMessage, *ecsBegin, *ecsEnd);
+ return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, protobufMessage);
}
bool RecursorPacketCache::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)
+ std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
{
- *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
+ *qhash = canHashPacket(queryPacket, true);
const auto& idx = d_packetCache.get<HashTag>();
auto range = idx.equal_range(tie(tag,*qhash));
qname = DNSName(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, qtype, qclass, 0);
- return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, protobufMessage, *ecsBegin, *ecsEnd);
+ return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, protobufMessage);
}
-void RecursorPacketCache::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 RecursorPacketCache::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, boost::optional<RecProtoBufMessage>&& protobufMessage)
{
auto& idx = d_packetCache.get<HashTag>();
auto range = idx.equal_range(tie(tag,qhash));
moveCacheItemToBack<SequencedTag>(d_packetCache, iter);
iter->d_packet = std::move(responsePacket);
iter->d_query = std::move(query);
- iter->d_ecsBegin = ecsBegin;
- iter->d_ecsEnd = ecsEnd;
iter->d_ttd = now + ttl;
iter->d_creation = now;
iter->d_vstate = valState;
if(iter == range.second) { // nothing to refresh
struct Entry e(qname, std::move(responsePacket), std::move(query));
e.d_qhash = qhash;
- e.d_ecsBegin = ecsBegin;
- e.d_ecsEnd = ecsEnd;
e.d_type = qtype;
e.d_class = qclass;
e.d_ttd = now+ttl;
return sum;
}
-void RecursorPacketCache::doPruneTo(unsigned int maxCached)
+void RecursorPacketCache::doPruneTo(size_t maxCached)
{
pruneCollection<SequencedTag>(*this, d_packetCache, maxCached);
}
RecursorPacketCache();
bool getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now, std::string* responsePacket, uint32_t* age, uint32_t* qhash);
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, uint32_t* qhash);
- 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);
+ 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, 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, 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, boost::optional<RecProtoBufMessage>&& protobufMessage);
+ void doPruneTo(size_t maxSize=250000);
uint64_t doDump(int fd);
int doWipePacketCache(const DNSName& name, uint16_t qtype=0xffff, bool subtree=false);
uint32_t d_tag;
uint16_t d_type;
uint16_t d_class;
- mutable uint16_t d_ecsBegin;
- mutable uint16_t d_ecsEnd;
mutable vState d_vstate;
inline bool operator<(const struct Entry& rhs) const;
packetCache_t d_packetCache;
- static bool qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t ecsBegin, uint16_t ecsEnd);
- bool checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage, uint16_t ecsBegin, uint16_t ecsEnd);
+ static bool qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass);
+ bool checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage);
public:
void preRemoval(const Entry& entry)
return ret;
}
-int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
+int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
{
// MUTEX SHOULD BE ACQUIRED
int32_t ttd = entry->d_ttd;
- if(variable && !entry->d_netmask.empty()) {
+ if (variable && (!entry->d_netmask.empty() || entry->d_rtag)) {
*variable = true;
}
/* we have nothing more specific for you */
break;
}
- auto key = boost::make_tuple(qname, qtype, best);
+ auto key = boost::make_tuple(qname, qtype, boost::none, best);
auto entry = map.d_map.find(key);
if (entry == map.d_map.end()) {
/* ecsIndex is not up-to-date */
}
/* we have nothing specific, let's see if we have a generic one */
- auto key = boost::make_tuple(qname, qtype, Netmask());
+ auto key = boost::make_tuple(qname, qtype, boost::none, Netmask());
auto entry = map.d_map.find(key);
if (entry != map.d_map.end()) {
if (entry->d_ttd > now) {
return map.d_map.end();
}
-std::pair<MemRecursorCache::NameOnlyHashedTagIterator_t, MemRecursorCache::NameOnlyHashedTagIterator_t> MemRecursorCache::getEntries(MapCombo& map, const DNSName &qname, const QType& qt)
+MemRecursorCache::Entries MemRecursorCache::getEntries(MapCombo& map, const DNSName &qname, const QType& qt, const OptTag& rtag )
{
// MUTEX SHOULD BE ACQUIRED
- if (!map.d_cachecachevalid || map.d_cachedqname != qname) {
+ if (!map.d_cachecachevalid || map.d_cachedqname != qname || map.d_cachedrtag != rtag) {
map.d_cachedqname = qname;
- const auto& idx = map.d_map.get<NameOnlyHashedTag>();
- map.d_cachecache = idx.equal_range(qname);
+ map.d_cachedrtag = rtag;
+ const auto& idx = map.d_map.get<NameAndRTagOnlyHashedTag>();
+ map.d_cachecache = idx.equal_range(tie(qname, rtag));
map.d_cachecachevalid = true;
}
return map.d_cachecache;
}
+
bool MemRecursorCache::entryMatches(MemRecursorCache::OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who)
{
+ // This code assumes that if a routing tag is present, it matches
// MUTEX SHOULD BE ACQUIRED
if (requireAuth && !entry->d_auth)
return false;
- return ((entry->d_qtype == qt || qt == QType::ANY ||
- (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA)))
- && (entry->d_netmask.empty() || entry->d_netmask.match(who)));
+ bool match = (entry->d_qtype == qt || qt == QType::ANY ||
+ (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA)))
+ && (entry->d_netmask.empty() || entry->d_netmask.match(who));
+ //cerr << match << "N " << qt << ':' << entry->d_qtype << ' ' << entry->d_netmask.toString() << ':' << who.toString() << endl;
+ return match;
}
// returns -1 for no hits
-int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
+int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, const OptTag& routingTag, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
{
time_t ttd=0;
// cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
auto& map = getMap(qname);
const lock l(map);
-
+
/* If we don't have any netmask-specific entries at all, let's just skip this
to be able to use the nice d_cachecache hack. */
- if (qtype != QType::ANY && !map.d_ecsIndex.empty()) {
-
+ if (qtype != QType::ANY && !map.d_ecsIndex.empty() && !routingTag) {
if (qtype == QType::ADDR) {
int32_t ret = -1;
auto entryA = getEntryUsingECSIndex(map, now, qname, QType::A, requireAuth, who);
if (entryA != map.d_map.end()) {
- ret = handleHit(map, entryA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
+ ret = handleHit(map, entryA, qname, res, signatures, authorityRecs, variable, state, wasAuth);
}
auto entryAAAA = getEntryUsingECSIndex(map, now, qname, QType::AAAA, requireAuth, who);
if (entryAAAA != map.d_map.end()) {
- int32_t ttdAAAA = handleHit(map, entryAAAA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
+ int32_t ttdAAAA = handleHit(map, entryAAAA, qname, res, signatures, authorityRecs, variable, state, wasAuth);
if (ret > 0) {
ret = std::min(ret, ttdAAAA);
} else {
else {
auto entry = getEntryUsingECSIndex(map, now, qname, qtype, requireAuth, who);
if (entry != map.d_map.end()) {
- return static_cast<int32_t>(handleHit(map, entry, qname, who, res, signatures, authorityRecs, variable, state, wasAuth) - now);
+ return static_cast<int32_t>(handleHit(map, entry, qname, res, signatures, authorityRecs, variable, state, wasAuth) - now);
}
return -1;
}
}
- auto entries = getEntries(map, qname, qt);
+ if (routingTag) {
+ auto entries = getEntries(map, qname, qt, routingTag);
- if(entries.first!=entries.second) {
- for(auto i=entries.first; i != entries.second; ++i) {
+ if (entries.first != entries.second) {
+ for (auto i=entries.first; i != entries.second; ++i) {
+
+ auto firstIndexIterator = map.d_map.project<OrderedTag>(i);
+ if (i->d_ttd <= now) {
+ moveCacheItemToFront<SequencedTag>(map.d_map, firstIndexIterator);
+ continue;
+ }
+
+ if (!entryMatches(firstIndexIterator, qtype, requireAuth, who)) {
+ continue;
+ }
+
+ ttd = handleHit(map, firstIndexIterator, qname, res, signatures, authorityRecs, variable, state, wasAuth);
+
+ if (qt.getCode() != QType::ANY && qt.getCode() != QType::ADDR) { // normally if we have a hit, we are done
+ break;
+ }
+ }
+ return static_cast<int32_t>(ttd-now);
+ }
+ }
+ // Try (again) without tag
+ auto entries = getEntries(map, qname, qt, boost::none);
+
+ if (entries.first != entries.second) {
+ for (auto i=entries.first; i != entries.second; ++i) {
auto firstIndexIterator = map.d_map.project<OrderedTag>(i);
if (i->d_ttd <= now) {
continue;
}
- if (!entryMatches(firstIndexIterator, qtype, requireAuth, who))
+ if (!entryMatches(firstIndexIterator, qtype, requireAuth, who)) {
continue;
+ }
- ttd = handleHit(map, firstIndexIterator, qname, who, res, signatures, authorityRecs, variable, state, wasAuth);
+ ttd = handleHit(map, firstIndexIterator, qname, res, signatures, authorityRecs, variable, state, wasAuth);
- if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done
+ if (qt.getCode() != QType::ANY && qt.getCode() != QType::ADDR) { // normally if we have a hit, we are done
break;
+ }
}
-
- // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
return static_cast<int32_t>(ttd-now);
}
return -1;
}
-void MemRecursorCache::replace(time_t now, 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, vState state)
+void MemRecursorCache::replace(time_t now, 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, const OptTag& routingTag, vState state)
{
auto& map = getMap(qname);
const lock l(map);
if (ednsmask) {
ednsmask = ednsmask->getNormalized();
}
- auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask());
+
+ // We only store with a tag if we have an ednsmask and the tag is available
+ // We only store an ednsmask if we do not have a tag and we do have a mask.
+ auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? routingTag : boost::none, (ednsmask && !routingTag) ? *ednsmask : Netmask());
bool isNew = false;
cache_t::iterator stored = map.d_map.find(key);
if (stored == map.d_map.end()) {
*/
if (isNew || stored->d_ttd <= now) {
/* don't bother building an ecsIndex if we don't have any netmask-specific entries */
- if (ednsmask && !ednsmask->empty()) {
+ if (!routingTag && ednsmask && !ednsmask->empty()) {
auto ecsIndexKey = boost::make_tuple(qname, qt.getCode());
auto ecsIndex = map.d_ecsIndex.find(ecsIndexKey);
if (ecsIndex == map.d_ecsIndex.end()) {
if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh?
if(ce.d_ttd > now) { // we still have valid data, ignore unauth data
- // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
+ // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n";
return;
}
else {
ce.d_records.clear();
ce.d_records.reserve(content.size());
- for(const auto i : content) {
+ for(const auto& i : content) {
/* Yes, we have altered the d_ttl value by adding time(nullptr) to it
prior to calling this function, so the TTL actually holds a TTD. */
ce.d_ttd=min(maxTTD, static_cast<time_t>(i.d_ttl)); // XXX this does weird things if TTLs differ in the set
- // cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
+ //cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
ce.d_records.push_back(i.d_content);
}
auto& map = getMap(name);
const lock l(map);
map.d_cachecachevalid = false;
- auto& idx = map.d_map.get<NameOnlyHashedTag>();
- size_t n = idx.erase(name);
- count += n;
- map.d_entriesCount -= n;
+ auto& idx = map.d_map.get<OrderedTag>();
+ auto range = idx.equal_range(name);
+ auto i = range.first;
+ while (i != range.second) {
+ i = idx.erase(i);
+ count++;
+ map.d_entriesCount--;
+ }
+
if (qtype == 0xffff) {
auto& ecsIdx = map.d_ecsIndex.get<OrderedTag>();
auto ecsIndexRange = ecsIdx.equal_range(name);
return false;
}
-bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState, boost::optional<time_t> capTTD)
+bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, const OptTag& routingTag, bool requireAuth, vState newState, boost::optional<time_t> capTTD)
{
auto& map = getMap(qname);
const lock l(map);
bool updated = false;
uint16_t qtype = qt.getCode();
- if (qtype != QType::ANY && qtype != QType::ADDR && !map.d_ecsIndex.empty()) {
+ if (qtype != QType::ANY && qtype != QType::ADDR && !map.d_ecsIndex.empty() && !routingTag) {
auto entry = getEntryUsingECSIndex(map, now, qname, qtype, requireAuth, who);
if (entry == map.d_map.end()) {
return false;
return true;
}
- auto entries = getEntries(map, qname, qt);
+ auto entries = getEntries(map, qname, qt, routingTag);
for(auto i = entries.first; i != entries.second; ++i) {
auto firstIndexIterator = map.d_map.project<OrderedTag>(i);
- if (!entryMatches(firstIndexIterator, qtype, requireAuth, who))
+ if (!entryMatches(firstIndexIterator, qtype, requireAuth, who)) {
continue;
+ }
i->d_state = newState;
if (capTTD) {
const auto& sidx = map.d_map.get<SequencedTag>();
time_t now = time(0);
- for (const auto i : sidx) {
- for (const auto j : i.d_records) {
+ for (const auto& i : sidx) {
+ for (const auto& j : i.d_records) {
count++;
try {
- fprintf(fp.get(), "%s %" PRId64 " IN %s %s ; (%s) auth=%i %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());
+ 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());
pruneMutexCollectionsVector<SequencedTag>(*this, d_maps, keep, cacheSize);
}
+namespace boost {
+ size_t hash_value(const MemRecursorCache::OptTag& o)
+ {
+ return o ? hash_value(o.get()) : 0xcafebaaf;
+ }
+}
pair<uint64_t,uint64_t> stats();
size_t ecsIndexSize();
- int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr);
+ typedef boost::optional<std::string> OptTag;
- 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, vState state=Indeterminate);
+ 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=vState::Indeterminate);
void doPrune(size_t keep);
uint64_t doDump(int fd);
size_t doWipeCache(const DNSName& name, bool sub, uint16_t qtype=0xffff);
bool doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL);
- bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState, boost::optional<time_t> capTTD);
+ bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, const OptTag& routingTag, bool requireAuth, vState newState, boost::optional<time_t> capTTD);
std::atomic<uint64_t> cacheHits{0}, cacheMisses{0};
struct CacheEntry
{
- CacheEntry(const boost::tuple<DNSName, uint16_t, Netmask>& key, bool auth):
- d_qname(key.get<0>()), d_netmask(key.get<2>().getNormalized()), d_state(Indeterminate), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth)
+ 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(vState::Indeterminate), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth)
{
}
std::vector<std::shared_ptr<DNSRecord>> d_authorityRecs;
DNSName d_qname;
Netmask d_netmask;
+ OptTag d_rtag;
mutable vState d_state;
mutable time_t d_ttd;
uint16_t d_qtype;
struct HashedTag {};
struct SequencedTag {};
- struct NameOnlyHashedTag {};
+ struct NameAndRTagOnlyHashedTag {};
struct OrderedTag {};
typedef multi_index_container<
CacheEntry,
member<CacheEntry,DNSName,&CacheEntry::d_qname>,
member<CacheEntry,uint16_t,&CacheEntry::d_qtype>,
+ member<CacheEntry,OptTag,&CacheEntry::d_rtag>,
member<CacheEntry,Netmask,&CacheEntry::d_netmask>
>,
- composite_key_compare<CanonDNSNameCompare, std::less<uint16_t>, std::less<Netmask> >
+ composite_key_compare<CanonDNSNameCompare, std::less<uint16_t>, std::less<OptTag>, std::less<Netmask> >
>,
sequenced<tag<SequencedTag> >,
- hashed_non_unique<tag<NameOnlyHashedTag>,
- member<CacheEntry,DNSName,&CacheEntry::d_qname>
+ hashed_non_unique<tag<NameAndRTagOnlyHashedTag>,
+ composite_key<
+ CacheEntry,
+ member<CacheEntry,DNSName,&CacheEntry::d_qname>,
+ member<CacheEntry,OptTag,&CacheEntry::d_rtag>
+ >
>
>
> cache_t;
typedef MemRecursorCache::cache_t::index<MemRecursorCache::OrderedTag>::type::iterator OrderedTagIterator_t;
- typedef MemRecursorCache::cache_t::index<MemRecursorCache::NameOnlyHashedTag>::type::iterator NameOnlyHashedTagIterator_t;
+ typedef MemRecursorCache::cache_t::index<MemRecursorCache::NameAndRTagOnlyHashedTag>::type::iterator NameAndRTagOnlyHashedTagIterator_t;
typedef multi_index_container<
ECSIndexEntry,
>
> ecsIndex_t;
+ typedef std::pair<NameAndRTagOnlyHashedTagIterator_t, NameAndRTagOnlyHashedTagIterator_t> Entries;
+
struct MapCombo
{
MapCombo() {}
cache_t d_map;
ecsIndex_t d_ecsIndex;
DNSName d_cachedqname;
- std::pair<MemRecursorCache::NameOnlyHashedTagIterator_t, MemRecursorCache::NameOnlyHashedTagIterator_t> d_cachecache;
+ OptTag d_cachedrtag;
+ Entries d_cachecache;
std::mutex mutex;
bool d_cachecachevalid{false};
std::atomic<uint64_t> d_entriesCount{0};
uint64_t d_contended_count{0};
uint64_t d_acquired_count{0};
+
+ void invalidate()
+ {
+ d_cachecachevalid = false;
+ }
};
vector<MapCombo> d_maps;
}
bool entryMatches(OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who);
- std::pair<NameOnlyHashedTagIterator_t, NameOnlyHashedTagIterator_t> getEntries(MapCombo& map, const DNSName &qname, const QType& qt);
+ Entries getEntries(MapCombo& map, const DNSName &qname, const QType& qt, const OptTag& rtag);
cache_t::const_iterator getEntryUsingECSIndex(MapCombo& map, time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who);
- int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth);
+ int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth);
public:
struct lock {
}
}
};
+
+namespace boost {
+ size_t hash_value(const MemRecursorCache::OptTag& rtag);
+}
filterpo.cc filterpo.hh \
fstrm_logger.cc fstrm_logger.hh \
gettime.cc gettime.hh \
- gss_context.cc gss_context.hh \
iputils.hh iputils.cc \
ixfr.cc ixfr.hh \
json.cc json.hh \
pdnsexception.hh \
pollmplexer.cc \
protobuf.cc protobuf.hh \
+ proxy-protocol.cc proxy-protocol.hh \
pubsuffix.hh pubsuffix.cc \
pubsuffixloader.cc \
qtype.hh qtype.cc \
+ query-local-address.hh query-local-address.cc \
rcpgenerator.cc rcpgenerator.hh \
rec-carbon.cc \
rec-lua-conf.hh rec-lua-conf.cc \
reczones.cc \
remote_logger.cc remote_logger.hh \
resolver.hh resolver.cc \
+ axfr-retriever.hh axfr-retriever.cc \
resolve-context.hh \
responsestats.hh responsestats.cc \
root-addresses.hh \
secpoll-recursor.cc secpoll-recursor.hh \
secpoll.cc secpoll.hh \
sholder.hh \
+ shuffle.cc shuffle.hh \
sillyrecords.cc \
snmp-agent.hh snmp-agent.cc \
sortlist.cc sortlist.hh \
uuid-utils.hh uuid-utils.cc \
validate.cc validate.hh validate-recursor.cc validate-recursor.hh \
version.cc version.hh \
+ views.hh \
webserver.cc webserver.hh \
ws-api.cc ws-api.hh \
ws-recursor.cc ws-recursor.hh \
testrunner_SOURCES = \
arguments.cc \
+ axfr-retriever.hh axfr-retriever.cc \
base32.cc \
base64.cc base64.hh \
circular_buffer.hh \
ednssubnet.cc ednssubnet.hh \
filterpo.cc filterpo.hh \
gettime.cc gettime.hh \
- gss_context.cc gss_context.hh \
iputils.cc iputils.hh \
ixfr.cc ixfr.hh \
logger.cc logger.hh \
pollmplexer.cc \
protobuf.cc protobuf.hh \
qtype.cc qtype.hh \
+ query-local-address.hh query-local-address.cc \
rcpgenerator.cc \
rec-protobuf.cc rec-protobuf.hh \
recpacketcache.cc recpacketcache.hh \
test-syncres_cc7.cc \
test-syncres_cc8.cc \
test-syncres_cc9.cc \
+ test-syncres_cc10.cc \
test-tsig.cc \
test-xpf_cc.cc \
testrunner.cc \
if !HAVE_SYSTEMD_PRIVATE_TMP
$(AM_V_GEN)perl -ni -e 'print unless /^PrivateTmp/' $@
endif
+if !HAVE_SYSTEMD_PRIVATE_USERS
+ $(AM_V_GEN)perl -ni -e 'print unless /^PrivateUsers/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_CLOCK
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectClock/' $@
+endif
if !HAVE_SYSTEMD_PROTECT_CONTROL_GROUPS
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectControlGroups/' $@
endif
if !HAVE_SYSTEMD_PROTECT_HOME
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectHome/' $@
endif
+if !HAVE_SYSTEMD_PROTECT_HOSTNAME
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectHostname/' $@
+endif
+if !HAVE_SYSTEMD_PROTECT_KERNEL_LOGS
+ $(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelLogs/' $@
+endif
if !HAVE_SYSTEMD_PROTECT_KERNEL_MODULES
$(AM_V_GEN)perl -ni -e 'print unless /^ProtectKernelModules/' $@
endif
if !HAVE_SYSTEMD_RESTRICT_REALTIME
$(AM_V_GEN)perl -ni -e 'print unless /^RestrictRealtime/' $@
endif
+if !HAVE_SYSTEMD_RESTRICT_SUIDSGID
+ $(AM_V_GEN)perl -ni -e 'print unless /^RestrictSUIDSGID/' $@
+endif
if !HAVE_SYSTEMD_SYSTEM_CALL_ARCHITECTURES
$(AM_V_GEN)perl -ni -e 'print unless /^SystemCallArchitectures/' $@
endif
pdns-recursor@.service: pdns-recursor.service
$(AM_V_GEN)sed -e 's!/pdns_recursor!& --config-name=%i!' \
-e 's!Recursor!& %i!' \
+ -e 's!RuntimeDirectory=.*!&-%i!' \
< $< > $@
systemdsystemunitdir = $(SYSTEMD_DIR)
FROM SNMPv2-CONF;
rec MODULE-IDENTITY
- LAST-UPDATED "201911140000Z"
+ LAST-UPDATED "202002170000Z"
ORGANIZATION "PowerDNS BV"
CONTACT-INFO "support@powerdns.com"
DESCRIPTION
REVISION "201911140000Z"
DESCRIPTION "Added qnameMinFallbackSuccess stats."
+ REVISION "202002170000Z"
+ DESCRIPTION "Added proxyProtocolInvalid metric."
+
::= { powerdns 2 }
powerdns OBJECT IDENTIFIER ::= { enterprises 43315 }
"Number of successful queries due to fallback mechanism within 'qname-minimization' setting"
::= { stats 100 }
+proxyProtocolInvalid OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of invalid proxy protocol headers received"
+ ::= { stats 101 }
+
+recordCacheContended OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of contended record cache lock acquisitions"
+ ::= { stats 102 }
+
+recordCacheAcquired OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of record cache lock acquisitions"
+ ::= { stats 103 }
+
---
--- Traps / Notifications
---
specialMemoryUsage,
rebalancedQueries,
trapReason,
- qnameMinFallbackSuccess
+ qnameMinFallbackSuccess,
+ proxyProtocolInvalid,
+ recordCacheContended,
+ recordCacheAcquired
}
STATUS current
DESCRIPTION "Objects conformance group for PowerDNS Recursor"
--- /dev/null
+../axfr-retriever.cc
\ No newline at end of file
--- /dev/null
+../axfr-retriever.hh
\ No newline at end of file
AC_CANONICAL_HOST
# Add some default CFLAGS and CXXFLAGS, can be appended to using the environment variables
-CFLAGS="-Wall -Wextra -Wshadow -Wno-unused-parameter -g -O2 $CFLAGS"
-CXXFLAGS="-Wall -Wextra -Wshadow -Wno-unused-parameter -g -O2 $CXXFLAGS"
+CFLAGS="-Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls -g -O2 $CFLAGS"
+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
--]]
function preresolve (dq)
- print ("prereesolve handler called for: "..dq.remoteaddr:toString().. ", local: ".. dq.localaddr:toString()..", ".. dq.qname:toString()..", ".. dq.qtype)
+ print ("preresolve handler called for: "..dq.remoteaddr:toString().. ", local: ".. dq.localaddr:toString()..", ".. dq.qname:toString()..", ".. dq.qtype)
dq.followupFunction="udpQueryResponse"
dq.udpCallback="gotdomaindetails"
dq.udpQueryDest=newCA("127.0.0.1:5555")
End of life statements
======================
+We aim to have a release every six months.
+The latest and previous release receive correctness, stability and security updates.
+The release before that gets critical security updates only.
+Older releases are marked end of life and receive no updates at all.
+Pre-releases do not receive immediate security updates.
+
The currently supported release train of the PowerDNS Recursor is 4.3.
PowerDNS Recursor 4.2 will only receive correctness, stability and
Note: Users with a commercial agreement with PowerDNS.COM BV or Open-Xchange
can receive extended support for releases which are End Of Life. If you are
such a user, these EOL statements do not apply to you.
+
+.. list-table:: PowerDNS Recursor Release Life Cycle
+ :header-rows: 1
+
+ * - Version
+ - Release date
+ - Security-Only updates
+ - End of Life
+ * - 4.3
+ - March 3 2020
+ - ~ March 2021
+ - ~ September 2021
+ * - 4.2
+ - July 16 2019
+ - ~ September 2020
+ - ~ March 2021
+ * - 4.1
+ - December 4 2017
+ - March 3 2020
+ - ~ September 2020
+ * - 4.0 and older
+ - EOL
+ - EOL
+ - EOL
+
The PowerDNS Recursor can log DNS query information over :doc:`Protocol Buffers <../lua-config/protobuf>`.
To enable this functionality, install the `protobuf <https://developers.google.com/protocol-buffers/>`_ library and compiler.
-The configure script will automatically detect this and bump the Boost version depencency to 1.42.
+The configure script will automatically detect this and bump the Boost version dependency to 1.42.
To disable building this functionality, use ``--without-protobuf``.
systemd notify support
^^^^^^^^^^^^^^^^^^^^^^
-During configure, ``configure`` will attempt to detect the availibility of `systemd or systemd-daemon <https://freedesktop.org/wiki/Software/systemd/>`_ headers.
+During configure, ``configure`` will attempt to detect the availability of `systemd or systemd-daemon <https://freedesktop.org/wiki/Software/systemd/>`_ headers.
To force the use of systemd (and failing configure if the headers do not exist), use ``--enable-systemd``.
To set the directory where the unit files should be installed, use ``--with-systemd=/path/to/unit/dir``.
This is the whole DNS algorithm in PowerDNS, all in less than 700 lines
of code. It contains a lot of tricky bits though, related to the cache.
+QName Minimization
+------------------
+
+Since the 4.3 release, the recursor implements a relaxed form of QName
+Minimization. This is a method to enhance privacy and described in the
+(draft) RFC 7816. By asking the authoritative server not the full
+QName, but one more label than we already know it is authoritative for
+we do not leak which exact names are queried to servers higher up in
+the hierarchy.
+
+The implementation uses a relaxed form of QName Minimization, following
+the recommendations found in the paper "A First Look at QNAME
+Minimization in the Domain Name System" by De Vries et all.
+
+We originally started with using NS probes as the example algorithm in
+the RFC draft recommends.
+
+We then quickly discovered that using NS probes were somewhat
+troublesome and after reading the mentioned paper we changed to QType
+A for probes, which worked better. We did not implemented the extra
+label prepend, not understanding why that would be needed (a more
+recent draft of the RFC came to the same conclusion).
+
+Following the recommendations in the paper we also implemented larger
+steps when many labels are present. We use steps 1-1-1-3-3-...; we
+already have a limit on the number of outgoing queries induced by a
+client query. We do a final full QName query if we get an unexpected
+error. This happens when we encounter authoritative servers that are
+not fully compliant, there are still many servers like that. The
+recursor records with respect to this fallback scenario in the
+``qname-min-fallback-success`` metric.
+
+For forwarded queries, we do not use QName Minimization.
+
+
Some of the things we glossed over
----------------------------------
====================
.. changelog::
- :version: 4.1.15
+ :version: 4.1.17
+ :released: 1st of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9283
+
+ Backport of CVE-2020-14196: Enforce webserver ACL.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9129
+ :tickets: 9127, 8640
+
+ Fix compilation on systems that do not define HOST_NAME_MAX.
+
+
+.. changelog::
+ :version: 4.1.16
+ :released: 19th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9117
+
+ Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+ CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8809
+
+ Update python dependencies for docs generation.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8868
+
+ Only log qname parsing errors when 'log-common-errors' is set.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8753
+
+ Update boost.m4.
+
+.. changelog::
+ :version: 4.1.15
:released: 6th of December 2019
.. change::
:pullreq: 5912
Fix going Insecure on NSEC3 hashes with too many iterations, since
- we could have gone Bogus on a positive answer synthetized from a
+ we could have gone Bogus on a positive answer synthesized from a
wildcard if the corresponding NSEC3 had more iterations that we were
willing to accept, while the correct result is Insecure.
:tags: Improvements
:pullreq: 5699
- Implement dynamic cache sizeing.
+ Implement dynamic cache sizing.
.. change::
:tags: Bug Fixes, DNSSEC
Changelogs for 4.2.x
====================
+.. changelog::
+ :version: 4.2.4
+ :released: 17th of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9334
+ :tickets: 9309
+
+ Validate cached DNSKEYs against the DSs, not the RRSIGs only.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9333
+ :tickets: 9297
+
+ Ignore cache-only for DNSKEYS/DS retrieval.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9332
+ :tickets: 9292
+
+ A ServFail while retrieving DS/DNSKEY records is just that.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9331
+ :tickets: 9188
+
+ Refuse DS records received from child zones.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9306
+ :tickets: 9268
+
+ Better exception handling in housekeeping/handlePolicyHit.
+
+.. changelog::
+ :version: 4.2.3
+ :released: 1st of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9284
+
+ Backport of CVE-2020-14196: Enforce webserver ACL.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9261
+ :tickets: 9251
+
+ Copy the negative cache entry before validating it.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9133
+ :tickets: 9127
+
+ Fix compilation on systems that do not define HOST_NAME_MAX.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9123
+ :tickets: 8640
+
+ Fix build with gcc-10
+
+.. changelog::
+ :version: 4.2.2
+ :released: 19th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9116
+
+ Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+ CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9081
+
+ Add ubuntu focal target.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8988
+
+ Update gen-version to use latest tag for version number.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8964, 8752
+ :tickets: 8875
+
+ Update boost.m4.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8869
+
+ Only log qname parsing errors when 'log-common-errors' is set.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8832
+
+ Refuse NSEC records with a bitmap length > 32.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8802
+
+ Avoid startup race by setting the state of a tread before starting it.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8696
+
+ Better detection of Bogus zone cuts for DNSSEC validation.
+
+ .. change::
+ :tags: Bug Fixes.
+ :pullreq: 8674
+
+ Debian postinst / do not fail on user creation if it already exists.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8686
+
+ Fix parsing `dont-throttle-names` and `dont-throttle-netmasks` as comma separated lists.
.. changelog::
:version: 4.2.1
Changelogs for 4.3.x
====================
+.. changelog::
+ :version: 4.3.4
+ :released: 8th of September 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9416
+ :tickets: 9375
+
+ Allow some more depth headroom for the no-qname-minimization fallback case.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9367
+
+ Resize hostname to final size in getCarbonHostname().
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9397
+ :tickets: 9073
+
+ Ensure runtime dirs for virtual services differ.
+
+.. changelog::
+ :version: 4.3.3
+ :released: 17th of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9330
+ :tickets: 9309
+
+ Validate cached DNSKEYs against the DSs, not the RRSIGs only.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9329
+ :tickets: 9297
+
+ Ignore cache-only for DNSKEYs and DS retrieval.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9328
+ :tickets: 9292
+
+ A ServFail while retrieving DS/DNSKEY records is just that.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9327
+ :tickets: 9188
+
+ Refuse DS records received from child zones.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9305
+ :tickets: 9268
+
+ Better exception handling in houseKeeping/handlePolicyHit.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9304
+ :tickets: 9299, 9301
+
+ Take initial refresh time from loaded zone.
+
+.. changelog::
+ :version: 4.3.2
+ :released: 1st of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9285
+
+ Backport of CVE-2020-14196: Enforce webserver ACL.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9262
+ :tickets: 9251
+
+ Copy the negative cache entry before validating it.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9242
+ :tickets: 9031
+
+ Fix compilation of the ports event multiplexer.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9243
+ :tickets: 9142
+
+ Defer the NOD lookup until after the response has been sent.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9245
+ :tickets: 9151
+
+ Fix the handling of DS queries for the root.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9246
+ :tickets: 9172
+
+ Fix RPZ removals when an update has several deltas.
+
+ .. change::
+ :tags: Bug Fixes.
+ :pullreq: 9247
+ :tickets: 9192, 9184
+
+ Correct depth increments.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9248
+ :tickets: 9194, 9202, 9216
+
+ CNAME loop detection.
+
+ .. change::
+ :tags: Bug Fixes.
+ :pullreq: 9249
+ :tickets: 9205
+
+ Limit the TTL of RRSIG records as well
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9128
+ :tickets: 9127
+
+ Fix compilation on systems that do not define HOST_NAME_MAX.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9122
+ :tickets: 8640
+
+ Fix build with gcc-10.
+
+.. changelog::
+ :version: 4.3.1
+ :released: 19th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9115
+
+ Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+ CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9082
+
+ Add ubuntu focal target.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9048
+ :tickets: 8778
+
+ RPZ dumpFile/seedFile: store/get SOA refresh on dump/load.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8963
+ :tickets: 8875
+
+ Update boost.m4.
+
.. changelog::
:version: 4.3.0
:released: 3rd of March 2020
:tags: Improvements
:pullreq: 8726
- Give an explicit messsage if something is wrong with socket-dir.
+ Give an explicit message if something is wrong with socket-dir.
.. changelog::
:version: 4.3.0-beta2
:tags: Improvements
:pullreq: 8440
- Fix -WShadow warnings (Aki Tuomi)
+ Fix -Wshadow warnings (Aki Tuomi)
.. change::
:tags: Improvements
--- /dev/null
+Changelogs for 4.4.x
+====================
+.. changelog::
+ :version: 4.4.0-rc1
+ :released: 21st of September 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9465
+ :tickets: 9448
+
+ Only do QName Minimization for the names inside a forwarded domain.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9458
+
+ Fix the parsing of `dont-throttle-netmasks` in the presence of `dont-throttle-names`.
+
+.. changelog::
+ :version: 4.4.0-beta1
+ :released: 31st of August 2020
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9376
+
+ Store RPZ trigger and hit in appliedPolicy and protobuf message
+ and log them in the trace log.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9414
+ :tickets: 9363
+
+ Apply filtering policies (RPZ) on CNAME chains as well.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9411
+
+ Fix warning: initialized lambda captures are a C++14 extension.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9375
+
+ Allow some more depth headroom for the no-qname-minimization fallback case.
+
+ .. change::
+ :tags: Internals, Improvements
+ :pullreq: 9412
+
+ Clean some coverity reported cases of exceptions thrown but not caught.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9391
+
+ Export record cache lock (contention) stats via the various channels.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9396
+
+ Allow multiple local data records when doing RPZ IP matching.
+
+ .. change::
+ :tags: Improvements, Internals
+ :pullreq: 9380
+
+ Replace the use of '1' by QClass::IN to improve readability.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9351
+ :tickets: 9227
+
+ If we have an NS in cache, use it in the forwarder case.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9196
+
+ Disable outgoing v4 when query-local-address has no v4 addresses.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9343
+
+ Resize hostname to final size in getCarbonHostname() (Aki Tuomi).
+
+ .. change::
+ :tags: Internals, Improvements
+ :pullreq: 9348
+ :tickets: 9279
+
+ Avoid name clashes on Solaris derived systems.
+
+.. 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
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9031
+ :tickets: 9025
+
+ Fix compilation of the ports event multiplexer.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9000
+
+ Fix warnings with llvm10 and -Wrange-loop-construct (Kirill Ponomarev).
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8985
+
+ Fix compilation without deprecated OpenSSL APIs (Rosen Penev).
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8967
+
+ Implement native DNS64 support, without Lua.
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8927
+
+ Add custom tags to RPZ hits.
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8910
+
+ Allow attaching a 'routing' tag string to a query in lua code and use that
+ tag in the record cache when appropriate.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8900
+ :tickets: 8739
+
+ Detect {Libre,Open}SSL functions availability during configure.
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8898
+
+ Share record cache between threads.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8887
+
+ Better handling of reconnections in Remote Logger.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8883
+ :tickets: 8629
+
+ Add 'queue full' metrics for our remote logger, log at debug only.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8876, 8740
+ :tickets: 8875
+
+ Update boost.m4
+
+ .. change::
+ :tags: New Features
+ :pullreq: 8874
+
+ Add support for Proxy Protocol between dnsdist and the recursor.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8812
+
+ Keep a masked network in the Netmask class.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8631
+
+ Replace include guard ifdef/define with pragma once (Chris Hofstaedtler).
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8830
+
+ Init zone's d_priority field.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8815
+
+ YaHTTP: Support bracketed IPv6 addresses
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8355
+
+ Rework NetmaskTree for better CPU and memory efficiency (Stephan Bosch).
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8777
+ :tickets: 8697
+
+ QName Minimization sometimes uses 1 label too many.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8778
+
+ RPZ dumpFile/seedFile: store/get SOA refresh on dump/load.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8783
+
+ Add 'IO wait' and 'steal' metrics on Linux.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8792
+
+ DNSName: Don't call strlen() when the length is already known.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8640
+
+ Fix build with gcc-10 (Sander Hoentjen).
+
+
.. toctree::
:maxdepth: 2
+ 4.4
4.3
4.2
4.1
messages from being logged f48d7b657ec32517f8bfcada3bfe6353ca313314
- Webserver now implements CORS for the API
ea89a97e864c43c1cb03f2959ad04c4ebe7580ad, fixing ticket #1984
-- Houskeeping thread would sometimes run multiple times simultaneously,
+- Housekeeping thread would sometimes run multiple times simultaneously,
which worked, but was odd cc59bce675e62e2b9657b42614ce8be3312cae82
New features:
We do this by retrieving the A records for ``www.example.com``, and translating them to AAAA records.
Elsewhere, a NAT64 device listens on these IPv6 addresses, and extracts the IPv4 address from each packet, and proxies it on.
-For maximum flexibility, DNS64 support is included in the :doc:`lua-scripting/index`.
+As of 4.4.0, an efficient implementation is built the recursor and can be enabled via the using the :ref:`dns64-prefix setting <setting-dns64-prefix>`.
+On earlier versions or for maximum flexibility, DNS64 support is included in the :doc:`lua-scripting/index`.
This allows for example to hand out custom IPv6 gateway ranges depending on the location of the requestor, enabling the use of NAT64 services close to the user.
Apart from faking AAAA records, it is also possible to also generate the associated PTR records.
Where fe80::21b:77ff:0:0 is your "Pref64" translation prefix and the "ip6.arpa" string is the reversed form of this Pref64 address.
Now ensure your script gets loaded by specifying it with :ref:`lua-dns-script=dns64.lua <setting-lua-dns-script>`.
+On our wiki, a user has kindly supplied `an example script with support for multiple prefixes <https://github.com/PowerDNS/pdns/wiki/DNS64-with-multiple-prefixes>`_.
+
To enhance DNS64, see the :doc:`lua-scripting/index` documentation.
Configuring DNSSEC key material must be done in the :ref:`setting-lua-config-file`, using :func:`addTA`.
This function takes 2 arguments: the node in the DNS-tree and the data of the corresponding DS record.
-To e.g. add a trust anchor for the root and powerdns.com, use the following config in the Lua file:
+To e.g. add a trust anchor for the root and example.com, use the following config in the Lua file:
.. code:: Lua
addTA('.', "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a") -- This is not an ICANN root
- addTA('powerdns.com', "44030 8 2 D4C3D5552B8679FAEEBC317E5F048B614B2E5F607DC57F1553182D49 AB2179F7")
+ addTA('example.com', "44030 8 2 D4C3D5552B8679FAEEBC317E5F048B614B2E5F607DC57F1553182D49 AB2179F7")
For PowerDNS Recursor 4.1.x and below, use the :func:`addDS` function instead.
The configuration file is called ``recursor.conf`` and is located in the ``SYSCONFDIR`` defined at compile-time.
This is usually ``/etc/powerdns``, ``/etc/pdns``, ``/etc/pdns-recursor``, ``/usr/local/etc`` or similar.
-Run ``pdns_recursor --no-config --config | grep config-dir`` to find this location on you installation.
+Run ``pdns_recursor --config=default | grep config-dir`` to find this location on you installation.
The PowerDNS Recursor listens on the local loopback interface by default, this can be changed with the :ref:`setting-local-address` setting.
{
"top-domains": 100,
- "domains": ".*\.example\.com$",
+ "domains": ".*\\.example\\.com$"
}
:property int top-domains: Number of top resolved domains that are automatically monitored for failures.
{
"top-domains": 100,
- "domains": ".*\.example\.com$",
+ "domains": ".*\\.example\\.com$",
"log": [
{
"first_occurred": 1234567890,
{
"name": "ns1.example.net",
"address": "192.0.2.53"
- },
+ }
]
- },
+ }
]
}
* ``forward-zones``, ``forward-zones-recurse`` and/or ``auth-zones``
settings must be set (possibly to the empty string) in a
- configuration file. These settings must not be overriden on the
+ configuration file. These settings must not be overridden on the
command line. Setting these options on the command line will
override what has been set in the dynamically generated
configuration files.
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>`_.
+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>`_.
+You can file a feature request on `GitHub feature request <https://github.com/PowerDNS/pdns/issues/new?template=feature_request.md>`_.
:param int reconnectWaitTime: How long to wait, in seconds, between two reconnection attempts
:param bool asyncConnect: When set to false (default) the first connection to the server during startup will block up to ``timeout`` seconds, otherwise the connection is done in a separate thread, after the first message has been queued..
-Protobol Buffers Definition
+Protocol Buffers Definition
---------------------------
The protocol buffers message types can be found in the `dnsmessage.proto <https://github.com/PowerDNS/pdns/blob/master/pdns/dnsmessage.proto>`_ file and is included here:
Options:
- * ``logQueries=true``: bool - log oputgoing queries
+ * ``logQueries=true``: bool - log outgoing queries
* ``logResponses=true``: bool - log incoming responses
- The follwing options apply to the settings of the framestream library. Refer to the documentation of that
+ The following options apply to the settings of the framestream library. Refer to the documentation of that
library for the default values, exact description and allowable values for these options.
For all these options, absence or a zero value has the effect of using the library-provided default value.
Frequently, Response Policy Zones get to be very large and change quickly, so it is customary to update them over IXFR.
It allows the use of third-party feeds, and near real-time policy updates.
+Evaluation order
+----------------
+
If multiple RPZs are loaded, they get consulted in the order they were
defined in. It is however possible from Lua to make queries skip specific
Response Policy Zones.
+The evaluation order of RPZ policies is not always straightforward. Before 4.4.0, the recursor first checked whether the source address of the client matched a "Client IP Address" filter
+in any RPZ zones, then if the qname matched a "QNAME" trigger. It would then start the regular resolution process and check whether any "NSDNAME" or "NSIP" rule was triggered, then after the resolution process was done check whether any of the final records matched a "Response IP Address" rule.
+It would stop as soon as a match was found and apply the requested decision immediately, unless the decision was a "passthru". In that last case it would resume the normal processing but would only evaluate the rules coming from a policy with a higher order than the one that matched.
+
+Since 4.4.0 the behaviour is a bit different, to better follow the RPZ specifications. The source address of the client is still checked first. Then the normal resolution process starts and the initial qname as well as any CNAME part of the chain starting from the qname is checked against "QNAME" rules. "NSDNAME" and "NSIP" rules are still checked during the remaining part of the process, and "Response IP Address" rules are applied to the final records in the end.
+This matches the precedence rules from the RPZ specifications that specify that "A policy rule match which occurs at an earlier stage of resolution is preferred to a policy rule match which occurs at a later stage".
+
+For performance and privacy reasons, the order of evaluation does not strictly follow the one mandated by the RPZ specifications. In particular matching on the client IP and qname is done first before any processing, NS IP and NS DNAME matching is done when a nameserver is about to be sent a query, and matching on response records is done then a stage of resolution is done.
+The RPZ specifications mention that a match on the response record from a higher order RPZ should take precedence on a qname match from a lower one. Doing so would require delaying evaluation of RPZ policies until the whole resolution process has been completed, which would mean that queries might have been sent to a malicious nameserver already, in addition to performance issues.
+
+Note that "RPZ rules do not apply to synthetic data generated by using RPZ rules. For example, if RPZ supplies a CNAME pointing to a walled garden, RPZ policies will not be used while following that CNAME. If RPZ supplies local data giving a particular A record, RPZ policies will not apply to that response IP address", as stated in section 6.1 of the RPZ specifications.
+
Configuring RPZ
---------------
An RPZ can be loaded from file or slaved from a master. To load from file, use for example:
^^^^^^^^^^
The name logged as 'appliedPolicy' in :doc:`protobuf <protobuf>` messages when this policy is applied.
+tags
+^^^^
+.. versionadded:: 4.4.0
+
+List of tags as string, that will be added to the policy tags exported over protobuf when a policy of this zone matches.
+
+overridesGettag
+^^^^^^^^^^^^^^^
+.. versionadded:: 4.4.0
+
+`gettag_ffi` can set an answer to a query.
+By default an RPZ hit overrides this answer, unless this option is set to `false`.
+The default is `true`.
+
zoneSizeHint
^^^^^^^^^^^^
An indication of the number of expected entries in the zone, speeding up the loading of huge zones by reserving space in advance.
localAddress
^^^^^^^^^^^^
The source IP address to use when transferring the RPZ.
-When unset, :ref:`setting-query-local-address` and :ref:`setting-query-local-address6` are used.
+When unset, :ref:`setting-query-local-address` is used.
axfrTimeout
^^^^^^^^^^^
To convert an address to human-friendly representation, use :meth:`:toString <ComboAddress:toString>` or :meth:`:toStringWithPort <ComboAddress:toStringWithPort()>`.
To get only the port number, use :meth:`:getPort() <ComboAddress:getPort>`.
-.. function:: NewCA(address) -> ComboAddress
+.. function:: newCA(address) -> ComboAddress
Creates a :class:`ComboAddress`.
------------------
The PowerDNS Recursor's Lua engine has the notion of a :class:`DNSName`, an object that represents a name in the DNS.
It is returned by several functions and has several functions to programmatically interact with it.
-:class:`DNSNames <DNSName>` can be compared agains each other using the :meth:`:equal <DNSName:equal>` function or the ``==`` operator.
+:class:`DNSNames <DNSName>` can be compared against each other using the :meth:`:equal <DNSName:equal>` function or the ``==`` operator.
As names in the DNS are case-insensitive, ``www.powerdns.com`` is equal to ``Www.PowerDNS.cOM``.
Creating a :class:`DNSName` is done with :func:`newDN()`.
.. 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
Set by :ref:`policyName <rpz-policyName>` in the :func:`rpzFile` and :func:`rpzMaster` configuration items.
It is advised to overwrite this when modifying the :attr:`DNSQuestion.appliedPolicy.policyKind`
- .. attribute:: DNSQuestion.appliedPolicy.policyAction
+ .. attribute:: DNSQuestion.appliedPolicy.policyType
- The action taken by the engine
+ The type of match for the policy.
+
+ - ``pdns.policytypes.None`` the empty policy type
+ - ``pdns.policytypes.QName`` a match on qname
+ - ``pdns.policytypes.ClientIP`` a match on client IP
+ - ``pdns.policytypes.ResponseIP`` a match on response IP
+ - ``pdns.policytypes.NSDName`` a match on the name of a nameserver
+ - ``pdns.policytypes.NSIP`` a match on the IP of a nameserver
.. attribute:: DNSQuestion.appliedPolicy.policyCustom
The TTL in seconds for the ``pdns.policyactions.Custom`` response
+ .. attribute:: DNSQuestion.appliedPolicy.policyTrigger
+
+ The trigger (left-hand) part of the RPZ rule that was matched
+
+ .. attribute:: DNSQuestion.appliedPolicy.policyHit
+
+ The value that was matched. This is a string representing a name or an address.
+
.. attribute:: DNSQuestion.wantsRPZ
A boolean that indicates the use of the Policy Engine.
Returns the :class:`DNSHeader` of the query or nil.
+ .. method:: DNSQuestion:getProxyProtocolValues() -> {ProxyProtocolValue}
+
+ .. versionadded:: 4.4.0
+
+ Get the Proxy Protocol Type-Length Values if any, as a table of :class:`ProxyProtocolValue` objects.
+
.. method:: DNSQuestion:getRecords() -> {DNSRecord}
Get a table of DNS Records in this DNS Question (or answer by now).
.. method:: EDNSOptionView:getContent()
Returns a NULL-safe string object of the first value of this EDNS option.
+
+The ProxyProtocolValue Class
+============================
+
+.. class:: ProxyProtocolValue
+
+ .. versionadded:: 4.4.0
+
+ An object that represents the value of a Proxy Protocol Type-Length Value
+
+ .. method:: ProxyProtocolValue:getContent() -> str
+
+ Returns a NULL-safe string object.
+
+ .. method:: ProxyProtocolValue:getType() -> int
+
+ Returns the type of this value.
Intercepting queries with Lua
=============================
+
To get a quick start, we have supplied a sample script that showcases all functionality described below.
Please find it `here <https://github.com/PowerDNS/pdns/blob/master/pdns/recursordist/contrib/powerdns-example-script.lua>`_.
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`)
- after the whole process is done and an answer is ready for the client (:func:`postresolve`)
- before an outgoing query is made to an authoritative server (:func:`preoutquery`)
+- after a filtering policy hit has occurred (:func:`policyEventFilter`)
Writing Lua PowerDNS Recursor scripts
-------------------------------------
:param DNSHeader dh: The DNS Header of the query.
-.. function:: gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp) -> int
+.. function:: gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp, proxyprotocolvalues) -> multiple values
+ gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp) -> int
gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions) -> int
.. versionchanged:: 4.1.0
The ``tcp`` parameter was added.
+ .. versionchanged:: 4.4.0
+
+ The ``proxyprotocolvalues`` parameter was added.
+
The ``gettag`` function is invoked when the Recursor attempts to discover in which packetcache an answer is available.
This function must return an integer, which is the tag number of the packetcache.
.. versionadded:: 4.1.0
It can also return a table whose keys and values are strings to fill the :attr:`DNSQuestion.data` table, as well as a ``requestorId`` value to fill the :attr:`DNSQuestion.requestorId` field and a ``deviceId`` value to fill the :attr:`DNSQuestion.deviceId` field.
+
.. versionadded:: 4.3.0
- Along the ``deviceId`` value that can be returned, it was addded a ``deviceName`` field to fill the :attr:`DNSQuestion.deviceName` field.
+ Along the ``deviceId`` value that can be returned, it was added a ``deviceName`` field to fill the :attr:`DNSQuestion.deviceName` field.
+ .. versionadded:: 4.4.0
+ A ``routingTag`` can be returned, which is used as an extra name to identify records in the record cache.
+ If a routing tag is set and a record would be stored with an ENDS subnetmask in the record cache, it will be
+ stored with the tag instead. New request using the same tag will be served by the record in the records cache,
+ avoiding querying authoritative servers.
+
The tagged packetcache can e.g. be used to answer queries from cache that have e.g. been filtered for certain IPs (this logic should be implemented in :func:`gettag`).
This ensure that queries are answered quickly compared to setting :attr:`dq.variable <DNSQuestion.variable>` to true.
In the latter case, repeated queries will pass through the entire Lua script.
:param int qtype: The query type of the query
:param ednsoptions: A table whose keys are EDNS option codes and values are :class:`EDNSOptionView` objects. This table is empty unless the :ref:`setting-gettag-needs-edns-options` option is set.
:param bool tcp: Added in 4.1.0, a boolean indicating whether the query was received over UDP (false) or TCP (true).
+ :param proxyprotocolvalues: Added in 4.4.0, a table of :class:`ProxyProtocolValue` objects representing the Type-Length Values received via the Proxy Protocol, if any.
+
+ :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)
:param DNSQuestion dq: The DNS question to handle
+.. function:: policyEventFilter(event)
+
+ .. versionadded:: 4.4.0
+
+ This hook is called when a filtering policy has been hit, before the decision has been applied, making it possible to change a policy decision by altering its content or to skip it entirely.
+ Using the :meth:`event:discardPolicy() <PolicyEvent:discardPolicy>` function, it is also possible to selectively disable one or more filtering policy, for example RPZ zones.
+ The return value indicates whether the policy hit should be completely ignored (true) or applied (false), possibly after editing the action to take in that latter case (see :ref:`modifyingpolicydecisions` below). when true is returned, the resolution process will resume as if the policy hit never took place.
+
+ As an example, to ignore the result of a policy hit for the example.com domain:
+
+ .. code-block:: Lua
+
+ function policyEventFilter(event)
+ if event.qname:equal("example.com") then
+ -- ignore that policy hit
+ return true
+ end
+ return false
+ end
+
+ To alter the decision of the policy hit instead:
+
+ .. code-block:: Lua
+
+ function policyEventFilter(event)
+ if event.qname:equal("example.com") then
+ -- replace the decision with a custom CNAME
+ event.appliedPolicy.policyKind = pdns.policykinds.Custom
+ event.appliedPolicy.policyCustom = "example.net"
+ -- returning false so that the hit is not ignored
+ return false
+ end
+ return false
+ end
+
+ :param :class:`PolicyEvent` event: The event to handle
+
Semantics
^^^^^^^^^
The functions must return ``true`` if they have taken over the query and wish that the nameserver should not proceed with its regular query-processing.
The :func:`ipfilter` and :func:`preoutquery` hooks are different, in that :func:`ipfilter` can only return a true of false value, and that :func:`preoutquery` can also set rcode -3 to signify that the whole query should be terminated.
+The func:`policyEventFilter` has a different meaning as well, where returning true means that the policy hit should be ignored and normal processing should be resumed.
+
A minimal sample script:
.. code-block:: Lua
Starting with version 4.0.1 of the recursor, it is possible to alter this decision inside the Lua hooks.
If the decision is modified in a Lua hook, ``false`` should be returned, as the query is not actually handled by Lua so the decision is picked up by the Recursor.
-The result of the policy decision is checked after :func:`preresolve` and :func:`postresolve`.
+The result of the policy decision is checked after :func:`preresolve` and :func:`postresolve` before 4.4.0. Beginning with version 4.4.0, the policy decision is checked after :func:`preresolve` and any :func:`policyEventFilter` call instead.
For example, if a decision is set to ``pdns.policykinds.NODATA`` by the policy engine and is unchanged in :func:`preresolve`, the query is replied to with a NODATA response immediately after :func:`preresolve`.
dnsrecord
comboaddress
netmask
+ policyevent
statistics
logging
hooks
nmg = newNMG()
nmg:addMask("127.0.0.0/8")
nmg:addMasks({"213.244.168.0/24", "130.161.0.0/16"})
- nmg:addMasks(dofile("bad.ips")) -- contains return {"ip1","ip2"..}
+ nmg:addMasks(dofile("bad-ips.lua")) -- a lua script file that contains: return {"ip1","ip2"..}
if nmg:match(dq.remoteaddr) then
print("Intercepting query from ", dq.remoteaddr)
Returns true if ``address`` matches any of the masks in the group.
- :param ComboAddress address: The IP addres to match the netmasks against.
+ :param ComboAddress address: The IP address to match the netmasks against.
--- /dev/null
+.. _scripting-policyevent:
+
+Policy Events
+=============
+
+Since 4.4.0, the Lua hook :func:`policyEventFilter` is called along with a :class:`PolicyEvent` object whenever a filtering policy matches.
+
+PolicyEvent class
+------------------
+
+.. class:: PolicyEvent
+
+ Represents an event related to a filtering policy.
+
+ .. method:: PolicyEvent:addPolicyTag(tag)
+
+ Add policyTag ``tag`` to the list of policyTags.
+
+ :param str tag: The tag to add
+
+ .. method:: PolicyEvent:getPolicyTags() -> {str}
+
+ Get the current policy tags as a table of strings.
+
+ .. method:: PolicyEvent:setPolicyTags(tags)
+
+ Set the policy tags to ``tags``, overwriting any existing policy tags.
+
+ :param {str} tags: The policy tags
+
+ .. method:: PolicyEvent:discardPolicy(policyname)
+
+ Skip the filtering policy (for example RPZ) named ``policyname`` for this query.
+
+ :param str policyname: The name of the policy to ignore.
+
+ .. attribute:: PolicyEvent.appliedPolicy
+
+ The decision that was made by the policy engine, see :ref:`modifyingpolicydecisions`.
+
+ .. attribute:: PolicyEvent.appliedPolicy.policyName
+
+ A string with the name of the policy.
+ Set by :ref:`policyName <rpz-policyName>` in the :func:`rpzFile` and :func:`rpzMaster` configuration items.
+ It is advised to overwrite this when modifying the :attr:`PolicyEvent.appliedPolicy.policyKind`
+
+ .. attribute:: PolicyEvent.appliedPolicy.policyAction
+
+ The action taken by the engine
+
+ .. attribute:: PolicyEvent.appliedPolicy.policyCustom
+
+ The CNAME content for the ``pdns.policyactions.Custom`` response, a string
+
+ .. attribute:: PolicyEvent.appliedPolicy.policyKind
+
+ The kind of policy response, there are several policy kinds:
+
+ - ``pdns.policykinds.Custom`` will return a NoError, CNAME answer with the value specified in :attr:`PolicyEvent.appliedPolicy.policyCustom`
+ - ``pdns.policykinds.Drop`` will simply cause the query to be dropped
+ - ``pdns.policykinds.NoAction`` will continue normal processing of the query
+ - ``pdns.policykinds.NODATA`` will return a NoError response with no value in the answer section
+ - ``pdns.policykinds.NXDOMAIN`` will return a response with a NXDomain rcode
+ - ``pdns.policykinds.Truncate`` will return a NoError, no answer, truncated response over UDP. Normal processing will continue over TCP
+
+ .. attribute:: PolicyEvent.appliedPolicy.policyTTL
+
+ The TTL in seconds for the ``pdns.policyactions.Custom`` response
+
+ .. attribute:: PolicyEvent.qname
+
+ :class:`DNSName` of the name the query is for.
+
+ .. attribute:: PolicyEvent.qtype
+
+ Type the query is for as an integer, can be compared against ``pdns.A``, ``pdns.AAAA``.
+
+ .. attribute:: PolicyEvent.isTcp
+
+ Whether the query was received over TCP.
+
+ .. attribute:: PolicyEvent.remote
+
+ :class:`ComboAddress` of the requestor.
+
Load root hints from this *filename*
--local-address=<address>
Listen on *address*, separated by spaces or commas.
+ Addresses specified can include port numbers; any which do not
+ include port numbers will listen on *--local-port*.
--local-port=<port>
Listen on *port*.
--log-common-errors
Maximum number of simultaneous TCP clients.
--max-tcp-per-client=<num>
If set, maximum number of TCP sessions per client (IP address).
---query-local-address=<address>
+--query-local-address=<address[,address...]>
Use *address* as Source IP address when sending queries.
---query-local-address6=<address>
- Send out local IPv6 queries from *address*. Disabled by default,
- which also disables outgoing IPv6 support. A useful setting is
- '::0'.
--quiet
Suppress logging of questions and answers.
--server-id=<text>
--socket-pid=<pid> When running in SMP mode, pid of **pdns_recursor** to
control.
--timeout=<num> Number of seconds to wait for the remote PowerDNS
- Recursor to respond. Set to 0 for infinite.
+ Recursor to respond.
Commands
--------
Using the Webserver
^^^^^^^^^^^^^^^^^^^
-The :doc:`API <http-api/index>` exposes a statistics endpoint at :http:get:`/api/v1/servers/:server_id/statistics`.
+The :doc:`API <http-api/index>` exposes a statistics endpoint at
+
+.. http:get:: /api/v1/servers/:server_id/statistics
+
This endpoint exports all statistics in a single JSON document.
Using ``rec_control``
qname-min-fallback-success
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 4.3.0
-number of successful queries due to fallback mechanism within :ref:`qname-minimization` setting.
+
+number of successful queries due to fallback mechanism within :ref:`setting-qname-minimization` setting.
ecs-queries
^^^^^^^^^^^
policy-result-noaction
^^^^^^^^^^^^^^^^^^^^^^
-packets that were not actioned upon by the RPZ/filter engine
+packets that were not acted upon by the RPZ/filter engine
policy-result-drop
^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^
packets that were sent a custom answer by the RPZ/filter engine
+proxy-protocol-invalid
+^^^^^^^^^^^^^^^^^^^^^^
+.. versionadded:: 4.4
+
+Invalid proxy-protocol headers received.
+
qa-latency
^^^^^^^^^^
shows the current latency average, in microseconds, exponentially weighted over past 'latency-statistic-size' packets
number of queries balanced to a different worker thread because the first selected one was above the target load configured with 'distribution-load-factor'
+record-cache-acquired
+^^^^^^^^^^^^^^^^^^^^^
+.. versionadded:: 4.4.0
+
+number of record cache lock acquisitions
+
+record-cache-contended
+^^^^^^^^^^^^^^^^^^^^^^
+.. versionadded:: 4.4.0
+
+number of contented record cache lock acquisitions
+
resource-limits
^^^^^^^^^^^^^^^
counts number of queries that could not be performed because of resource limits
A common security technique for detecting domains that may be suspicious or be associated with bad actors such as hosting malware, phishing or botnet command and control, is to investigate domains that haven't been seen before, i.e. are newly observed.
-Deciding whether a domain is truly a new domain would involve deterministic methods, such as maintaining a database of all domains ever seen, and comparing all domain lookups against that database. Such a mechanism would not be scalable in a recursor, and so is best suited to offline analysis. However, determining candidate domains for such an offline service is a problem that can be solved in the recursor, given that sending all domain lookups to such an offline service would still be prohibitely costly, and given that the true number of newly observed domains is likely to be relatively small in a given time period.
+Deciding whether a domain is truly a new domain would involve deterministic methods, such as maintaining a database of all domains ever seen, and comparing all domain lookups against that database. Such a mechanism would not be scalable in a recursor, and so is best suited to offline analysis. However, determining candidate domains for such an offline service is a problem that can be solved in the recursor, given that sending all domain lookups to such an offline service would still be prohibitively costly, and given that the true number of newly observed domains is likely to be relatively small in a given time period.
A simple method to determine a candidate domain would simply be to check if the domain was not in the recursor cache; indeed this is a method used by many security researchers. However, while that does produce a smaller list of candidate domains, cache misses are still relatively common, particularly in deployments where techniques such as EDNS client-subnet are used.
-Therefore, a feature has been developed for the recursor which uses probablistic data structures (specifically a Stable Bloom Filter (SBF): [http://webdocs.cs.ualberta.ca/~drafiei/papers/DupDet06Sigmod.pdf]). This recursor feature is named "Newly Observed Domain" or "NOD" for short.
+Therefore, a feature has been developed for the recursor which uses probabilistic data structures (specifically a Stable Bloom Filter (SBF): [http://webdocs.cs.ualberta.ca/~drafiei/papers/DupDet06Sigmod.pdf]). This recursor feature is named "Newly Observed Domain" or "NOD" for short.
-The use of a probablistic data structure means that the memory and CPU usage for the NOD feature is minimal, however it does mean that there can be false positives (a domain flagged as new when it is not), and false negatives (a domain that is new is not detected). The size of the SBF data structure can be tuned to reduce the FP/FN rate, although it is created with a default size (67108864 cells) that should provide a reasonably low FP/FN rate. To configure a different size use the ``new-domain-db-size`` setting to specify a higher or lower cell count. Each cell consumes 1-bit of RAM (per recursor thread) and 1-byte of disk space.
+The use of a probabilistic data structure means that the memory and CPU usage for the NOD feature is minimal, however it does mean that there can be false positives (a domain flagged as new when it is not), and false negatives (a domain that is new is not detected). The size of the SBF data structure can be tuned to reduce the FP/FN rate, although it is created with a default size (67108864 cells) that should provide a reasonably low FP/FN rate. To configure a different size use the ``new-domain-db-size`` setting to specify a higher or lower cell count. Each cell consumes 1-bit of RAM (per recursor thread) and 1-byte of disk space.
NOD is disabled by default, and must be enabled through the use of the following setting in recursor.conf:
Unique Domain Response
~~~~~~~~~~~~~~~~~~~~~~
-A similar feature to NOD is Unique Domain Response (UDR). This feature uses the same probablistic data structures as NOD to store information about unique responses for a given lookup domain. Determining if a particular response is unique for a given lookup domain is extremly useful for determining potential security issues such as:
+A similar feature to NOD is Unique Domain Response (UDR). This feature uses the same probabilistic data structures as NOD to store information about unique responses for a given lookup domain. Determining if a particular response is unique for a given lookup domain is extremely useful for determining potential security issues such as:
* Fast-Flux Domain Names
* Cache-Poisoning Attacks
When wiping cache entries, matching entries in *all* caches (packet cache, recursor cache, negative cache) are removed.
-When debugging resolving issues, it can be advantagious to have a dump of all the cache entries.
+When debugging resolving issues, it can be advantageous to have a dump of all the cache entries.
:doc:`rec_control <manpages/rec_control.1>` can write the caches of all threads to a file::
rec_control dump-cache /tmp/cache
--- /dev/null
+PowerDNS Security Advisory 2020-01: Denial of Service
+=====================================================
+
+- CVE: CVE-2020-10995
+- Date: May 19th 2020
+- Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+- Not affected: 4.1.16, 4.2.2, 4.3.1
+- Severity: Medium
+- Impact: Degraded Service
+- Exploit: This problem can be triggered via a crafted reply
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+- Workaround: None
+
+An issue in the DNS protocol has been found that allow malicious
+parties to use recursive DNS services to attack third party
+authoritative name servers. The attack uses a crafted reply by an
+authoritative name server to amplify the resulting traffic between the
+recursive and other authoritative name servers. Both types of service
+can suffer degraded performance as an effect.
+
+This issue has been assigned CVE-2020-10995.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is
+affected. PowerDNS Recursor 4.1.16, 4.2.2 and 4.3.1 contain a
+mitigation to limit the impact of this DNS protocol issue.
+
+Please note that at the time of writing, PowerDNS Recursor 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/recursor/appendices/EOL.html.
+
+We would like to thank Lior Shafir, Yehuda Afek and Anat Bremler-Barr
+for finding and subsequently reporting this issue!
--- /dev/null
+PowerDNS Security Advisory 2020-02: Insufficient validation of DNSSEC signatures
+================================================================================
+
+- CVE: CVE-2020-12244
+- Date: May 19th 2020
+- Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+- Not affected: 4.3.1, 4.2.2, 4.1.16
+- Severity: Medium
+- Impact: Denial of existence spoofing
+- Exploit: This problem can be triggered by an attacker in position
+ of man-in-the-middle
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+- Workaround: None
+
+An issue has been found in PowerDNS Recursor 4.1.0 through 4.3.0 where
+records in the answer section of a NXDOMAIN response lacking an SOA
+were not properly validated in SyncRes::processAnswer. This would
+allow an attacker in position of man-in-the-middle to send a NXDOMAIN
+answer for a name that does exist, bypassing DNSSEC validation.
+
+This issue has been assigned CVE-2020-12244.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is affected.
+
+Please note that at the time of writing, PowerDNS Authoritative 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/authoritative/appendices/EOL.html.
+
+We would like to thank Matt Nordhoff for finding and subsequently
+reporting this issue!
+
--- /dev/null
+PowerDNS Security Advisory 2020-03: Information disclosure
+==========================================================
+
+- CVE: CVE-2020-10030
+- Date: May 19th 2020
+- Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+- Not affected: 4.3.1, 4.2.2, 4.1.16
+- Severity: Low
+- Impact: Information Disclosure, Denial of Service
+- Exploit: This problem can be triggered via a crafted hostname
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+- Workaround: None
+
+An issue has been found in PowerDNS Recursor allowing an
+attacker with enough privileges to change the system's hostname to
+cause disclosure of uninitialized memory content via a stack-based
+out-of-bounds read.
+It only occurs on systems where gethostname() does not null-terminate
+the returned string if the hostname is larger than the supplied buffer.
+Linux systems are not affected because the buffer is always large enough.
+OpenBSD systems are not affected because the returned hostname is always
+null-terminated.
+Under some conditions this issue can lead to the writing of one null-byte
+out-of-bounds on the stack, causing a denial of service or possibly
+arbitrary code execution.
+
+This issue has been assigned CVE-2020-10030.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is affected.
+
+Please note that at the time of writing, PowerDNS Recursor 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/recursor/appendices/EOL.html.
+
+We would like to thank Valentei Sergey for finding and subsequently
+reporting this issue!
+
--- /dev/null
+PowerDNS Security Advisory 2020-04: Access restriction bypass
+=============================================================
+
+- CVE: CVE-2020-14196
+- Date: July 1st 2020
+- Affects: PowerDNS Recursor up to and including 4.3.1, 4.2.2 and 4.1.16
+- Not affected: 4.3.2, 4.2.3, 4.1.17
+- Severity: Low
+- Impact: Access restriction bypass
+- Exploit: This problem can be triggered by sending HTTP queries
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+- Workaround: Disable the webserver, set a password or an API key.
+ Additionally, restrict the binding address using the
+ `webserver-address` setting to local addresses only and/or use a
+ firewall to disallow web requests from untrusted sources reaching the
+ webserver listening address.
+
+An issue has been found in PowerDNS Recursor where the ACL applied to
+the internal web server via `webserver-allow-from` is not properly
+enforced, allowing a remote attacker to send HTTP queries to the
+internal web server, bypassing the restriction.
+
+In the default configuration the API webserver is not enabled. Only
+installations using a non-default value for `webserver` and
+`webserver-address` are affected.
+
``allow-from``
--------------
-- IP ranges, separated by commas
-- Default: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
+- IP addresses or netmasks, separated by commas
+- Default: 127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10
Netmasks (both IPv4 and IPv6) that are allowed to use the server.
The default allows access only from :rfc:`1918` private IP addresses.
Due to the aggressive nature of the internet these days, it is highly recommended to not open up the recursor for the entire internet.
Questions from IP addresses not listed here are ignored and do not get an answer.
+When the Proxy Protocol is enabled (see `proxy-protocol-from`_), the recursor will check the address of the client IP advertised in the Proxy Protocol header instead of the one of the proxy.
+
+Note that specifying an IP address without a netmask uses an implicit netmask of /32 or /128.
+
.. _setting-allow-from-file:
``allow-from-file``
--------------
- 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
handle incoming queries and distribute them to other threads based on a hash of the query, to maximize the cache hit
ratio.
+.. _setting-dns64-prefix:
+
+``dns64-prefix``
+----------------
+.. versionadded:: 4.4.0
+
+- Netmask, as a string
+- Default: None
+
+Enable DNS64 (:rfc:`6147`) support using the supplied /96 IPv6 prefix. This will generate 'fake' AAAA records for names
+with only `A` records, as well as 'fake' PTR records to make sure that reverse lookup of DNS64-generated IPv6 addresses
+generate the right name.
+See :doc:`dns64` for more flexible but slower alternatives using Lua.
+
.. _setting-dnssec:
``dnssec``
.. versionadded:: 4.2.0
- Comma separated list of netmasks
-- Default: 0.0.0.0/0, ::, !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
+- Default: 0.0.0.0/0, ::/0, !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
List of requestor netmasks for which the requestor IP Address should be used as the :rfc:`EDNS Client Subnet <7871>` for outgoing queries. Outgoing queries for requestors that do not match this list will use the `ecs-scope-zero-address`_ instead.
Valid incoming ECS values from `use-incoming-edns-subnet`_ are not replaced.
`edns-subnet-whitelist`_ when `use-incoming-edns-subnet`_ is set and the query has
an ECS source prefix-length set to 0.
The default is to look for the first usable (not an ``any`` one) address in
-`query-local-address`_ then `query-local-address6`_. If no suitable address is
+`query-local-address`_ (starting with IPv4). If no suitable address is
found, the recursor fallbacks to sending 127.0.0.1.
.. _setting-edns-outgoing-bufsize:
``local-address``
-----------------
-- IP addresses, comma separated
-- Default: 127.0.0.1
+- IPv4/IPv6 Addresses, with optional port numbers, separated by commas or whitespace
+- Default: ``0.0.0.0, ::``
-Local IPv4 or IPv6 addresses to bind to.
-Addresses can also contain port numbers, for IPv4 specify like this: ``192.0.2.4:5300``, for IPv6: ``[::1]:5300``.
+Local IP addresses to which we bind. Each address specified can
+include a port number; if no port is included then the
+:ref:`setting-local-port` port will be used for that address. If a
+port number is specified, it must be separated from the address with a
+':'; for an IPv6 address the address must be enclosed in square
+brackets.
-**Warning**: When binding to wildcard addresses, UNIX semantics mean that answers may not be sent from the address a query was received on.
-It is highly recommended to bind to explicit addresses.
+Examples::
+
+ local-address=127.0.0.1 ::1
+ local-address=0.0.0.0:5353
+ local-address=[::]:8053
+ local-address=127.0.0.1:53, [::1]:5353
.. _setting-local-port:
The minimum value of this setting is 15. i.e. setting this to lower than 15 will make this value 15.
-.. _setting max-concurrent-requests-per-tcp-connection:
+.. _setting-max-concurrent-requests-per-tcp-connection:
``max-concurrent-requests-per-tcp-connection``
----------------------------------------------
If qname-minimization is enabled, the number will be forced to be 100
at a minimum to allow for the extra queries qname-minimization generates when the cache is empty.
+.. _setting-max-ns-address-qperq:
+
+``max-ns-address-qperq``
+------------------------
+.. versionadded:: 4.1.16
+.. versionadded:: 4.2.2
+.. versionadded:: 4.3.1
+
+- Integer
+- Default: 10
+
+The maximum number of outgoing queries with empty replies for
+resolving nameserver names to addresses we allow during the resolution
+of a single client query. If IPv6 is enabled, an A and a AAAA query
+for a name counts as 1. If a zone publishes more than this number of
+NS records, the limit is further reduced for that zone by lowering
+it by the number of NS records found above the
+`max-ns-address-qperq`_ value. The limit wil not be reduced to a
+number lower than 5.
+
.. _setting-max-negative-ttl:
``max-negative-ttl``
Total maximum number of internal recursion calls the server may use to answer a single query.
0 means unlimited.
The value of `stack-size`_ should be increased together with this one to prevent the stack from overflowing.
+If `qname-minimization`_ is enabled, the fallback code in case of a failing resolve is allowed an additional `max-recursion-depth/2`.
+
.. versionchanged:: 4.1.0
- Default: no (disabled)
Whether to track newly observed domains, i.e. never seen before. This
-is a probablistic algorithm, using a stable bloom filter to store
+is a probabilistic algorithm, using a stable bloom filter to store
records of previously seen domains. When enabled for the first time,
all domains will appear to be newly observed, so the feature is best
left enabled for e.g. a week or longer before using the results. Note
- 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:
Whether to compute the latency of responses in protobuf messages using the timestamp set by the kernel when the query packet was received (when available), instead of computing it based on the moment we start processing the query.
+.. _setting-proxy-protocol-from:
+
+``proxy-protocol-from``
+-----------------------
+.. versionadded:: 4.4.0
+
+- 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.
+Queries that are not prefixed with such a header will not be accepted from clients in these ranges. Queries prefixed by headers from clients that are not listed in these ranges will be dropped.
+
+Note that once a Proxy Protocol header has been received, the source address from the proxy header instead of the address of the proxy will be checked against the `allow-from`_ ACL,
+
+.. _setting-proxy-protocol-maximum-size:
+
+``proxy-protocol-maximum-size``
+-------------------------------
+.. versionadded:: 4.4.0
+
+- Integer
+- Default: 512
+
+The maximum size, in bytes, of a Proxy Protocol payload (header, addresses and ports, and TLV values). Queries with a larger payload will be dropped.
+
.. _setting-public-suffix-list-file:
``public-suffix-list-file``
``query-local-address``
-----------------------
-- IPv4 Address, comma separated
+.. versionchanged:: 4.4.0
+ IPv6 addresses can be set with this option as well.
+
+- IP addresses, comma separated
- Default: 0.0.0.0
-Send out local queries from this address, or addresses, by adding multiple addresses, increased spoofing resilience is achieved.
+Send out local queries from this address, or addresses. By adding multiple
+addresses, increased spoofing resilience is achieved. When no address of a certain
+address family is configured, there are *no* queries sent with that address family.
+In the default configuration this means that IPv6 is not used for outgoing queries.
.. _setting-query-local-address6:
``query-local-address6``
------------------------
+.. deprecated:: 4.4.0
+ Use :ref:`setting-query-local-address` for IPv4 and IPv6.
+
- IPv6 addresses, comma separated
- Default: unset
Don't log queries.
+.. _setting-record-cache-shards:
+
+``record-cache-shards``
+------------------------
+.. versionadded:: 4.4.0
+
+- Integer
+- Default: 1024
+
+Sets the number of shards in the record cache. If you have high
+contention as reported by
+``record-cache-contented/record-cache-acquired``, you can try to
+enlarge this value or run with fewer threads.
+
.. _setting-reuseport:
``reuseport``
- Boolean
- Default: no
-If ``SO_REUSEPORT`` support is available, allows multiple processes to open a listening socket on the same port.
+If ``SO_REUSEPORT`` support is available, allows multiple threads and processes to open listening sockets for the same port.
-Since 4.1.0, when ``pdns-distributes-queries`` is set to false and ``reuseport`` is enabled, every thread will open a separate listening socket to let the kernel distribute the incoming queries, avoiding any thundering herd issue as well as the distributor thread being a bottleneck, thus leading to much higher performance on multi-core boxes.
+Since 4.1.0, when ``pdns-distributes-queries`` is set to false and ``reuseport`` is enabled, every worker-thread will open a separate listening socket to let the kernel distribute the incoming queries instead of running a distributor thread (which could otherwise be a bottleneck) and avoiding thundering herd issues, thus leading to much higher performance on multi-core boxes.
.. _setting-rng:
- String
- Default: auto
-Specify which random number generator to use. Permissible choises are
+Specify which random number generator to use. Permissible choices are
- auto - choose automatically
- sodium - Use libsodium ``randombytes_uniform``
- openssl - Use libcrypto ``RAND_bytes``
- kiss - Use simple settable deterministic RNG. **FOR TESTING PURPOSES ONLY!**
.. note::
- Not all choises are available on all systems.
+ Not all choices are available on all systems.
.. _setting-root-nx-trust:
``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
--------------
--[[
- Both Google and Bing offer ways to enforce the use of their 'safesearch' or 'strict' functionality
+ Google, Youtube, Bing and DuckDuckGo offer ways to enforce the use of their 'safesearch' or 'strict' functionality
for some or all of your users. This script provides a 'handleSafeSearch' function that
implements enforced safe search for Google and Bing.
For Bing, only 'www.bing.com' is relevant.
+ For Youtube: https://support.google.com/a/answer/6214622?hl=en - option 1. Note that they offer both a very strict search, and a moderate. Usually, moderate is a good balance. If you want really strict, change the youtubedomains values to 11 instead of 1.
+
+ For DuckDuckGo: https://help.duckduckgo.com/duckduckgo-help-pages/features/safe-search/ (bottom)
+
There is a comment below in preresolve where you could insert code to determine if a particular user should be filtered or not
]]--
googledomains["ipv6"..v]=2 -- this too - change to 1 to get v4 instead of NXDOMAIN
end
+youtubedomains={}
+youtubedomains['www.youtube.com'] = 1
+youtubedomains['m.youtube.com'] = 1
+youtubedomains['youtubei.googleapis.com'] = 1
+youtubedomains['youtube.googleapis.com'] = 1
+youtubedomains['www.youtube-nocookie.com'] = 1
function handleSafeSearch(dq)
local name = dq.qname:toStringNoDot():lower();
- local status = googledomains[name]
- if( status == 1) then
+ local statusg = googledomains[name]
+ local statusyt = youtubedomains[name]
+
+ if( statusg == 1) then
dq:addAnswer(pdns.CNAME, "forcesafesearch.google.com")
dq.rcode=0
dq.followupFunction="followCNAMERecords"
return true
- elseif( status == 2) then
+
+ elseif( statusyt == 1) then
+ dq:addAnswer(pdns.CNAME, "restrictmoderate.youtube.com")
+ dq.rcode=0
+ dq.followupFunction="followCNAMERecords"
+ return true
+
+ elseif( statusyt == 11) then
+ dq:addAnswer(pdns.CNAME, "restrict.youtube.com")
+ dq.rcode=0
+ dq.followupFunction="followCNAMERecords"
+ return true
+
+ elseif( statusg == 2) then
dq.rcode=pdns.NXDOMAIN
-- inserting actual SOA record is a nice touch but requires figuring out country code
return true
+
elseif(name=="www.bing.com") then
dq:addAnswer(pdns.CNAME, "strict.bing.com")
dq.rcode=0
dq.followupFunction="followCNAMERecords"
return true
+
+ elseif(name=="duckduckgo.com" or name=="www.duckduckgo.com") then
+ dq:addAnswer(pdns.CNAME, "safe.duckduckgo.com")
+ dq.rcode=0
+ dq.followupFunction="followCNAMERecords"
+ return true
end
return false
+++ /dev/null
-../gss_context.cc
\ No newline at end of file
+++ /dev/null
-../gss_context.hh
\ No newline at end of file
#include "cachecleaner.hh"
#include "utility.hh"
+NegCache::NegCache(size_t mapsCount) :
+ d_maps(mapsCount)
+{
+}
+
+NegCache::~NegCache()
+{
+ try {
+ typedef std::unique_ptr<lock> lock_t;
+ vector<lock_t> locks;
+ for (auto& map : d_maps) {
+ locks.push_back(lock_t(new lock(map)));
+ }
+ }
+ catch (...) {
+ }
+}
+
+size_t NegCache::size() const
+{
+ size_t count = 0;
+ for (const auto& map : d_maps) {
+ count += map.d_entriesCount;
+ }
+ return count;
+}
+
/*!
* Set ne to the NegCacheEntry for the last label in qname and return true if there
* was one.
* \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())
// An 'ENT' QType entry, used as "whole name" in the neg-cache context.
static const QType qtnull(0);
DNSName lastLabel = qname.getLastLabel();
- negcache_t::const_iterator ni = d_negcache.find(tie(lastLabel, qtnull));
- while (ni != d_negcache.end() && ni->d_name == lastLabel && ni->d_auth.isRoot() && ni->d_qtype == qtnull) {
+ auto& map = getMap(lastLabel);
+ const lock l(map);
+
+ negcache_t::const_iterator ni = map.d_map.find(tie(lastLabel, qtnull));
+
+ while (ni != map.d_map.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);
- moveCacheItemToBack<SequenceTag>(d_negcache, ni);
+ if (now.tv_sec < ni->d_ttd) {
+ ne = *ni;
+ moveCacheItemToBack<SequenceTag>(map.d_map, ni);
return true;
}
- moveCacheItemToFront<SequenceTag>(d_negcache, ni);
+ moveCacheItemToFront<SequenceTag>(map.d_map, ni);
++ni;
}
return false;
* \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& map = getMap(qname);
+ const lock l(map);
+
+ const auto& idx = map.d_map.get<NegCacheEntry>();
auto range = idx.equal_range(qname);
auto ni = range.first;
// We have an entry
if ((!typeMustMatch && ni->d_qtype.getCode() == 0) || ni->d_qtype == qtype) {
// We match the QType or the whole name is denied
- auto firstIndexIterator = d_negcache.project<0>(ni);
+ auto firstIndexIterator = map.d_map.project<CompositeKey>(ni);
- if ((uint32_t)now.tv_sec < ni->d_ttd) {
+ if (now.tv_sec < ni->d_ttd) {
// Not expired
- *ne = &(*ni);
- moveCacheItemToBack<SequenceTag>(d_negcache, firstIndexIterator);
+ ne = *ni;
+ moveCacheItemToBack<SequenceTag>(map.d_map, firstIndexIterator);
return true;
}
// expired
- moveCacheItemToFront<SequenceTag>(d_negcache, firstIndexIterator);
+ moveCacheItemToFront<SequenceTag>(map.d_map, firstIndexIterator);
}
++ni;
}
*/
void NegCache::add(const NegCacheEntry& ne)
{
- lruReplacingInsert<SequenceTag>(d_negcache, ne);
+ auto& map = getMap(ne.d_name);
+ const lock l(map);
+ bool inserted = lruReplacingInsert<SequenceTag>(map.d_map, ne);
+ if (inserted) {
+ map.d_entriesCount++;
+ }
}
/*!
* \param qtype The type of the entry to replace
* \param newState The new validation state
*/
-void NegCache::updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState, boost::optional<uint32_t> capTTD)
+void NegCache::updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState, boost::optional<time_t> capTTD)
{
- auto range = d_negcache.equal_range(tie(qname, qtype));
+ auto& map = getMap(qname);
+ const lock l(map);
+ auto range = map.d_map.equal_range(tie(qname, qtype));
if (range.first != range.second) {
range.first->d_validationState = newState;
*
* \param qname The name of the entries to be counted
*/
-uint64_t NegCache::count(const DNSName& qname) const
+size_t NegCache::count(const DNSName& qname) const
{
- return d_negcache.count(tie(qname));
+ const auto& map = getMap(qname);
+ const lock l(map);
+ return map.d_map.count(tie(qname));
}
/*!
* \param qname The name of the entries to be counted
* \param qtype The type of the entries to be counted
*/
-uint64_t NegCache::count(const DNSName& qname, const QType qtype) const
+size_t NegCache::count(const DNSName& qname, const QType qtype) const
{
- return d_negcache.count(tie(qname, qtype));
+ const auto& map = getMap(qname);
+ const lock l(map);
+ return map.d_map.count(tie(qname, qtype));
}
/*!
* \param name The DNSName of the entries to wipe
* \param subtree Should all entries under name be removed?
*/
-uint64_t NegCache::wipe(const DNSName& name, bool subtree)
+size_t NegCache::wipe(const DNSName& name, bool subtree)
{
- uint64_t ret(0);
+ size_t ret = 0;
if (subtree) {
- for (auto i = d_negcache.lower_bound(tie(name)); i != d_negcache.end();) {
- if (!i->d_name.isPartOf(name))
- break;
- i = d_negcache.erase(i);
- ret++;
+ for (auto& m : d_maps) {
+ const lock l(m);
+ for (auto i = m.d_map.lower_bound(tie(name)); i != m.d_map.end();) {
+ if (!i->d_name.isPartOf(name))
+ break;
+ i = m.d_map.erase(i);
+ ret++;
+ m.d_entriesCount--;
+ }
}
return ret;
}
- ret = count(name);
- auto range = d_negcache.equal_range(tie(name));
- d_negcache.erase(range.first, range.second);
+ auto& map = getMap(name);
+ const lock l(map);
+ auto range = map.d_map.equal_range(tie(name));
+ auto i = range.first;
+ while (i != range.second) {
+ i = map.d_map.erase(i);
+ ret++;
+ map.d_entriesCount--;
+ }
return ret;
}
*/
void NegCache::clear()
{
- d_negcache.clear();
+ for (auto& m : d_maps) {
+ const lock l(m);
+ m.d_map.clear();
+ m.d_entriesCount = 0;
+ }
}
/*!
*
* \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);
+ size_t cacheSize = size();
+ pruneMutexCollectionsVector<SequenceTag>(*this, d_maps, maxEntries, cacheSize);
}
/*!
*
* \param fp A pointer to an open FILE object
*/
-uint64_t NegCache::dumpToFile(FILE* fp)
+size_t NegCache::dumpToFile(FILE* fp) const
{
- uint64_t ret(0);
+ size_t ret = 0;
struct timeval now;
Utility::gettimeofday(&now, nullptr);
- 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]);
- 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]);
- }
- 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());
+ for (const auto& m : d_maps) {
+ const lock l(m);
+ auto& sidx = m.d_map.get<SequenceTag>();
+ for (const NegCacheEntry& ne : sidx) {
+ ret++;
+ int64_t ttl = ne.d_ttd - now.tv_sec;
+ fprintf(fp, "%s %" PRId64 " IN %s VIA %s ; (%s)\n", ne.d_name.toString().c_str(), ttl, 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(), ttl, 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(), ttl, sig.d_content->getZoneRepresentation().c_str());
+ }
+ for (const auto& rec : ne.DNSSECRecords.records) {
+ fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), ttl, 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", sig.d_name.toString().c_str(), ttl, sig.d_content->getZoneRepresentation().c_str());
+ }
}
}
return ret;
*/
#pragma once
+#include <mutex>
+#include <vector>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include "dnsparser.hh"
using namespace ::boost::multi_index;
-/* FIXME should become part of the normal cache (I think) and shoudl become more like
+/* FIXME should become part of the normal cache (I think) and should become more like
* struct {
* vector<DNSRecord> records;
* vector<DNSRecord> signatures;
class NegCache : public boost::noncopyable
{
public:
+ NegCache(size_t mapsCount = 1024);
+ ~NegCache();
+
struct NegCacheEntry
{
- DNSName d_name; // The denied name
- QType d_qtype; // The denied type
- DNSName d_auth; // The denying name (aka auth)
- 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};
- uint32_t getTTD() const
+ DNSName d_name; // The denied name
+ DNSName d_auth; // The denying name (aka auth)
+ mutable time_t d_ttd; // Timestamp when this entry should die
+ mutable vState d_validationState{vState::Indeterminate};
+ QType d_qtype; // The denied type
+ time_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);
- uint64_t count(const DNSName& qname) const;
- uint64_t count(const DNSName& qname, const QType qtype) const;
- void prune(unsigned int maxEntries);
+ void updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState, boost::optional<time_t> capTTD);
+ 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);
+ size_t count(const DNSName& qname) const;
+ size_t count(const DNSName& qname, const QType qtype) const;
+ void prune(size_t maxEntries);
void clear();
- uint64_t dumpToFile(FILE* fd);
- uint64_t wipe(const DNSName& name, bool subtree = false);
-
- uint64_t size()
- {
- return d_negcache.size();
- };
+ size_t dumpToFile(FILE* fd) const;
+ size_t wipe(const DNSName& name, bool subtree = false);
+ size_t size() const;
void preRemoval(const NegCacheEntry& entry)
{
member<NegCacheEntry, DNSName, &NegCacheEntry::d_name>>>>
negcache_t;
- // Required for the cachecleaner
- typedef negcache_t::nth_index<1>::type negcache_sequence_t;
+ struct MapCombo
+ {
+ MapCombo() {}
+ MapCombo(const MapCombo&) = delete;
+ MapCombo& operator=(const MapCombo&) = delete;
+ negcache_t d_map;
+ mutable std::mutex mutex;
+ std::atomic<uint64_t> d_entriesCount{0};
+ mutable uint64_t d_contended_count{0};
+ mutable uint64_t d_acquired_count{0};
+ void invalidate() {}
+ };
+
+ vector<MapCombo> d_maps;
+
+ MapCombo& getMap(const DNSName& qname)
+ {
+ return d_maps[qname.hash() % d_maps.size()];
+ }
+ const MapCombo& getMap(const DNSName& qname) const
+ {
+ return d_maps[qname.hash() % d_maps.size()];
+ }
+
+public:
+ struct lock
+ {
+ lock(const MapCombo& map) :
+ m(map.mutex)
+ {
+ if (!m.try_lock()) {
+ m.lock();
+ map.d_contended_count++;
+ }
+ map.d_acquired_count++;
+ }
+ ~lock()
+ {
+ m.unlock();
+ }
- // Stores the negative cache entries
- negcache_t d_negcache;
+ private:
+ std::mutex& m;
+ };
};
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
+# Setting PrivateUsers=true prevents us from opening our sockets
+ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
+ProtectHostname=true
+ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=full
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
+RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=~ @clock @debug @module @mount @raw-io @reboot @swap @cpu-emulation @obsolete
--- /dev/null
+../proxy-protocol.cc
\ No newline at end of file
--- /dev/null
+../proxy-protocol.hh
\ No newline at end of file
--- /dev/null
+../query-local-address.cc
\ No newline at end of file
--- /dev/null
+../query-local-address.hh
\ No newline at end of file
"Number of packets dropped because of (Lua) policy decision")},
{"policy-result-noaction",
MetricDefinition(PrometheusMetricType::counter,
- "Number of packets that were not actioned upon by the RPZ/filter engine")},
+ "Number of packets that were not acted upon by the RPZ/filter engine")},
{"policy-result-drop",
MetricDefinition(PrometheusMetricType::counter,
"Number of packets that were dropped by the RPZ/filter engine")},
--- /dev/null
+../shuffle.cc
\ No newline at end of file
--- /dev/null
+../shuffle.hh
\ No newline at end of file
std::string zoneName("Unit test policy 0");
auto zone = std::make_shared<DNSFilterEngine::Zone>();
zone->setName(zoneName);
- BOOST_CHECK_EQUAL(*(zone->getName()), zoneName);
+ BOOST_CHECK_EQUAL(zone->getName(), zoneName);
zone->setDomain(DNSName("powerdns.com."));
BOOST_CHECK_EQUAL(zone->getDomain(), DNSName("powerdns.com."));
zone->setSerial(42);
{
/* blocked qname */
- auto matchingPolicy = dfe.getQueryPolicy(blockedName, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ auto matchingPolicy = dfe.getQueryPolicy(blockedName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
DNSFilterEngine::Policy zonePolicy;
BOOST_CHECK(zonePolicy == matchingPolicy);
/* but a subdomain should not be blocked (not a wildcard, and this is not suffix domain matching */
- matchingPolicy = dfe.getQueryPolicy(DNSName("sub") + blockedName, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ matchingPolicy = dfe.getQueryPolicy(DNSName("sub") + blockedName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
BOOST_CHECK(zone->findExactQNamePolicy(DNSName("sub") + blockedName, zonePolicy) == false);
}
{
/* blocked NS name via wildcard */
- const auto matchingPolicy = dfe.getQueryPolicy(DNSName("sub.sub.wildcard-blocked."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(DNSName("sub.sub.wildcard-blocked."), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
/* looking for wildcard-blocked. should not match *.wildcard-blocked. */
- const auto notMatchingPolicy = dfe.getQueryPolicy(DNSName("wildcard-blocked."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto notMatchingPolicy = dfe.getQueryPolicy(DNSName("wildcard-blocked."), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(notMatchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
/* a direct lookup would not match */
{
/* blocked client IP */
- const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), clientIP, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getClientPolicy(clientIP, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
DNSFilterEngine::Policy zonePolicy;
{
/* not blocked */
- const auto matchingPolicy = dfe.getQueryPolicy(DNSName("totally.legit."), ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
DNSFilterEngine::Policy zonePolicy;
BOOST_CHECK(zone->findClientPolicy(ComboAddress("192.0.2.142"), zonePolicy) == false);
{
const DNSName tstName("bcbsks.com.102.112.2o7.net.");
- auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ auto matchingPolicy = dfe.getQueryPolicy(tstName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
}
{
const DNSName tstName("2o7.net.");
- auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ auto matchingPolicy = dfe.getQueryPolicy(tstName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
}
{
const DNSName tstName("112.2o7.net.");
- auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ auto matchingPolicy = dfe.getQueryPolicy(tstName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_WARN_MESSAGE(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None, m);
BOOST_WARN_MESSAGE(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction, m);
}
{
const DNSName tstName("102.112.2o7.net.");
- auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ auto matchingPolicy = dfe.getQueryPolicy(tstName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_WARN_MESSAGE(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None, m);
BOOST_WARN_MESSAGE(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction, m);
}
{
const DNSName tstName("com.112.2o7.net.");
- auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ auto matchingPolicy = dfe.getQueryPolicy(tstName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_WARN_MESSAGE(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None, m);
BOOST_WARN_MESSAGE(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction, m);
}
{
const DNSName tstName("wcmatch.2o7.net.");
- auto matchingPolicy = dfe.getQueryPolicy(tstName, address, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ auto matchingPolicy = dfe.getQueryPolicy(tstName, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Drop);
}
{
/* exact type does not exist, but we have a CNAME */
- const auto matchingPolicy = dfe.getQueryPolicy(bad1, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad1, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad1, QType::A);
{
/* exact type exists */
- const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad2, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
{
/* exact type exists */
- const auto matchingPolicy = dfe.getQueryPolicy(bad2, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad2, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
}
}
+BOOST_AUTO_TEST_CASE(test_filter_policies_local_data_netmask)
+{
+ DNSFilterEngine dfe;
+
+ std::string zoneName("Unit test policy local data using netmasks");
+ auto zone = std::make_shared<DNSFilterEngine::Zone>();
+ zone->setName(zoneName);
+
+ const DNSName name("foo.example.com");
+ const Netmask nm1("192.168.1.0/24");
+
+ zone->addClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.2.3.4")}));
+ zone->addClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.2.3.5")}));
+ zone->addClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::AAAA, QClass::IN, "::1234")}));
+ BOOST_CHECK_EQUAL(zone->size(), 1U);
+
+ dfe.addZone(zone);
+
+ { // A query should match two records
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.168.1.1"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
+ BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+ auto records = matchingPolicy.getCustomRecords(DNSName(), QType::A);
+ BOOST_CHECK_EQUAL(records.size(), 2U);
+ const auto& record1 = records.at(0);
+ BOOST_CHECK(record1.d_type == QType::A);
+ BOOST_CHECK(record1.d_class == QClass::IN);
+ auto content1 = std::dynamic_pointer_cast<ARecordContent>(record1.d_content);
+ BOOST_CHECK(content1 != nullptr);
+ BOOST_CHECK_EQUAL(content1->getCA().toString(), "1.2.3.4");
+
+ const auto& record2 = records.at(1);
+ BOOST_CHECK(record2.d_type == QType::A);
+ BOOST_CHECK(record2.d_class == QClass::IN);
+ auto content2 = std::dynamic_pointer_cast<ARecordContent>(record2.d_content);
+ BOOST_CHECK(content2 != nullptr);
+ BOOST_CHECK_EQUAL(content2->getCA().toString(), "1.2.3.5");
+ }
+
+ { // AAAA query should match 1 record
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.168.1.1"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
+ BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+ auto records = matchingPolicy.getCustomRecords(DNSName(), QType::AAAA);
+ BOOST_CHECK_EQUAL(records.size(), 1U);
+ const auto& record1 = records.at(0);
+ BOOST_CHECK(record1.d_type == QType::AAAA);
+ BOOST_CHECK(record1.d_class == QClass::IN);
+ auto content1 = std::dynamic_pointer_cast<AAAARecordContent>(record1.d_content);
+ BOOST_CHECK(content1 != nullptr);
+ BOOST_CHECK_EQUAL(content1->getCA().toString(), "::1234");
+ }
+
+ // Try to zap 1 nonexisting record
+ zone->rmClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.1.1.1")}));
+
+ // Zap a record using a wider netmask
+ zone->rmClientTrigger(Netmask("192.168.0.0/16"), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.2.3.4")}));
+
+ // Zap a record using a narrow netmask
+ zone->rmClientTrigger(Netmask("192.168.1.1/32"), DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.2.3.4")}));
+
+ // Zap 1 existing record
+ zone->rmClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.2.3.5")}));
+
+ { // A query should match one record now
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.168.1.1"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
+ BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+ auto records = matchingPolicy.getCustomRecords(DNSName(), QType::A);
+ BOOST_CHECK_EQUAL(records.size(), 1U);
+ const auto& record1 = records.at(0);
+ BOOST_CHECK(record1.d_type == QType::A);
+ BOOST_CHECK(record1.d_class == QClass::IN);
+ auto content1 = std::dynamic_pointer_cast<ARecordContent>(record1.d_content);
+ BOOST_CHECK(content1 != nullptr);
+ BOOST_CHECK_EQUAL(content1->getCA().toString(), "1.2.3.4");
+ }
+ { // AAAA query should still match one record
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.168.1.1"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
+ BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+ auto records = matchingPolicy.getCustomRecords(DNSName(), QType::AAAA);
+ BOOST_CHECK_EQUAL(records.size(), 1U);
+ const auto& record1 = records.at(0);
+ BOOST_CHECK(record1.d_type == QType::AAAA);
+ BOOST_CHECK(record1.d_class == QClass::IN);
+ auto content1 = std::dynamic_pointer_cast<AAAARecordContent>(record1.d_content);
+ BOOST_CHECK(content1 != nullptr);
+ BOOST_CHECK_EQUAL(content1->getCA().toString(), "::1234");
+ }
+
+ // Zap one more A record
+ zone->rmClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.2.3.4")}));
+
+ // Zap now nonexisting record
+ zone->rmClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::A, QClass::IN, "1.2.3.4")}));
+
+ { // AAAA query should still match one record
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.168.1.1"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
+ BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
+ auto records = matchingPolicy.getCustomRecords(DNSName(), QType::AAAA);
+ BOOST_CHECK_EQUAL(records.size(), 1U);
+ const auto& record1 = records.at(0);
+ BOOST_CHECK(record1.d_type == QType::AAAA);
+ BOOST_CHECK(record1.d_class == QClass::IN);
+ auto content1 = std::dynamic_pointer_cast<AAAARecordContent>(record1.d_content);
+ BOOST_CHECK(content1 != nullptr);
+ BOOST_CHECK_EQUAL(content1->getCA().toString(), "::1234");
+ }
+
+ // Zap AAAA record
+ zone->rmClientTrigger(nm1, DNSFilterEngine::Policy(DNSFilterEngine::PolicyKind::Custom, DNSFilterEngine::PolicyType::ClientIP, 0, nullptr, {DNSRecordContent::mastermake(QType::AAAA, QClass::IN, "::1234")}));
+
+ { // there should be no match left
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.168.1.1"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
+ BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
+ }
+}
+
BOOST_AUTO_TEST_CASE(test_multiple_filter_policies)
{
DNSFilterEngine dfe;
{
/* zone 1 should match first */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad, QType::A);
{
/* zone 2 has an exact match for badUnderWildcard, but the wildcard from the first zone should match first */
- const auto matchingPolicy = dfe.getQueryPolicy(badUnderWildcard, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(badUnderWildcard, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(badUnderWildcard, QType::A);
{
/* zone 1 should still match if zone 2 has been disabled */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone2->getName()), true}}, DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, {{zone2->getName(), true}}, DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad, QType::A);
{
/* if zone 1 is disabled, zone 2 should match */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, {{zone1->getName(), true}}, DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad, QType::A);
{
/* if both zones are disabled, we should not match */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}, {*(zone2->getName()), true}}, DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, {{zone1->getName(), true}, {zone2->getName(), true}}, DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
}
}
{
/* client IP should match before qname */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.128"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.0.2.128"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::ClientIP);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad, QType::A);
{
/* client IP and qname should match, but zone 1 is disabled and zone2's priority is too high */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.128"), {{*(zone1->getName()), true}}, 1);
+ const auto matchingPolicy = dfe.getClientPolicy(ComboAddress("192.0.2.128"), {{zone1->getName(), true}}, 1);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
}
{
/* zone 1 should match first */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, std::unordered_map<std::string, bool>(), DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad, QType::A);
{
/* zone 1 should still match if we require a priority < 1 */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), 1);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, std::unordered_map<std::string, bool>(), 1);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad, QType::A);
{
/* nothing should match if we require a priority < 0 */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), std::unordered_map<std::string, bool>(), 0);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, std::unordered_map<std::string, bool>(), 0);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
}
{
/* if we disable zone 1, zone 2 should match */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, DNSFilterEngine::maximumPriority);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, {{zone1->getName(), true}}, DNSFilterEngine::maximumPriority);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::QName);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom);
auto records = matchingPolicy.getCustomRecords(bad, QType::A);
{
/* if we disable zone 1, zone 2 should match, except if we require a priority < 1 */
- const auto matchingPolicy = dfe.getQueryPolicy(bad, ComboAddress("192.0.2.142"), {{*(zone1->getName()), true}}, 1);
+ const auto matchingPolicy = dfe.getQueryPolicy(bad, {{zone1->getName(), true}}, 1);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
}
{
/* blocked NS name, except policy 1 is disabled and policy2's priority is too high */
- auto matchingPolicy = dfe.getProcessingPolicy(nsName, {{*(zone1->getName()), true}}, 1);
+ auto matchingPolicy = dfe.getProcessingPolicy(nsName, {{zone1->getName(), true}}, 1);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
}
{
/* blocked NS ip, except policy 1 is disabled and policy2's priority is too high */
- auto matchingPolicy = dfe.getProcessingPolicy(nsIP, {{*(zone1->getName()), true}}, 1);
+ auto matchingPolicy = dfe.getProcessingPolicy(nsIP, {{zone1->getName(), true}}, 1);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
}
DNSRecord dr;
dr.d_type = QType::A;
dr.d_content = DNSRecordContent::mastermake(QType::A, QClass::IN, responseIP.toString());
- const auto matchingPolicy = dfe.getPostPolicy({dr}, {{*(zone1->getName()), true}}, 1);
+ const auto matchingPolicy = dfe.getPostPolicy({dr}, {{zone1->getName(), true}}, 1);
BOOST_CHECK(matchingPolicy.d_type == DNSFilterEngine::PolicyType::None);
BOOST_CHECK(matchingPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction);
}
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)
BOOST_AUTO_TEST_CASE(test_dumpToFile)
{
- NegCache cache;
+ NegCache cache(1);
vector<string> expected;
expected.push_back("www1.powerdns.com. 600 IN TYPE0 VIA powerdns.com. ; (Indeterminate)\n");
- expected.push_back("www1.powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
- expected.push_back("www1.powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+ expected.push_back("powerdns.com. 600 IN SOA ns1. hostmaster. 1 2 3 4 5 ; (Indeterminate)\n");
+ expected.push_back("powerdns.com. 600 IN RRSIG SOA 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+ expected.push_back("powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
+ expected.push_back("powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
expected.push_back("www2.powerdns.com. 600 IN TYPE0 VIA powerdns.com. ; (Indeterminate)\n");
- expected.push_back("www2.powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
- expected.push_back("www2.powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+ expected.push_back("powerdns.com. 600 IN SOA ns1. hostmaster. 1 2 3 4 5 ; (Indeterminate)\n");
+ expected.push_back("powerdns.com. 600 IN RRSIG SOA 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+ expected.push_back("powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
+ expected.push_back("powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
struct timeval now;
Utility::gettimeofday(&now, 0);
cache.add(genNegCacheEntry(DNSName("www1.powerdns.com"), DNSName("powerdns.com"), now));
cache.add(genNegCacheEntry(DNSName("www2.powerdns.com"), DNSName("powerdns.com"), now));
- FILE* fp = tmpfile();
+ auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(tmpfile(), fclose);
if (!fp)
BOOST_FAIL("Temporary file could not be opened");
- cache.dumpToFile(fp);
+ cache.dumpToFile(fp.get());
- rewind(fp);
+ rewind(fp.get());
char* line = nullptr;
size_t len = 0;
ssize_t read;
for (const auto& str : expected) {
- read = getline(&line, &len, fp);
+ read = getline(&line, &len, fp.get());
if (read == -1)
BOOST_FAIL("Unable to read a line from the temp file");
BOOST_CHECK_EQUAL(line, str);
last allocation if any. */
free(line);
}
-
- fclose(fp);
}
BOOST_AUTO_TEST_CASE(test_count)
BOOST_AUTO_TEST_SUITE(nod_cc)
-bool pdns_exception(PDNSException const& ex) { return true; }
+static bool pdns_exception(PDNSException const& ex) { return true; }
BOOST_AUTO_TEST_CASE(test_basic)
{
std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
time_t now = time(nullptr);
+ time_t ttd = now + 30;
+ DNSName power("powerdns.com.");
+ DNSRecord dr0;
+ ComboAddress dr0Content("2001:DB8::");
+ dr0.d_name = power;
+ dr0.d_type = QType::AAAA;
+ dr0.d_class = QClass::IN;
+ dr0.d_content = std::make_shared<AAAARecordContent>(dr0Content);
+ dr0.d_ttl = static_cast<uint32_t>(ttd);
+ dr0.d_place = DNSResourceRecord::ANSWER;
+
+ records.push_back(dr0);
+
BOOST_CHECK_EQUAL(MRC.size(), 0U);
MRC.replace(now, DNSName("hello"), QType(QType::A), records, signatures, authRecords, true, boost::none);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
int64_t expected = counter - delcounter;
for (; delcounter < counter; ++delcounter) {
- if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who, nullptr)) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who) > 0) {
matches++;
+ BOOST_REQUIRE_EQUAL(retrieved.size(), records.size());
+ BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
}
}
BOOST_CHECK_EQUAL(matches, expected);
- BOOST_CHECK_EQUAL(retrieved.size(), records.size());
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 0U);
- time_t ttd = now + 30;
- DNSName power("powerdns.com.");
DNSRecord dr1;
ComboAddress dr1Content("2001:DB8::1");
dr1.d_name = power;
dr2.d_place = DNSResourceRecord::AUTHORITY;
// insert a subnet specific entry
+ records.clear();
records.push_back(dr1);
MRC.replace(now, power, QType(QType::AAAA), records, signatures, authRecords, true, boost::optional<Netmask>("192.0.2.1/25"));
BOOST_CHECK_EQUAL(MRC.size(), 1U);
- retrieved.clear();
// subnet specific should be returned for a matching subnet
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.2"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.2")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
- retrieved.clear();
// subnet specific should not be returned for a different subnet
- BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0);
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1")), 0);
BOOST_CHECK_EQUAL(retrieved.size(), 0U);
// remove everything
BOOST_CHECK_EQUAL(MRC.size(), 1U);
// NON-subnet specific should always be returned
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
- retrieved.clear();
// insert a subnet specific entry for the same name but a different QType
records.clear();
BOOST_CHECK_EQUAL(MRC.size(), 3U);
// we should still get the NON-subnet specific entry
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
- retrieved.clear();
// we should get the subnet specific entry if we are from the right subnet
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.3"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
- retrieved.clear();
// but nothing from a different subnet
- BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0);
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1")), 0);
BOOST_CHECK_EQUAL(retrieved.size(), 0U);
- retrieved.clear();
// QType::ANY should return any qtype, so from the right subnet we should get all of them
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("192.0.2.3"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now));
BOOST_CHECK_EQUAL(retrieved.size(), 3U);
for (const auto& rec : retrieved) {
BOOST_CHECK(rec.d_type == QType::A || rec.d_type == QType::AAAA || rec.d_type == QType::TXT);
for (const auto& rec : retrieved) {
BOOST_CHECK(rec.d_place == DNSResourceRecord::ANSWER);
}
- retrieved.clear();
// but only the non-subnet specific from the another subnet
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
BOOST_CHECK_EQUAL(retrieved.size(), 2U);
for (const auto& rec : retrieved) {
BOOST_CHECK(rec.d_type == QType::A || rec.d_type == QType::TXT);
}
- retrieved.clear();
// QType::ADDR should return both A and AAAA but no TXT, so two entries from the right subnet
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("192.0.2.3"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now));
BOOST_CHECK_EQUAL(retrieved.size(), 2U);
bool gotA = false;
bool gotAAAA = false;
}
BOOST_CHECK(gotA);
BOOST_CHECK(gotAAAA);
- retrieved.clear();
// but only the non-subnet specific one from the another subnet
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK(retrieved.at(0).d_type == QType::A);
- retrieved.clear();
// entries are only valid until ttd, we should not get anything after that because they are expired
- BOOST_CHECK_LT(MRC.get(ttd + 5, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0);
+ BOOST_CHECK_LT(MRC.get(ttd + 5, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1")), 0);
BOOST_CHECK_EQUAL(retrieved.size(), 0U);
- retrieved.clear();
// let's age the records for our existing QType::TXT entry so they are now only valid for 5s
uint32_t newTTL = 5;
BOOST_CHECK_EQUAL(MRC.doAgeCache(now, power, QType::TXT, newTTL), true);
// we should still be able to retrieve it
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), static_cast<int32_t>(newTTL));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1")), static_cast<int32_t>(newTTL));
BOOST_CHECK_EQUAL(retrieved.size(), 1U);
BOOST_CHECK(retrieved.at(0).d_type == QType::TXT);
// please note that this is still a TTD at this point
BOOST_CHECK_EQUAL(retrieved.at(0).d_ttl, now + newTTL);
- retrieved.clear();
// but 10s later it should be gone
- BOOST_CHECK_LT(MRC.get(now + 10, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0);
+ BOOST_CHECK_LT(MRC.get(now + 10, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1")), 0);
BOOST_CHECK_EQUAL(retrieved.size(), 0U);
- retrieved.clear();
// wipe everything
MRC.doWipeCache(DNSName("."), true);
records.push_back(dr2);
MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
BOOST_CHECK_EQUAL(retrieved.size(), 1U);
DNSRecord dr3;
// non-auth should not replace valid auth
MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
// but non-auth _should_ replace expired auth
MRC.replace(ttd + 1, power, QType(QType::A), records, signatures, authRecords, false, boost::none);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
- BOOST_CHECK_EQUAL(MRC.get(ttd + 1, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (dr3.d_ttl - (ttd + 1)));
+ BOOST_CHECK_EQUAL(MRC.get(ttd + 1, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (dr3.d_ttl - (ttd + 1)));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
// let's first check that non-auth is not returned when we need authoritative data
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("127.0.0.1"), nullptr), -now);
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("127.0.0.1")), -now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
BOOST_CHECK_EQUAL(MRC.size(), 3U);
// we should get the most specific entry for 192.168.0.1, so the second one
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr4Content.toString());
- retrieved.clear();
// wipe everything
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
// we should not get it when we need authoritative data
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("192.168.0.1"), nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("192.168.0.1")), -1);
BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
- retrieved.clear();
// but we should when we are OK with non-auth
- BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
- retrieved.clear();
}
catch (const PDNSException& e) {
cerr << "Had error: " << e.reason << endl;
/* the TTL should not have been raisd */
std::vector<DNSRecord> retrieved;
- BOOST_CHECK_EQUAL(MRC.get(now, ghost, QType(QType::NS), false, &retrieved, ComboAddress("192.0.2.2"), nullptr), (ttd - now));
+ BOOST_CHECK_EQUAL(MRC.get(now, ghost, QType(QType::NS), false, &retrieved, ComboAddress("192.0.2.2")), (ttd - now));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(retrieved.at(0).d_ttl, static_cast<uint32_t>(ttd));
}
/* the remaining entry should be power2, but to get it
we need to go back in the past a bit */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), 1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), 1);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
/* check that power1 is gone */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1);
/* clear everything up */
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 2U);
/* trigger a miss (expired) for power2 */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -now);
/* power2 should have been moved to the front of the expunge
queue, and should this time be removed first */
/* the remaining entry should be power1, but to get it
we need to go back in the past a bit */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), 1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), 1);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
/* check that power2 is gone */
- BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
}
BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries)
BOOST_CHECK_EQUAL(MRC.size(), 1U);
/* the remaining entry should be power2 */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
/* check that power1 is gone */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1);
/* clear everything up */
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 1U);
/* the remaining entry should be power1 */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
/* check that power2 is gone */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
/* clear everything up */
MRC.doWipeCache(DNSName("."), true);
BOOST_CHECK_EQUAL(MRC.size(), 2U);
/* get a hit for power1 */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
BOOST_CHECK_EQUAL(MRC.size(), 1U);
/* the remaining entry should be power1 */
- BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd - now);
+ BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
/* check that power2 is gone */
- BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1);
+ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1);
MRC.doPrune(0);
BOOST_CHECK_EQUAL(MRC.size(), 0U);
/* check that we can still retrieve the remaining ones */
size_t found = 0;
for (size_t i = 0; i <= 255; i++) {
- retrieved.clear();
ComboAddress whoLoop("192.0.2." + std::to_string(i));
auto ret = MRC.get(now, power1, QType(QType::A), false, &retrieved, whoLoop);
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
/* no entry in the ECS index, no non-specific entry either */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), -1);
/* insert a non-specific entry */
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
/* retrieve the non-specific entry */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
/* there is an ECS index for that entry but no match, and no non-specific entry */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.4")), -1);
BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
/* there is an ECS index for that entry and we get a match */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
/* there is an ECS index for that entry and we get a match,
but it has expired. No other match, no non-specific entry */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now + ttl + 1, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), -1);
BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
MRC.replace(now + ttl + 1, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31"));
BOOST_CHECK_EQUAL(MRC.size(), 1U);
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
/* check that we get the most specific one as long as it's still valid */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), 5);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
/* there is an ECS index for that entry and we get a match,
but it has expired.
The second ECS is a match too, and is valid. */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now + 5 + 1, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), (ttd - (now + 5 + 1)));
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1U);
/* there is an ECS index for that entry and it doesn't match. No other match, but we have a non-specific entry */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.255")), ttd - now);
BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
/* no entry in the ECS index, no non-specific entry either */
- retrieved.clear();
BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), -1);
/* insert a specific entry */
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
}
+BOOST_AUTO_TEST_CASE(test_RecursorCacheTagged)
+{
+ MemRecursorCache MRC;
+
+ std::vector<std::shared_ptr<DNSRecord>> authRecords;
+ std::vector<std::shared_ptr<RRSIGRecordContent>> signatures;
+ time_t now = time(nullptr);
+ time_t ttd = now + 30;
+
+ DNSName power("powerdns.com.");
+ DNSRecord dr0;
+ ComboAddress dr0Content("192.0.2.40");
+ dr0.d_name = power;
+ dr0.d_type = QType::A;
+ dr0.d_class = QClass::IN;
+ dr0.d_content = std::make_shared<ARecordContent>(dr0Content);
+ dr0.d_ttl = static_cast<uint32_t>(ttd);
+ dr0.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> rset0;
+ rset0.push_back(dr0);
+
+ DNSRecord dr0tagged;
+ ComboAddress dr0taggedContent("192.0.2.100");
+ dr0tagged.d_name = power;
+ dr0tagged.d_type = QType::A;
+ dr0tagged.d_class = QClass::IN;
+ dr0tagged.d_content = std::make_shared<ARecordContent>(dr0taggedContent);
+ dr0tagged.d_ttl = static_cast<uint32_t>(ttd);
+ dr0tagged.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> rset0tagged;
+ rset0tagged.push_back(dr0tagged);
+
+ BOOST_CHECK_EQUAL(MRC.size(), 0U);
+ // An entry without edns subnet gets stored without tag as well
+ MRC.replace(ttd, DNSName("hello"), QType(QType::A), rset0, signatures, authRecords, true, boost::none, boost::none);
+ MRC.replace(ttd, DNSName("hello"), QType(QType::A), rset0, signatures, authRecords, true, boost::none, string("mytag"));
+ BOOST_CHECK_EQUAL(MRC.size(), 1U);
+ BOOST_CHECK_EQUAL(MRC.doWipeCache(DNSName("hello"), false, QType::A), 1U);
+ BOOST_CHECK_EQUAL(MRC.size(), 0U);
+ BOOST_CHECK_EQUAL(MRC.bytes(), 0U);
+
+ ComboAddress nobody;
+ ComboAddress who("192.0.2.1");
+
+ uint64_t counter = 0;
+ try {
+ for (counter = 0; counter < 100; ++counter) {
+ DNSName a = DNSName("hello ") + DNSName(std::to_string(counter));
+ BOOST_CHECK_EQUAL(DNSName(a.toString()), a);
+
+ MRC.replace(now, a, QType(QType::A), rset0, signatures, authRecords, true, boost::none, string("mytagA"));
+ MRC.replace(now, a, QType(QType::A), rset0, signatures, authRecords, true, boost::none, string("mytagB"));
+ // After this, we have untagged entries, since no address was specified for both replace calls
+ }
+
+ BOOST_CHECK_EQUAL(MRC.size(), counter);
+
+ std::vector<DNSRecord> retrieved;
+ int64_t matches = 0;
+ int64_t expected = counter;
+
+ for (counter = 0; counter < 110; counter++) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, boost::none) > 0) {
+ matches++;
+ BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
+ }
+ }
+ BOOST_CHECK_EQUAL(matches, expected);
+
+ matches = 0;
+ for (counter = 0; counter < 110; ++counter) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, string("mytagB")) > 0) {
+ matches++;
+ BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
+ }
+ }
+ BOOST_CHECK_EQUAL(matches, expected);
+
+ matches = 0;
+ for (counter = 0; counter < 110; counter++) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, who, string("mytagX")) > 0) {
+ matches++;
+ BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
+ }
+ }
+ BOOST_CHECK_EQUAL(matches, expected);
+
+ // Now insert some tagged entries
+ for (counter = 0; counter < 50; ++counter) {
+ DNSName a = DNSName("hello ") + DNSName(std::to_string(counter));
+ MRC.replace(now, a, QType(QType::A), rset0tagged, signatures, authRecords, true, boost::optional<Netmask>("128.0.0.0/8"), string("mytagA"));
+ }
+ BOOST_CHECK_EQUAL(MRC.size(), 150U);
+
+ matches = 0;
+ for (counter = 0; counter < 110; counter++) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, boost::none) > 0) {
+ matches++;
+ BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
+ }
+ }
+ BOOST_CHECK_EQUAL(matches, 100U);
+
+ matches = 0;
+ for (counter = 0; counter < 110; ++counter) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, string("mytagA")) > 0) {
+ matches++;
+ if (counter < 50) {
+ BOOST_CHECK_EQUAL(retrieved.size(), rset0tagged.size());
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0taggedContent.toString());
+ }
+ else {
+ BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
+ }
+ }
+ }
+ BOOST_CHECK_EQUAL(matches, 100U);
+
+ matches = 0;
+ for (counter = 0; counter < 110; counter++) {
+ if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(counter)), QType(QType::A), false, &retrieved, nobody, string("mytagX")) > 0) {
+ matches++;
+ BOOST_CHECK_EQUAL(retrieved.size(), rset0.size());
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
+ }
+ }
+ BOOST_CHECK_EQUAL(matches, 100U);
+
+ MRC.doWipeCache(DNSName("."), true);
+ BOOST_CHECK_EQUAL(MRC.size(), 0U);
+
+ DNSRecord dr1;
+ ComboAddress dr1Content("192.0.2.41");
+ dr1.d_name = power;
+ dr1.d_type = QType::A;
+ dr1.d_class = QClass::IN;
+ dr1.d_content = std::make_shared<ARecordContent>(dr1Content);
+ dr1.d_ttl = static_cast<uint32_t>(ttd);
+ dr1.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> rset1;
+ rset1.push_back(dr1);
+
+ DNSRecord dr2;
+ ComboAddress dr2Content("192.0.2.42");
+ dr2.d_name = power;
+ dr2.d_type = QType::A;
+ dr2.d_class = QClass::IN;
+ dr2.d_content = std::make_shared<ARecordContent>(dr2Content);
+ dr2.d_ttl = static_cast<uint32_t>(ttd);
+ dr2.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> rset2;
+ rset2.push_back(dr2);
+
+ DNSRecord dr3;
+ ComboAddress dr3Content("192.0.2.43");
+ dr3.d_name = power;
+ dr3.d_type = QType::A;
+ dr3.d_class = QClass::IN;
+ dr3.d_content = std::make_shared<ARecordContent>(dr3Content);
+ dr3.d_ttl = static_cast<uint32_t>(ttd);
+ dr3.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> rset3;
+ rset3.push_back(dr3);
+
+ // insert a tagged entry
+ MRC.replace(now, power, QType(QType::A), rset1, signatures, authRecords, true, boost::optional<Netmask>("192.0.2.0/24"), string("mytag"));
+ BOOST_CHECK_EQUAL(MRC.size(), 1U);
+
+ // tagged specific should be returned for a matching tag
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
+ BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
+
+ // tag specific should not be returned for a different tag
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("othertag")), 0);
+ BOOST_CHECK_EQUAL(retrieved.size(), 0U);
+
+ // insert a new entry without tag
+ MRC.replace(now, power, QType(QType::A), rset2, signatures, authRecords, true, boost::optional<Netmask>("192.0.3.0/24"), boost::none);
+ BOOST_CHECK_EQUAL(MRC.size(), 2U);
+
+ // tagged specific should be returned for a matching tag
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
+ BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
+
+ // if no tag given nothing should be retrieved if address doesn't match
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none), 0);
+ BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
+
+ // if no tag given and no-non-tagged entries matches nothing should be returned
+ BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), 0);
+ BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
+
+ // Insert untagged entry with no netmask
+ MRC.replace(now, power, QType(QType::A), rset3, signatures, authRecords, true, boost::none, boost::none);
+ BOOST_CHECK_EQUAL(MRC.size(), 3U);
+
+ // Retrieval with no address and no tag should get that one
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress(), boost::none), (ttd - now));
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
+
+ // If no tag given match non-tagged entry
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), (ttd - now));
+ BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
+
+ // If no tag given we should be able to retrieve the netmask specific record
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.3.1"), boost::none), (ttd - now));
+ BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
+
+ // tagged specific should still be returned for a matching tag, address is not used
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now));
+ BOOST_REQUIRE_EQUAL(retrieved.size(), 1U);
+ BOOST_CHECK_EQUAL(getRR<ARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
+
+ // remove everything
+ MRC.doWipeCache(DNSName("."), true);
+ BOOST_CHECK_EQUAL(MRC.size(), 0U);
+ }
+ catch (const PDNSException& e) {
+ cerr << "Had error: " << e.reason << endl;
+ throw;
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
#endif
#include "rpzloader.hh"
+#include "syncres.hh"
+
#include <boost/test/unit_test.hpp>
// Provide stubs for some symbols
bool g_logRPZChanges{false};
-ComboAddress getQueryLocalAddress(int family, uint16_t port)
-{
- cerr << "getQueryLocalAddress() STUBBED IN TEST!" << endl;
- BOOST_ASSERT(false);
- return ComboAddress();
-}
BOOST_AUTO_TEST_SUITE(rpzloader_cc)
BOOST_AUTO_TEST_SUITE(test_secpoll_cc)
-bool checkBasicMessage1(const PDNSException& ex)
+static bool checkBasicMessage1(const PDNSException& ex)
{
BOOST_CHECK_EQUAL(ex.reason, "Had empty answer on NOERROR RCODE");
return true;
}
-bool checkBasicMessage2(const PDNSException& ex)
+static bool checkBasicMessage2(const PDNSException& ex)
{
- BOOST_CHECK_EQUAL(ex.reason, "RCODE was not NOERROR but " + RCode::to_s(1));
+ BOOST_CHECK_EQUAL(ex.reason, "RCODE was " + RCode::to_s(1));
return true;
}
-bool checkBasicMessage3(const PDNSException& ex)
+static bool checkBasicMessage3(const PDNSException& ex)
{
BOOST_CHECK_EQUAL(ex.reason, "No TXT record found in response");
return true;
}
-bool checkBasicMessage4(const PDNSException& ex)
+static bool checkBasicMessage4(const PDNSException& ex)
{
BOOST_CHECK(ex.reason.find("Could not parse status number: stoi") == 0);
return true;
}
-bool checkBasicMessage5(const PDNSException& ex)
+static bool checkBasicMessage5(const PDNSException& ex)
{
BOOST_CHECK(ex.reason.find("Could not parse status number: stoi") == 0);
return true;
GlobalStateHolder<LuaConfigItems> g_luaconfs;
GlobalStateHolder<SuffixMatchNode> g_dontThrottleNames;
GlobalStateHolder<NetmaskGroup> g_dontThrottleNetmasks;
-std::unique_ptr<MemRecursorCache> s_RC{nullptr};
+std::unique_ptr<MemRecursorCache> g_recCache{nullptr};
+std::unique_ptr<NegCache> g_negCache{nullptr};
unsigned int g_numThreads = 1;
bool g_lowercaseOutgoing = false;
return theArg;
}
-int getMTaskerTID()
+void primeRootNSZones(bool, unsigned int)
{
- return 0;
}
-void primeRootNSZones(bool)
+bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret) const
{
+ return false;
}
-bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret) const
+bool RecursorLua4::policyHitEventFilter(const ComboAddress& remote, const DNSName& qname, const QType& qtype, bool tcp, DNSFilterEngine::Policy& policy, std::unordered_set<std::string>& tags, std::unordered_map<std::string, bool>& dicardedPolicies) const
{
return false;
}
#include "root-addresses.hh"
-void primeHints(void)
+bool primeHints(void)
{
vector<DNSRecord> nsset;
- if (!s_RC)
- s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
+ if (!g_recCache)
+ g_recCache = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
+ if (!g_negCache)
+ g_negCache = std::unique_ptr<NegCache>(new NegCache());
DNSRecord arr, aaaarr, nsrr;
nsrr.d_name = g_rootdnsname;
arr.d_content = std::make_shared<ARecordContent>(ComboAddress(rootIps4[c - 'a']));
vector<DNSRecord> aset;
aset.push_back(arr);
- s_RC->replace(time(nullptr), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true); // auth, nuke it all
+ g_recCache->replace(time(nullptr), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true); // auth, nuke it all
if (rootIps6[c - 'a'] != NULL) {
aaaarr.d_content = std::make_shared<AAAARecordContent>(ComboAddress(rootIps6[c - 'a']));
vector<DNSRecord> aaaaset;
aaaaset.push_back(aaaarr);
- s_RC->replace(time(nullptr), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true);
+ g_recCache->replace(time(nullptr), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true);
}
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
+ g_recCache->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()
g_log.toConsole(Logger::Error);
}
- s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
+ g_recCache = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
+ g_negCache = std::unique_ptr<NegCache>(new NegCache());
SyncRes::s_maxqperq = 50;
+ SyncRes::s_maxnsaddressqperq = 10;
SyncRes::s_maxtotusec = 1000 * 7000;
SyncRes::s_maxdepth = 40;
SyncRes::s_maxnegttl = 3600;
SyncRes::s_packetcacheservfailttl = 60;
SyncRes::s_serverdownmaxfails = 64;
SyncRes::s_serverdownthrottletime = 60;
+ SyncRes::s_doIPv4 = true;
SyncRes::s_doIPv6 = true;
SyncRes::s_ecsipv4limit = 24;
SyncRes::s_ecsipv6limit = 56;
sr->setLogMode(debug == false ? SyncRes::LogNone : SyncRes::Log);
SyncRes::setDomainMap(std::make_shared<SyncRes::domainmap_t>());
- SyncRes::clearNegCache();
+ g_negCache->clear();
}
void setDNSSECValidation(std::unique_ptr<SyncRes>& sr, const DNSSECMode& mode)
const uint16_t type = records[recordsCount - 1].d_type;
sortedRecords_t recordcontents;
- for (const auto record : records) {
+ for (const auto& record : records) {
if (record.d_name == name && record.d_type == type) {
recordcontents.insert(record.d_content);
}
extern GlobalStateHolder<LuaConfigItems> g_luaconfs;
-ArgvMap& arg();
-int getMTaskerTID();
-
-void primeHints(void);
-
void initSR(bool debug = false);
void initSR(std::unique_ptr<SyncRes>& sr, bool dnssec = false, bool debug = false, time_t fakeNow = 0);
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)
BOOST_CHECK(ret[2].d_type == QType::A);
BOOST_CHECK_EQUAL(ret[2].d_name, cnameTarget);
- // Check if we correctly return a synthesizd CNAME, should send out just 1 more query
+ // Check if we correctly return a synthesized CNAME, should send out just 1 more query
ret.clear();
res = sr->beginResolve(uncachedTarget, QType(QType::A), QClass::IN, 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(), 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(), 1U);
+}
+
+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(), 0U);
+}
+
+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(), 0U);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(syncres_cc2)
-BOOST_AUTO_TEST_CASE(test_referral_depth)
+static void do_test_referral_depth(bool limited)
{
std::unique_ptr<SyncRes> sr;
initSR(sr);
}
else if (domain == DNSName("ns3.powerdns.org.")) {
addRecordToLW(res, domain, QType::NS, "ns4.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
- }
- else if (domain == DNSName("ns4.powerdns.org.")) {
- addRecordToLW(res, domain, QType::NS, "ns5.powerdns.org.", DNSResourceRecord::AUTHORITY, 172800);
- addRecordToLW(res, domain, QType::A, "192.0.2.1", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, "ns4.powerdns.org.", 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);
- addRecordToLW(res, domain, QType::A, "192.0.2.2");
+ if (domain == DNSName("www.powerdns.com.")) {
+ addRecordToLW(res, domain, QType::A, "192.0.2.2");
+ }
+ else {
+ addRecordToLW(res, domain, QType::A, "192.0.2.1");
+ }
return 1;
}
return 0;
});
- /* Set the maximum depth low */
- SyncRes::s_maxdepth = 10;
-
- try {
- vector<DNSRecord> ret;
- sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
- BOOST_CHECK(false);
+ if (limited) {
+ /* Set the maximum depth low */
+ SyncRes::s_maxdepth = 4;
+ try {
+ vector<DNSRecord> ret;
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(false);
+ }
+ catch (const ImmediateServFailException& e) {
+ BOOST_CHECK(e.reason.find("max-recursion-depth") != string::npos);
+ }
}
- catch (const ImmediateServFailException& e) {
+ else {
+ // Check if the setup with high limit is OK.
+ SyncRes::s_maxdepth = 50;
+ try {
+ vector<DNSRecord> ret;
+ int rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(rcode, RCode::NoError);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1U);
+ BOOST_CHECK_EQUAL(ret[0].d_name, target);
+ BOOST_REQUIRE(ret[0].d_type == QType::A);
+ BOOST_CHECK(getRR<ARecordContent>(ret[0])->getCA() == ComboAddress("192.0.2.2"));
+ }
+ catch (const ImmediateServFailException& e) {
+ BOOST_CHECK(false);
+ }
}
}
+BOOST_AUTO_TEST_CASE(test_referral_depth)
+{
+ // Test with limit
+ do_test_referral_depth(true);
+}
+
+BOOST_AUTO_TEST_CASE(test_referral_depth_ok)
+{
+ // Test with default limit
+ do_test_referral_depth(false);
+}
+
BOOST_AUTO_TEST_CASE(test_cname_qperq)
{
std::unique_ptr<SyncRes> sr;
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
/* one for target1 and one for the entire TLD */
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_LE(ret[0].d_ttl, SyncRes::s_maxnegttl);
/* one for target1 and one for the entire TLD */
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
/* we should have sent only one query */
BOOST_CHECK_EQUAL(queriesCount, 1U);
/* even with root-nx-trust on and a NX answer from the root,
we should not have cached the entire TLD this time. */
- BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_REQUIRE(ret[0].d_type == QType::A);
BOOST_CHECK(getRR<ARecordContent>(ret[0])->getCA() == ComboAddress("192.0.2.2"));
- BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 3U);
}
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
/* one for target1 */
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1U);
/* one for target1 */
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
/* we should have sent three queries */
BOOST_CHECK_EQUAL(queriesCount, 3U);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
// Now test without RFC 8020 to see the cache and query count grow
SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
// New query
ret.clear();
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 3U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
ret.clear();
res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 3U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 3U);
ret.clear();
res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 5U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 4U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 4U);
// reset
SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
vector<DNSRecord> ret;
int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_CHECK_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 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);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 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);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 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);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
// Now test without RFC 8020 to see the cache and query count grow
SyncRes::s_hardenNXD = SyncRes::HardenNXD::No;
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);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
// New query
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);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 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);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 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(g_negCache->size(), 4U);
// reset
SyncRes::s_hardenNXD = SyncRes::HardenNXD::DNSSEC;
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 3U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
ret.clear();
res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
}
BOOST_AUTO_TEST_CASE(test_rfc8020_nodata_bis)
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 3U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
ret.clear();
res = sr->beginResolve(target2, QType(QType::TXT), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
ret.clear();
res = sr->beginResolve(target3, QType(QType::TXT), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 2U);
}
BOOST_AUTO_TEST_CASE(test_skip_negcache_for_variable_response)
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
BOOST_CHECK_EQUAL(ret.size(), 2U);
/* no negative cache entry because the response was variable */
- BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 0U);
+ BOOST_CHECK_EQUAL(g_negCache->size(), 0U);
}
BOOST_AUTO_TEST_CASE(test_ecs_cache_limit_allowed)
/* should have been cached */
const ComboAddress who("192.0.2.128");
vector<DNSRecord> cached;
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
}
/* should have been cached because /24 is more specific than /16 but TTL limit is nof effective */
const ComboAddress who("192.0.2.128");
vector<DNSRecord> cached;
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
}
/* should have been cached */
const ComboAddress who("192.0.2.128");
vector<DNSRecord> cached;
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
}
/* should have been cached */
const ComboAddress who("192.0.2.128");
vector<DNSRecord> cached;
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
}
/* should have NOT been cached because TTL of 60 is too small and /24 is more specific than /16 */
const ComboAddress who("192.0.2.128");
vector<DNSRecord> cached;
- BOOST_REQUIRE_LT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_LT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 0U);
}
std::vector<shared_ptr<RRSIGRecordContent>> sigs;
addRecordToList(records, target, QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, now + 3600);
- s_RC->replace(now, target, QType(QType::NS), records, sigs, vector<std::shared_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
+ g_recCache->replace(now, target, QType(QType::NS), records, sigs, vector<std::shared_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(queriesCount, 5U);
}
+BOOST_AUTO_TEST_CASE(test_completely_flawed_big_nsset)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ primeHints();
+
+ const DNSName target("powerdns.com.");
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([&queriesCount, target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ if (isRootServer(ip) && domain == target) {
+ setLWResult(res, 0, false, false, true);
+ // 20 NS records
+ for (int i = 0; i < 20; i++) {
+ string n = string("pdns-public-ns") + std::to_string(i) + string(".powerdns.com.");
+ addRecordToLW(res, domain, QType::NS, n, DNSResourceRecord::AUTHORITY, 172800);
+ }
+ return 1;
+ }
+ else if (domain.toString().length() > 14 && domain.toString().substr(0, 14) == "pdns-public-ns") {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
+ return 1;
+ }
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ try {
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(0);
+ }
+ catch (const ImmediateServFailException& ex) {
+ BOOST_CHECK_EQUAL(ret.size(), 0U);
+ // one query to get NSs, then A and AAAA for each NS, 5th NS hits the limit
+ // limit is reduced to 5, because zone publishes many (20) NS
+ BOOST_CHECK_EQUAL(queriesCount, 11U);
+ }
+}
+
BOOST_AUTO_TEST_CASE(test_cache_hit)
{
std::unique_ptr<SyncRes> sr;
return 0;
});
- /* we populate the cache with eveything we need */
+ /* we populate the cache with everything we need */
time_t now = sr->getNow().tv_sec;
std::vector<DNSRecord> records;
std::vector<shared_ptr<RRSIGRecordContent>> sigs;
addRecordToList(records, target, QType::A, "192.0.2.1", DNSResourceRecord::ANSWER, now + 3600);
- s_RC->replace(now, target, QType(QType::A), records, sigs, vector<std::shared_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
+ g_recCache->replace(now, target, QType(QType::A), records, sigs, vector<std::shared_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
const ComboAddress who;
vector<DNSRecord> cached;
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_GT(cached[0].d_ttl, now);
BOOST_CHECK_EQUAL((cached[0].d_ttl - now), SyncRes::s_minimumTTL);
cached.clear();
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::NS), false, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::NS), false, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_GT(cached[0].d_ttl, now);
BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_maxcachettl);
const ComboAddress who("192.0.2.128");
vector<DNSRecord> cached;
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_GT(cached[0].d_ttl, now);
BOOST_CHECK_EQUAL((cached[0].d_ttl - now), SyncRes::s_minimumECSTTL);
cached.clear();
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::NS), false, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::NS), false, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_GT(cached[0].d_ttl, now);
BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_maxcachettl);
cached.clear();
- BOOST_REQUIRE_GT(s_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_GT(cached[0].d_ttl, now);
BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_minimumTTL);
std::vector<shared_ptr<RRSIGRecordContent>> sigs;
addRecordToList(records, target, QType::A, "192.0.2.42", DNSResourceRecord::ANSWER, now - 60);
- s_RC->replace(now - 3600, target, QType(QType::A), records, sigs, vector<std::shared_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
+ g_recCache->replace(now - 3600, target, QType(QType::A), records, sigs, vector<std::shared_ptr<DNSRecord>>(), true, boost::optional<Netmask>());
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
/* check that we correctly cached only the answer entry, not the additional one */
const ComboAddress who;
vector<DNSRecord> cached;
- BOOST_REQUIRE_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_EQUAL(QType(cached.at(0).d_type).getName(), QType(QType::A).getName());
BOOST_CHECK_EQUAL(getRR<ARecordContent>(cached.at(0))->getCA().toString(), ComboAddress("192.0.2.2").toString());
sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
setLWResult(res, 0, true, false, true);
addRecordToLW(res, domain, QType::A, "192.0.2.42");
- addRecordToLW(res, domain, QType::ANY, "0 0");
+ addRecordToLW(res, domain, QType::ANY, "\\# 0");
addRecordToLW(res, domain, QType::OPT, "");
return 1;
});
const ComboAddress who;
vector<DNSRecord> cached;
vector<std::shared_ptr<RRSIGRecordContent>> signatures;
- BOOST_REQUIRE_EQUAL(s_RC->get(now, target, QType(QType::A), false, &cached, who, &signatures), -1);
+ BOOST_REQUIRE_EQUAL(g_recCache->get(now, target, QType(QType::A), false, &cached, who, boost::none, &signatures), -1);
}
BOOST_AUTO_TEST_CASE(test_special_types)
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);
}
+BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_nord_dnssec)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ /* signed */
+ const DNSName parent("test.");
+ const DNSName target1("a.test.");
+ const DNSName target2("b.test.");
+
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("test."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+ g_luaconfs.setState(luaconfsCopy);
+
+ const ComboAddress forwardedNS("192.0.2.42:53");
+ size_t queriesCount = 0;
+ size_t DSforParentCount = 0;
+
+ SyncRes::AuthDomain ad;
+ ad.d_rdForward = false;
+ ad.d_servers.push_back(forwardedNS);
+ (*SyncRes::t_sstorage.domainmap)[DNSName("test.")] = ad;
+
+ sr->setAsyncCallback([parent, target1, target2, keys, forwardedNS, &queriesCount, &DSforParentCount](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++;
+
+ BOOST_CHECK_EQUAL(sendRDQuery, false);
+
+ if (type == QType::DS && domain == parent) {
+ DSforParentCount++;
+ }
+ if (type == QType::DS || type == QType::DNSKEY) {
+ if (domain != parent && domain.isPartOf(parent)) {
+ return genericDSAndDNSKEYHandler(res, domain, domain, type, keys, false /* no cut / delegation */);
+ }
+ else {
+ return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
+ }
+ }
+
+ if (isRootServer(ip)) {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, parent, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 42);
+ addRRSIG(keys, res->d_records, g_rootdnsname, 300);
+ addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ }
+
+ if (ip != forwardedNS) {
+ return 0;
+ }
+
+ if (domain == target1 && type == QType::A) {
+
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, target1, QType::A, "192.0.2.1");
+ addRRSIG(keys, res->d_records, parent, 300);
+
+ return 1;
+ }
+ if (domain == target2 && type == QType::A) {
+
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, target2, QType::A, "192.0.2.2");
+ addRRSIG(keys, res->d_records, parent, 300);
+
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
+ BOOST_REQUIRE_EQUAL(ret.size(), 2U);
+ BOOST_CHECK_EQUAL(queriesCount, 5U);
+ BOOST_CHECK_EQUAL(DSforParentCount, 1U);
+
+ /* again, to test the cache */
+ ret.clear();
+ res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
+ BOOST_REQUIRE_EQUAL(ret.size(), 2U);
+ BOOST_CHECK_EQUAL(queriesCount, 5U);
+ BOOST_CHECK_EQUAL(DSforParentCount, 1U);
+
+ /* new target should no cause a DS query for tets. */
+ ret.clear();
+ res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
+ BOOST_REQUIRE_EQUAL(ret.size(), 2U);
+ BOOST_CHECK_EQUAL(queriesCount, 7U);
+ BOOST_CHECK_EQUAL(DSforParentCount, 1U);
+}
+
BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd_dnssec_bogus)
{
std::unique_ptr<SyncRes> sr;
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), 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 */
+ g_recCache = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
+ g_negCache = std::unique_ptr<NegCache>(new NegCache());
+ sr->setDNSSECValidationRequested(false);
+ primeHints();
+
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
+ /* 13 NS + 1 RRSIG */
+ BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+ BOOST_CHECK_EQUAL(queriesCount, 3U);
+
+ /* now we ask for the DNSKEYs (still without validation) */
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::DNSKEY), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
+ /* 1 SOA + 1 RRSIG */
+ BOOST_REQUIRE_EQUAL(ret.size(), 2U);
+ BOOST_CHECK_EQUAL(queriesCount, 4U);
+
+ /* again, to test the cache WITH validation */
+ sr->setDNSSECValidationRequested(true);
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+ BOOST_CHECK_EQUAL(queriesCount, 4U);
}
BOOST_AUTO_TEST_CASE(test_dnssec_bogus_rrsig_signed_with_unknown_dnskey)
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
/* 13 NS + 0 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 13U);
/* no RRSIG so no query for DNSKEYs */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 13U);
/* check that we capped the TTL to max-cache-bogus-ttl */
for (const auto& record : ret) {
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
/* no supported DS so no query for DNSKEYs */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 1U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
/* no supported DS so no query for DNSKEYs */
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 1U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 2U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 3U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 3U);
ret.clear();
res = sr->beginResolve(DNSName("com."), QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 3U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("com."), QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 1U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 7U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 7U);
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
}
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* Should be insecure because of the NTA */
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 5U);
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* Should be insecure because of the NTA */
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 5U);
}
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* Should be insecure because of the NTA */
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 8U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 8U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
/* because we pass along the duplicated NSEC3 */
BOOST_REQUIRE_EQUAL(ret.size(), 9U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
/* because we pass along the duplicated NSEC3 */
BOOST_REQUIRE_EQUAL(ret.size(), 9U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 8U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 8U);
BOOST_CHECK_EQUAL(queriesCount, 6U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 10U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 10U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
/* A + RRSIG, NSEC + RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
/* A + RRSIG, NSEC + RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
}
if (domain == hero && type == QType::NS) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, hero, QType::NS, "dns1.p03.nsone.net.", DNSResourceRecord::AUTHORITY, 3600);
- addDS(DNSName(hero), 300, res->d_records, keys);
- addRRSIG(keys, res->d_records, DNSName(hero), 300);
+ addDS(hero, 300, res->d_records, keys);
+ addRRSIG(keys, res->d_records, com, 300);
}
else if (domain == nsone && type == QType::A) {
setLWResult(res, 0, false, false, true);
addRecordToLW(res, nsone, QType::NS, "dns1.p01.nsone.net.", DNSResourceRecord::AUTHORITY, 3600);
addNSECRecordToLW(nsone, DNSName("zzz.nsone.net."), {QType::NS, QType::SOA, QType::RRSIG, QType::DNSKEY}, 600, res->d_records);
- addRRSIG(keys, res->d_records, nsone, 300);
+ addRRSIG(keys, res->d_records, net, 300);
addRecordToLW(res, "dns1.p01.nsone.net", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
}
else {
vector<DNSRecord> ret;
int res = sr->beginResolve(DNSName("herokuapp.com."), QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 12U);
ret.clear();
res = sr->beginResolve(DNSName("dns1.p03.nsone.net."), QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 16U);
}
+BOOST_AUTO_TEST_CASE(test_dnssec_secure_servfail_ds)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target("powerdns.com.");
+ const ComboAddress targetAddr("192.0.2.42");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+ generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ /* make sure that the signature inception and validity times are computed
+ based on the SyncRes time, not the current one, in case the function
+ takes too long. */
+
+ const time_t fixedNow = sr->getNow().tv_sec;
+
+ sr->setAsyncCallback([target, targetAddr, &queriesCount, keys, fixedNow](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ DNSName auth = domain;
+ if (domain == target) {
+ auth = DNSName("powerdns.com.");
+ }
+
+ if (type == QType::DS && domain == DNSName("powerdns.com.")) {
+ /* time out */
+ return 0;
+ }
+
+ if (type == QType::DS || type == QType::DNSKEY) {
+ return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
+ }
+
+ if (isRootServer(ip)) {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+ addDS(DNSName("com."), 300, res->d_records, keys);
+ addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ }
+
+ if (ip == ComboAddress("192.0.2.1:53")) {
+ if (domain == DNSName("com.")) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
+ addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ addRRSIG(keys, res->d_records, domain, 300);
+ }
+ else {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
+ /* do NOT include the DS here */
+ //addDS(auth, 300, res->d_records, keys);
+ //addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+ }
+ return 1;
+ }
+
+ if (ip == ComboAddress("192.0.2.2:53")) {
+ if (type == QType::NS) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
+ addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+ addRRSIG(keys, res->d_records, auth, 300);
+ }
+ else {
+ setLWResult(res, RCode::NoError, true, false, true);
+ addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+ addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+ }
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ try {
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(false);
+ }
+ catch (const ImmediateServFailException& e) {
+ BOOST_CHECK(e.reason.find("Server Failure while retrieving DS records for powerdns.com") != string::npos);
+ }
+
+ /* and a second time to check nothing was cached */
+ try {
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(false);
+ }
+ catch (const ImmediateServFailException& e) {
+ BOOST_CHECK(e.reason.find("Server Failure while retrieving DS records for powerdns.com") != string::npos);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_secure_servfail_dnskey)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target("powerdns.com.");
+ const ComboAddress targetAddr("192.0.2.42");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+ generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ /* make sure that the signature inception and validity times are computed
+ based on the SyncRes time, not the current one, in case the function
+ takes too long. */
+
+ const time_t fixedNow = sr->getNow().tv_sec;
+
+ sr->setAsyncCallback([target, targetAddr, &queriesCount, keys, fixedNow](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ DNSName auth = domain;
+ if (domain == target) {
+ auth = DNSName("powerdns.com.");
+ }
+
+ if (type == QType::DNSKEY && domain == DNSName("powerdns.com.")) {
+ /* time out */
+ return 0;
+ }
+
+ if (type == QType::DS || type == QType::DNSKEY) {
+ return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
+ }
+
+ if (isRootServer(ip)) {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+ addDS(DNSName("com."), 300, res->d_records, keys);
+ addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ return 1;
+ }
+
+ if (ip == ComboAddress("192.0.2.1:53")) {
+ if (domain == DNSName("com.")) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
+ addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+ addRRSIG(keys, res->d_records, domain, 300);
+ }
+ else {
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
+ addDS(auth, 300, res->d_records, keys);
+ addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+ }
+ return 1;
+ }
+
+ if (ip == ComboAddress("192.0.2.2:53")) {
+ if (type == QType::NS) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
+ addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+ addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+ addRRSIG(keys, res->d_records, auth, 300);
+ }
+ else {
+ setLWResult(res, RCode::NoError, true, false, true);
+ addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+ addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+ }
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ try {
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(false);
+ }
+ catch (const ImmediateServFailException& e) {
+ BOOST_CHECK(e.reason.find("Server Failure while retrieving DNSKEY records for powerdns.com") != string::npos);
+ }
+
+ /* and a second time to check nothing was cached */
+ try {
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(false);
+ }
+ catch (const ImmediateServFailException& e) {
+ BOOST_CHECK(e.reason.find("Server Failure while retrieving DNSKEY records for powerdns.com") != string::npos);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
BOOST_CHECK_EQUAL(dsQueriesCount, 3U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 9U);
BOOST_CHECK_EQUAL(dsQueriesCount, 3U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
}
+BOOST_AUTO_TEST_CASE(test_dnssec_ds_root)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target(".");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ if (type == QType::DS) {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
+ addRRSIG(keys, res->d_records, DNSName("."), 300);
+ addNSECRecordToLW(domain, DNSName("aaa."), {QType::DNSKEY, QType::SOA, QType::NS, QType::NSEC, QType::RRSIG}, 600, res->d_records);
+ addRRSIG(keys, res->d_records, DNSName("."), 300);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
+ BOOST_REQUIRE_EQUAL(ret.size(), 4U);
+ BOOST_CHECK_EQUAL(queriesCount, 1U);
+
+ /* again, to test the cache */
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
+ BOOST_REQUIRE_EQUAL(ret.size(), 4U);
+ BOOST_CHECK_EQUAL(queriesCount, 1U);
+}
+
BOOST_AUTO_TEST_CASE(test_dnssec_dnskey_signed_child)
{
/* check that we don't accept a signer below us */
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 10U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 10U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 7U);
BOOST_CHECK_EQUAL(dsQueriesCount, 2U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 7U);
BOOST_CHECK_EQUAL(dsQueriesCount, 2U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_CHECK_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_CHECK_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 8U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK(ret[0].d_type == QType::A);
/* 4 NS: com at ., com at com, powerdns.com at com, powerdns.com at powerdns.com
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 7U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK(ret[0].d_type == QType::SOA);
BOOST_CHECK_EQUAL(queriesCount, 4U);
ret.clear();
res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK(ret[0].d_type == QType::SOA);
BOOST_CHECK_EQUAL(queriesCount, 4U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK(ret[0].d_type == QType::SOA);
BOOST_CHECK_EQUAL(queriesCount, 6U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NXDomain);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 6U);
BOOST_CHECK(ret[0].d_type == QType::SOA);
BOOST_CHECK_EQUAL(queriesCount, 6U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::DS || record.d_type == QType::RRSIG);
ret.clear();
res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::DS || record.d_type == QType::RRSIG);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::SOA || record.d_type == QType::NSEC || record.d_type == QType::RRSIG);
ret.clear();
res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
for (const auto& record : ret) {
BOOST_CHECK(record.d_type == QType::SOA || record.d_type == QType::NSEC || record.d_type == QType::RRSIG);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 9U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 9U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 7U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 7U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
/* 4 NS (com from root, com from com, powerdns.com from com,
powerdns.com from powerdns.com)
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK_EQUAL(queriesCount, 7U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
BOOST_CHECK_EQUAL(queriesCount, 11U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 12U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 12U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
/* no RRSIG to show */
BOOST_CHECK_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 10U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_CHECK_EQUAL(ret.size(), 2U);
BOOST_CHECK_EQUAL(queriesCount, 10U);
}
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* should be insecure but we have a TA for powerdns.com. */
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
/* We got a RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK(ret[0].d_type == QType::A);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 2U);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 5U);
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
/* should be insecure but we have a TA for powerdns.com., but no RRSIG so Bogus */
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
/* No RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK(ret[0].d_type == QType::A);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 1U);
BOOST_CHECK(ret[0].d_type == QType::A);
BOOST_CHECK_EQUAL(queriesCount, 4U);
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
/* 13 NS + 1 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 1U);
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 14U);
BOOST_CHECK_EQUAL(queriesCount, 1U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
/* 13 NS + 0 RRSIG */
BOOST_REQUIRE_EQUAL(ret.size(), 13U);
BOOST_CHECK_EQUAL(queriesCount, 1U);
ret.clear();
res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
BOOST_REQUIRE_EQUAL(ret.size(), 13U);
BOOST_CHECK_EQUAL(queriesCount, 1U);
}
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 0U);
/* com|NS, powerdns.com|NS, powerdns.com|A */
BOOST_CHECK_EQUAL(queriesCount, 3U);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 0U);
+ /* we don't store empty results */
+ BOOST_CHECK_EQUAL(queriesCount, 4U);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nxdomain)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target("powerdns.com.");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ if (type == QType::DS || type == QType::DNSKEY) {
+ return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
+ }
+ else {
+
+ setLWResult(res, RCode::NXDomain, true, false, true);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 0U);
+ /* com|NS, powerdns.com|NS, powerdns.com|A */
+ BOOST_CHECK_EQUAL(queriesCount, 3U);
+
+ /* again, to test the cache */
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 0U);
/* we don't store empty results */
BOOST_CHECK_EQUAL(queriesCount, 4U);
denialMap[std::make_pair(DNSName("example.org."), QType::NSEC)] = pair;
dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+ BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
/* let's check that d.example.org. is not denied by this proof */
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_1)
denialMap[std::make_pair(DNSName("z.example.org."), QType::NSEC)] = pair;
dState denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+ BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
/* let's check that d.example.org. is not denied by this proof */
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_2)
denialMap[std::make_pair(DNSName("y.example.org."), QType::NSEC)] = pair;
dState denialState = getDenial(denialMap, DNSName("z.example.org."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+ BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
/* let's check that d.example.org. is not denied by this proof */
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec_denial_only_one_nsec)
denialMap[std::make_pair(DNSName("a.example.org."), QType::NSEC)] = pair;
dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+ BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false, false);
/* let's check that d.example.org. is not denied by this proof */
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec_root_nxd_denial)
denialMap[std::make_pair(DNSName("."), QType::NSEC)] = pair;
dState denialState = getDenial(denialMap, DNSName("b."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+ BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
}
BOOST_AUTO_TEST_CASE(test_nsec_ancestor_nxqtype_denial)
dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, false);
/* no data means the qname/qtype is not denied, because an ancestor
delegation NSEC can only deny the DS */
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
/* it can not be used to deny any RRs below that owner name either */
denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
- BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+ BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
}
BOOST_AUTO_TEST_CASE(test_nsec_insecure_delegation_denial)
/* Insecure because the NS is not set, so while it does
denies the DS, it can't prove an insecure delegation */
dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec_nxqtype_cname)
/* this NSEC is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
dState denialState = getDenial(denialMap, DNSName("a.powerdns.com."), QType::A, true, true);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec3_nxqtype_cname)
/* this NSEC3 is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
dState denialState = getDenial(denialMap, DNSName("a.powerdns.com."), QType::A, false, true);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec_nxdomain_denial_missing_wildcard)
denialMap[std::make_pair(DNSName("a.powerdns.com."), QType::NSEC)] = pair;
dState denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec3_nxdomain_denial_missing_wildcard)
denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
dState denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, false, false);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec_ent_denial)
/* this NSEC is valid to prove a NXQTYPE at c.powerdns.com because it proves that
it is an ENT */
dState denialState = getDenial(denialMap, DNSName("c.powerdns.com."), QType::AAAA, true, true);
- BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+ BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
/* this NSEC is not valid to prove a NXQTYPE at b.powerdns.com,
it could prove a NXDOMAIN if it had an additional wildcard denial */
denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::AAAA, true, true);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
/* this NSEC is not valid to prove a NXQTYPE for QType::A at a.c.powerdns.com either */
denialState = getDenial(denialMap, DNSName("a.c.powerdns.com."), QType::A, true, true);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
/* if we add the wildcard denial proof, we should get a NXDOMAIN proof for b.powerdns.com */
recordContents.clear();
denialMap[std::make_pair(DNSName(").powerdns.com."), QType::NSEC)] = pair;
denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, true, false);
- BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+ BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
+
+ /* this NSEC is NOT valid to prove a NXDOMAIN at c.powerdns.com because it proves that
+ it exists and is an ENT */
+ denialState = getDenial(denialMap, DNSName("c.powerdns.com."), QType::AAAA, true, false);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec3_ancestor_nxqtype_denial)
dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, true);
/* no data means the qname/qtype is not denied, because an ancestor
delegation NSEC3 can only deny the DS */
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
- BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+ BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
/* it can not be used to deny any RRs below that owner name either */
/* Add NSEC3 for the next closer */
denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, true);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_nsec3_denial_too_many_iterations)
dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, true);
/* since we refuse to compute more than g_maxNSEC3Iterations iterations, it should be Insecure */
- BOOST_CHECK_EQUAL(denialState, INSECURE);
+ BOOST_CHECK_EQUAL(denialState, dState::INSECURE);
}
BOOST_AUTO_TEST_CASE(test_nsec3_insecure_delegation_denial)
/* Insecure because the NS is not set, so while it does
denies the DS, it can't prove an insecure delegation */
dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
- BOOST_CHECK_EQUAL(denialState, NODATA);
+ BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
}
BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_validity)
vector<DNSRecord> ret;
int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
BOOST_REQUIRE_EQUAL(ret.size(), 4U);
BOOST_CHECK_EQUAL(queriesCount, 4U);
/* check that the entry has not been negatively cached for longer than the RRSIG validity */
- const NegCache::NegCacheEntry* ne = nullptr;
- 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);
+ NegCache::NegCacheEntry ne;
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
+ BOOST_REQUIRE_EQUAL(g_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;
- 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);
+ NegCache::NegCacheEntry ne;
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
+ BOOST_REQUIRE_EQUAL(g_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);
const ComboAddress who;
vector<DNSRecord> cached;
vector<std::shared_ptr<RRSIGRecordContent>> signatures;
- BOOST_REQUIRE_EQUAL(s_RC->get(tnow, target, QType(QType::A), true, &cached, who, &signatures), 1);
+ BOOST_REQUIRE_EQUAL(g_recCache->get(tnow, target, QType(QType::A), true, &cached, who, boost::none, &signatures), 1);
BOOST_REQUIRE_EQUAL(cached.size(), 1U);
BOOST_REQUIRE_EQUAL(signatures.size(), 1U);
BOOST_CHECK_EQUAL((cached[0].d_ttl - tnow), 1);
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;
- 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);
+ NegCache::NegCacheEntry ne;
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
+ BOOST_REQUIRE_EQUAL(g_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_CHECK_EQUAL(g_negCache->size(), 1U);
+ BOOST_REQUIRE_EQUAL(g_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;
- 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);
+ NegCache::NegCacheEntry ne;
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
+ BOOST_REQUIRE_EQUAL(g_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(g_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;
- 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);
+ NegCache::NegCacheEntry ne;
+ BOOST_CHECK_EQUAL(g_negCache->size(), 1U);
+ BOOST_REQUIRE_EQUAL(g_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(g_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(g_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);
vector<DNSRecord> cached;
bool wasAuth = false;
- auto ttl = s_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who, nullptr, nullptr, nullptr, nullptr, &wasAuth);
+ auto ttl = g_recCache->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth);
BOOST_REQUIRE_GE(ttl, 1);
BOOST_REQUIRE_LE(ttl, 42);
BOOST_CHECK_EQUAL(cached.size(), 1U);
cached.clear();
/* Also check that the the part in additional is still not auth */
- BOOST_REQUIRE_GE(s_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who, nullptr, nullptr, nullptr, nullptr, &wasAuth), -1);
+ BOOST_REQUIRE_GE(g_recCache->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth), -1);
BOOST_CHECK_EQUAL(cached.size(), 1U);
BOOST_CHECK_EQUAL(wasAuth, false);
}
const ComboAddress who;
vector<DNSRecord> cached;
- BOOST_CHECK_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
cached.clear();
- BOOST_CHECK_LT(s_RC->get(now, target, QType(QType::AAAA), true, &cached, who), 0);
- BOOST_CHECK_EQUAL(s_RC->get(now, DNSName("not-sanitization.powerdns.com."), QType(QType::DNAME), true, &cached, who), -1);
- BOOST_CHECK_LT(s_RC->get(now, target, QType(QType::MX), true, &cached, who), 0);
- BOOST_CHECK_EQUAL(s_RC->get(now, DNSName("not-sanitization.powerdns.com."), QType(QType::SOA), true, &cached, who), -1);
- BOOST_CHECK_LT(s_RC->get(now, target, QType(QType::TXT), false, &cached, who), 0);
- BOOST_CHECK_EQUAL(s_RC->get(now, DNSName("powerdns.com."), QType(QType::AAAA), false, &cached, who), -1);
+ BOOST_CHECK_LT(g_recCache->get(now, target, QType(QType::AAAA), true, &cached, who), 0);
+ BOOST_CHECK_EQUAL(g_recCache->get(now, DNSName("not-sanitization.powerdns.com."), QType(QType::DNAME), true, &cached, who), -1);
+ BOOST_CHECK_LT(g_recCache->get(now, target, QType(QType::MX), true, &cached, who), 0);
+ BOOST_CHECK_EQUAL(g_recCache->get(now, DNSName("not-sanitization.powerdns.com."), QType(QType::SOA), true, &cached, who), -1);
+ BOOST_CHECK_LT(g_recCache->get(now, target, QType(QType::TXT), false, &cached, who), 0);
+ BOOST_CHECK_EQUAL(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::AAAA), false, &cached, who), -1);
}
BOOST_AUTO_TEST_CASE(test_records_sanitization_keep_relevant_additional_aaaa)
const ComboAddress who;
vector<DNSRecord> cached;
- BOOST_CHECK_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
cached.clear();
/* not auth since it was in the additional section */
- BOOST_CHECK_LT(s_RC->get(now, target, QType(QType::AAAA), true, &cached, who), 0);
- BOOST_CHECK_GT(s_RC->get(now, target, QType(QType::AAAA), false, &cached, who), 0);
+ BOOST_CHECK_LT(g_recCache->get(now, target, QType(QType::AAAA), true, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, target, QType(QType::AAAA), false, &cached, who), 0);
}
BOOST_AUTO_TEST_CASE(test_records_sanitization_keep_glue)
setLWResult(res, 0, false, false, true);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, "powerdns.com.", QType::DS, "1 8 2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAA", DNSResourceRecord::AUTHORITY, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
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 {
const ComboAddress who;
vector<DNSRecord> cached;
- BOOST_CHECK_GT(s_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, target, QType(QType::A), true, &cached, who), 0);
cached.clear();
- BOOST_CHECK_GT(s_RC->get(now, DNSName("com."), QType(QType::NS), false, &cached, who), 0);
- BOOST_CHECK_GT(s_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who), 0);
- BOOST_CHECK_GT(s_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::AAAA), false, &cached, who), 0);
- BOOST_CHECK_GT(s_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who), 0);
- BOOST_CHECK_GT(s_RC->get(now, DNSName("pdns-public-ns1.powerdns.com."), QType(QType::A), false, &cached, who), 0);
- 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);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("com."), QType(QType::NS), false, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("a.gtld-servers.net."), QType(QType::AAAA), false, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("pdns-public-ns1.powerdns.com."), QType(QType::A), false, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("pdns-public-ns1.powerdns.com."), QType(QType::AAAA), false, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("pdns-public-ns2.powerdns.com."), QType(QType::A), false, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->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(g_recCache->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)
const ComboAddress who;
vector<DNSRecord> cached;
- BOOST_CHECK_GT(s_RC->get(now, DNSName("powerdns.com."), QType(QType::SOA), true, &cached, who), 0);
+ BOOST_CHECK_GT(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::SOA), true, &cached, who), 0);
cached.clear();
- BOOST_CHECK_LT(s_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who), 0);
- BOOST_CHECK_LT(s_RC->get(now, DNSName("spoofed.ns."), QType(QType::A), false, &cached, who), 0);
- BOOST_CHECK_LT(s_RC->get(now, DNSName("spoofed.ns."), QType(QType::AAAA), false, &cached, who), 0);
+ BOOST_CHECK_LT(g_recCache->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who), 0);
+ BOOST_CHECK_LT(g_recCache->get(now, DNSName("spoofed.ns."), QType(QType::A), false, &cached, who), 0);
+ BOOST_CHECK_LT(g_recCache->get(now, DNSName("spoofed.ns."), QType(QType::AAAA), false, &cached, who), 0);
}
BOOST_AUTO_TEST_SUITE_END()
#include <iostream>
#include <dnsrecords.hh>
-bool init_unit_test()
+static bool init_unit_test()
{
reportAllTypes();
return true;
--- /dev/null
+../views.hh
\ No newline at end of file
}
}
-void primeHints(void)
+bool primeHints(void)
{
// prime root cache
- const vState validationState = Insecure;
+ const vState validationState = vState::Insecure;
vector<DNSRecord> nsset;
t_rootNSZones.clear();
arr.d_content=std::make_shared<ARecordContent>(ComboAddress(rootIps4[c-'a']));
vector<DNSRecord> aset;
aset.push_back(arr);
- s_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, validationState); // auth, nuke it all
+ g_recCache->replace(time(0), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState); // auth, nuke it all
if (rootIps6[c-'a'] != NULL) {
aaaarr.d_content=std::make_shared<AAAARecordContent>(ComboAddress(rootIps6[c-'a']));
vector<DNSRecord> aaaaset;
aaaaset.push_back(aaaarr);
- s_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, validationState);
+ g_recCache->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState);
}
nsset.push_back(nsrr);
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, validationState); // auth, etc see above
+ g_recCache->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, validationState);
+ g_recCache->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, validationState); // and stuff in the cache
+
+ g_recCache->doWipeCache(g_rootdnsname, false, QType::NS);
+ g_recCache->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);
// so make a local copy
set<DNSName> copy(t_rootNSZones);
for (const auto & qname: copy) {
- s_RC->doWipeCache(qname, false, QType::NS);
+ g_recCache->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);
}
}
}
-void convertServersForAD(const std::string& input, SyncRes::AuthDomain& ad, const char* sepa, bool verbose=true)
+static void convertServersForAD(const std::string& input, SyncRes::AuthDomain& ad, const char* sepa, bool verbose=true)
{
vector<string> servers;
stringtok(servers, input, sepa);
g_log<<endl;
}
-void* pleaseWipeNegCache()
-{
- SyncRes::clearNegCache();
- return 0;
-}
-
-void* pleaseUseNewSDomainsMap(std::shared_ptr<SyncRes::domainmap_t> newmap)
+static void* pleaseUseNewSDomainsMap(std::shared_ptr<SyncRes::domainmap_t> newmap)
{
SyncRes::setDomainMap(newmap);
return 0;
}
}
- 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));
+ for(const auto& i : oldAndNewDomains) {
+ 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) {
#endif
#include <atomic>
-#include <condition_variable>
#include <queue>
+#include <mutex>
#include <thread>
#include "iputils.hh"
#include "base64.hh"
#include "dnswriter.hh"
#include "dnsparser.hh"
-
+#include "query-local-address.hh"
#include "dns_random.hh"
#include <poll.h>
-#include "gss_context.hh"
#include "namespaces.hh"
+using pdns::resolver::parseResult;
+
int makeQuerySocket(const ComboAddress& local, bool udpOrTCP, bool nonLocalBind)
{
ComboAddress ourLocal(local);
locals["default4"] = -1;
locals["default6"] = -1;
try {
- if(!::arg()["query-local-address"].empty())
- locals["default4"] = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
- if(!::arg()["query-local-address6"].empty())
- locals["default6"] = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
+ if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+ locals["default4"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
+ }
+ if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+ locals["default6"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
+ }
}
catch(...) {
if(locals["default4"]>=0)
// choose socket based on local
if (local.sin4.sin_family == 0) {
// up to us.
- sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
- if (sock == -1) {
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
string ipv = remote.sin4.sin_family == AF_INET ? "4" : "6";
- string qla = remote.sin4.sin_family == AF_INET ? "" : "6";
- throw ResolverException("No IPv" + ipv + " socket available, is query-local-address" + qla + " unset?");
+ throw ResolverException("No IPv" + ipv + " socket available, is such an address configured in query-local-address?");
}
+ sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
} else {
std::string lstr = local.toString();
std::map<std::string, int>::iterator lptr;
return randomid;
}
-static int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
-{
- result->clear();
-
- if(mdp.d_header.rcode)
- return mdp.d_header.rcode;
-
- if(origQname.countLabels()) { // not AXFR
- if(mdp.d_header.id != id)
- throw ResolverException("Remote nameserver replied with wrong id");
- if(mdp.d_header.qdcount != 1)
- throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
- if(mdp.d_qname != origQname)
- throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
- }
+namespace pdns {
+ namespace resolver {
+ int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
+ {
+ result->clear();
+
+ if(mdp.d_header.rcode)
+ return mdp.d_header.rcode;
+
+ if(origQname.countLabels()) { // not AXFR
+ if(mdp.d_header.id != id)
+ throw ResolverException("Remote nameserver replied with wrong id");
+ if(mdp.d_header.qdcount != 1)
+ throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
+ if(mdp.d_qname != origQname)
+ throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
+ }
- vector<DNSResourceRecord> ret;
- DNSResourceRecord rr;
- result->reserve(mdp.d_answers.size());
+ vector<DNSResourceRecord> ret;
+ DNSResourceRecord rr;
+ result->reserve(mdp.d_answers.size());
- for (const auto& i: mdp.d_answers) {
- rr.qname = i.first.d_name;
- rr.qtype = i.first.d_type;
- rr.ttl = i.first.d_ttl;
- rr.content = i.first.d_content->getZoneRepresentation(true);
- result->push_back(rr);
- }
+ for (const auto& i: mdp.d_answers) {
+ rr.qname = i.first.d_name;
+ rr.qtype = i.first.d_type;
+ rr.ttl = i.first.d_ttl;
+ rr.content = i.first.d_content->getZoneRepresentation(true);
+ result->push_back(rr);
+ }
- return 0;
-}
+ return 0;
+ }
+
+ } // namespace resolver
+} // namespace pdns
bool Resolver::tryGetSOASerial(DNSName *domain, ComboAddress* remote, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
{
}
}
-AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
- const DNSName& domain,
- const TSIGTriplet& tt,
- const ComboAddress* laddr,
- size_t maxReceivedBytes,
- uint16_t timeout)
- : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
-{
- ComboAddress local;
- if (laddr != nullptr) {
- local = ComboAddress(*laddr);
- } else {
- string qlas = remote.sin4.sin_family == AF_INET ? "query-local-address" : "query-local-address6";
- if (::arg()[qlas].empty()) {
- throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". " + qlas + " is unset");
- }
- local=ComboAddress(::arg()[qlas]);
- }
- d_sock = -1;
- try {
- d_sock = makeQuerySocket(local, false); // make a TCP socket
- if (d_sock < 0)
- throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
- d_buf = shared_array<char>(new char[65536]);
- d_remote = remote; // mostly for error reporting
- this->connect(timeout);
- d_soacount = 0;
-
- vector<uint8_t> packet;
- DNSPacketWriter pw(packet, domain, QType::AXFR);
- pw.getHeader()->id = dns_random_uint16();
-
- if(!tt.name.empty()) {
- if (tt.algo == DNSName("hmac-md5"))
- d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
- else
- d_trc.d_algoName = tt.algo;
- d_trc.d_time = time(0);
- d_trc.d_fudge = 300;
- d_trc.d_origID=ntohs(pw.getHeader()->id);
- d_trc.d_eRcode=0;
- addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
- }
-
- uint16_t replen=htons(packet.size());
- Utility::iovec iov[2];
- iov[0].iov_base=reinterpret_cast<char*>(&replen);
- iov[0].iov_len=2;
- iov[1].iov_base=packet.data();
- iov[1].iov_len=packet.size();
-
- int ret=Utility::writev(d_sock, iov, 2);
- if(ret < 0)
- throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
- if(ret != (int)(2+packet.size())) {
- throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
- }
-
- int res = waitForData(d_sock, timeout, 0);
-
- if(!res)
- throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
- if(res<0)
- throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
- }
- catch(...) {
- if(d_sock >= 0)
- close(d_sock);
- d_sock = -1;
- throw;
- }
-}
-
-AXFRRetriever::~AXFRRetriever()
-{
- close(d_sock);
-}
-
-
-
-int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
-{
- if(d_soacount > 1)
- return false;
-
- // d_sock is connected and is about to spit out a packet
- int len=getLength(timeout);
- if(len<0)
- throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
-
- if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
- throw ResolverException("Reached the maximum number of received bytes during AXFR");
-
- timeoutReadn(len, timeout);
-
- d_receivedBytes += (uint16_t) len;
-
- MOADNSParser mdp(false, d_buf.get(), len);
-
- int err = mdp.d_header.rcode;
-
- if(err) {
- throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
- }
-
- try {
- d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
- }
- catch(const std::runtime_error& re) {
- throw ResolverException(re.what());
- }
-
- if(!records) {
- err = parseResult(mdp, DNSName(), 0, 0, &res);
-
- if (!err) {
- for(const auto& answer : mdp.d_answers)
- if (answer.first.d_type == QType::SOA)
- d_soacount++;
- }
- }
- else {
- records->clear();
- records->reserve(mdp.d_answers.size());
-
- for(auto& r: mdp.d_answers) {
- if (r.first.d_type == QType::SOA) {
- d_soacount++;
- }
-
- records->push_back(std::move(r.first));
- }
- }
-
- return true;
-}
-
-void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
-{
- time_t start=time(nullptr);
- int n=0;
- int numread;
- while(n<bytes) {
- int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
- if(res<0)
- throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
- if(!res)
- throw ResolverException("Timeout while reading data from remote nameserver over TCP");
-
- numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
- if(numread<0)
- throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
- if(numread==0)
- throw ResolverException("Remote nameserver closed TCP connection");
- n+=numread;
- }
-}
-
-void AXFRRetriever::connect(uint16_t timeout)
-{
- setNonBlocking( d_sock );
-
- int err;
-
- if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
- try {
- closesocket(d_sock);
- }
- catch(const PDNSException& e) {
- d_sock=-1;
- throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
- }
-
- throw ResolverException("connect: "+stringerror());
- }
-
- if(!err)
- goto done;
-
- err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
-
- if(!err) {
- try {
- closesocket(d_sock); // timeout
- }
- catch(const PDNSException& e) {
- d_sock=-1;
- throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
- }
-
- d_sock=-1;
- errno=ETIMEDOUT;
-
- throw ResolverException("Timeout connecting to server");
- }
- else if(err < 0) {
- throw ResolverException("Error connecting: "+stringerror());
- }
- else {
- Utility::socklen_t len=sizeof(err);
- if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
- throw ResolverException("Error connecting: "+stringerror()); // Solaris
-
- if(err)
- throw ResolverException("Error connecting: "+string(strerror(err)));
- }
-
- done:
- setBlocking( d_sock );
- // d_sock now connected
-}
-
-int AXFRRetriever::getLength(uint16_t timeout)
-{
- timeoutReadn(2, timeout);
- return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
-}
-
std::map<std::string, int> locals;
};
-class AXFRRetriever : public boost::noncopyable
-{
- public:
- AXFRRetriever(const ComboAddress& remote,
- const DNSName& zone,
- const TSIGTriplet& tt = TSIGTriplet(),
- const ComboAddress* laddr = NULL,
- size_t maxReceivedBytes=0,
- uint16_t timeout=10);
- ~AXFRRetriever();
- int getChunk(Resolver::res_t &res, vector<DNSRecord>* records=0, uint16_t timeout=10);
-
- private:
- void connect(uint16_t timeout);
- int getLength(uint16_t timeout);
- void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10);
-
- shared_array<char> d_buf;
- string d_domain;
- int d_sock;
- int d_soacount;
- ComboAddress d_remote;
- TSIGTCPVerifier d_tsigVerifier;
-
- size_t d_receivedBytes;
- size_t d_maxReceivedBytes;
- TSIGRecordContent d_trc;
-};
+namespace pdns {
+ namespace resolver {
+ int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result);
+ } // namespace resolver
+} // namespace pdns
#include "dns_random.hh"
#include "backends/gsql/ssql.hh"
#include "communicator.hh"
+#include "query-local-address.hh"
extern StatBag S;
extern CommunicatorClass Communicator;
-pthread_mutex_t PacketHandler::s_rfc2136lock=PTHREAD_MUTEX_INITIALIZER;
+std::mutex PacketHandler::s_rfc2136lock;
// Implement section 3.2.1 and 3.2.2 of RFC2136
int PacketHandler::checkUpdatePrerequisites(const DNSRecord *rr, DomainInfo *di) {
if (rrType == QType::NSEC3PARAM) {
g_log<<Logger::Notice<<msgPrefix<<"Adding/updating NSEC3PARAM for zone, resetting ordernames."<<endl;
- NSEC3PARAMRecordContent nsec3param(rr->d_content->getZoneRepresentation(), di->zone.toString() /* FIXME400 huh */);
+ *ns3pr = NSEC3PARAMRecordContent(rr->d_content->getZoneRepresentation(), di->zone.toString() /* FIXME400 huh */);
*narrow = false; // adding a NSEC3 will cause narrow mode to be dropped, as you cannot specify that in a NSEC3PARAM record
- d_dk.setNSEC3PARAM(di->zone, nsec3param, (*narrow));
-
- *haveNSEC3 = d_dk.getNSEC3PARAM(di->zone, ns3pr, narrow);
+ d_dk.setNSEC3PARAM(di->zone, *ns3pr, (*narrow));
+ *haveNSEC3 = true;
vector<DNSResourceRecord> rrs;
set<DNSName> qnames, nssets, dssets;
d_dk.unsetNSEC3PARAM(rr->d_name);
else if (rr->d_class == QClass::NONE) {
NSEC3PARAMRecordContent nsec3rr(rr->d_content->getZoneRepresentation(), di->zone.toString() /* FIXME400 huh */);
- if (ns3pr->getZoneRepresentation() == nsec3rr.getZoneRepresentation())
+ if (*haveNSEC3 && ns3pr->getZoneRepresentation() == nsec3rr.getZoneRepresentation())
d_dk.unsetNSEC3PARAM(rr->d_name);
else
return 0;
} else
return 0;
- // We retrieve new values, other RR's in this update package might need it as well.
- *haveNSEC3 = d_dk.getNSEC3PARAM(di->zone, ns3pr, narrow);
+ // Update NSEC3 variables, other RR's in this update package might need them as well.
+ *haveNSEC3 = false;
+ *narrow = false;
vector<DNSResourceRecord> rrs;
set<DNSName> qnames, nssets, dssets, ents;
for(const auto& remote : di.masters) {
g_log<<Logger::Notice<<msgPrefix<<"Forwarding packet to master "<<remote<<endl;
- ComboAddress local;
- if (remote.sin4.sin_family == AF_INET && !::arg()["query-local-address"].empty()) {
- local = ComboAddress(::arg()["query-local-address"]);
- } else if(remote.sin4.sin_family == AF_INET6 && !::arg()["query-local-address6"].empty()) {
- local = ComboAddress(::arg()["query-local-address6"]);
- } else {
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
continue;
}
+ auto local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
int sock = makeQuerySocket(local, false); // create TCP socket. RFC2136 section 6.2 seems to be ok with this.
if(sock < 0) {
g_log<<Logger::Error<<msgPrefix<<"Error creating socket: "<<stringerror()<<endl;
closesocket(sock);
}
catch(const PDNSException& e) {
- g_log<<Logger::Error<<"Error closing master forwarding socket after a timeout occured: "<<e.reason<<endl;
+ g_log<<Logger::Error<<"Error closing master forwarding socket after a timeout occurred: "<<e.reason<<endl;
}
continue;
}
closesocket(sock);
}
catch(const PDNSException& e) {
- g_log<<Logger::Error<<"Error closing master forwarding socket after an error occured: "<<e.reason<<endl;
+ g_log<<Logger::Error<<"Error closing master forwarding socket after an error occurred: "<<e.reason<<endl;
}
continue;
}
return RCode::Refused;
}
- if (p.d_tsig_algo == TSIG_GSS) {
- GssName inputname(p.d_peer_principal); // match against principal since GSS
- for(const auto& key: tsigKeys) {
- if (inputname.match(key)) {
- validKey = true;
- break;
- }
- }
- } else {
- for(const auto& key: tsigKeys) {
- if (inputkey == DNSName(key)) { // because checkForCorrectTSIG has already been performed earlier on, if the names of the ky match with the domain given. THis is valid.
- validKey=true;
- break;
- }
+ for(const auto& key: tsigKeys) {
+ if (inputkey == DNSName(key)) { // because checkForCorrectTSIG has already been performed earlier on, if the names of the ky match with the domain given. THis is valid.
+ validKey=true;
+ break;
}
}
}
- Lock l(&s_rfc2136lock); //TODO: i think this lock can be per zone, not for everything
+ std::lock_guard<std::mutex> l(s_rfc2136lock); //TODO: i think this lock can be per zone, not for everything
g_log<<Logger::Info<<msgPrefix<<"starting transaction."<<endl;
if (!di.backend->startTransaction(p.qdomain, -1)) { // Not giving the domain_id means that we do not delete the existing records.
g_log<<Logger::Error<<msgPrefix<<"Backend for domain "<<p.qdomain<<" does not support transaction. Can't do Update packet."<<endl;
if (rr->d_place == DNSResourceRecord::ANSWER) {
int res = checkUpdatePrerequisites(rr, &di);
if (res>0) {
- g_log<<Logger::Error<<msgPrefix<<"Failed PreRequisites check for "<<rr->d_name.toLogString()<<", returning "<<RCode::to_s(res)<<endl;
+ g_log<<Logger::Error<<msgPrefix<<"Failed PreRequisites check for "<<rr->d_name<<", returning "<<RCode::to_s(res)<<endl;
di.backend->abortTransaction();
return res;
}
bool narrow=false;
bool haveNSEC3 = d_dk.getNSEC3PARAM(di.zone, &ns3pr, &narrow);
bool isPresigned = d_dk.isPresigned(di.zone);
+ string soaEditSetting;
+ d_dk.getSoaEdit(di.zone, soaEditSetting);
// 3.4.2 - Perform the updates.
// There's a special condition where deleting the last NS record at zone apex is never deleted (3.4.2.4)
// Section 3.6 - Update the SOA serial - outside of performUpdate because we do a SOA update for the complete update message
if (changedRecords > 0 && !updatedSerial) {
- increaseSerial(msgPrefix, &di, haveNSEC3, narrow, &ns3pr);
+ increaseSerial(msgPrefix, &di, soaEditSetting, haveNSEC3, narrow, &ns3pr);
changedRecords++;
}
S.deposit("dnsupdate-changes", changedRecords);
+ d_dk.clearMetaCache(di.zone);
// Purge the records!
string zone(di.zone.toString());
zone.append("$");
}
}
-void PacketHandler::increaseSerial(const string &msgPrefix, const DomainInfo *di, bool haveNSEC3, bool narrow, const NSEC3PARAMRecordContent *ns3pr) {
+void PacketHandler::increaseSerial(const string &msgPrefix, const DomainInfo *di, const string& soaEditSetting, bool haveNSEC3, bool narrow, const NSEC3PARAMRecordContent *ns3pr) {
SOAData sd;
if (!di->backend->getSOA(di->zone, sd)) {
throw PDNSException("SOA-Serial update failed because there was no SOA. Wowie.");
if (!soaEdit2136Setting.empty()) {
soaEdit2136 = soaEdit2136Setting[0];
if (pdns_iequals(soaEdit2136, "SOA-EDIT") || pdns_iequals(soaEdit2136,"SOA-EDIT-INCREASE") ){
- string soaEditSetting;
- d_dk.getSoaEdit(di->zone, soaEditSetting);
if (soaEditSetting.empty()) {
- g_log<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-DNSUPDATE increase on DNS update, but SOA-EDIT is not set for domain \""<< di->zone.toLogString() <<"\". Using DEFAULT for SOA-EDIT-DNSUPDATE"<<endl;
+ g_log<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-DNSUPDATE increase on DNS update, but SOA-EDIT is not set for domain \""<< di->zone <<"\". Using DEFAULT for SOA-EDIT-DNSUPDATE"<<endl;
soaEdit2136 = "DEFAULT";
} else
soaEdit = soaEditSetting;
#include "dnsrecords.hh"
#include "ixfr.hh"
#include "syncres.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "logger.hh"
#include "rec-lua-conf.hh"
#include "rpzloader.hh"
#include "zoneparser-tng.hh"
#include "threadname.hh"
+#include "query-local-address.hh"
Netmask makeNetmaskFromRPZ(const DNSName& name)
{
if(dr.d_name.isPartOf(rpzNSDname)) {
DNSName filt=dr.d_name.makeRelative(rpzNSDname);
if(addOrRemove)
- zone->addNSTrigger(filt, std::move(pol));
+ zone->addNSTrigger(filt, std::move(pol), defpolApplied);
else
zone->rmNSTrigger(filt, std::move(pol));
} else if(dr.d_name.isPartOf(rpzClientIP)) {
DNSName filt=dr.d_name.makeRelative(rpzClientIP);
auto nm=makeNetmaskFromRPZ(filt);
if(addOrRemove)
- zone->addClientTrigger(nm, std::move(pol));
+ zone->addClientTrigger(nm, std::move(pol), defpolApplied);
else
zone->rmClientTrigger(nm, std::move(pol));
DNSName filt=dr.d_name.makeRelative(rpzIP);
auto nm=makeNetmaskFromRPZ(filt);
if(addOrRemove)
- zone->addResponseTrigger(nm, std::move(pol));
+ zone->addResponseTrigger(nm, std::move(pol), defpolApplied);
else
zone->rmResponseTrigger(nm, std::move(pol));
} else if(dr.d_name.isPartOf(rpzNSIP)) {
DNSName filt=dr.d_name.makeRelative(rpzNSIP);
auto nm=makeNetmaskFromRPZ(filt);
if(addOrRemove)
- zone->addNSIPTrigger(nm, std::move(pol));
+ zone->addNSIPTrigger(nm, std::move(pol), defpolApplied);
else
zone->rmNSIPTrigger(nm, std::move(pol));
} else {
ComboAddress local(localAddress);
if (local == ComboAddress())
- local = getQueryLocalAddress(master.sin4.sin_family, 0);
+ local = pdns::getQueryLocalAddress(master.sin4.sin_family, 0);
AXFRRetriever axfr(master, zoneName, tt, &local, maxReceivedBytes, axfrTimeout);
unsigned int nrecords=0;
std::string temp = dumpZoneFileName + "XXXXXX";
int fd = mkstemp(&temp.at(0));
if (fd < 0) {
- g_log<<Logger::Warning<<"Unable to open a file to dump the content of the RPZ zone "<<zoneName.toLogString()<<endl;
+ g_log<<Logger::Warning<<"Unable to open a file to dump the content of the RPZ zone "<<zoneName<<endl;
return false;
}
auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(fd, "w+"), fclose);
if (!fp) {
close(fd);
- g_log<<Logger::Warning<<"Unable to open a file pointer to dump the content of the RPZ zone "<<zoneName.toLogString()<<endl;
+ g_log<<Logger::Warning<<"Unable to open a file pointer to dump the content of the RPZ zone "<<zoneName<<endl;
return false;
}
fd = -1;
newZone->dump(fp.get());
}
catch(const std::exception& e) {
- g_log<<Logger::Warning<<"Error while dumping the content of the RPZ zone "<<zoneName.toLogString()<<": "<<e.what()<<endl;
+ g_log<<Logger::Warning<<"Error while dumping the content of the RPZ zone "<<zoneName<<": "<<e.what()<<endl;
return false;
}
if (fflush(fp.get()) != 0) {
- g_log<<Logger::Warning<<"Error while flushing the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+ g_log<<Logger::Warning<<"Error while flushing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
return false;
}
if (fsync(fileno(fp.get())) != 0) {
- g_log<<Logger::Warning<<"Error while syncing the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+ g_log<<Logger::Warning<<"Error while syncing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
return false;
}
if (fclose(fp.release()) != 0) {
- g_log<<Logger::Warning<<"Error while writing the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+ g_log<<Logger::Warning<<"Error while writing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
return false;
}
if (rename(temp.c_str(), dumpZoneFileName.c_str()) != 0) {
- g_log<<Logger::Warning<<"Error while moving the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+ g_log<<Logger::Warning<<"Error while moving the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
return false;
}
time_t refresh;
DNSName zoneName = oldZone->getDomain();
- std::string polName = oldZone->getName() ? *(oldZone->getName()) : zoneName.toString();
+ std::string polName = oldZone->getName().empty() ? oldZone->getName() : zoneName.toString();
while (!sr) {
/* if we received an empty sr, the zone was not really preloaded */
sr = loadRPZFromServer(master, zoneName, newZone, defpol, defpolOverrideLocal, maxTTL, tt, maxReceivedBytes, localAddress, axfrTimeout);
newZone->setSerial(sr->d_st.serial);
newZone->setRefresh(sr->d_st.refresh);
+ // This period gets used below this loop
+ oldZone->setRefresh(sr->d_st.refresh);
setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), true);
g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
ComboAddress local(localAddress);
if (local == ComboAddress()) {
- local = getQueryLocalAddress(master.sin4.sin_family, 0);
+ local = pdns::getQueryLocalAddress(master.sin4.sin_family, 0);
}
try {
continue;
}
- g_log<<Logger::Info<<"Processing "<<deltas.size()<<" delta"<<addS(deltas)<<" for RPZ "<<zoneName<<endl;
-
- oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
- /* we need to make a _full copy_ of the zone we are going to work on */
- std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
+ try {
+ g_log<<Logger::Info<<"Processing "<<deltas.size()<<" delta"<<addS(deltas)<<" for RPZ "<<zoneName<<endl;
+
+ oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
+ /* we need to make a _full copy_ of the zone we are going to work on */
+ std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
+ /* initialize the current serial to the last one */
+ std::shared_ptr<SOARecordContent> currentSR = sr;
+
+ int totremove=0, totadd=0;
+ bool fullUpdate = false;
+ for(const auto& delta : deltas) {
+ const auto& remove = delta.first;
+ const auto& add = delta.second;
+ if(remove.empty()) {
+ g_log<<Logger::Warning<<"IXFR update is a whole new zone"<<endl;
+ newZone->clear();
+ fullUpdate = true;
+ }
+ for(const auto& rr : remove) { // should always contain the SOA
+ if(rr.d_type == QType::NS)
+ continue;
+ if(rr.d_type == QType::SOA) {
+ auto oldsr = getRR<SOARecordContent>(rr);
+ if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) {
+ // cout<<"Got good removal of SOA serial "<<oldsr->d_st.serial<<endl;
+ }
+ else {
+ if (!oldsr) {
+ throw std::runtime_error("Unable to extract serial from SOA record while processing the removal part of an update");
+ }
+ else {
+ throw std::runtime_error("Received an unexpected serial (" + std::to_string(oldsr->d_st.serial) + ", expecting " + std::to_string(currentSR->d_st.serial) + ") from SOA record while processing the removal part of an update");
+ }
+ }
+ }
+ else {
+ totremove++;
+ g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
+ RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL);
+ }
+ }
- int totremove=0, totadd=0;
- bool fullUpdate = false;
- for(const auto& delta : deltas) {
- const auto& remove = delta.first;
- const auto& add = delta.second;
- if(remove.empty()) {
- g_log<<Logger::Warning<<"IXFR update is a whole new zone"<<endl;
- newZone->clear();
- fullUpdate = true;
+ for(const auto& rr : add) { // should always contain the new SOA
+ if(rr.d_type == QType::NS)
+ continue;
+ if(rr.d_type == QType::SOA) {
+ auto tempSR = getRR<SOARecordContent>(rr);
+ // g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<currentSR->d_st.serial<<endl;
+ if (tempSR) {
+ currentSR = tempSR;
+ }
+ }
+ else {
+ totadd++;
+ g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
+ RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL);
+ }
+ }
}
- for(const auto& rr : remove) { // should always contain the SOA
- if(rr.d_type == QType::NS)
- continue;
- if(rr.d_type == QType::SOA) {
- auto oldsr = getRR<SOARecordContent>(rr);
- if(oldsr && oldsr->d_st.serial == sr->d_st.serial) {
- // cout<<"Got good removal of SOA serial "<<oldsr->d_st.serial<<endl;
- }
- else
- g_log<<Logger::Error<<"GOT WRONG SOA SERIAL REMOVAL, SHOULD TRIGGER WHOLE RELOAD"<<endl;
- }
- else {
- totremove++;
- g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
- RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL);
- }
+
+ /* only update sr now that all changes have been converted */
+ if (currentSR) {
+ sr = currentSR;
}
+ g_log<<Logger::Info<<"Had "<<totremove<<" RPZ removal"<<addS(totremove)<<", "<<totadd<<" addition"<<addS(totadd)<<" for "<<zoneName<<" New serial: "<<sr->d_st.serial<<endl;
+ newZone->setSerial(sr->d_st.serial);
+ newZone->setRefresh(sr->d_st.refresh);
+ setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), fullUpdate);
+
+ /* we need to replace the existing zone with the new one,
+ but we don't want to touch anything else, especially other zones,
+ since they might have been updated by another RPZ IXFR tracker thread.
+ */
+ g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
+ lci.dfe.setZone(zoneIdx, newZone);
+ });
- for(const auto& rr : add) { // should always contain the new SOA
- if(rr.d_type == QType::NS)
- continue;
- if(rr.d_type == QType::SOA) {
- auto newsr = getRR<SOARecordContent>(rr);
- // g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<newsr->d_st.serial<<endl;
- if (newsr) {
- sr = newsr;
- }
- }
- else {
- totadd++;
- g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
- RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL);
- }
+ if (!dumpZoneFileName.empty()) {
+ dumpZoneToDisk(zoneName, newZone, dumpZoneFileName);
}
+ refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U);
}
- g_log<<Logger::Info<<"Had "<<totremove<<" RPZ removal"<<addS(totremove)<<", "<<totadd<<" addition"<<addS(totadd)<<" for "<<zoneName<<" New serial: "<<sr->d_st.serial<<endl;
- newZone->setSerial(sr->d_st.serial);
- newZone->setRefresh(sr->d_st.refresh);
- setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), fullUpdate);
-
- /* we need to replace the existing zone with the new one,
- but we don't want to touch anything else, especially other zones,
- since they might have been updated by another RPZ IXFR tracker thread.
- */
- g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
- lci.dfe.setZone(zoneIdx, newZone);
- });
-
- if (!dumpZoneFileName.empty()) {
- dumpZoneToDisk(zoneName, newZone, dumpZoneFileName);
+ catch (const std::exception& e) {
+ g_log << Logger::Error << "Error while applying the update received over XFR for "<<zoneName<<", skipping the update: "<< e.what() <<endl;
}
- refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U);
}
}
#include "dnssecinfra.hh"
#include "dns_random.hh"
-#include "gss_context.hh"
StatBag S;
try
{
if(argc < 4) {
- cerr<<"Syntax: saxfr IP-address port zone [showdetails] [showflags] [unhash] [gss:remote-principal] [tsig:keyname:algo:secret]"<<endl;
+ cerr<<"Syntax: saxfr IP-address port zone [showdetails] [showflags] [unhash] [tsig:keyname:algo:secret]"<<endl;
exit(EXIT_FAILURE);
}
bool showdetails=false;
bool showflags=false;
bool unhash=false;
- bool gss=false;
bool tsig=false;
TSIGHashEnum tsig_algo;
DNSName tsig_key;
showflags=true;
if (strcmp(argv[i], "unhash") == 0)
unhash=true;
- if (strncmp(argv[i], "gss:",4) == 0) {
- gss=true;
- tsig=true;
- tsig_algo=TSIG_GSS;
- remote_principal = string(argv[i]+4);
- if (remote_principal.empty()) {
- cerr<<"Remote principal is required"<<endl;
- exit(EXIT_FAILURE);
- }
- }
if (strncmp(argv[i], "tsig:",5) == 0) {
vector<string> parts;
tsig=true;
Socket sock(dest.sin4.sin_family, SOCK_STREAM);
sock.connect(dest);
- if (gss) {
-#ifndef ENABLE_GSS_TSIG
- cerr<<"No GSS support compiled in"<<endl;
- exit(EXIT_FAILURE);
-#else
- string input,output;
- GssContext gssctx;
- gssctx.generateLabel(argv[3]);
- gssctx.setPeerPrincipal(remote_principal);
-
- while(gssctx.init(input, output) && gssctx.valid() == false) {
- input="";
- DNSPacketWriter pwtkey(packet, gssctx.getLabel(), QType::TKEY, QClass::ANY);
- TKEYRecordContent tkrc;
- tkrc.d_algo = DNSName("gss-tsig.");
- tkrc.d_inception = time((time_t*)NULL);
- tkrc.d_expiration = tkrc.d_inception+15;
- tkrc.d_mode = 3;
- tkrc.d_error = 0;
- tkrc.d_keysize = output.size();
- tkrc.d_key = output;
- tkrc.d_othersize = 0;
- pwtkey.getHeader()->id = dns_random_uint16();
- pwtkey.startRecord(gssctx.getLabel(), QType::TKEY, 3600, QClass::ANY, DNSResourceRecord::ADDITIONAL, false);
- tkrc.toPacket(pwtkey);
- pwtkey.commit();
- for(const string& msg : gssctx.getErrorStrings()) {
- cerr<<msg<<endl;
- }
-
- len = htons(packet.size());
- if(sock.write((char *) &len, 2) != 2)
- throw PDNSException("tcp write failed");
- sock.writen(string((char*)&packet[0], packet.size()));
- if(sock.read((char *) &len, 2) != 2)
- throw PDNSException("tcp read failed");
-
- len=ntohs(len);
- std::unique_ptr<char[]> creply(new char[len]);
- int n=0;
- int numread;
- while(n<len) {
- numread=sock.read(creply.get()+n, len-n);
- if(numread<0)
- throw PDNSException("tcp read failed");
- n+=numread;
- }
-
- MOADNSParser mdp(false, string(creply.get(), len));
- if (mdp.d_header.rcode != 0) {
- throw PDNSException(string("Remote server refused: ") + std::to_string(mdp.d_header.rcode));
- }
- for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
- if(i->first.d_type != QType::TKEY) continue;
- // recover TKEY record
- tkrc = TKEYRecordContent(i->first.d_content->getZoneRepresentation());
- input = tkrc.d_key;
- }
- }
-
- if (gssctx.valid() == false) {
- cerr<<"Could not create GSS context"<<endl;
- exit(EXIT_FAILURE);
- }
-
- tsig_key = DNSName(gssctx.getLabel());
-#endif
- }
-
DNSPacketWriter pw(packet, DNSName(argv[3]), 252);
pw.getHeader()->id = dns_random_uint16();
#include "ednsoptions.hh"
#include "ednssubnet.hh"
#include "misc.hh"
+#include "proxy-protocol.hh"
#include "sstuff.hh"
#include "statbag.hh"
#include <boost/array.hpp>
StatBag S;
-bool hidettl = false;
+static bool hidettl = false;
-string ttl(uint32_t ttl)
+static string ttl(uint32_t ttl)
{
if (hidettl)
return "[ttl]";
return std::to_string(ttl);
}
-void usage()
+static void usage()
{
cerr << "sdig" << endl;
cerr << "Syntax: sdig IP-ADDRESS-OR-DOH-URL PORT QNAME QTYPE "
"[dnssec] [ednssubnet SUBNET/MASK] [hidesoadetails] [hidettl] "
- "[recurse] [showflags] [tcp] [xpf XPFDATA] [class CLASSNUM]"
+ "[recurse] [showflags] [tcp] [xpf XPFDATA] [class CLASSNUM] "
+ "[proxy UDP(0)/TCP(1) SOURCE-IP-ADDRESS-AND-PORT DESTINATION-IP-ADDRESS-AND-PORT]"
<< endl;
}
-const string nameForClass(uint16_t qclass, uint16_t qtype)
+static const string nameForClass(uint16_t qclass, uint16_t qtype)
{
if (qtype == QType::OPT)
return "IN";
}
}
-void fillPacket(vector<uint8_t>& packet, const string& q, const string& t,
- bool dnssec, const boost::optional<Netmask> ednsnm,
- bool recurse, uint16_t xpfcode, uint16_t xpfversion,
- uint64_t xpfproto, char* xpfsrc, char* xpfdst,
- uint16_t qclass)
+static std::unordered_set<uint16_t> s_expectedIDs;
+
+static void fillPacket(vector<uint8_t>& packet, const string& q, const string& t,
+ bool dnssec, const boost::optional<Netmask> ednsnm,
+ bool recurse, uint16_t xpfcode, uint16_t xpfversion,
+ uint64_t xpfproto, char* xpfsrc, char* xpfdst,
+ uint16_t qclass, uint16_t qid)
{
DNSPacketWriter pw(packet, DNSName(q), DNSRecordContent::TypeToNumber(t), qclass);
if (recurse) {
pw.getHeader()->rd = true;
}
+
+ pw.getHeader()->id = htons(qid);
}
-void printReply(const string& reply, bool showflags, bool hidesoadetails)
+static void printReply(const string& reply, bool showflags, bool hidesoadetails)
{
MOADNSParser mdp(false, reply);
+ if (!s_expectedIDs.count(ntohs(mdp.d_header.id))) {
+ cout << "ID " << ntohs(mdp.d_header.id) << " was not expected, this response was not meant for us!"<<endl;
+ }
+ s_expectedIDs.erase(ntohs(mdp.d_header.id));
+
cout << "Reply to question for qname='" << mdp.d_qname.toString()
<< "', qtype=" << DNSRecordContent::NumberToType(mdp.d_qtype) << endl;
cout << "Rcode: " << mdp.d_header.rcode << " ("
uint16_t xpfcode = 0, xpfversion = 0, xpfproto = 0;
char *xpfsrc = NULL, *xpfdst = NULL;
uint16_t qclass = QClass::IN;
+ string proxyheader;
for (int i = 1; i < argc; i++) {
if ((string)argv[i] == "--help") {
}
qclass = atoi(argv[++i]);
}
+ if (strcmp(argv[i], "proxy") == 0) {
+ if(argc < i+4) {
+ cerr<<"proxy needs three arguments"<<endl;
+ exit(EXIT_FAILURE);
+ }
+ bool ptcp = atoi(argv[++i]);
+ ComboAddress src(argv[++i]);
+ ComboAddress dest(argv[++i]);
+ proxyheader = makeProxyHeader(ptcp, src, dest, {});
+ }
}
}
if (doh) {
#ifdef HAVE_LIBCURL
vector<uint8_t> packet;
+ s_expectedIDs.insert(0);
fillPacket(packet, name, type, dnssec, ednsnm, recurse, xpfcode, xpfversion,
- xpfproto, xpfsrc, xpfdst, qclass);
+ xpfproto, xpfsrc, xpfdst, qclass, 0);
MiniCurl mc;
MiniCurl::MiniCurlHeaders mch;
mch.insert(std::make_pair("Content-Type", "application/dns-message"));
mch.insert(std::make_pair("Accept", "application/dns-message"));
string question(packet.begin(), packet.end());
+ // FIXME: how do we use proxyheader here?
reply = mc.postURL(argv[1], question, mch);
printReply(reply, showflags, hidesoadetails);
#else
} else if (fromstdin) {
std::istreambuf_iterator<char> begin(std::cin), end;
reply = string(begin, end);
+
+ ComboAddress source, destination;
+ bool wastcp;
+ bool proxy = false;
+ std::vector<ProxyProtocolValue> ignoredValues;
+ ssize_t offset = parseProxyHeader(reply, proxy, source, destination, wastcp, ignoredValues);
+ if (offset && proxy) {
+ cout<<"proxy "<<(wastcp ? "tcp" : "udp")<<" headersize="<<offset<<" source="<<source.toStringWithPort()<<" destination="<<destination.toStringWithPort()<<endl;
+ reply = reply.substr(offset);
+ }
+
+ if (tcp) {
+ reply = reply.substr(2);
+ }
+
printReply(reply, showflags, hidesoadetails);
} else if (tcp) {
+ uint16_t counter = 0;
Socket sock(dest.sin4.sin_family, SOCK_STREAM);
sock.connect(dest);
+ sock.writen(proxyheader);
for (const auto& it : questions) {
vector<uint8_t> packet;
+ s_expectedIDs.insert(counter);
fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, xpfcode,
- xpfversion, xpfproto, xpfsrc, xpfdst, qclass);
+ xpfversion, xpfproto, xpfsrc, xpfdst, qclass, counter);
+ counter++;
uint16_t len = htons(packet.size());
if (sock.write((const char *)&len, 2) != 2)
} else // udp
{
vector<uint8_t> packet;
+ s_expectedIDs.insert(0);
fillPacket(packet, name, type, dnssec, ednsnm, recurse, xpfcode, xpfversion,
- xpfproto, xpfsrc, xpfdst, qclass);
+ xpfproto, xpfsrc, xpfdst, qclass, 0);
string question(packet.begin(), packet.end());
Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
+ question = proxyheader + question;
sock.sendTo(question, dest);
int result = waitForData(sock.getHandle(), 10);
if (result < 0)
processSecPoll(res, ret, security_status, security_message);
} catch(const PDNSException &pe) {
S.set("security-status", security_status);
- g_log<<Logger::Warning<<"Could not retrieve security status update for '" + pkgv + "' on '"+ query + "': "<<pe.reason<<endl;
+ g_log<<Logger::Warning<<"Failed to retrieve security status update for '" + pkgv + "' on '"+ query + "': "<<pe.reason<<endl;
return;
}
boost::replace_all(qstring, "+", "_");
boost::replace_all(qstring, "~", "_");
- vState state = Indeterminate;
+ vState state = vState::Indeterminate;
DNSName query(qstring);
int res = sr.beginResolve(query, QType(QType::TXT), 1, ret);
state = sr.getValidationState();
}
- if(state == Bogus) {
- g_log<<Logger::Error<<"Could not retrieve security status update for '" +pkgv+ "' on '"<<query<<"', DNSSEC validation result was Bogus!"<<endl;
+ if(state == vState::Bogus) {
+ g_log<<Logger::Error<<"Failed to retrieve security status update for '" +pkgv+ "' on '"<<query<<"', DNSSEC validation result was Bogus!"<<endl;
if(g_security_status == 1) // If we were OK, go to unknown
g_security_status = 0;
return;
processSecPoll(res, ret, security_status, security_message);
} catch(const PDNSException &pe) {
g_security_status = security_status;
- g_log<<Logger::Warning<<"Could not retrieve security status update for '" << pkgv << "' on '"<< query << "': "<<pe.reason<<endl;
+ g_log<<Logger::Warning<<"Failed to retrieve security status update for '" << pkgv << "' on '"<< query << "': "<<pe.reason<<endl;
return;
}
#include "dnsrecords.hh"
#include "pdnsexception.hh"
#include "misc.hh"
+#include "secpoll.hh"
bool isReleaseVersion(const std::string &version) {
return std::count(version.begin(), version.end(), '.') == 2;
}
-void setSecPollToUnknownOnOK(int &secPollStatus) {
+static void setSecPollToUnknownOnOK(int &secPollStatus) {
if(secPollStatus == 1) // it was ok, now it is unknown
secPollStatus = 0;
}
secPollMessage.clear();
if (res != 0) { // not NOERROR
setSecPollToUnknownOnOK(secPollStatus);
- throw PDNSException("RCODE was not NOERROR but " + RCode::to_s(res));
+ throw PDNSException("RCODE was " + RCode::to_s(res));
}
if (ret.empty()) { // empty NOERROR... wat?
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string>
+
+#include "shuffle.hh"
+#include "dns_random.hh"
+#include "dnsparser.hh"
+
+// shuffle, maintaining some semblance of order
+void pdns::shuffle(std::vector<DNSZoneRecord>& rrs)
+{
+ std::vector<DNSZoneRecord>::iterator first, second;
+
+ // We assume the CNAMES are listed first in the ANSWER section and the the other records
+ // and we want to shuffle the other records only
+
+ // First we scan for the first non-CNAME ANSWER record
+ for (first = rrs.begin(); first != rrs.end(); ++first) {
+ if (first->dr.d_place == DNSResourceRecord::ANSWER && first->dr.d_type != QType::CNAME) {
+ break;
+ }
+ }
+ // And then for one past the last ANSWER record
+ for (second = first; second != rrs.end(); ++second)
+ if (second->dr.d_place != DNSResourceRecord::ANSWER)
+ break;
+
+ // Now shuffle the non-CNAME ANSWER records
+ dns_random_engine r;
+ if (second - first > 1) {
+ shuffle(first, second, r);
+ }
+
+ // now shuffle the ADDITIONAL records in the same manner as the ANSWER records
+ for (first = second; first != rrs.end(); ++first) {
+ if (first->dr.d_place == DNSResourceRecord::ADDITIONAL && first->dr.d_type != QType::CNAME) {
+ break;
+ }
+ }
+ for (second = first; second != rrs.end(); ++second) {
+ if (second->dr.d_place != DNSResourceRecord::ADDITIONAL) {
+ break;
+ }
+ }
+
+ if (second - first > 1) {
+ shuffle(first, second, r);
+ }
+ // we don't shuffle the rest
+}
+
+// shuffle, maintaining some semblance of order
+static void shuffle(std::vector<DNSRecord>& rrs)
+{
+ // This shuffles in the same style as the above method, keeping CNAME in the front and RRSIGs at the end
+ std::vector<DNSRecord>::iterator first, second;
+ for (first = rrs.begin(); first != rrs.end(); ++first) {
+ if (first->d_place == DNSResourceRecord::ANSWER && first->d_type != QType::CNAME) {
+ break;
+ }
+ }
+ for (second = first; second != rrs.end(); ++second) {
+ if (second->d_place != DNSResourceRecord::ANSWER || second->d_type == QType::RRSIG) {
+ break;
+ }
+ }
+
+ pdns::dns_random_engine r;
+ if (second - first > 1) {
+ shuffle(first, second, r);
+ }
+
+ // now shuffle the additional records
+ for (first = second; first != rrs.end(); ++first) {
+ if (first->d_place == DNSResourceRecord::ADDITIONAL && first->d_type != QType::CNAME) {
+ break;
+ }
+ }
+ for (second = first; second != rrs.end(); ++second) {
+ if (second->d_place != DNSResourceRecord::ADDITIONAL) {
+ break;
+ }
+ }
+
+ if (second - first > 1) {
+ shuffle(first, second, r);
+ }
+ // we don't shuffle the rest
+}
+
+static uint16_t mapTypesToOrder(uint16_t type)
+{
+ if (type == QType::CNAME)
+ return 0;
+ if (type == QType::RRSIG)
+ return 65535;
+ else
+ return 1;
+}
+
+// make sure rrs is sorted in d_place order to avoid surprises later
+// then shuffle the parts that desire shuffling
+void pdns::orderAndShuffle(vector<DNSRecord>& rrs)
+{
+ std::stable_sort(rrs.begin(), rrs.end(), [](const DNSRecord& a, const DNSRecord& b) {
+ return std::make_tuple(a.d_place, mapTypesToOrder(a.d_type)) < std::make_tuple(b.d_place, mapTypesToOrder(b.d_type));
+ });
+ shuffle(rrs);
+}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+#include <vector>
+
+struct DNSRecord;
+struct DNSZoneRecord;
+
+namespace pdns
+{
+void shuffle(std::vector<DNSZoneRecord>& rrs);
+void orderAndShuffle(std::vector<DNSRecord>& rrs);
+}
#endif
#include "signingpipe.hh"
#include "misc.hh"
+#include "dns_random.hh"
#include <poll.h>
#include <sys/socket.h>
rwVect = waitForRW(wantRead, wantWrite, -1); // wait for something to happen
if(wantWrite && !rwVect.second.empty()) {
- random_shuffle(rwVect.second.begin(), rwVect.second.end()); // pick random available worker
+ shuffle(rwVect.second.begin(), rwVect.second.end(), pdns::dns_random_engine()); // pick random available worker
auto ptr = d_rrsetToSign.release();
writen2(*rwVect.second.begin(), &ptr, sizeof(ptr));
d_rrsetToSign = make_unique<rrset_t>();
if(wantWrite) { // our optimization above failed, we now wait synchronously
rwVect = waitForRW(false, wantWrite, -1); // wait for something to happen
- random_shuffle(rwVect.second.begin(), rwVect.second.end()); // pick random available worker
+ shuffle(rwVect.second.begin(), rwVect.second.end(), pdns::dns_random_engine()); // pick random available worker
auto ptr = d_rrsetToSign.release();
writen2(*rwVect.second.begin(), &ptr, sizeof(ptr));
d_rrsetToSign = make_unique<rrset_t>();
#include "dnsbackend.hh"
#include "ueberbackend.hh"
#include "packethandler.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "logger.hh"
#include "dns.hh"
#include "arguments.hh"
#include "inflighter.cc"
#include "namespaces.hh"
#include "common_startup.hh"
+#include "query-local-address.hh"
#include "ixfr.hh"
-void CommunicatorClass::addSuckRequest(const DNSName &domain, const ComboAddress& master)
+void CommunicatorClass::addSuckRequest(const DNSName &domain, const ComboAddress& master, bool force)
{
- Lock l(&d_lock);
+ 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);
grouped[{x.d_name, x.d_type}].second.push_back(x);
di.backend->startTransaction(domain, -1);
- for(const auto g : grouped) {
+ for(const auto& g : grouped) {
vector<DNSRecord> rrset;
{
DNSZoneRecord zrr;
- B.lookup(QType(g.first.second), g.first.first+domain, di.id);
- while(B.get(zrr)) {
+ di.backend->lookup(QType(g.first.second), g.first.first+domain, di.id);
+ while(di.backend->get(zrr)) {
zrr.dr.d_name.makeUsRelative(domain);
rrset.push_back(zrr.dr);
}
}
-void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote)
+void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote, bool force)
{
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
if(d_inprogress.count(domain)) {
return;
}
DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper
bool wrongDomainKind = false;
// this checks three error conditions & sets wrongDomainKind if we hit the third
- if(!B.getDomainInfo(domain, di) || !di.backend || (wrongDomainKind = true, di.kind != DomainInfo::Slave)) { // di.backend and B are mostly identical
+ if(!B.getDomainInfo(domain, di) || !di.backend || (wrongDomainKind = true, !force && di.kind != DomainInfo::Slave)) { // di.backend and B are mostly identical
if(wrongDomainKind)
g_log<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"', not configured as slave"<<endl;
else
return;
}
} else {
- if(remote.sin4.sin_family == AF_INET && !::arg()["query-local-address"].empty()) {
- laddr = ComboAddress(::arg()["query-local-address"]);
- } else if(remote.sin4.sin_family == AF_INET6 && !::arg()["query-local-address6"].empty()) {
- laddr = ComboAddress(::arg()["query-local-address6"]);
- } else {
- bool isv6 = remote.sin4.sin_family == AF_INET6;
- g_log<<Logger::Error<<"Unable to AXFR, destination address is IPv" << (isv6 ? "6" : "4") << ", but query-local-address"<< (isv6 ? "6" : "") << " is unset!"<<endl;
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
+ bool isV6 = remote.sin4.sin_family == AF_INET6;
+ g_log<<Logger::Error<<"Unable to AXFR, destination address is "<<remote<<" (IPv"<< (isV6 ? "6" : "4") <<
+ ", but that address family is not enabled for outgoing traffic (query-local-address)"<<endl;
return;
}
+ laddr = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
}
bool hadDnssecZone = false;
}
catch(ResolverException &re) {
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
// The AXFR probably failed due to a problem on the master server. If SOA-checks against this master
// still succeed, we would constantly try to AXFR the zone. To avoid this, we add the zone to the list of
// failed slave-checks. This will suspend slave-checks (and subsequent AXFR) for this zone for some time.
Identifier send(DomainNotificationInfo& dni)
{
- random_shuffle(dni.di.masters.begin(), dni.di.masters.end());
+ shuffle(dni.di.masters.begin(), dni.di.masters.end(), pdns::dns_random_engine());
try {
return std::make_tuple(dni.di.zone,
*dni.di.masters.begin(),
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;
}
void CommunicatorClass::addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote)
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
DomainInfo ours = di;
ours.backend = 0;
void CommunicatorClass::addTrySuperMasterRequest(const DNSPacket& p)
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
DNSPacket ours = p;
if(d_potentialsupermasters.insert(ours).second)
d_any_sem.post(); // kick the loop!
vector<DomainNotificationInfo> sdomains;
set<DNSPacket, cmp> trysuperdomains;
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
set<DomainInfo> requeue;
rdomains.reserve(d_tocheck.size());
for(const auto& di: d_tocheck) {
TSIGRecordContent trc;
DNSName tsigkeyname;
dp.getTSIGDetails(&trc, &tsigkeyname);
- P->trySuperMasterSynchronous(dp, tsigkeyname); // FIXME could use some error loging
+ P->trySuperMasterSynchronous(dp, tsigkeyname); // FIXME could use some error logging
}
if(rdomains.empty()) { // if we have priority domains, check them first
B->getUnfreshSlaveInfos(&rdomains);
sdomains.reserve(rdomains.size());
DNSSECKeeper dk(B); // NOW HEAR THIS! This DK uses our B backend, so no interleaved access!
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
time_t now = time(0);
if(sdomains.empty())
{
if(d_slaveschanged) {
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
g_log<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already, "<<d_inprogress.size()<<" in progress"<<endl;
}
d_slaveschanged = !rdomains.empty();
return;
}
else {
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
g_log<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
(sdomains.size()>1 ? "" : "s")<<
" checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl;
}
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;
if(!ssr.d_freshness.count(di.id)) { // If we don't have an answer for the domain
uint64_t newCount = 1;
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
const auto failedEntry = d_failedSlaveRefresh.find(di.zone);
if (failedEntry != d_failedSlaveRefresh.end())
newCount = d_failedSlaveRefresh[di.zone].first + 1;
}
{
- Lock l(&d_lock);
+ std::lock_guard<std::mutex> l(d_lock);
const auto wasFailedDomain = d_failedSlaveRefresh.find(di.zone);
if (wasFailedDomain != d_failedSlaveRefresh.end())
d_failedSlaveRefresh.erase(di.zone);
}
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);
}
}
}
virtual ~SNMPAgent()
{
#ifdef HAVE_NET_SNMP
+
close(d_trapPipe[0]);
close(d_trapPipe[1]);
#endif /* HAVE_NET_SNMP */
{
#ifdef HAVE_NET_SNMP
d_thread = std::thread(&SNMPAgent::worker, this);
+ d_thread.detach();
#endif /* HAVE_NET_SNMP */
}
#include <inttypes.h>
namespace anonpdns {
-char B64Decode1(char cInChar)
+static char B64Decode1(char cInChar)
{
// The incoming character will be A-Z, a-z, 0-9, +, /, or =.
// The idea is to quickly determine which grouping the
return iIndex;
}
-inline char B64Encode1(unsigned char uc)
+static inline char B64Encode1(unsigned char uc)
{
if (uc < 26)
{
}
};
-pthread_mutex_t s_testlock=PTHREAD_MUTEX_INITIALIZER;
+std::mutex s_testlock;
struct GetLockUncontendedTest
{
void operator()() const
{
- pthread_mutex_lock(&s_testlock);
- pthread_mutex_unlock(&s_testlock);
+ s_testlock.lock();
+ s_testlock.unlock();
}
};
*
* copied from sqlite 3.3.6 // cmouse
*/
-int pdns_sqlite3_clear_bindings(sqlite3_stmt *pStmt){
+#if SQLITE_VERSION_NUMBER < 3003009
+static int pdns_sqlite3_clear_bindings(sqlite3_stmt *pStmt){
int i;
int rc = SQLITE_OK;
for(i=1; rc==SQLITE_OK && i<=sqlite3_bind_parameter_count(pStmt); i++){
}
return rc;
}
+#endif
static string SSQLite3ErrorString(sqlite3 *db)
{
void SSQLite3::execute(const string& query) {
char *errmsg;
- int rc;
- if (sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg) == SQLITE_BUSY) {
+ std::string errstr1;
+ int rc = sqlite3_exec(m_pDB, query.c_str(), nullptr, nullptr, &errmsg);
+ if (rc != SQLITE_OK) {
+ errstr1 = errmsg;
+ sqlite3_free(errmsg);
+ }
+ if (rc == SQLITE_BUSY) {
if (m_in_transaction) {
- std::string errstr(errmsg);
- sqlite3_free(errmsg);
- throw("Failed to execute query: " + errstr);
+ throw SSqlException("Failed to execute query: " + errstr1);
} else {
- if ((rc = sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) && rc != SQLITE_DONE && rc != SQLITE_ROW) {
- std::string errstr(errmsg);
+ rc = sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg);
+ std::string errstr2;
+ if (rc != SQLITE_OK) {
+ errstr2 = errmsg;
sqlite3_free(errmsg);
- throw("Failed to execute query: " + errstr);
+ throw SSqlException("Failed to execute query: " + errstr2);
}
}
+ } else if (rc != SQLITE_OK) {
+ throw SSqlException("Failed to execute query: " + errstr1);
}
}
StatBag::StatBag()
{
d_doRings=false;
+ d_allowRedeclare=false;
}
void StatBag::exists(const string &key)
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)
+{
+ if(d_stats.count(key)) {
+ if (d_allowRedeclare) {
+ *d_stats[key] = 0;
+ return;
+ }
+ else {
+ throw PDNSException("Attempt to re-declare statbag '"+key+"'");
+ }
+ }
+
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)
{
+ if(d_funcstats.count(key) && !d_allowRedeclare) {
+ throw PDNSException("Attempt to re-declare func statbag '"+key+"'");
+ }
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)
void StatBag::declareComboRing(const string &name, const string &help, unsigned int size)
{
- d_comborings.emplace(name, size);
- d_comborings[name].setHelp(help);
+ d_comboRings.emplace(name, size);
+ d_comboRings[name].setHelp(help);
registerRingStats(name);
}
}
vector<pair<string, unsigned int> > ret;
- if (d_comborings.count(name)) {
+ if (d_comboRings.count(name)) {
typedef pair<SComboAddress, unsigned int> stor_t;
- vector<stor_t> raw =d_comborings[name].get();
+ vector<stor_t> raw =d_comboRings[name].get();
for(const stor_t& stor : raw) {
ret.push_back(make_pair(stor.first.ca.toString(), stor.second));
}
{
if(d_rings.count(name))
d_rings[name].reset();
- if(d_comborings.count(name))
- d_comborings[name].reset();
+ if(d_comboRings.count(name))
+ d_comboRings[name].reset();
if(d_dnsnameqtyperings.count(name))
d_dnsnameqtyperings[name].reset();
}
{
if(d_rings.count(name))
d_rings[name].resize(newsize);
- if(d_comborings.count(name))
- d_comborings[name].resize(newsize);
+ if(d_comboRings.count(name))
+ d_comboRings[name].resize(newsize);
if(d_dnsnameqtyperings.count(name))
return d_dnsnameqtyperings[name].resize(newsize);
}
{
if(d_rings.count(name))
return d_rings[name].getSize();
- if(d_comborings.count(name))
- return d_comborings[name].getSize();
+ if(d_comboRings.count(name))
+ return d_comboRings[name].getSize();
if(d_dnsnameqtyperings.count(name))
return d_dnsnameqtyperings[name].getSize();
return 0;
{
if(d_rings.count(name))
return d_rings[name].getEntriesCount();
- if(d_comborings.count(name))
- return d_comborings[name].getEntriesCount();
+ if(d_comboRings.count(name))
+ return d_comboRings[name].getEntriesCount();
if(d_dnsnameqtyperings.count(name))
return d_dnsnameqtyperings[name].getEntriesCount();
return 0;
{
if(d_rings.count(name))
return d_rings[name].getHelp();
- if(d_comborings.count(name))
- return d_comborings[name].getHelp();
+ if(d_comboRings.count(name))
+ return d_comboRings[name].getHelp();
if(d_dnsnameqtyperings.count(name))
return d_dnsnameqtyperings[name].getHelp();
return "";
vector<string> ret;
for(auto i=d_rings.begin();i!=d_rings.end();++i)
ret.push_back(i->first);
- for(auto i=d_comborings.begin();i!=d_comborings.end();++i)
+ for(auto i=d_comboRings.begin();i!=d_comboRings.end();++i)
ret.push_back(i->first);
for(const auto &i : d_dnsnameqtyperings)
ret.push_back(i.first);
bool StatBag::ringExists(const string &name)
{
- return d_rings.count(name) || d_comborings.count(name) || d_dnsnameqtyperings.count(name);
+ return d_rings.count(name) || d_comboRings.count(name) || d_dnsnameqtyperings.count(name);
}
void StatBag::blacklist(const string& str) {
{
public:
StatRing(unsigned int size=10000);
- // Some older C++ libs have trouble emplacing without a copy-contructor, so provide one
+ // Some older C++ libs have trouble emplacing without a copy-constructor, so provide one
StatRing(const StatRing &);
StatRing & operator=(const StatRing &) = delete;
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<SComboAddress> >d_comboRings;
map<string,StatRing<std::tuple<DNSName, QType> > >d_dnsnameqtyperings;
typedef boost::function<uint64_t(const std::string&)> func_t;
typedef map<string, func_t> funcstats_t;
funcstats_t d_funcstats;
bool d_doRings;
+
std::set<string> d_blacklist;
void registerRingStats(const string& name);
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);
void ringAccount(const char* name, const ComboAddress &item)
{
if(d_doRings) {
- if(!d_comborings.count(name))
- throw runtime_error("Attempting to account to non-existent comboring '"+std::string(name)+"'");
- d_comborings[name].account(item);
+ if(!d_comboRings.count(name))
+ throw runtime_error("Attempting to account to non-existent comboRing '"+std::string(name)+"'");
+ d_comboRings[name].account(item);
}
}
void ringAccount(const char* name, const DNSName &dnsname, const QType &qtype)
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
string getValueStr(const string &key); //!< read a value behind a key, and return it as a string
string getValueStrZero(const string &key); //!< read a value behind a key, and return it as a string, and zero afterwards
void blacklist(const string &str);
+
+ bool d_allowRedeclare; // only set this true during tests, never in production code
};
inline void StatBag::deposit(const string &key, int value)
return theArg;
}
-void usage() {
+static void usage() {
cerr<<"stubquery"<<endl;
cerr<<"Syntax: stubquery QUESTION [QUESTION-TYPE]"<<endl;
}
// s_resolversForStub contains the ComboAddresses that are used by
// stubDoResolve
static vector<ComboAddress> s_resolversForStub;
-static pthread_rwlock_t s_resolversForStubLock = PTHREAD_RWLOCK_INITIALIZER;
+static ReadWriteLock s_resolversForStubLock;
static bool s_stubResolvConfigured = false;
// /etc/resolv.conf last modification time
unsigned int SyncRes::s_maxbogusttl;
unsigned int SyncRes::s_maxcachettl;
unsigned int SyncRes::s_maxqperq;
+unsigned int SyncRes::s_maxnsaddressqperq;
unsigned int SyncRes::s_maxtotusec;
unsigned int SyncRes::s_maxdepth;
unsigned int SyncRes::s_minimumTTL;
uint8_t SyncRes::s_ecsipv4cachelimit;
uint8_t SyncRes::s_ecsipv6cachelimit;
+bool SyncRes::s_doIPv4;
bool SyncRes::s_doIPv6;
bool SyncRes::s_nopacketcache;
bool SyncRes::s_rootNXTrust;
}
/** everything begins here - this is the entry point just after receiving a packet */
-int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret)
+int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret, unsigned int depth)
{
- vState state = Indeterminate;
+ vState state = vState::Indeterminate;
s_queries++;
d_wasVariable=false;
d_wasOutOfBand=false;
if (doSpecialNamesResolve(qname, qtype, qclass, ret)) {
- d_queryValidationState = Insecure; // this could fool our stats into thinking a validation took place
+ d_queryValidationState = vState::Insecure; // this could fool our stats into thinking a validation took place
return 0; // so do check before updating counters (we do now)
}
auto qtypeCode = qtype.getCode();
/* rfc6895 section 3.1 */
- if ((qtypeCode >= 128 && qtypeCode <= 254) || qtypeCode == QType::RRSIG || qtypeCode == QType::NSEC3 || qtypeCode == QType::OPT || qtypeCode == 65535) {
+ if (qtypeCode == 0 || (qtypeCode >= 128 && qtypeCode <= 254) || qtypeCode == QType::RRSIG || qtypeCode == QType::NSEC3 || qtypeCode == QType::OPT || qtypeCode == 65535) {
return -1;
}
return -1;
set<GetBestNSAnswer> beenthere;
- int res=doResolve(qname, qtype, ret, 0, beenthere, state);
+ int res=doResolve(qname, qtype, ret, depth, beenthere, state);
d_queryValidationState = state;
if (shouldValidate()) {
- if (d_queryValidationState != Indeterminate) {
+ if (d_queryValidationState != vState::Indeterminate) {
g_stats.dnssecValidations++;
}
increaseDNSSECStateCounter(d_queryValidationState);
return doOOBResolve(iter->second, qname, qtype, ret, res);
}
-bool SyncRes::isForwardOrAuth(const DNSName &qname) const {
+bool SyncRes::isRecursiveForwardOrAuth(const DNSName &qname) const {
DNSName authname(qname);
domainmap_t::const_iterator iter = getBestAuthZone(&authname);
- return iter != t_sstorage.domainmap->end();
+ return iter != t_sstorage.domainmap->end() && (iter->second.isAuth() || iter->second.shouldRecurse());
}
uint64_t SyncRes::doEDNSDump(int fd)
uint64_t count=0;
const auto& throttleMap = t_sstorage.throttle.getThrottleMap();
- for(const auto i : throttleMap)
+ for(const auto& i : throttleMap)
{
count++;
char tmp[26];
int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state) {
- if (!getQNameMinimization() || isForwardOrAuth(qname)) {
+ string prefix = d_prefix;
+ prefix.append(depth, ' ');
+ auto luaconfsLocal = g_luaconfs.getLocal();
+
+ /* Apply qname (including CNAME chain) filtering policies */
+ if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
+ if (luaconfsLocal->dfe.getQueryPolicy(qname, d_discardedPolicies, d_appliedPolicy)) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ bool done = false;
+ int rcode = RCode::NoError;
+ handlePolicyHit(prefix, qname, qtype, ret, done, rcode, depth);
+ if (done) {
+ return rcode;
+ }
+ }
+ }
+
+ // In the auth or recursive forward case, it does not make sense to do qname-minimization
+ if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) {
return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, state);
}
// moves to three labels per iteration after three iterations.
DNSName child;
- string prefix = d_prefix;
- prefix.append(depth, ' ');
prefix.append(string("QM ") + qname.toString() + "|" + qtype.getName());
QLOG("doResolve");
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");
+ if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && (d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NXDOMAIN || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NODATA)) {
+ ret.clear();
+ }
ret.insert(ret.end(), retq.begin(), retq.end());
+
return res;
}
QLOG("Step0 Not cached");
const unsigned int qnamelen = qname.countLabels();
+ DNSName fwdomain(qname);
+ const bool forwarded = getBestAuthZone(&fwdomain) != t_sstorage.domainmap->end();
+ if (forwarded) {
+ QLOG("Step0 qname is in a forwarded domain " << fwdomain);
+ }
+
for (unsigned int i = 0; i <= qnamelen; ) {
// Step 1
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, boost::make_optional(forwarded, fwdomain));
+ if (forwarded) {
+ break;
+ }
}
if (bestns.size() == 0) {
- // Something terrible is wrong
- QLOG("Step1 No ancestor found return ServFail");
- return RCode::ServFail;
+ if (!forwarded) {
+ // Something terrible is wrong
+ QLOG("Step1 No ancestor found return ServFail");
+ return RCode::ServFail;
+ }
+ child = fwdomain;
+ } else {
+ QLOG("Step1 Ancestor from cache is " << bestns[0].d_name);
+ if (forwarded) {
+ child = bestns[0].d_name.isPartOf(fwdomain) ? bestns[0].d_name : fwdomain;
+ QLOG("Step1 Final Ancestor (using forwarding info) is " << child);
+ } else {
+ child = bestns[0].d_name;
+ }
}
- const DNSName& ancestor(bestns[0].d_name);
- QLOG("Step1 Ancestor from cache is " << ancestor.toString());
- child = ancestor;
-
unsigned int targetlen = std::min(child.countLabels() + (i > 3 ? 3 : 1), qnamelen);
for (; i <= qnamelen; i++) {
// 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);
+ // We might have hit a depth level check, but we still want to allow some recursion levels in the fallback
+ // no-qname-minimization case. This has the effect that a qname minimization fallback case might reach 150% of
+ // maxdepth.
+ res = doResolveNoQNameMinimization(qname, qtype, ret, depth/2, 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;
-
- if(s_maxdepth && depth > s_maxdepth)
- throw ImmediateServFailException("More than "+std::to_string(s_maxdepth)+" (max-recursion-depth) levels of recursion needed while resolving "+qname.toLogString());
+ 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();
+ LOG(prefix << qname << ": " << msg << endl);
+ throw ImmediateServFailException(msg);
+ }
int res=0;
// This is a difficult way of expressing "this is a normal query", i.e. not getRootNS.
*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;
+ // Here we have an issue. If we were prevented from going out to the network (cache-only was set, possibly because we
+ // are in QM Step0) we might have a CNAME but not the corresponding target.
+ // It means that we will sometimes go to the next steps when we are in fact done, but that's fine since
+ // we will get the records from the cache, resulting in a small overhead.
+ // This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
+ // RPZ rules will not be evaluated anymore (we already matched).
+ const bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
+
+ if (fromCache && (!d_cacheonly || stoppedByPolicyHit)) {
+ *fromCache = true;
+ }
+ /* Apply Post filtering policies */
+
+ if (d_wantsRPZ && !stoppedByPolicyHit) {
+ auto luaLocal = g_luaconfs.getLocal();
+ if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ bool done = false;
+ handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
+ if (done && fromCache) {
+ *fromCache = true;
+ }
+ }
+ }
+
return res;
}
- if(doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, res, state)) {
+ if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, res, state)) {
// we done
d_wasOutOfBand = wasAuthZone;
- if (fromCache)
+ if (fromCache) {
*fromCache = true;
+ }
+
+ if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
+ auto luaLocal = g_luaconfs.getLocal();
+ if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ bool done = false;
+ handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
+ }
+ }
+
return res;
}
}
- if(d_cacheonly)
+ if (d_cacheonly) {
return 0;
+ }
LOG(prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl);
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);
+
+ res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere, state, stopAtDelegation);
- if(!(res=doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere, state, stopAtDelegation)))
+ /* Apply Post filtering policies */
+ if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
+ auto luaLocal = g_luaconfs.getLocal();
+ if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ bool done = false;
+ handlePolicyHit(prefix, qname, qtype, ret, done, res, depth);
+ }
+ }
+
+ if (!res) {
return 0;
+ }
LOG(prefix<<qname<<": failed (res="<<res<<")"<<endl);
/** This function explicitly goes out for A or AAAA addresses
*/
-vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly)
+vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
{
typedef vector<DNSRecord> res_t;
typedef vector<ComboAddress> ret_t;
bool oldCacheOnly = setCacheOnly(cacheOnly);
bool oldRequireAuthData = d_requireAuthData;
bool oldValidationRequested = d_DNSSECValidationRequested;
+ const unsigned int startqueries = d_outqueries;
d_requireAuthData = false;
d_DNSSECValidationRequested = false;
try {
- vState newState = Indeterminate;
+ vState newState = vState::Indeterminate;
res_t resv4;
// If IPv4 ever becomes second class, we should revisit this
- if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) { // this consults cache, OR goes out
+ if (s_doIPv4 && doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) { // this consults cache, OR goes out
for (auto const &i : resv4) {
if (i.d_type == QType::A) {
if (auto rec = getRR<ARecordContent>(i)) {
}
}
}
- if (s_doIPv6) {
+ if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
if (ret.empty()) {
// We did not find IPv4 addresses, try to get IPv6 ones
- newState = Indeterminate;
+ newState = vState::Indeterminate;
res_t resv6;
if (doResolve(qname, QType::AAAA, resv6, depth+1, beenthere, newState) == 0) { // this consults cache, OR goes out
for (const auto &i : resv6) {
// We have some IPv4 records, don't bother with going out to get IPv6, but do consult the cache
// Once IPv6 adoption matters, this needs to be revisited
res_t cset;
- if (s_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote) > 0) {
+ if (g_recCache->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote, d_routingTag) > 0) {
for (const auto &i : cset) {
if (i.d_ttl > (unsigned int)d_now.tv_sec ) {
if (auto rec = getRR<AAAARecordContent>(i)) {
of a NS and keep processing the current query */
}
+ if (ret.empty() && d_outqueries > startqueries) {
+ // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
+ addressQueriesForNS++;
+ }
d_requireAuthData = oldRequireAuthData;
d_DNSSECValidationRequested = oldValidationRequested;
setCacheOnly(oldCacheOnly);
t_sstorage.nsSpeeds[qname].purge(speeds);
if(ret.size() > 1) {
- random_shuffle(ret.begin(), ret.end());
+ shuffle(ret.begin(), ret.end(), pdns::dns_random_engine());
speedOrderCA so(speeds);
stable_sort(ret.begin(), ret.end(), so);
return ret;
}
-void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vector<DNSRecord>& bestns, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>& beenthere)
+void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vector<DNSRecord>& bestns, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>& beenthere, const boost::optional<DNSName>& cutOffDomain)
{
string prefix;
DNSName subdomain(qname);
bestns.clear();
bool brokeloop;
do {
+ if (cutOffDomain && (subdomain == *cutOffDomain || !subdomain.isPartOf(*cutOffDomain))) {
+ break;
+ }
brokeloop=false;
LOG(prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl);
vector<DNSRecord> ns;
*flawedNSSet = false;
- if(s_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote) > 0) {
+ if(g_recCache->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote, d_routingTag) > 0) {
bestns.reserve(ns.size());
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),
- false, doLog() ? &aset : 0, d_cacheRemote) > 5)) {
+ if(nrr && (!nrr->getNS().isPartOf(subdomain) || g_recCache->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);
LOG(prefix<<qname<<": within bailiwick: "<< nrr->getNS().isPartOf(subdomain));
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) {
- s_RC->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, aa, newState, s_maxbogusttl + d_now.tv_sec);
+ if (newState == vState::Bogus) {
+ g_recCache->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, d_routingTag, aa, newState, s_maxbogusttl + d_now.tv_sec);
}
else {
- s_RC->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, aa, newState, boost::none);
+ g_recCache->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, d_routingTag, aa, newState, boost::none);
}
}
+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<<": Looking for CNAME cache hit of '"<<qname<<"|CNAME"<<"'"<<endl);
/* we don't require auth data for forward-recurse lookups */
- if (s_RC->get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) {
+ if (g_recCache->get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) {
foundName = qname;
foundQT = QType(QType::CNAME);
}
if (dnameName == qname && qtype != QType::DNAME) { // The client does not want a DNAME, but we've reached the QNAME already. So there is no match
break;
}
- if (s_RC->get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) {
+ if (g_recCache->get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) {
foundName = dnameName;
foundQT = QType(QType::DNAME);
break;
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;
+ // Be aware that going out on the network might be disabled (cache-only), for example because we are in QM Step0,
+ // so you can't trust that a real lookup will have been made.
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;
}
/*!
- * Convience function to push the records from records into ret with a new TTL
+ * Convenience function to push the records from records into ret with a new TTL
*
* \param records DNSRecords that need to go into ret
* \param ttl The new TTL for these records
- * \param ret The vector of DNSRecords that should contian the records with the modified TTL
+ * \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) {
+ boost::optional<time_t> capTTD = boost::none;
+ if (state == vState::Bogus) {
capTTD = d_now.tv_sec + s_maxbogusttl;
}
- t_sstorage.negcache.updateValidationStatus(ne->d_name, ne->d_qtype, state, capTTD);
+ g_negCache->updateValidationStatus(ne.d_name, ne.d_qtype, state, capTTD);
}
}
prefix.append(depth, ' ');
}
- // sqname and sqtype are used contain 'higher' names if we have them (e.g. powerdns.com|SOA when we find a negative entry for doesnotexists.powerdns.com|A)
+ // sqname and sqtype are used contain 'higher' names if we have them (e.g. powerdns.com|SOA when we find a negative entry for doesnotexist.powerdns.com|A)
DNSName sqname(qname);
QType sqt(qtype);
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() &&
+ g_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 (g_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 ||
+ g_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 (g_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_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) {
+
+ if(g_recCache->get(d_now.tv_sec, sqname, sqt, !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) {
LOG(prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ");
- if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == Indeterminate && d_requireAuthData) {
+ if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
/* This means we couldn't figure out the state when this entry was cached,
most likely because we hadn't computed the zone cuts yet. */
computeZoneCuts(subdomain, g_rootdnsname, depth);
vState recordState = getValidationStatus(qname, false);
- if (recordState == Secure) {
- LOG(prefix<<sqname<<": got Indeterminate state from the cache, validating.."<<endl);
- cachedState = SyncRes::validateRecordsWithSigs(depth, sqname, sqt, sqname, cset, signatures);
+ if (recordState == vState::Secure) {
+ LOG(prefix<<sqname<<": got vState::Indeterminate state from the cache, validating.."<<endl);
+ if (sqt == QType::DNSKEY) {
+ cachedState = validateDNSKeys(sqname, cset, signatures, depth);
+ }
+ else {
+ cachedState = SyncRes::validateRecordsWithSigs(depth, sqname, sqt, sqname, cset, signatures);
+ }
}
else {
cachedState = recordState;
}
- if (cachedState != Indeterminate) {
- LOG(prefix<<qname<<": got Indeterminate state from the cache, validation result is "<<vStates[cachedState]<<endl);
- if (cachedState == Bogus) {
+ if (cachedState != vState::Indeterminate) {
+ LOG(prefix<<qname<<": got vState::Indeterminate state from the cache, validation result is "<<cachedState<<endl);
+ if (cachedState == vState::Bogus) {
capTTL = s_maxbogusttl;
}
updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
if(found && !expired) {
if (!giveNegative)
res=0;
- LOG(prefix<<qname<<": updating validation state with cache content for "<<qname<<" to "<<vStates[cachedState]<<endl);
+ LOG(prefix<<qname<<": updating validation state with cache content for "<<qname<<" to "<<cachedState<<endl);
state = cachedState;
return true;
}
return rnameservers;
}
- random_shuffle(rnameservers.begin(),rnameservers.end());
+ shuffle(rnameservers.begin(),rnameservers.end(), pdns::dns_random_engine());
speedOrder so;
stable_sort(rnameservers.begin(),rnameservers.end(), so);
speed=t_sstorage.nsSpeeds[nsName].get(d_now);
speeds[val]=speed;
}
- random_shuffle(nameservers.begin(),nameservers.end());
+ shuffle(nameservers.begin(),nameservers.end(), pdns::dns_random_engine());
speedOrderCA so(speeds);
stable_sort(nameservers.begin(),nameservers.end(), so);
ret.insert(ret.end(), ne.DNSSECRecords.signatures.begin(), ne.DNSSECRecords.signatures.end());
}
+static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType& qtype, const std::vector<DNSRecord>& records)
+{
+ if (qtype == QType::CNAME) {
+ return true;
+ }
+
+ for (const auto& record : records) {
+ if (record.d_type == QType::CNAME) {
+ if (auto content = getRR<CNAMERecordContent>(record)) {
+ if (qname == content->getTarget()) {
+ /* we have a CNAME whose target matches the entry we are about to
+ generate, so it will complete the current records, not replace
+ them
+ */
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static void removeConflictingRecord(std::vector<DNSRecord>& records, const DNSName& name, uint16_t dtype)
+{
+ for (auto it = records.begin(); it != records.end(); ) {
+ bool remove = false;
+
+ if (it->d_class == QClass::IN &&
+ (it->d_type == QType::CNAME || dtype == QType::CNAME || it->d_type == dtype) &&
+ it->d_name == name) {
+ remove = true;
+ }
+ else if (it->d_class == QClass::IN &&
+ it->d_type == QType::RRSIG &&
+ it->d_name == name) {
+ if (auto rrc = getRR<RRSIGRecordContent>(*it)) {
+ if (rrc->d_type == QType::CNAME || rrc->d_type == dtype) {
+ /* also remove any RRSIG that could conflict */
+ remove = true;
+ }
+ }
+ }
+
+ if (remove) {
+ it = records.erase(it);
+ }
+ else {
+ ++it;
+ }
+ }
+}
+
+void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType& qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
+{
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ return;
+ }
+
+ /* don't account truncate actions for TCP queries, since they are not applied */
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !d_queryReceivedOverTCP) {
+ ++g_stats.policyResults[d_appliedPolicy.d_kind];
+ }
+
+ if (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
+ LOG(prefix << qname << "|" << qtype.getName() << d_appliedPolicy.getLogString() << endl);
+ }
+
+ switch (d_appliedPolicy.d_kind) {
+
+ case DNSFilterEngine::PolicyKind::NoAction:
+ return;
+
+ case DNSFilterEngine::PolicyKind::Drop:
+ ++g_stats.policyDrops;
+ throw ImmediateQueryDropException();
+
+ case DNSFilterEngine::PolicyKind::NXDOMAIN:
+ ret.clear();
+ rcode = RCode::NXDomain;
+ done = true;
+ return;
+
+ case DNSFilterEngine::PolicyKind::NODATA:
+ ret.clear();
+ rcode = RCode::NoError;
+ done = true;
+ return;
+
+ case DNSFilterEngine::PolicyKind::Truncate:
+ if (!d_queryReceivedOverTCP) {
+ ret.clear();
+ rcode = RCode::NoError;
+ throw SendTruncatedAnswerException();
+ }
+ return;
+
+ case DNSFilterEngine::PolicyKind::Custom:
+ {
+ if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
+ ret.clear();
+ }
+
+ rcode = RCode::NoError;
+ done = true;
+ auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
+ for (auto& dr : spoofed) {
+ removeConflictingRecord(ret, dr.d_name, dr.d_type);
+ }
+
+ for (auto& dr : spoofed) {
+ ret.push_back(dr);
+
+ if (dr.d_name == qname && dr.d_type == QType::CNAME && qtype != QType::CNAME) {
+ if (auto content = getRR<CNAMERecordContent>(dr)) {
+ vState newTargetState = vState::Indeterminate;
+ handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
+ }
+ }
+ }
+ }
+ }
+}
+
bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers)
{
/* we skip RPZ processing if:
- it was disabled (d_wantsRPZ is false) ;
- we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
- process any further RPZ rules.
+ process any further RPZ rules. Except that we need to process rules of higher priority..
*/
- if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
+ if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
for (auto const &ns : nameservers) {
bool match = dfe.getProcessingPolicy(ns.first, d_discardedPolicies, d_appliedPolicy);
- if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
- LOG(", however nameserver "<<ns.first<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
- return true;
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+ LOG(", however nameserver "<<ns.first<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+ return true;
+ }
}
// Traverse all IP addresses for this NS to see if they have an RPN NSIP policy
for (auto const &address : ns.second.first) {
match = dfe.getProcessingPolicy(address, d_discardedPolicies, d_appliedPolicy);
- if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
- LOG(", however nameserver "<<ns.first<<" IP address "<<address.toString()<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
- return true;
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+ LOG(", however nameserver "<<ns.first<<" IP address "<<address.toString()<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+ return true;
+ }
}
}
}
- it was disabled (d_wantsRPZ is false) ;
- we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
- process any further RPZ rules.
+ process any further RPZ rules. Except that we need to process rules of higher priority..
*/
- if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
+ if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy);
- if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
- LOG(" (blocked by RPZ policy '"+(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")+"')");
- return true;
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
+ LOG(" (blocked by RPZ policy '" + d_appliedPolicy.getName() + "')");
+ return true;
+ }
}
}
return false;
}
-vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
+vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int &retrieveAddressesForNS)
{
vector<ComboAddress> result;
if(!tns->first.empty()) {
LOG(prefix<<qname<<": Trying to resolve NS '"<<tns->first<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
- result = getAddrs(tns->first, depth+2, beenthere, cacheOnly);
+ result = getAddrs(tns->first, depth, beenthere, cacheOnly, retrieveAddressesForNS);
pierceDontQuery=false;
}
else {
void SyncRes::updateValidationState(vState& state, const vState stateUpdate)
{
- LOG(d_prefix<<"validation state was "<<std::string(vStates[state])<<", state update is "<<std::string(vStates[stateUpdate]));
+ LOG(d_prefix<<"validation state was "<<state<<", state update is "<<stateUpdate);
- if (stateUpdate == TA) {
- state = Secure;
+ if (stateUpdate == vState::TA) {
+ state = vState::Secure;
}
- else if (stateUpdate == NTA) {
- state = Insecure;
+ else if (stateUpdate == vState::NTA) {
+ state = vState::Insecure;
}
- else if (stateUpdate == Bogus) {
- state = Bogus;
+ else if (stateUpdate == vState::Bogus) {
+ state = vState::Bogus;
}
- else if (state == Indeterminate) {
+ else if (state == vState::Indeterminate) {
state = stateUpdate;
}
- else if (stateUpdate == Insecure) {
- if (state != Bogus) {
- state = Insecure;
+ else if (stateUpdate == vState::Insecure) {
+ if (state != vState::Bogus) {
+ state = vState::Insecure;
}
}
- LOG(", validation state is now "<<std::string(vStates[state])<<endl);
+ LOG(", validation state is now "<<state<<endl);
}
vState SyncRes::getTA(const DNSName& zone, dsmap_t& ds)
if (luaLocal->dsAnchors.empty()) {
LOG(d_prefix<<": No trust anchors configured, everything is Insecure"<<endl);
/* We have no TA, everything is insecure */
- return Insecure;
+ return vState::Insecure;
}
std::string reason;
if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
LOG(d_prefix<<": got NTA for '"<<zone<<"'"<<endl);
- return NTA;
+ return vState::NTA;
}
if (getTrustAnchor(luaLocal->dsAnchors, zone, ds)) {
LOG(d_prefix<<": got TA for '"<<zone<<"'"<<endl);
- return TA;
+ return vState::TA;
}
else {
LOG(d_prefix<<": no TA found for '"<<zone<<"' among "<< luaLocal->dsAnchors.size()<<endl);
if (zone.isRoot()) {
/* No TA for the root */
- return Insecure;
+ return vState::Insecure;
}
- return Indeterminate;
+ return vState::Indeterminate;
}
static size_t countSupportedDS(const dsmap_t& dsmap)
{
vState result = getTA(zone, ds);
- if (result != Indeterminate || taOnly) {
+ if (result != vState::Indeterminate || taOnly) {
if (foundCut) {
- *foundCut = (result != Indeterminate);
+ *foundCut = (result != vState::Indeterminate);
}
- if (result == TA) {
+ if (result == vState::TA) {
if (countSupportedDS(ds) == 0) {
ds.clear();
- result = Insecure;
+ result = vState::Insecure;
}
else {
- result = Secure;
+ result = vState::Secure;
}
}
- else if (result == NTA) {
- result = Insecure;
+ else if (result == vState::NTA) {
+ result = vState::Insecure;
}
return result;
}
- bool oldSkipCNAME = d_skipCNAMECheck;
- d_skipCNAMECheck = true;
-
std::set<GetBestNSAnswer> beenthere;
std::vector<DNSRecord> dsrecords;
- vState state = Indeterminate;
+ vState state = vState::Indeterminate;
+ const bool oldCacheOnly = setCacheOnly(false);
int rcode = doResolve(zone, QType(QType::DS), dsrecords, depth + 1, beenthere, state);
- d_skipCNAMECheck = oldSkipCNAME;
+ setCacheOnly(oldCacheOnly);
+
+ if (rcode == RCode::ServFail) {
+ throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
+ }
if (rcode == RCode::NoError || (rcode == RCode::NXDomain && !bogusOnNXD)) {
uint8_t bestDigestType = 0;
- a signed zone (Secure) to an unsigned one (Insecure)
- an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
*/
- return state == Secure ? Insecure : state;
+ return state == vState::Secure ? vState::Insecure : state;
} else {
/* we have a DS */
if (foundCut) {
}
LOG(d_prefix<<": returning Bogus state from "<<__func__<<"("<<zone<<")"<<endl);
- return Bogus;
+ return vState::Bogus;
}
bool SyncRes::haveExactValidationStatus(const DNSName& domain)
vState SyncRes::getValidationStatus(const DNSName& subdomain, bool allowIndeterminate)
{
- vState result = Indeterminate;
+ vState result = vState::Indeterminate;
if (!shouldValidate()) {
return result;
do {
const auto& it = d_cutStates.find(name);
if (it != d_cutStates.cend()) {
- if (allowIndeterminate || it->second != Indeterminate) {
- LOG(d_prefix<<": got status "<<vStates[it->second]<<" for name "<<subdomain<<" (from "<<name<<")"<<endl);
+ if (allowIndeterminate || it->second != vState::Indeterminate) {
+ LOG(d_prefix<<": got status "<<it->second<<" for name "<<subdomain<<" (from "<<name<<")"<<endl);
return it->second;
}
}
{
bool foundCut = false;
dsmap_t ds;
- vState dsState = getDSRecords(qname, ds, newState == Bogus || existingState == Insecure || existingState == Bogus, depth, false, &foundCut);
+ vState dsState = getDSRecords(qname, ds, newState == vState::Bogus || existingState == vState::Insecure || existingState == vState::Bogus, depth, false, &foundCut);
- if (dsState != Indeterminate) {
+ if (dsState != vState::Indeterminate) {
newState = dsState;
}
}
const bool oldCacheOnly = setCacheOnly(false);
+ const bool oldWantsRPZ = d_wantsRPZ;
+ d_wantsRPZ = false;
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()) {
setCacheOnly(oldCacheOnly);
+ d_wantsRPZ = oldWantsRPZ;
return;
}
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);
+ d_wantsRPZ = oldWantsRPZ;
}
vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures, unsigned int depth)
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;
}
isCNAMEAnswer = false;
}
- /* if we have a positive answer synthetized from a wildcard,
+ /* if we have a positive answer synthesized from a wildcard,
we need to store the corresponding NSEC/NSEC3 records proving
that the exact name did not exist in the negative cache */
if(gatherWildcardProof) {
}
}
}
- 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
count can be lower than the name's label count if it was
- synthetized from the wildcard. Note that the difference might
+ synthesized from the wildcard. Note that the difference might
be > 1. */
if (rec.d_name == qname && isWildcardExpanded(labelCount, rrsig)) {
gatherWildcardProof = true;
We still want to gather the corresponding NSEC/NSEC3 records
to pass them to our client in case it wants to validate by itself.
*/
- LOG(prefix<<qname<<": RRSIG indicates the name was synthetized from a wildcard, we need a wildcard proof"<<endl);
+ LOG(prefix<<qname<<": RRSIG indicates the name was synthesized from a wildcard, we need a wildcard proof"<<endl);
needWildcardProof = true;
}
else {
- LOG(prefix<<qname<<": RRSIG indicates the name was synthetized from a wildcard expanded onto itself, we need to gather wildcard proof"<<endl);
+ LOG(prefix<<qname<<": RRSIG indicates the name was synthesized from a wildcard expanded onto itself, we need to gather wildcard proof"<<endl);
}
wildcardLabelsCount = rrsig->d_labels;
}
}
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;
if (!t_sstorage.domainmap->empty()) {
// Check if we are authoritative for a zone in this answer
DNSName tmp_qname(rec.d_name);
+ // We may be auth for domain example.com, but the DS record needs to come from the parent (.com) nameserver
+ if (rec.d_type == QType::DS) {
+ tmp_qname.chopOff();
+ }
auto auth_domain_iter=getBestAuthZone(&tmp_qname);
if(auth_domain_iter!=t_sstorage.domainmap->end() &&
auth.countLabels() <= auth_domain_iter->first.countLabels()) {
}
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));
}
}
if (doCache) {
- s_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState);
+ g_recCache->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, d_routingTag, recordState);
}
}
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)
+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, int& rcode, unsigned int depth)
{
bool done = false;
DNSName dnameTarget, dnameOwner;
LOG(prefix<<qname<<": got negative caching indication for name '"<<qname<<"' (accept="<<rec.d_name.isPartOf(auth)<<"), newtarget='"<<newtarget<<"'"<<endl);
rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
- if(newtarget.empty()) // only add a SOA if we're not going anywhere after this
+ // only add a SOA if we're not going anywhere after this
+ if (newtarget.empty()) {
ret.push_back(rec);
+ }
NegCache::NegCacheEntry ne;
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);
}
We have a regression test making sure we do exactly that.
*/
if(!wasVariable() && newtarget.empty()) {
- t_sstorage.negcache.add(ne);
+ g_negCache->add(ne);
if(s_rootNXTrust && ne.d_auth.isRoot() && auth.isRoot() && lwr.d_aabit) {
ne.d_name = ne.d_name.getLastLabel();
- t_sstorage.negcache.add(ne);
+ g_negCache->add(ne);
}
}
}
ret.push_back(rec);
if (auto content = getRR<CNAMERecordContent>(rec)) {
- newtarget=content->getTarget();
+ newtarget=DNSName(content->getTarget());
}
} else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME
ret.push_back(rec);
}
}
}
- /* if we have a positive answer synthetized from a wildcard, we need to
+ /* if we have a positive answer synthesized from a wildcard, we need to
return the corresponding NSEC/NSEC3 records from the AUTHORITY section
proving that the exact name did not exist */
else if(gatherWildcardProof && (rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::AUTHORITY) {
{
LOG(prefix<<qname<<": answer is in: resolved to '"<< rec.d_content->getZoneRepresentation()<<"|"<<DNSRecordContent::NumberToType(rec.d_type)<<"'"<<endl);
- done=true;
+ done = true;
+ rcode = RCode::NoError;
- if (state == Secure && needWildcardProof) {
- /* We have a positive answer synthetized from a wildcard, we need to check that we have
+ 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 {
updateValidationStatusInCache(qname, qtype, lwr.d_aabit, st);
}
}
+
ret.push_back(rec);
}
else if((rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::ANSWER) {
}
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);
if(!wasVariable()) {
- t_sstorage.negcache.add(ne);
+ g_negCache->add(ne);
}
if (qname == newauth && qtype == QType::DS) {
}
}
}
- else if(!done && rec.d_place==DNSResourceRecord::AUTHORITY && rec.d_type==QType::SOA &&
- lwr.d_rcode==RCode::NoError && qname.isPartOf(rec.d_name)) {
+ else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA &&
+ lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
LOG(prefix<<qname<<": got negative caching indication for '"<< qname<<"|"<<qtype.getName()<<"'"<<endl);
if(!newtarget.empty()) {
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(!wasVariable()) {
if(qtype.getCode()) { // prevents us from blacking out a whole domain
- t_sstorage.negcache.add(ne);
+ g_negCache->add(ne);
}
}
cnamerec.d_type = QType::CNAME;
cnamerec.d_ttl = dnameTTL;
cnamerec.d_content = std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget));
- ret.push_back(cnamerec);
+ ret.push_back(std::move(cnamerec));
}
return done;
}
return true;
}
+void SyncRes::handleNewTarget(const std::string& prefix, const DNSName& qname, const DNSName& newtarget, uint16_t qtype, std::vector<DNSRecord>& ret, int& rcode, int depth, const std::vector<DNSRecord>& recordsFromAnswer, vState& state)
+{
+ if (newtarget == qname) {
+ LOG(prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl);
+ ret.clear();
+ rcode = RCode::ServFail;
+ return;
+ }
+
+ if (depth > 10) {
+ LOG(prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl);
+ rcode = RCode::ServFail;
+ return;
+ }
+
+ // 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;
+ }
+
+ 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, recordsFromAnswer);
+ }
+
+ rcode = RCode::NoError;
+ return;
+ }
+
+ LOG(prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl);
+
+ set<GetBestNSAnswer> beenthere;
+ vState cnameState = vState::Indeterminate;
+ rcode = doResolve(newtarget, QType(qtype), ret, depth + 1, beenthere, cnameState);
+ LOG(prefix<<qname<<": updating validation state for response to "<<qname<<" from "<<state<<" with the state from the CNAME quest: "<<cnameState<<endl);
+ updateValidationState(state, cnameState);
+}
+
bool SyncRes::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)
{
string prefix;
DNSName newauth;
DNSName newtarget;
- bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount);
+ bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, *rcode, depth);
- if(done){
+ if (done){
LOG(prefix<<qname<<": status=got results, this level of recursion done"<<endl);
- LOG(prefix<<qname<<": validation status is "<<vStates[state]<<endl);
- *rcode = RCode::NoError;
+ LOG(prefix<<qname<<": validation status is "<<state<<endl);
return true;
}
- if(!newtarget.empty()) {
- if(newtarget == qname) {
- LOG(prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl);
- *rcode = RCode::ServFail;
- return true;
- }
-
- if(depth > 10) {
- LOG(prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl);
- *rcode = RCode::ServFail;
- return true;
- }
-
- if (qtype == QType::DS) {
- LOG(prefix<<qname<<": status=got a CNAME referral, but we are looking for a DS"<<endl);
-
- if(d_doDNSSEC)
- addNXNSECS(ret, lwr.d_records);
-
- *rcode = RCode::NoError;
- return true;
- }
- else {
- LOG(prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl);
-
- set<GetBestNSAnswer> beenthere2;
- vState cnameState = 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);
- updateValidationState(state, cnameState);
- return true;
- }
+ if (!newtarget.empty()) {
+ handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
+ return true;
}
if(lwr.d_rcode == RCode::NXDomain) {
LOG(prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl);
+ if (state == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
+ LOG(prefix<<qname<<": NXDOMAIN without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus"<<endl);
+ updateValidationState(state, vState::Bogus);
+ }
+
if(d_doDNSSEC)
addNXNSECS(ret, lwr.d_records);
if(nsset.empty() && !lwr.d_rcode && (negindic || lwr.d_aabit || sendRDQuery)) {
LOG(prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA) " : "")<<(lwr.d_aabit ? "(have aa bit) " : "")<<endl);
- if(state == Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
- updateValidationState(state, Bogus);
+ if(state == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
+ LOG(prefix<<qname<<": NODATA without a negative indication (missing SOA in authority) in a DNSSEC secure zone, going Bogus"<<endl);
+ updateValidationState(state, vState::Bogus);
}
if(d_doDNSSEC)
nameservers.clear();
for (auto const &nameserver : nsset) {
- if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) {
+ if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
bool match = dfe.getProcessingPolicy(nameserver, d_discardedPolicies, d_appliedPolicy);
- if (match && d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
- LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
- throw PolicyHitException();
+ if (match) {
+ mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
+ if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ }
+ else {
+ LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+ throw PolicyHitException();
+ }
+ }
}
}
nameservers.insert({nameserver, {{}, false}});
if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
/* RPZ hit */
- throw PolicyHitException();
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ }
+ else {
+ throw PolicyHitException();
+ }
}
LOG(endl);
+ unsigned int addressQueriesForNS = 0;
for(;;) { // we may get more specific nameservers
auto rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() );
+ // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
+ // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
+ // This is to "punish" zones that publish many non-resolving NS names.
+ // We always allow 5 NS name resolving attempts with empty results.
+ unsigned int nsLimit = s_maxnsaddressqperq;
+ if (rnameservers.size() > nsLimit) {
+ int newLimit = static_cast<int>(nsLimit) - (rnameservers.size() - nsLimit);
+ nsLimit = std::max(5, newLimit);
+ }
+
for(auto tns=rnameservers.cbegin();;++tns) {
+ if (addressQueriesForNS >= nsLimit) {
+ throw ImmediateServFailException(std::to_string(nsLimit)+" (adjusted max-ns-address-qperq) or more queries with empty results for NS addresses sent resolving "+qname.toLogString());
+ }
if(tns==rnameservers.cend()) {
LOG(prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth<<"'"<<endl);
if(!auth.isRoot() && flawedNSSet) {
LOG(prefix<<qname<<": Ageing nameservers for level '"<<auth<<"', next query might succeed"<<endl);
- if(s_RC->doAgeCache(d_now.tv_sec, auth, QType::NS, 10))
+ if(g_recCache->doAgeCache(d_now.tv_sec, auth, QType::NS, 10))
g_stats.nsSetInvalidations++;
}
return -1;
LOG(prefix<<qname<<": Domain is out-of-band"<<endl);
/* setting state to indeterminate since validation is disabled for local auth zone,
and Insecure would be misleading. */
- state = Indeterminate;
+ state = vState::Indeterminate;
d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, lwr.d_rcode);
lwr.d_tcbit=false;
lwr.d_aabit=true;
}
else {
/* if tns is empty, retrieveAddressesForNS() knows we have hardcoded servers (i.e. "forwards") */
- remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly);
+ remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
if(remoteIPs.empty()) {
LOG(prefix<<qname<<": Failed to get IP for NS "<<tns->first<<", trying next if available"<<endl);
LOG(endl);
if (hitPolicy) { //implies d_wantsRPZ
/* RPZ hit */
- throw PolicyHitException();
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ }
+ else {
+ throw PolicyHitException();
+ }
}
}
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;
#include "ednssubnet.hh"
#include "filterpo.hh"
#include "negcache.hh"
+#include "proxy-protocol.hh"
#include "sholder.hh"
#ifdef HAVE_CONFIG_H
cont_t d_cont;
};
+extern std::unique_ptr<NegCache> g_negCache;
+
class SyncRes : public boost::noncopyable
{
public:
};
struct ThreadLocalStorage {
- NegCache negcache;
nsspeeds_t nsSpeeds;
throttle_t throttle;
ednsstatus_t ednsstatus;
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();
{
return t_sstorage.fails.value(server);
}
-
- static void clearNegCache()
- {
- t_sstorage.negcache.clear();
- }
-
- static uint64_t getNegCacheSize()
- {
- return t_sstorage.negcache.size();
- }
-
- static void pruneNegCache(unsigned int maxEntries)
- {
- t_sstorage.negcache.prune(maxEntries);
- }
-
- static uint64_t wipeNegCache(const DNSName& name, bool subtree = false)
- {
- return t_sstorage.negcache.wipe(name, subtree);
- }
-
static void setDomainMap(std::shared_ptr<domainmap_t> newMap)
{
t_sstorage.domainmap = newMap;
}
-
static const std::shared_ptr<domainmap_t> getDomainMap()
{
return t_sstorage.domainmap;
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
return d_queryValidationState;
}
+ void setQueryReceivedOverTCP(bool tcp)
+ {
+ d_queryReceivedOverTCP = tcp;
+ }
+
static thread_local ThreadLocalStorage t_sstorage;
static std::atomic<uint64_t> s_queries;
static unsigned int s_minimumTTL;
static unsigned int s_minimumECSTTL;
static unsigned int s_maxqperq;
+ static unsigned int s_maxnsaddressqperq;
static unsigned int s_maxtotusec;
static unsigned int s_maxdepth;
static unsigned int s_maxnegttl;
static uint8_t s_ecsipv6limit;
static uint8_t s_ecsipv4cachelimit;
static uint8_t s_ecsipv6cachelimit;
+ static bool s_doIPv4;
static bool s_doIPv6;
static bool s_noEDNSPing;
static bool s_noEDNS;
std::unordered_map<std::string,bool> d_discardedPolicies;
DNSFilterEngine::Policy d_appliedPolicy;
+ std::unordered_set<std::string> d_policyTags;
+ boost::optional<string> d_routingTag;
+
unsigned int d_authzonequeries;
unsigned int d_outqueries;
unsigned int d_tcpoutqueries;
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);
- void getBestNSFromCache(const DNSName &qname, const QType &qtype, vector<DNSRecord>&bestns, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>& beenthere);
+ void getBestNSFromCache(const DNSName &qname, const QType &qtype, vector<DNSRecord>&bestns, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>& beenthere, const boost::optional<DNSName>& cutOffDomain = boost::none);
DNSName getBestNSNamesFromCache(const DNSName &qname, const QType &qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>&beenthere);
inline vector<std::pair<DNSName, float>> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
inline vector<ComboAddress> shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd);
bool moreSpecificThan(const DNSName& a, const DNSName &b) const;
- vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly);
+ vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS);
bool nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers);
bool nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress&);
bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery);
- vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
+ vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int& addressQueriesForNS);
void sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, bool rdQuery);
RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool sendRDQuery);
- bool 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);
+ bool 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, int& rcode, unsigned int depth);
bool doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t qclass, vector<DNSRecord> &ret);
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);
bool lookForCut(const DNSName& qname, unsigned int depth, const vState existingState, vState& newState);
void computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned int depth);
+ void handleNewTarget(const std::string& prefix, const DNSName& qname, const DNSName& newtarget, uint16_t qtype, std::vector<DNSRecord>& ret, int& rcode, int depth, const std::vector<DNSRecord>& recordsFromAnswer, vState& state);
+
+ void handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType& qtype, vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth);
+
void setUpdatingRootNS()
{
d_updatingRootNS = 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};
bool d_wasVariable{false};
bool d_qNameMinimization{false};
+ bool d_queryReceivedOverTCP{false};
LogMode d_lm;
};
return a.domain < b.domain;
}
};
-extern std::unique_ptr<MemRecursorCache> s_RC;
+extern std::unique_ptr<MemRecursorCache> g_recCache;
extern thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
typedef MTasker<PacketID,string> MT_t;
MT_t* getMT();
std::map<vState, std::atomic<uint64_t> > dnssecResults;
std::map<DNSFilterEngine::PolicyKind, std::atomic<uint64_t> > policyResults;
std::atomic<uint64_t> rebalancedQueries{0};
+ std::atomic<uint64_t> proxyProtocolInvalidCount{0};
};
//! represents a running TCP/IP client session
return d_fd;
}
+ std::vector<ProxyProtocolValue> proxyProtocolValues;
std::string data;
const ComboAddress d_remote;
+ ComboAddress d_source;
+ ComboAddress d_destination;
size_t queriesCount{0};
- enum stateenum {BYTE0, BYTE1, GETQUESTION, DONE} state{BYTE0};
+ size_t proxyProtocolGot{0};
+ ssize_t proxyProtocolNeed{0};
+ enum stateenum {PROXYPROTOCOLHEADER, BYTE0, BYTE1, GETQUESTION, DONE} state{BYTE0};
uint16_t qlen{0};
uint16_t bytesread{0};
uint16_t d_requestsInFlight{0}; // number of mthreads spawned for this connection
{
};
+class ImmediateQueryDropException
+{
+};
+
+class SendTruncatedAnswerException
+{
+};
+
typedef boost::circular_buffer<ComboAddress> addrringbuf_t;
extern thread_local std::unique_ptr<addrringbuf_t> t_servfailremotes, t_largeanswerremotes, t_remotes, t_bogusremotes, t_timeouts;
std::string reloadAuthAndForwards();
ComboAddress parseIPAndPort(const std::string& input, uint16_t port);
-ComboAddress getQueryLocalAddress(int family, uint16_t port);
typedef boost::function<void*(void)> pipefunc_t;
void broadcastFunction(const pipefunc_t& func);
void distributeAsyncFunction(const std::string& question, const pipefunc_t& func);
int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<DNSRecord>& ret);
int followCNAMERecords(std::vector<DNSRecord>& ret, const QType& qtype);
+int getFakeAAAARecords(const DNSName& qname, ComboAddress prefix, vector<DNSRecord>& ret);
+int getFakePTRRecords(const DNSName& qname, vector<DNSRecord>& ret);
template<class T> T broadcastAccFunction(const boost::function<T*()>& func);
uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree, uint16_t qtype=0xffff);
uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree=false);
void doCarbonDump(void*);
-void primeHints(void);
-void primeRootNSZones(bool);
+bool primeHints(void);
+void primeRootNSZones(bool, unsigned int depth);
extern __thread struct timeval g_now;
#include "common_startup.hh"
#include "packethandler.hh"
#include "statbag.hh"
-#include "resolver.hh"
#include "communicator.hh"
#include "namespaces.hh"
#include "signingpipe.hh"
\brief This file implements the tcpreceiver that receives and answers questions over TCP/IP
*/
-pthread_mutex_t TCPNameserver::s_plock = PTHREAD_MUTEX_INITIALIZER;
+std::mutex TCPNameserver::s_plock;
std::unique_ptr<Semaphore> TCPNameserver::d_connectionroom_sem{nullptr};
std::unique_ptr<PacketHandler> TCPNameserver::s_P{nullptr};
unsigned int TCPNameserver::d_maxTCPConnections = 0;
catch(PDNSException &ae) {
g_log<<Logger::Error<<"TCP server is unable to launch backends - will try again when questions come in: "<<ae.reason<<endl;
}
- pthread_create(&d_tid, 0, launcher, static_cast<void *>(this));
-}
-void *TCPNameserver::launcher(void *data)
-{
- static_cast<TCPNameserver *>(data)->thread();
- return 0;
+ std::thread th(std::bind(&TCPNameserver::thread, this));
+ th.detach();
}
// throws PDNSException if things didn't go according to plan, returns 0 if really 0 bytes were read
}
}
-void connectWithTimeout(int fd, struct sockaddr* remote, size_t socklen)
-{
- int err;
- Utility::socklen_t len=sizeof(err);
-
- if((err=connect(fd, remote, socklen))<0 && errno!=EINPROGRESS)
- throw NetworkError("connect: "+stringerror());
-
- if(!err)
- goto done;
-
- err=waitForRWData(fd, false, 5, 0);
- if(err == 0)
- throw NetworkError("Timeout connecting to remote");
- if(err < 0)
- throw NetworkError("Error connecting to remote");
-
- if(getsockopt(fd, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
- throw NetworkError("Error connecting to remote: "+stringerror()); // Solaris
-
- if(err)
- throw NetworkError("Error connecting to remote: "+string(strerror(err)));
-
- done:
- ;
-}
-
void TCPNameserver::sendPacket(std::unique_ptr<DNSPacket>& p, int outsock)
{
g_rs.submitResponse(*p, false);
}
}
-void *TCPNameserver::doConnection(void *data)
+void TCPNameserver::doConnection(int fd)
{
setThreadName("pdns/tcpConnect");
std::unique_ptr<DNSPacket> packet;
- // Fix gcc-4.0 error (on AMD64)
- int fd=(int)(long)data; // gotta love C (generates a harmless warning on opteron)
ComboAddress remote;
socklen_t remotelen=sizeof(remote);
size_t transactions = 0;
start = time(NULL);
}
- pthread_detach(pthread_self());
if(getpeername(fd, (struct sockaddr *)&remote, &remotelen) < 0) {
g_log<<Logger::Warning<<"Received question from socket which had no remote address, dropping ("<<stringerror()<<")"<<endl;
d_connectionroom_sem->post();
catch(const PDNSException& e) {
g_log<<Logger::Error<<"Error closing TCP socket: "<<e.reason<<endl;
}
- return 0;
+ return;
}
setNonBlocking(fd);
}
}
{
- Lock l(&s_plock);
+ std::lock_guard<std::mutex> l(s_plock);
if(!s_P) {
g_log<<Logger::Error<<"TCP server is without backend connections, launching"<<endl;
s_P=make_unique<PacketHandler>();
}
}
catch(PDNSException &ae) {
- Lock l(&s_plock);
+ std::lock_guard<std::mutex> l(s_plock);
s_P.reset(); // on next call, backend will be recycled
g_log<<Logger::Error<<"TCP nameserver had error, cycling backend: "<<ae.reason<<endl;
}
g_log<<Logger::Error<<"Error closing TCP socket: "<<e.reason<<endl;
}
decrementClientCount(remote);
-
- return 0;
}
return false;
} else {
getTSIGHashEnum(trc.d_algoName, q->d_tsig_algo);
- if (q->d_tsig_algo == TSIG_GSS) {
- GssContext gssctx(keyname);
- if (!gssctx.getPeerPrincipal(q->d_peer_principal)) {
- g_log<<Logger::Warning<<"Failed to extract peer principal from GSS context with keyname '"<<keyname<<"'"<<endl;
- }
- }
}
DNSSECKeeper dk(s_P->getBackend());
-
- if (q->d_tsig_algo == TSIG_GSS) {
- vector<string> princs;
- s_P->getBackend()->getDomainMetadata(q->qdomain, "GSS-ALLOW-AXFR-PRINCIPAL", princs);
- for(const std::string& princ : princs) {
- if (q->d_peer_principal == princ) {
- g_log<<Logger::Warning<<"AXFR of domain '"<<q->qdomain<<"' allowed: TSIG signed request with authorized principal '"<<q->d_peer_principal<<"' and algorithm 'gss-tsig'"<<endl;
- return true;
- }
- }
- g_log<<Logger::Warning<<"AXFR of domain '"<<q->qdomain<<"' denied: TSIG signed request with principal '"<<q->d_peer_principal<<"' and algorithm 'gss-tsig' is not permitted"<<endl;
- return false;
- }
-
if(!dk.TSIGGrantsAccess(q->qdomain, keyname)) {
g_log<<Logger::Error<<"AXFR '"<<q->qdomain<<"' denied: key with name '"<<keyname<<"' and algorithm '"<<getTSIGAlgoName(q->d_tsig_algo)<<"' does not grant access to zone"<<endl;
return false;
// determine if zone exists and AXFR is allowed using existing backend before spawning a new backend.
SOAData sd;
{
- Lock l(&s_plock);
+ 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 AXFR
if(!s_P) {
g_log<<Logger::Error<<"TCP server is without backend connections in doAXFR, launching"<<endl;
DNSName algorithm=trc.d_algoName; // FIXME400: check
if (algorithm == DNSName("hmac-md5.sig-alg.reg.int"))
algorithm = DNSName("hmac-md5");
- if (algorithm != DNSName("gss-tsig")) {
- if(!db.getTSIGKey(tsigkeyname, &algorithm, &tsig64)) {
- g_log<<Logger::Error<<"TSIG key '"<<tsigkeyname<<"' for domain '"<<target<<"' not found"<<endl;
- return 0;
- }
- if (B64Decode(tsig64, tsigsecret) == -1) {
- g_log<<Logger::Error<<"Unable to Base-64 decode TSIG key '"<<tsigkeyname<<"' for domain '"<<target<<"'"<<endl;
- return 0;
- }
+
+ if(!db.getTSIGKey(tsigkeyname, &algorithm, &tsig64)) {
+ g_log<<Logger::Error<<"TSIG key '"<<tsigkeyname<<"' for domain '"<<target<<"' not found"<<endl;
+ return 0;
+ }
+ if (B64Decode(tsig64, tsigsecret) == -1) {
+ g_log<<Logger::Error<<"Unable to Base-64 decode TSIG key '"<<tsigkeyname<<"' for domain '"<<target<<"'"<<endl;
+ return 0;
}
}
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;
{
- Lock l(&s_plock);
+ 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
if(!s_P) {
g_log<<Logger::Error<<"TCP server is without backend connections in doIXFR, launching"<<endl;
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");
- Lock 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);
}
// sem_init(&d_connectionroom_sem,0,::arg().asNum("max-tcp-connections"));
d_connectionroom_sem = make_unique<Semaphore>( ::arg().asNum( "max-tcp-connections" ));
d_maxTCPConnections = ::arg().asNum( "max-tcp-connections" );
- d_tid=0;
vector<string>locals;
stringtok(locals,::arg()["local-ipv6"]," ,");
s_clientsCount[remote]++;
}
- pthread_t tid;
d_connectionroom_sem->wait(); // blocks if no connections are available
int room;
if(room<1)
g_log<<Logger::Warning<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl;
- int err;
- if((err = pthread_create(&tid, 0, &doConnection, reinterpret_cast<void*>(fd)))) {
- g_log<<Logger::Error<<"Error creating thread: "<<stringerror(err)<<endl;
+ try {
+ std::thread connThread(doConnection, fd);
+ connThread.detach();
+ }
+ catch (std::exception& e) {
+ g_log<<Logger::Error<<"Error creating thread: "<<e.what()<<endl;
d_connectionroom_sem->post();
close(fd);
decrementClientCount(remote);
static int doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q, int outsock);
static int doIXFR(std::unique_ptr<DNSPacket>& q, int outsock);
static bool canDoAXFR(std::unique_ptr<DNSPacket>& q);
- static void *doConnection(void *data);
- static void *launcher(void *data);
+ static void doConnection(int fd);
static void decrementClientCount(const ComboAddress& remote);
void thread(void);
- static pthread_mutex_t s_plock;
+ static std::mutex s_plock;
static std::mutex s_clientsCountMutex;
static std::map<ComboAddress,size_t,ComboAddress::addressOnlyLessThan> s_clientsCount;
static std::unique_ptr<PacketHandler> s_P;
- pthread_t d_tid;
static std::unique_ptr<Semaphore> d_connectionroom_sem;
static unsigned int d_maxTCPConnections;
static NetmaskGroup d_ng;
#endif
#if defined(HAVE_ARC4RANDOM)
-BOOST_AUTO_TEST_CASE(test_dns_random_getrandom_average) {
+BOOST_AUTO_TEST_CASE(test_dns_random_arc4random_average) {
::arg().set("rng")="arc4random";
::arg().set("entropy-source")="/dev/urandom";
}
}
+BOOST_AUTO_TEST_CASE(getEDNSOptionsWithoutEDNS) {
+ const ComboAddress remote("192.168.1.25");
+ const DNSName name("www.powerdns.com.");
+ const ComboAddress origRemote("127.0.0.1");
+ const ComboAddress v4("192.0.2.1");
+
+ {
+ /* no EDNS and no other additional record */
+ vector<uint8_t> query;
+ DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->rd = 1;
+ pw.commit();
+
+ /* large enough packet */
+ char packet[1500];
+ memcpy(packet, query.data(), query.size());
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ uint16_t qclass;
+ DNSName qname(packet, query.size(), sizeof(dnsheader), false, &qtype, &qclass, &consumed);
+ DNSQuestion dq(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(packet), sizeof(packet), query.size(), false, nullptr);
+
+ BOOST_CHECK(!parseEDNSOptions(dq));
+ }
+
+ {
+ /* nothing in additional (so no EDNS) but a record in ANSWER */
+ vector<uint8_t> query;
+ DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->rd = 1;
+ pw.startRecord(name, QType::A, 60, QClass::IN, DNSResourceRecord::ANSWER);
+ pw.xfrIP(v4.sin4.sin_addr.s_addr);
+ pw.commit();
+
+ /* large enough packet */
+ char packet[1500];
+ memcpy(packet, query.data(), query.size());
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ uint16_t qclass;
+ DNSName qname(packet, query.size(), sizeof(dnsheader), false, &qtype, &qclass, &consumed);
+ DNSQuestion dq(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(packet), sizeof(packet), query.size(), false, nullptr);
+
+ BOOST_CHECK(!parseEDNSOptions(dq));
+ }
+
+ {
+ /* nothing in additional (so no EDNS) but a record in AUTHORITY */
+ vector<uint8_t> query;
+ DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->rd = 1;
+ pw.startRecord(name, QType::A, 60, QClass::IN, DNSResourceRecord::AUTHORITY);
+ pw.xfrIP(v4.sin4.sin_addr.s_addr);
+ pw.commit();
+
+ /* large enough packet */
+ char packet[1500];
+ memcpy(packet, query.data(), query.size());
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ uint16_t qclass;
+ DNSName qname(packet, query.size(), sizeof(dnsheader), false, &qtype, &qclass, &consumed);
+ DNSQuestion dq(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast<dnsheader*>(packet), sizeof(packet), query.size(), false, nullptr);
+
+ BOOST_CHECK(!parseEDNSOptions(dq));
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END();
#include "dnswriter.hh"
#include "dnsdist-cache.hh"
#include "gettime.hh"
+#include "packetcache.hh"
BOOST_AUTO_TEST_SUITE(test_dnsdistpacketcache_cc)
static DNSDistPacketCache g_PC(500000);
-static void *threadMangler(void* off)
+static void threadMangler(unsigned int offset)
{
struct timespec queryTime;
gettime(&queryTime); // does not have to be accurate ("realTime") in tests
try {
ComboAddress remote;
bool dnssecOK = false;
- unsigned int offset=(unsigned int)(unsigned long)off;
for(unsigned int counter=0; counter < 100000; ++counter) {
DNSName a=DNSName("hello ")+DNSName(std::to_string(counter+offset));
vector<uint8_t> query;
cerr<<"Had error: "<<e.reason<<endl;
throw;
}
- return 0;
}
AtomicCounter g_missing;
-static void *threadReader(void* off)
+static void threadReader(unsigned int offset)
{
bool dnssecOK = false;
struct timespec queryTime;
gettime(&queryTime); // does not have to be accurate ("realTime") in tests
try
{
- unsigned int offset=(unsigned int)(unsigned long)off;
vector<DNSResourceRecord> entry;
ComboAddress remote;
for(unsigned int counter=0; counter < 100000; ++counter) {
cerr<<"Had error in threadReader: "<<e.reason<<endl;
throw;
}
- return 0;
}
BOOST_AUTO_TEST_CASE(test_PacketCacheThreaded) {
try {
- pthread_t tid[4];
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadMangler, (void*)(i*1000000UL));
- void* res;
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 4; ++i) {
+ threads.push_back(std::thread(threadMangler, i*1000000UL));
+ }
+
+ for (auto& t : threads) {
+ t.join();
+ }
+
+ threads.clear();
BOOST_CHECK_EQUAL(g_PC.getSize() + g_PC.getDeferredInserts() + g_PC.getInsertCollisions(), 400000U);
BOOST_CHECK_SMALL(1.0*g_PC.getInsertCollisions(), 10000.0);
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadReader, (void*)(i*1000000UL));
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ for (int i = 0; i < 4; ++i) {
+ threads.push_back(std::thread(threadReader, i*1000000UL));
+ }
+
+ for (auto& t : threads) {
+ t.join();
+ }
BOOST_CHECK((g_PC.getDeferredInserts() + g_PC.getDeferredLookups() + g_PC.getInsertCollisions()) >= g_missing);
}
boost::optional<Netmask> subnetOut;
bool dnssecOK = false;
- /* lookup for a query with an ECS value of 10.0.118.46/32,
+ /* lookup for a query with a first ECS value,
insert a corresponding response */
{
vector<uint8_t> query;
pwQ.getHeader()->id = qid;
DNSPacketWriter::optvect_t ednsOptions;
EDNSSubnetOpts opt;
- opt.source = Netmask("10.0.118.46/32");
+ opt.source = Netmask("10.0.59.220/32");
ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
pwQ.addOpt(512, 0, 0, ednsOptions);
pwQ.commit();
BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
}
- /* now lookup for the same query with an ECS value of 10.0.123.193/32
+ /* now lookup for the same query with a different ECS value,
we should get the same key (collision) but no match */
{
vector<uint8_t> query;
pwQ.getHeader()->id = qid;
DNSPacketWriter::optvect_t ednsOptions;
EDNSSubnetOpts opt;
- opt.source = Netmask("10.0.123.193/32");
+ opt.source = Netmask("10.0.167.48/32");
ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
pwQ.addOpt(512, 0, 0, ednsOptions);
pwQ.commit();
BOOST_CHECK_EQUAL(subnetOut->toString(), opt.source.toString());
BOOST_CHECK_EQUAL(PC.getLookupCollisions(), 1U);
}
+
+#if 0
+ /* to be able to compute a new collision if the packet cache hashing code is updated */
+ {
+ DNSDistPacketCache pc(10000);
+ DNSPacketWriter::optvect_t ednsOptions;
+ EDNSSubnetOpts opt;
+ std::map<uint32_t, Netmask> colMap;
+ size_t collisions = 0;
+ size_t total = 0;
+ //qname = DNSName("collision-with-ecs-parsing.cache.tests.powerdns.com.");
+
+ for (size_t idxA = 0; idxA < 256; idxA++) {
+ for (size_t idxB = 0; idxB < 256; idxB++) {
+ for (size_t idxC = 0; idxC < 256; idxC++) {
+ vector<uint8_t> secondQuery;
+ DNSPacketWriter pwFQ(secondQuery, qname, QType::AAAA, QClass::IN, 0);
+ pwFQ.getHeader()->rd = 1;
+ pwFQ.getHeader()->qr = false;
+ pwFQ.getHeader()->id = 0x42;
+ opt.source = Netmask("10." + std::to_string(idxA) + "." + std::to_string(idxB) + "." + std::to_string(idxC) + "/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pwFQ.addOpt(512, 0, 0, ednsOptions);
+ pwFQ.commit();
+ secondKey = pc.getKey(qname.toDNSString(), qname.wirelength(), secondQuery.data(), secondQuery.size(), false);
+ auto pair = colMap.insert(std::make_pair(secondKey, opt.source));
+ total++;
+ if (!pair.second) {
+ collisions++;
+ cerr<<"Collision between "<<colMap[secondKey].toString()<<" and "<<opt.source.toString()<<" for key "<<secondKey<<endl;
+ goto done;
+ }
+ }
+ }
+ }
+ done:
+ cerr<<"collisions: "<<collisions<<endl;
+ cerr<<"total: "<<total<<endl;
+ }
+#endif
}
BOOST_AUTO_TEST_CASE(test_PCDNSSECCollision) {
// CASE_L can be used where this is not the case. See LOC below for a good example why this might happen
/* (CASE_S(QType::NAME, "zone format", "line format")) */
-/* (CASE_L(QType::NAME, "zone format", "canonic zone format", "line format")) */
+/* (CASE_L(QType::NAME, "zone format", "canonical zone format", "line format")) */
#define _CASE_L(type, inval, zoneval, lineval, broken) case_t(type, BINARY(inval), BINARY(zoneval), BINARY(lineval), broken)
#define CASE_L(type, inval, zoneval, lineval) _CASE_L(type, inval, zoneval, lineval, broken_marker::WORKING)
// X.509 as per PKIX
(CASE_S(QType::CERT, "1 0 0 MIIB9DCCAV2gAwIBAgIJAKxUfFVXhw7HMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNVBAMMCHJlYy50ZXN0MB4XDTEzMDUxMjE5NDgwOVoXDTEzMDYxMTE5NDgwOVowEzERMA8GA1UEAwwIcmVjLnRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANKCu5aN/ewOXRPfzAo27XMXhYFCThCjfInTAUIEkzs6jBFZ/eyyIa/kFoiD0tAKwfFfykYU+9XgXeLjetD7rYt3SN3bzzCznoBGbGHHM0Fecrn0LV+tC/NfBB61Yx7e0AMUxmxIeLNRQW5ca5CW8qcIiiQ4fl0BScUjc5+E9QLHAgMBAAGjUDBOMB0GA1UdDgQWBBRzcVu/2bwrgkES+FhYbxZqr7mUgjAfBgNVHSMEGDAWgBRzcVu/2bwrgkES+FhYbxZqr7mUgjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAFVQ8dZBOasOhsWzA/xpAV0WdsqVkxBxrkGIRlbHHBFqOBOOz2MFSzUNx4mDy0qDKI28gcWmWaVsxoQ9VFLD6YRJuUoM8MDNcZDJbKpfDumjvvfnUAK+SiM2c4Ur3xpf0wanCA60/q2bOtFiB0tfAH6RVuIgMC3qjHAIaKEld+fE", "\x00\x01\x00\x00\x00\x30\x82\x01\xf4\x30\x82\x01\x5d\xa0\x03\x02\x01\x02\x02\x09\x00\xac\x54\x7c\x55\x57\x87\x0e\xc7\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x1e\x17\x0d\x31\x33\x30\x35\x31\x32\x31\x39\x34\x38\x30\x39\x5a\x17\x0d\x31\x33\x30\x36\x31\x31\x31\x39\x34\x38\x30\x39\x5a\x30\x13\x31\x11\x30\x0f\x06\x03\x55\x04\x03\x0c\x08\x72\x65\x63\x2e\x74\x65\x73\x74\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xd2\x82\xbb\x96\x8d\xfd\xec\x0e\x5d\x13\xdf\xcc\x0a\x36\xed\x73\x17\x85\x81\x42\x4e\x10\xa3\x7c\x89\xd3\x01\x42\x04\x93\x3b\x3a\x8c\x11\x59\xfd\xec\xb2\x21\xaf\xe4\x16\x88\x83\xd2\xd0\x0a\xc1\xf1\x5f\xca\x46\x14\xfb\xd5\xe0\x5d\xe2\xe3\x7a\xd0\xfb\xad\x8b\x77\x48\xdd\xdb\xcf\x30\xb3\x9e\x80\x46\x6c\x61\xc7\x33\x41\x5e\x72\xb9\xf4\x2d\x5f\xad\x0b\xf3\x5f\x04\x1e\xb5\x63\x1e\xde\xd0\x03\x14\xc6\x6c\x48\x78\xb3\x51\x41\x6e\x5c\x6b\x90\x96\xf2\xa7\x08\x8a\x24\x38\x7e\x5d\x01\x49\xc5\x23\x73\x9f\x84\xf5\x02\xc7\x02\x03\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30\x16\x80\x14\x73\x71\x5b\xbf\xd9\xbc\x2b\x82\x41\x12\xf8\x58\x58\x6f\x16\x6a\xaf\xb9\x94\x82\x30\x0c\x06\x03\x55\x1d\x13\x04\x05\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x81\x81\x00\x55\x50\xf1\xd6\x41\x39\xab\x0e\x86\xc5\xb3\x03\xfc\x69\x01\x5d\x16\x76\xca\x95\x93\x10\x71\xae\x41\x88\x46\x56\xc7\x1c\x11\x6a\x38\x13\x8e\xcf\x63\x05\x4b\x35\x0d\xc7\x89\x83\xcb\x4a\x83\x28\x8d\xbc\x81\xc5\xa6\x59\xa5\x6c\xc6\x84\x3d\x54\x52\xc3\xe9\x84\x49\xb9\x4a\x0c\xf0\xc0\xcd\x71\x90\xc9\x6c\xaa\x5f\x0e\xe9\xa3\xbe\xf7\xe7\x50\x02\xbe\x4a\x23\x36\x73\x85\x2b\xdf\x1a\x5f\xd3\x06\xa7\x08\x0e\xb4\xfe\xad\x9b\x3a\xd1\x62\x07\x4b\x5f\x00\x7e\x91\x56\xe2\x20\x30\x2d\xea\x8c\x70\x08\x68\xa1\x25\x77\xe7\xc4"))
+ (CASE_S(QType::APL,"1:10.0.0.0/32", "\x00\x01\x20\x01\x0a"))
+ (CASE_S(QType::APL,"1:10.1.1.1/32", "\x00\x01\x20\x04\x0a\x01\x01\x01"))
+ (CASE_S(QType::APL,"1:10.1.1.0/24", "\x00\x01\x18\x03\x0a\x01\x01"))
+ (CASE_S(QType::APL,"1:60.0.0.0/8", "\x00\x01\x08\x01\x3c"))
+ (CASE_S(QType::APL,"1:10.1.1.1/32", "\x00\x01\x20\x04\x0a\x01\x01\x01"))
+ (CASE_S(QType::APL,"!1:10.1.1.1/32", "\x00\x01\x20\x84\x0a\x01\x01\x01"))
+ (CASE_S(QType::APL,"1:255.255.255.255/32", "\x00\x01\x20\x04\xff\xff\xff\xff"))
+ (CASE_S(QType::APL,"2:100::/8", "\x00\x02\x08\x01\x01"))
+ (CASE_S(QType::APL,"2:20::/16", "\x00\x02\x10\x02\x00\x20"))
+ (CASE_S(QType::APL,"2:2000::/8", "\x00\x02\x08\x01\x20"))
+ (CASE_S(QType::APL,"2:fe00::/8", "\x00\x02\x08\x01\xfe"))
+ (CASE_S(QType::APL,"2:fe80::/16", "\x00\x02\x10\x02\xfe\x80"))
+ (CASE_S(QType::APL,"2:2001::1/128", "\x00\x02\x80\x10\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"))
+ (CASE_S(QType::APL,"!2:2001::1/128", "\x00\x02\x80\x90\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"))
+ (CASE_S(QType::APL,"2:fe80:1234:5678:9910:8bc:3359:b2e8:720e/128", "\x00\x02\x80\x10\xfe\x80\x12\x34\x56\x78\x99\x10\x08\xbc\x33\x59\xb2\xe8\x72\x0e"))
+ (CASE_S(QType::APL,"2:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128","\x00\x02\x80\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"))
+ (CASE_S(QType::APL,"", ""))
+ (CASE_S(QType::APL,"1:10.0.0.0/32 1:10.1.1.1/32", "\x00\x01\x20\x01\x0a\x00\x01\x20\x04\x0a\x01\x01\x01"))
+ (CASE_S(QType::APL,"1:10.0.0.0/32 2:100::/8", "\x00\x01\x20\x01\x0a\x00\x02\x08\x01\x01"))
(CASE_L(QType::DS, "20642 8 2 04443ABE7E94C3985196BEAE5D548C727B044DDA5151E60D7CD76A9F D931D00E", "20642 8 2 04443abe7e94c3985196beae5d548c727b044dda5151e60d7cd76a9fd931d00e", "\x50\xa2\x08\x02\x04\x44\x3a\xbe\x7e\x94\xc3\x98\x51\x96\xbe\xae\x5d\x54\x8c\x72\x7b\x04\x4d\xda\x51\x51\xe6\x0d\x7c\xd7\x6a\x9f\xd9\x31\xd0\x0e"))
(CASE_S(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88"))
(CASE_L(QType::SSHFP, "1 1 aa65e3415a50d9b3519c2b17aceb815fc253 8d88", "1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88", "\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88"))
}
recData = rec->serialize(DNSName("rec.test"));
- std::shared_ptr<DNSRecordContent> rec2 = DNSRecordContent::unserialize(DNSName("rec.test"),q.getCode(),recData);
- BOOST_CHECK_MESSAGE(rec2 != NULL, "unserialize(rec.test, " << q.getCode() << ", recData) should not return NULL");
+ std::shared_ptr<DNSRecordContent> rec2 = DNSRecordContent::deserialize(DNSName("rec.test"),q.getCode(),recData);
+ BOOST_CHECK_MESSAGE(rec2 != NULL, "deserialize(rec.test, " << q.getCode() << ", recData) should not return NULL");
if (rec2 == NULL) continue;
// now verify the zone representation (here it can be different!)
REC_CHECK_EQUAL(rec2->getZoneRepresentation(), zoneval);
}
}
-bool test_dnsrecords_cc_predicate( std::exception const &ex ) { return true; }
+static bool test_dnsrecords_cc_predicate( std::exception const &ex ) { return true; }
// these *MUST NOT* parse properly!
BOOST_AUTO_TEST_CASE(test_record_types_bad_values) {
BOOST_CHECK_EQUAL(makeHexDump(std::string(pak.begin(),pak.end())), makeHexDump(packet));
}
+// special record test, because Unknown record types are the worst
+BOOST_AUTO_TEST_CASE(test_unknown_records_in) {
+
+ auto validUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1 42");
+
+ // we need at least two parts
+ BOOST_CHECK_THROW(auto notEnoughPartsUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\#"), MOADNSException);
+
+ // two parts are OK when the RDATA size is 0, not OK otherwise
+ auto validEmptyUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 0");
+ BOOST_CHECK_THROW(auto twoPartsNotZeroUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1"), MOADNSException);
+
+ // the first part has to be "\#"
+ BOOST_CHECK_THROW(auto invalidFirstPartUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\$ 0"), MOADNSException);
+
+ // RDATA length is not even
+ BOOST_CHECK_THROW(auto unevenUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1 A"), MOADNSException);
+
+ // RDATA length is not equal to the expected size
+ BOOST_CHECK_THROW(auto wrongRDATASizeUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 2 AA"), MOADNSException);
+
+ // RDATA is invalid (invalid hex value)
+ try {
+ auto invalidRDATAUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1 JJ");
+ // we should not reach that code
+ BOOST_CHECK(false);
+ // but if we do let's see what we got (likely what was left over on the stack)
+ BOOST_CHECK_EQUAL(invalidRDATAUnknown->getZoneRepresentation(), "\\# 1 jj");
+ }
+ catch (const MOADNSException& e)
+ {
+ // it's expected to end up there
+ }
+}
+
// special record test, because EUI are odd
BOOST_AUTO_TEST_CASE(test_eui_records_in) {
BOOST_AUTO_TEST_SUITE(test_lock_hh)
-static std::vector<std::unique_ptr<pthread_rwlock_t> > g_locks;
+static std::vector<ReadWriteLock> g_locks(1000);
static void lthread()
{
std::vector<ReadLock> rlocks;
for(auto& pp : g_locks)
- rlocks.emplace_back(&*pp);
-
+ rlocks.emplace_back(pp);
}
BOOST_AUTO_TEST_CASE(test_pdns_lock)
{
- for(unsigned int n=0; n < 1000; ++n) {
- auto p = make_unique<pthread_rwlock_t>();
- pthread_rwlock_init(p.get(), 0);
- g_locks.emplace_back(std::move(p));
- }
-
std::vector<ReadLock> rlocks;
for(auto& pp : g_locks)
- rlocks.emplace_back(&*pp);
+ rlocks.emplace_back(pp);
std::thread thr(lthread);
thr.join();
std::vector<WriteLock> wlocks;
for(auto& pp : g_locks)
- wlocks.emplace_back(&*pp);
+ wlocks.emplace_back(pp);
// on macOS, this TryReadLock throws (EDEADLK) instead of simply failing
// so we catch the exception and consider that success for this test
bool gotit = false;
try {
- TryReadLock trl(&*g_locks[0]);
+ TryReadLock trl(g_locks.at(0));
gotit = trl.gotIt();
}
catch(const PDNSException &e) {
BOOST_CHECK(!gotit);
wlocks.clear();
- TryReadLock trl2(&*g_locks[0]);
- BOOST_CHECK(trl2.gotIt());
-
- for(auto& pp : g_locks) {
- pthread_rwlock_destroy(pp.get());
- }
+
+ {
+ TryReadLock trl2(g_locks.at(0));
+ BOOST_CHECK(trl2.gotIt());
+ }
+
+ g_locks.clear();
}
BOOST_AUTO_TEST_SUITE_END()
#include <boost/assign/list_of.hpp>
#include <boost/tuple/tuple.hpp>
-#include "misc.hh"
-#include "dns.hh"
+
#include <arpa/inet.h>
-#include <utility>
+
+#include "dns.hh"
+#include "iputils.hh"
+#include "misc.hh"
+#include "utility.hh"
using std::string;
BOOST_CHECK(rfc1982check<uint64_t>(UINT64_MAX/2, UINT64_MAX-10));
}
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_CASE(test_reverse_name_to_ip)
+{
+ static const ComboAddress v4("192.0.2.1");
+ static const ComboAddress v6("2001:DB8::42");
+ BOOST_CHECK_EQUAL(reverseNameFromIP(v4).toString(), "1.2.0.192.in-addr.arpa.");
+ 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()
#include "auth-querycache.hh"
#include "arguments.hh"
#include <utility>
+#include <thread>
+
extern StatBag S;
BOOST_AUTO_TEST_SUITE(test_packetcache_cc)
static AuthQueryCache* g_QC;
static AtomicCounter g_QCmissing;
-static void *threadQCMangler(void* a)
+static void threadQCMangler(unsigned int offset)
try
{
vector<DNSZoneRecord> records;
- unsigned int offset=(unsigned int)(unsigned long)a;
for(unsigned int counter=0; counter < 100000; ++counter)
g_QC->insert(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), vector<DNSZoneRecord>(records), 3600, 1);
- return 0;
}
catch(PDNSException& e) {
cerr<<"Had error: "<<e.reason<<endl;
throw;
}
-static void *threadQCReader(void* a)
+static void threadQCReader(unsigned int offset)
try
{
- unsigned int offset=(unsigned int)(unsigned long)a;
vector<DNSZoneRecord> entry;
for(unsigned int counter=0; counter < 100000; ++counter)
if(!g_QC->getEntry(DNSName("hello ")+DNSName(std::to_string(counter+offset)), QType(QType::A), entry, 1)) {
g_QCmissing++;
}
- return 0;
}
catch(PDNSException& e) {
cerr<<"Had error in threadQCReader: "<<e.reason<<endl;
AuthQueryCache QC;
QC.setMaxEntries(1000000);
g_QC=&QC;
- pthread_t tid[4];
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadQCMangler, (void*)(i*1000000UL));
- void* res;
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ std::vector<std::thread> manglers;
+ for (int i=0; i < 4; ++i) {
+ manglers.push_back(std::thread(threadQCMangler, i*1000000UL));
+ }
+
+ for (auto& t : manglers) {
+ t.join();
+ }
+ manglers.clear();
BOOST_CHECK_EQUAL(QC.size() + S.read("deferred-cache-inserts"), 400000U);
BOOST_CHECK_SMALL(1.0*S.read("deferred-cache-inserts"), 10000.0);
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadQCReader, (void*)(i*1000000UL));
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ std::vector<std::thread> readers;
+ for (int i=0; i < 4; ++i) {
+ readers.push_back(std::thread(threadQCReader, i*1000000UL));
+ }
+
+ for (auto& t : readers) {
+ t.join();
+ }
+ readers.clear();
BOOST_CHECK(S.read("deferred-cache-inserts") + S.read("deferred-cache-lookup") >= g_QCmissing);
// BOOST_CHECK_EQUAL(S.read("deferred-cache-lookup"), 0); // cache cleaning invalidates this
static AuthPacketCache* g_PC;
static AtomicCounter g_PCmissing;
-static void *threadPCMangler(void* a)
+static void threadPCMangler(unsigned int offset)
try
{
- unsigned int offset=(unsigned int)(unsigned long)a;
for(unsigned int counter=0; counter < 100000; ++counter) {
vector<uint8_t> pak;
DNSName qname = DNSName("hello ")+DNSName(std::to_string(counter+offset));
we directly compute the hash instead of querying the
cache because 1/ it's faster 2/ no deferred-lookup issues
*/
- q.setHash(g_PC->canHashPacket(q.getString()));
+ q.setHash(g_PC->canHashPacket(q.getString(), false));
const unsigned int maxTTL = 3600;
g_PC->insert(q, r, maxTTL);
}
-
- return 0;
}
catch(PDNSException& e) {
cerr<<"Had error: "<<e.reason<<endl;
throw;
}
-static void *threadPCReader(void* a)
+static void threadPCReader(unsigned int offset)
try
{
- unsigned int offset=(unsigned int)(unsigned long)a;
vector<DNSZoneRecord> entry;
for(unsigned int counter=0; counter < 100000; ++counter) {
vector<uint8_t> pak;
g_PCmissing++;
}
}
-
- return 0;
}
catch(PDNSException& e) {
cerr<<"Had error in threadPCReader: "<<e.reason<<endl;
g_PC=&PC;
g_PCmissing = 0;
- pthread_t tid[4];
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadPCMangler, (void*)(i*1000000UL));
- void* res;
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ std::vector<std::thread> manglers;
+ for (int i=0; i < 4; ++i) {
+ manglers.push_back(std::thread(threadPCMangler, i*1000000UL));
+ }
+
+ for (auto& t : manglers) {
+ t.join();
+ }
+ manglers.clear();
BOOST_CHECK_EQUAL(PC.size() + S.read("deferred-packetcache-inserts"), 400000UL);
BOOST_CHECK_EQUAL(S.read("deferred-packetcache-lookup"), 0UL);
BOOST_CHECK_SMALL(1.0*S.read("deferred-packetcache-inserts"), 10000.0);
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadPCReader, (void*)(i*1000000UL));
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ std::vector<std::thread> readers;
+ for (int i=0; i < 4; ++i) {
+ readers.push_back(std::thread(threadPCReader, i*1000000UL));
+ }
+
+ for (auto& t : readers) {
+ t.join();
+ }
+ readers.clear();
/*
cerr<<"Misses: "<<S.read("packetcache-miss")<<endl;
}
bool g_stopCleaning;
-static void *cacheCleaner(void*)
+static void cacheCleaner()
try
{
while(!g_stopCleaning) {
g_QC->cleanup();
}
-
- return 0;
}
catch(PDNSException& e) {
cerr<<"Had error in cacheCleaner: "<<e.reason<<endl;
sleep(1);
g_QC=&QC;
- pthread_t tid[4];
+ std::vector<std::thread> readers;
+ for (int i=0; i < 4; ++i) {
+ if (i < 3) {
+ readers.push_back(std::thread(threadQCReader, i*1000000UL));
+ }
+ else {
+ readers.push_back(std::thread(cacheCleaner));
+ }
+ }
- pthread_create(&tid[0], 0, threadQCReader, (void*)(0*1000000UL));
- pthread_create(&tid[1], 0, threadQCReader, (void*)(1*1000000UL));
- pthread_create(&tid[2], 0, threadQCReader, (void*)(2*1000000UL));
- // pthread_create(&tid[2], 0, threadMangler, (void*)(0*1000000UL));
- pthread_create(&tid[3], 0, cacheCleaner, 0);
+ for (int i = 0; i < 3 ; ++i) {
+ readers.at(i).join();
+ }
- void *res;
- for(int i=0; i < 3 ; ++i)
- pthread_join(tid[i], &res);
g_stopCleaning=true;
- pthread_join(tid[3], &res);
+ readers.at(3).join();
+
+ readers.clear();
}
catch(PDNSException& e) {
cerr<<"Had error in test_QueryCacheClean: "<<e.reason<<endl;
uint16_t qtype = QType::AAAA;
EDNSSubnetOpts opt;
DNSPacketWriter::optvect_t ednsOptions;
+ static const std::unordered_set<uint16_t> optionsToSkip{ EDNSOptionCode::COOKIE };
+ static const std::unordered_set<uint16_t> noOptionsToSkip{ };
{
/* same query, different IDs */
pw1.getHeader()->qr = false;
pw1.getHeader()->id = 0x42;
string spacket1((const char*)&packet[0], packet.size());
- auto hash1 = PacketCache::canHashPacket(spacket1);
+ auto hash1 = PacketCache::canHashPacket(spacket1, false);
packet.clear();
DNSPacketWriter pw2(packet, qname, qtype);
pw2.getHeader()->qr = false;
pw2.getHeader()->id = 0x84;
string spacket2((const char*)&packet[0], packet.size());
- auto hash2 = PacketCache::canHashPacket(spacket2);
+ auto hash2 = PacketCache::canHashPacket(spacket2, false);
BOOST_CHECK_EQUAL(hash1, hash2);
- BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname));
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, optionsToSkip));
}
{
pw1.getHeader()->rd = true;
pw1.getHeader()->qr = false;
pw1.getHeader()->id = 0x42;
- opt.source = Netmask("10.0.18.199/32");
+ opt.source = Netmask("10.0.152.74/32");
ednsOptions.clear();
ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
pw1.addOpt(512, 0, 0, ednsOptions);
pw1.commit();
string spacket1((const char*)&packet[0], packet.size());
- auto hash1 = PacketCache::canHashPacket(spacket1);
+ auto hash1 = PacketCache::canHashPacket(spacket1, false);
packet.clear();
DNSPacketWriter pw2(packet, qname, qtype);
pw2.getHeader()->rd = true;
pw2.getHeader()->qr = false;
pw2.getHeader()->id = 0x84;
- opt.source = Netmask("10.0.131.66/32");
+ opt.source = Netmask("10.2.70.250/32");
ednsOptions.clear();
ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
pw2.addOpt(512, 0, 0, ednsOptions);
pw2.commit();
string spacket2((const char*)&packet[0], packet.size());
- auto hash2 = PacketCache::canHashPacket(spacket2);
+ auto hash2 = PacketCache::canHashPacket(spacket2, false);
BOOST_CHECK_EQUAL(hash1, hash2);
/* the hash is the same but we should _not_ match */
- BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname));
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname, optionsToSkip));
+
+#if 0
+ /* to be able to compute a new collision if the hashing function is updated */
+ {
+ std::map<uint32_t, Netmask> colMap;
+ size_t collisions = 0;
+ size_t total = 0;
+
+ for (size_t idxA = 0; idxA < 256; idxA++) {
+ for (size_t idxB = 0; idxB < 256; idxB++) {
+ for (size_t idxC = 0; idxC < 256; idxC++) {
+ vector<uint8_t> secondQuery;
+ DNSPacketWriter pwFQ(secondQuery, qname, QType::AAAA, QClass::IN, 0);
+ pwFQ.getHeader()->rd = 1;
+ pwFQ.getHeader()->qr = false;
+ pwFQ.getHeader()->id = 0x42;
+ opt.source = Netmask("10." + std::to_string(idxA) + "." + std::to_string(idxB) + "." + std::to_string(idxC) + "/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pwFQ.addOpt(512, 0, 0, ednsOptions);
+ pwFQ.commit();
+ auto secondKey = PacketCache::canHashPacket(std::string(reinterpret_cast<const char *>(secondQuery.data()), secondQuery.size()), false);
+ auto pair = colMap.insert(std::make_pair(secondKey, opt.source));
+ total++;
+ if (!pair.second) {
+ collisions++;
+ cerr<<"Collision between "<<colMap[secondKey].toString()<<" and "<<opt.source.toString()<<" for key "<<secondKey<<endl;
+ goto done1;
+ }
+ }
+ }
+ }
+ done1:
+ cerr<<"collisions: "<<collisions<<endl;
+ cerr<<"total: "<<total<<endl;
+ }
+#endif
}
{
pw1.getHeader()->rd = true;
pw1.getHeader()->qr = false;
pw1.getHeader()->id = 0x42;
- opt.source = Netmask("47.8.0.0/32");
+ opt.source = Netmask("10.0.34.159/32");
ednsOptions.clear();
ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
pw1.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
pw1.commit();
string spacket1((const char*)&packet[0], packet.size());
- auto hash1 = PacketCache::canHashPacket(spacket1);
+ auto hash1 = PacketCache::canHashPacket(spacket1, false);
packet.clear();
DNSPacketWriter pw2(packet, qname, qtype);
pw2.getHeader()->rd = true;
pw2.getHeader()->qr = false;
pw2.getHeader()->id = 0x84;
- opt.source = Netmask("18.43.1.0/32");
+ opt.source = Netmask("10.0.179.58/32");
ednsOptions.clear();
ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
/* no EDNSOpts::DNSSECOK !! */
pw2.commit();
string spacket2((const char*)&packet[0], packet.size());
- auto hash2 = PacketCache::canHashPacket(spacket2);
+ auto hash2 = PacketCache::canHashPacket(spacket2, false);
BOOST_CHECK_EQUAL(hash1, hash2);
/* the hash is the same but we should _not_ match */
- BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname));
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname, optionsToSkip));
}
{
EDNSCookiesOpt cookiesOpt;
cookiesOpt.client = string("deadbeef");
cookiesOpt.server = string("deadbeef");
- cookiesOpt.server[4] = -42;
- cookiesOpt.server[5] = -6;
- cookiesOpt.server[6] = 1;
- cookiesOpt.server[7] = 0;
ednsOptions.push_back(std::make_pair(EDNSOptionCode::COOKIE, makeEDNSCookiesOptString(cookiesOpt)));
pw1.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
pw1.commit();
string spacket1((const char*)&packet[0], packet.size());
- auto hash1 = PacketCache::canHashPacket(spacket1);
+ auto hash1 = PacketCache::canHashPacket(spacket1, false);
packet.clear();
DNSPacketWriter pw2(packet, qname, qtype);
ednsOptions.clear();
ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
cookiesOpt.client = string("deadbeef");
- cookiesOpt.server = string("deadbeef");
- cookiesOpt.server[4] = 29;
- cookiesOpt.server[5] = -79;
- cookiesOpt.server[6] = 1;
- cookiesOpt.server[7] = 0;
+ cookiesOpt.server = string("badc0fee");
ednsOptions.push_back(std::make_pair(EDNSOptionCode::COOKIE, makeEDNSCookiesOptString(cookiesOpt)));
pw2.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
pw2.commit();
string spacket2((const char*)&packet[0], packet.size());
- auto hash2 = PacketCache::canHashPacket(spacket2);
+ auto hash2 = PacketCache::canHashPacket(spacket2, false);
BOOST_CHECK_EQUAL(hash1, hash2);
/* the hash is the same but we should _not_ match */
- BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname));
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname, noOptionsToSkip));
+ /* but it does match if we skip cookies, though */
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, optionsToSkip));
+
+#if 0
+ {
+ /* to be able to compute a new collision if the packet cache hashing code is updated */
+ std::map<uint32_t, Netmask> colMap;
+ size_t collisions = 0;
+ size_t total = 0;
+
+ for (size_t idxA = 0; idxA < 256; idxA++) {
+ for (size_t idxB = 0; idxB < 256; idxB++) {
+ for (size_t idxC = 0; idxC < 256; idxC++) {
+ vector<uint8_t> secondQuery;
+ DNSPacketWriter pwFQ(secondQuery, qname, QType::AAAA, QClass::IN, 0);
+ pwFQ.getHeader()->rd = 1;
+ pwFQ.getHeader()->qr = false;
+ pwFQ.getHeader()->id = 0x42;
+ opt.source = Netmask("10." + std::to_string(idxA) + "." + std::to_string(idxB) + "." + std::to_string(idxC) + "/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pwFQ.addOpt(512, 0, 32768, ednsOptions);
+ pwFQ.commit();
+ auto secondKey = PacketCache::canHashPacket(std::string(reinterpret_cast<const char *>(secondQuery.data()), secondQuery.size()), false);
+ colMap.insert(std::make_pair(secondKey, opt.source));
+
+ secondQuery.clear();
+ DNSPacketWriter pwSQ(secondQuery, qname, QType::AAAA, QClass::IN, 0);
+ pwSQ.getHeader()->rd = 1;
+ pwSQ.getHeader()->qr = false;
+ pwSQ.getHeader()->id = 0x42;
+ opt.source = Netmask("10." + std::to_string(idxA) + "." + std::to_string(idxB) + "." + std::to_string(idxC) + "/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pwSQ.addOpt(512, 0, 0, ednsOptions);
+ pwSQ.commit();
+ secondKey = PacketCache::canHashPacket(std::string(reinterpret_cast<const char *>(secondQuery.data()), secondQuery.size()), false);
+
+ total++;
+ if (colMap.count(secondKey)) {
+ collisions++;
+ cerr<<"Collision between "<<colMap[secondKey].toString()<<" and "<<opt.source.toString()<<" for key "<<secondKey<<endl;
+ goto done2;
+ }
+ }
+ }
+ }
+ done2:
+ cerr<<"collisions: "<<collisions<<endl;
+ cerr<<"total: "<<total<<endl;
+ }
+#endif
}
}
uint16_t qtype = QType::AAAA;
EDNSSubnetOpts opt;
DNSPacketWriter::optvect_t ednsOptions;
- uint16_t ecsBegin;
- uint16_t ecsEnd;
{
vector<uint8_t> packet;
*(ptr + 1) = 255;
/* truncate the end of the OPT header to try to trigger an out of bounds read */
spacket1.resize(spacket1.size() - 6);
- PacketCache::canHashPacket(spacket1, &ecsBegin, &ecsEnd);
- /* no ECS */
- BOOST_CHECK_EQUAL(ecsBegin, 0);
- BOOST_CHECK_EQUAL(ecsEnd, 0);
+ BOOST_CHECK_NO_THROW(PacketCache::canHashPacket(spacket1, true));
}
}
uint16_t qtype = QType::AAAA;
EDNSSubnetOpts opt;
DNSPacketWriter::optvect_t ednsOptions;
- uint16_t ecsBegin;
- uint16_t ecsEnd;
+ static const std::unordered_set<uint16_t> optionsToSkip{ EDNSOptionCode::COOKIE, EDNSOptionCode::ECS };
{
/* same query, different IDs */
pw1.getHeader()->qr = false;
pw1.getHeader()->id = 0x42;
string spacket1((const char*)&packet[0], packet.size());
- auto hash1 = PacketCache::canHashPacket(spacket1, &ecsBegin, &ecsEnd);
- /* no ECS */
- BOOST_CHECK_EQUAL(ecsBegin, 0);
- BOOST_CHECK_EQUAL(ecsEnd, 0);
+ auto hash1 = PacketCache::canHashPacket(spacket1, true);
packet.clear();
DNSPacketWriter pw2(packet, qname, qtype);
pw2.getHeader()->qr = false;
pw2.getHeader()->id = 0x84;
string spacket2((const char*)&packet[0], packet.size());
- auto hash2 = PacketCache::canHashPacket(spacket2, &ecsBegin, &ecsEnd);
- /* no ECS */
- BOOST_CHECK_EQUAL(ecsBegin, 0);
- BOOST_CHECK_EQUAL(ecsEnd, 0);
+ auto hash2 = PacketCache::canHashPacket(spacket2, true);
BOOST_CHECK_EQUAL(hash1, hash2);
- BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, ecsBegin, ecsEnd));
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, optionsToSkip));
}
{
pw1.commit();
string spacket1((const char*)&packet[0], packet.size());
- auto hash1 = PacketCache::canHashPacket(spacket1, &ecsBegin, &ecsEnd);
- /* ECS value */
- BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
- BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+ auto hash1 = PacketCache::canHashPacket(spacket1, true);
packet.clear();
DNSPacketWriter pw2(packet, qname, qtype);
pw2.commit();
string spacket2((const char*)&packet[0], packet.size());
- auto hash2 = PacketCache::canHashPacket(spacket2, &ecsBegin, &ecsEnd);
- /* ECS value */
- BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
- BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+ auto hash2 = PacketCache::canHashPacket(spacket2, true);
BOOST_CHECK_EQUAL(hash1, hash2);
/* the hash is the same and we don't hash the ECS so we should match */
- BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, ecsBegin, ecsEnd));
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, optionsToSkip));
}
{
pw1.commit();
string spacket1((const char*)&packet[0], packet.size());
- auto hash1 = PacketCache::canHashPacket(spacket1, &ecsBegin, &ecsEnd);
- /* ECS value */
- BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
- BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+ auto hash1 = PacketCache::canHashPacket(spacket1, true);
packet.clear();
DNSPacketWriter pw2(packet, qname, qtype);
pw2.commit();
string spacket2((const char*)&packet[0], packet.size());
- auto hash2 = PacketCache::canHashPacket(spacket2, &ecsBegin, &ecsEnd);
- /* ECS value */
- BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
- BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+ auto hash2 = PacketCache::canHashPacket(spacket2, true);
BOOST_CHECK_EQUAL(hash1, hash2);
/* the hash is the same but we should _not_ match, even though we skip the ECS part, because the cookies are different */
- BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname, ecsBegin, ecsEnd));
+ static const std::unordered_set<uint16_t> skipECSOnly{ EDNSOptionCode::ECS };
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname, skipECSOnly));
+
+ /* we do match if we skip the cookie as well */
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, optionsToSkip));
}
}
--- /dev/null
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+#include <boost/test/unit_test.hpp>
+
+#include "iputils.hh"
+#include "proxy-protocol.hh"
+
+using namespace boost;
+using std::string;
+
+
+BOOST_AUTO_TEST_SUITE(test_proxy_protocol_cc)
+
+#define BINARY(s) (std::string(s, sizeof(s) - 1))
+
+#define PROXYMAGIC "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
+#define PROXYMAGICLEN sizeof(PROXYMAGIC)-1
+
+static string proxymagic(PROXYMAGIC, PROXYMAGICLEN);
+
+BOOST_AUTO_TEST_CASE(test_roundtrip) {
+ std::vector<ProxyProtocolValue> values;
+ string proxyheader;
+
+ bool ptcp = true;
+ ComboAddress src("65.66.67.68:18762"); // 18762 = 0x494a = "IJ"
+ ComboAddress dest("69.70.71.72:19276"); // 19276 = 0x4b4c = "KL"
+ proxyheader = makeProxyHeader(ptcp, src, dest, values);
+
+ BOOST_CHECK_EQUAL(proxyheader, BINARY(
+ PROXYMAGIC
+ "\x21" // version | command
+ "\x11" // ipv4=0x10 | TCP=0x1
+ "\x00\x0c" // 4 bytes IPv4 * 2 + 2 port numbers = 8 + 2 * 2 =12 = 0xc
+ "ABCD" // 65.66.67.68
+ "EFGH" // 69.70.71.72
+ "IJ" // src port
+ "KL" // dst port
+ ));
+
+ bool proxy;
+ bool ptcp2;
+ ComboAddress src2, dest2;
+
+ BOOST_CHECK_EQUAL(parseProxyHeader(proxyheader, proxy, src2, dest2, ptcp2, values), 28);
+
+ BOOST_CHECK_EQUAL(proxy, true);
+ BOOST_CHECK_EQUAL(ptcp2, true);
+ BOOST_CHECK(src2 == src);
+ BOOST_CHECK(dest2 == dest);
+}
+
+BOOST_AUTO_TEST_CASE(test_local_proxy_header) {
+ auto payload = makeLocalProxyHeader();
+
+ BOOST_CHECK_EQUAL(payload, BINARY(
+ PROXYMAGIC
+ "\x20" // version | command
+ "\x00" // protocol family and address are set to 0
+ "\x00\x00" // no content
+ ));
+
+ bool proxy;
+ bool tcp = false;
+ ComboAddress src, dest;
+ std::vector<ProxyProtocolValue> values;
+
+ BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src, dest, tcp, values), 16);
+
+ BOOST_CHECK_EQUAL(proxy, false);
+ BOOST_CHECK_EQUAL(tcp, false);
+ BOOST_CHECK_EQUAL(values.size(), 0U);
+}
+
+BOOST_AUTO_TEST_CASE(test_tlv_values_content_len_signedness) {
+ std::string largeValue;
+ /* this value will make the content length parsing fail in case of signedness mistake */
+ largeValue.resize(65128, 'A');
+ const std::vector<ProxyProtocolValue> values = { { "foo", 0 }, { largeValue, 255 }};
+
+ const bool tcp = false;
+ const ComboAddress src("[2001:db8::1]:0");
+ const ComboAddress dest("[::1]:65535");
+ const auto payload = makeProxyHeader(tcp, src, dest, values);
+
+ bool proxy;
+ bool tcp2;
+ ComboAddress src2;
+ ComboAddress dest2;
+ std::vector<ProxyProtocolValue> parsedValues;
+
+ BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src2, dest2, tcp2, parsedValues), 16 + 36 + 6 + 65131);
+ BOOST_CHECK_EQUAL(proxy, true);
+ BOOST_CHECK_EQUAL(tcp2, tcp);
+ BOOST_CHECK(src2 == src);
+ BOOST_CHECK(dest2 == dest);
+ BOOST_REQUIRE_EQUAL(parsedValues.size(), values.size());
+ for (size_t idx = 0; idx < values.size(); idx++) {
+ BOOST_CHECK_EQUAL(parsedValues.at(idx).type, values.at(idx).type);
+ BOOST_CHECK_EQUAL(parsedValues.at(idx).content, values.at(idx).content);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_tlv_values_length_signedness) {
+ std::string largeValue;
+ /* this value will make the TLV length parsing fail in case of signedness mistake */
+ largeValue.resize(65000, 'A');
+ const std::vector<ProxyProtocolValue> values = { { "foo", 0 }, { largeValue, 255 }};
+
+ const bool tcp = false;
+ const ComboAddress src("[2001:db8::1]:0");
+ const ComboAddress dest("[::1]:65535");
+ const auto payload = makeProxyHeader(tcp, src, dest, values);
+
+ bool proxy;
+ bool tcp2;
+ ComboAddress src2;
+ ComboAddress dest2;
+ std::vector<ProxyProtocolValue> parsedValues;
+
+ BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src2, dest2, tcp2, parsedValues), 16 + 36 + 6 + 65003);
+ BOOST_CHECK_EQUAL(proxy, true);
+ BOOST_CHECK_EQUAL(tcp2, tcp);
+ BOOST_CHECK(src2 == src);
+ BOOST_CHECK(dest2 == dest);
+ BOOST_REQUIRE_EQUAL(parsedValues.size(), values.size());
+ for (size_t idx = 0; idx < values.size(); idx++) {
+ BOOST_CHECK_EQUAL(parsedValues.at(idx).type, values.at(idx).type);
+ BOOST_CHECK_EQUAL(parsedValues.at(idx).content, values.at(idx).content);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_parsing_invalid_headers) {
+ const std::vector<ProxyProtocolValue> noValues;
+
+ const bool tcp = false;
+ const ComboAddress src("[2001:db8::1]:0");
+ const ComboAddress dest("[::1]:65535");
+ const auto payload = makeProxyHeader(tcp, src, dest, noValues);
+
+ bool proxy;
+ bool tcp2;
+ ComboAddress src2;
+ ComboAddress dest2;
+ std::vector<ProxyProtocolValue> values;
+
+ {
+ /* just checking that everything works */
+ BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src2, dest2, tcp2, values), 52);
+ BOOST_CHECK_EQUAL(proxy, true);
+ BOOST_CHECK_EQUAL(tcp2, tcp);
+ BOOST_CHECK(src2 == src);
+ BOOST_CHECK(dest2 == dest);
+ BOOST_CHECK_EQUAL(values.size(), 0U);
+ }
+
+ {
+ /* too short (not even full header) */
+ std::string truncated = payload;
+ truncated.resize(15);
+ BOOST_CHECK_EQUAL(parseProxyHeader(truncated, proxy, src2, dest2, tcp2, values), -1);
+ }
+
+ {
+ /* too short (missing address part) */
+ std::string truncated = payload;
+ truncated.resize(/* full header */ 16 + /* two IPv6s + port */ 36 - /* truncation */ 1);
+ BOOST_CHECK_EQUAL(parseProxyHeader(truncated, proxy, src2, dest2, tcp2, values), -1);
+ }
+
+ {
+ /* too short (missing TLV) */
+ values = { { "foo", 0 }, { "bar", 255 }} ;
+ const auto payloadWithValues = makeProxyHeader(tcp, src, dest, values);
+
+ std::string truncated = payloadWithValues;
+ truncated.resize(/* full header */ 16 + /* two IPv6s + port */ 36 + /* TLV 1 */ 6 + /* TLV 2 */ 6 - /* truncation */ 2);
+ BOOST_CHECK_EQUAL(parseProxyHeader(truncated, proxy, src2, dest2, tcp2, values), -2);
+ }
+
+ {
+ /* invalid magic */
+ std::string invalid = payload;
+ invalid.at(4) = 42;
+ BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0);
+ }
+
+ {
+ /* invalid version */
+ std::string invalid = payload;
+ invalid.at(12) = 0x10 | 0x01;
+ BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0);
+ }
+
+ {
+ /* invalid command */
+ std::string invalid = payload;
+ invalid.at(12) = 0x20 | 0x02;
+ BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0);
+ }
+
+ {
+ /* invalid family */
+ std::string invalid = payload;
+ invalid.at(13) = (0x04 << 4) | 0x01 /* STREAM */;
+ BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0);
+ }
+
+ {
+ /* invalid address */
+ std::string invalid = payload;
+ invalid.at(13) = (0x02 /* AF_INET */ << 4) | 0x03;
+ BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0);
+ }
+
+ {
+ /* TLV advertised len gets out of bounds */
+ values = { { "foo", 0 }, { "bar", 255 }} ;
+ const auto payloadWithValues = makeProxyHeader(tcp, src, dest, values);
+ std::string invalid = payloadWithValues;
+ /* full header (16) + two IPv6s + port (36) + TLV (6) TLV 2 (6) */
+ invalid.at(59) += 1;
+ BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0);
+ }
+}
+
+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, 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, 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, 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, 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, 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, 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, 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, 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, boost::none);
BOOST_CHECK_EQUAL(rpc.size(), 2U);
/* We still get the correct response for the first tag */
BOOST_AUTO_TEST_CASE(test_generic_signers)
{
- for (const auto signer : signers) {
+ for (const auto& signer : signers) {
DNSKEYRecordContent drc;
auto dcke = DNSCryptoKeyEngine::makeFromISCString(drc, signer.iscMap);
#include <boost/tuple/tuple.hpp>
#include <stdint.h>
+#include <thread>
#include "misc.hh"
#include "dns.hh"
#include "statbag.hh"
using std::string;
-static void *threadMangler(void* a)
+static void threadMangler(AtomicCounter* ac)
{
- AtomicCounter* ac=(AtomicCounter*)a;
for(unsigned int n=0; n < 1000000; ++n)
(*ac)++;
- return 0;
}
-static void *threadMangler2(void* a)
+static void threadMangler2(StatBag* S)
{
- StatBag* S = (StatBag*)a;
for(unsigned int n=0; n < 1000000; ++n)
S->inc("c");
- return 0;
}
BOOST_CHECK_EQUAL(s.read("a"), n+1);
AtomicCounter* acc = s.getPointer("c");
- pthread_t tid[4];
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadMangler, (void*)acc);
- void* res;
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ std::vector<std::thread> manglers;
+ for (int i=0; i < 4; ++i) {
+ manglers.push_back(std::thread(threadMangler, acc));
+ }
+
+ for (auto& t : manglers) {
+ t.join();
+ }
+ manglers.clear();
BOOST_CHECK_EQUAL(s.read("c"), 4000000U);
s.set("c", 0);
- for(int i=0; i < 4; ++i)
- pthread_create(&tid[i], 0, threadMangler2, (void*)&s);
+ for (int i=0; i < 4; ++i) {
+ manglers.push_back(std::thread(threadMangler2, &s));
+ }
- for(int i=0; i < 4 ; ++i)
- pthread_join(tid[i], &res);
+ for (auto& t : manglers) {
+ t.join();
+ }
+ manglers.clear();
BOOST_CHECK_EQUAL(s.read("c"), 4000000U);
--- /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();
}
-bool init_unit_test() {
+static bool init_unit_test() {
reportAllTypes();
return true;
}
// entry point:
int main(int argc, char* argv[])
{
+ S.d_allowRedeclare = true;
return boost::unit_test::unit_test_main( &init_unit_test, argc, argv );
}
TKEYRecordContent tkey_in;
std::shared_ptr<TKEYRecordContent> tkey_out(new TKEYRecordContent());
DNSName name;
- bool sign = false;
if (!p.getTKEYRecord(&tkey_in, &name)) {
g_log<<Logger::Error<<"TKEY request but no TKEY RR found"<<endl;
tkey_out->d_inception = time((time_t*)NULL);
tkey_out->d_expiration = tkey_out->d_inception+15;
- GssContext ctx(name);
-
if (tkey_in.d_mode == 3) { // establish context
if (tkey_in.d_algo == DNSName("gss-tsig.")) {
- std::vector<std::string> meta;
- DNSName tmpName(name);
- do {
- if (B.getDomainMetadata(tmpName, "GSS-ACCEPTOR-PRINCIPAL", meta) && meta.size()>0) {
- break;
- }
- } while(tmpName.chopOff());
-
- if (meta.size()>0) {
- ctx.setLocalPrincipal(meta[0]);
- }
- // try to get a context
- if (!ctx.accept(tkey_in.d_key, tkey_out->d_key))
- tkey_out->d_error = 19;
- else
- sign = true;
+ tkey_out->d_error = 19;
} else {
tkey_out->d_error = 21; // BADALGO
}
r->setRcode(RCode::NotAuth);
return;
}
- if (ctx.valid())
- ctx.destroy();
- else
- tkey_out->d_error = 20; // BADNAME (because we have no support for anything here)
+
+ tkey_out->d_error = 20; // BADNAME (because we have no support for anything here)
} else {
if (p.d_havetsig == false && tkey_in.d_mode != 2) { // unauthenticated
if (p.d.opcode == Opcode::Update)
zrr.dr.d_content = tkey_out;
zrr.dr.d_place = DNSResourceRecord::ANSWER;
r->addRecord(std::move(zrr));
-
- if (sign)
- {
- TSIGRecordContent trc;
- trc.d_algoName = DNSName("gss-tsig");
- trc.d_time = tkey_out->d_inception;
- trc.d_fudge = 300;
- trc.d_mac = "";
- trc.d_origID = p.d.id;
- trc.d_eRcode = 0;
- trc.d_otherData = "";
- // this should cause it to lookup name context
- r->setTSIGDetails(trc, name, name.toStringNoDot(), "", false);
- }
-
r->commitD();
}
for(const auto& sig : csp.second.signatures) {
cerr<<"got rrsig "<<sig->d_signer<<"/"<<sig->d_tag<<endl;
vState state = getKeysFor(tro, sig->d_signer, keys);
- cerr<<"! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
+ cerr<<"! state = "<<state<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
// dsmap.insert(make_pair(dsrc.d_tag, dsrc));
}
}
else {
cerr<<"no sigs, hoping for Insecure"<<endl;
vState state = getKeysFor(tro, qname, keys);
- cerr<<"! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
+ cerr<<"! state = "<<state<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
}
cerr<<"! validated "<<validrrsets.size()<<" RRsets out of "<<cspmap.size()<<endl;
#include "digests.hh"
#include "base64.hh"
#include "dnssecinfra.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "arguments.hh"
#include "dns_random.hh"
+#include "query-local-address.hh"
StatBag S;
int main(int argc, char** argv)
try
{
- ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
- ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::";
+ pdns::parseQueryLocalAddress(":: 0.0.0.0");
reportAllTypes();
#include "dns_random.hh"
#include "misc.hh"
#include "pdnsexception.hh"
+#include "tsigutils.hh"
#include <string>
/*
#include "tsigverifier.hh"
#include "dnssecinfra.hh"
-#include "gss_context.hh"
bool TSIGTCPVerifier::check(const string& data, const MOADNSParser& mdp)
{
extern StatBag S;
vector<UeberBackend *>UeberBackend::instances;
-pthread_mutex_t UeberBackend::instances_lock=PTHREAD_MUTEX_INITIALIZER;
+std::mutex UeberBackend::instances_lock;
// initially we are blocked
bool UeberBackend::d_go=false;
-pthread_mutex_t UeberBackend::d_mut = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t UeberBackend::d_cond = PTHREAD_COND_INITIALIZER;
+std::mutex UeberBackend::d_mut;
+std::condition_variable UeberBackend::d_cond;
//! Loads a module and reports it to all UeberBackend threads
bool UeberBackend::loadmodule(const string &name)
void UeberBackend::go(void)
{
- pthread_mutex_lock(&d_mut);
- d_go=true;
- pthread_cond_broadcast(&d_cond);
- pthread_mutex_unlock(&d_mut);
+ {
+ std::unique_lock<std::mutex> l(d_mut);
+ d_go = true;
+ }
+ d_cond.notify_all();
}
bool UeberBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial)
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)
UeberBackend::UeberBackend(const string &pname)
{
- pthread_mutex_lock(&instances_lock);
- instances.push_back(this); // report to the static list of ourself
- pthread_mutex_unlock(&instances_lock);
+ {
+ std::lock_guard<std::mutex> l(instances_lock);
+ instances.push_back(this); // report to the static list of ourself
+ }
d_negcached=0;
d_ancount=0;
d_cache_ttl = ::arg().asNum("query-cache-ttl");
d_negcache_ttl = ::arg().asNum("negquery-cache-ttl");
- d_tid=pthread_self();
- d_stale=false;
+ d_stale = false;
backends=BackendMakers().all(pname=="key-only");
}
-void del(DNSBackend* d)
+static void del(DNSBackend* d)
{
delete d;
}
void UeberBackend::cleanup()
{
- pthread_mutex_lock(&instances_lock);
-
- remove(instances.begin(),instances.end(),this);
- instances.resize(instances.size()-1);
-
- pthread_mutex_unlock(&instances_lock);
+ {
+ std::lock_guard<std::mutex> l(instances_lock);
+ remove(instances.begin(),instances.end(),this);
+ instances.resize(instances.size()-1);
+ }
for_each(backends.begin(),backends.end(),del);
}
}
DLOG(g_log<<"UeberBackend received question for "<<qtype.getName()<<" of "<<qname<<endl);
- if(!d_go) {
- pthread_mutex_lock(&d_mut);
- while (d_go==false) {
- g_log<<Logger::Error<<"UeberBackend is blocked, waiting for 'go'"<<endl;
- pthread_cond_wait(&d_cond, &d_mut);
- g_log<<Logger::Error<<"Broadcast received, unblocked"<<endl;
- }
- pthread_mutex_unlock(&d_mut);
+ if (!d_go) {
+ g_log<<Logger::Error<<"UeberBackend is blocked, waiting for 'go'"<<endl;
+ std::unique_lock<std::mutex> l(d_mut);
+ d_cond.wait(l, []{ return d_go == true; });
+ g_log<<Logger::Error<<"Broadcast received, unblocked"<<endl;
}
d_domain_id=zoneId;
#include <map>
#include <string>
#include <algorithm>
-#include <pthread.h>
-#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
*/
static vector<UeberBackend *>instances;
- static pthread_mutex_t instances_lock;
+ static std::mutex instances_lock;
static bool loadmodule(const string &name);
static bool loadModules(const vector<string>& modules, const string& path);
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);
bool searchRecords(const string &pattern, int maxResults, vector<DNSResourceRecord>& result);
bool searchComments(const string &pattern, int maxResults, vector<Comment>& result);
private:
- pthread_t d_tid;
handle d_handle;
vector<DNSZoneRecord> d_answers;
vector<DNSZoneRecord>::const_iterator d_cachehandleiter;
- static pthread_mutex_t d_mut;
- static pthread_cond_t d_cond;
+ static std::mutex d_mut;
+ static std::condition_variable d_cond;
struct Question
{
#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.
- If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
- useful when we have a positive answer synthetized from a wildcard and we only need to prove that the exact
+ useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
name does not exist.
*/
dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype, bool referralToUnsigned, bool wantsNoDataProof, bool needWildcardProof, unsigned int wildcardLabelsCount)
{
bool nsec3Seen = false;
if (!needWildcardProof && wildcardLabelsCount == 0) {
- throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthetized from a wildcard");
+ throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
}
for(const auto& v : validrrsets) {
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;
}
/*
qname.prependRawLabel(labelsToAdd.back());
labelsToAdd.pop_back();
auto records = dro.get(qname, (uint16_t)QType::NS);
- for (const auto record : records) {
+ for (const auto& record : records) {
if(record.d_type != QType::NS || record.d_name != qname)
continue;
foundCut = true;
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;
}
}
- vector<uint16_t> toSignTags;
- for (const auto& key : tkeys) {
- toSignTags.push_back(key->getTag());
- }
-
// cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
// these counts could be off if we somehow ended up with
// duplicate keys. Should switch to a type that prevents that.
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)
DNSName getSigner(const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
{
- for (const auto sig : signatures) {
+ for (const auto& sig : signatures) {
if (sig) {
return sig->d_signer;
}
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
{
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#ifdef __cpp_lib_string_view
+using pdns_string_view = std::string_view;
+#else
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 106400
+// string_view already exists in 1.61.0 but string_view::at() is not usable with modern compilers, see:
+// https://github.com/boostorg/utility/pull/26
+#include <boost/utility/string_view.hpp>
+using pdns_string_view = boost::string_view;
+#elif BOOST_VERSION >= 105300
+#include <boost/utility/string_ref.hpp>
+using pdns_string_view = boost::string_ref;
+#else
+using pdns_string_view = std::string;
+#endif
+#endif
void WebServer::registerBareHandler(const string& url, HandlerFunction handler)
{
- YaHTTP::THandlerFunction f = boost::bind(&bareHandlerWrapper, handler, _1, _2);
+ YaHTTP::THandlerFunction f = [=](YaHTTP::Request* req, YaHTTP::Response* resp){return bareHandlerWrapper(handler, req, resp);};
YaHTTP::Router::Any(url, f);
}
}
void WebServer::registerApiHandler(const string& url, HandlerFunction handler, bool allowPassword) {
- HandlerFunction f = boost::bind(&WebServer::apiWrapper, this, handler, _1, _2, allowPassword);
+ HandlerFunction f = std::bind(&WebServer::apiWrapper, this, handler, std::placeholders::_1, std::placeholders::_2, allowPassword);
registerBareHandler(url, f);
}
}
void WebServer::registerWebHandler(const string& url, HandlerFunction handler) {
- HandlerFunction f = boost::bind(&WebServer::webWrapper, this, handler, _1, _2);
+ HandlerFunction f = std::bind(&WebServer::webWrapper, this, handler, std::placeholders::_1, std::placeholders::_2);
registerBareHandler(url, f);
}
static const std::set<uint16_t> exclusiveEntryTypes = { QType::CNAME };
AuthWebServer::AuthWebServer() :
- d_tid(0),
d_start(time(nullptr)),
d_min10(0),
d_min5(0),
void AuthWebServer::go()
{
S.doRings();
- pthread_create(&d_tid, 0, webThreadHelper, this);
- pthread_create(&d_tid, 0, statThreadHelper, this);
+ std::thread webT(std::bind(&AuthWebServer::webThread, this));
+ webT.detach();
+ std::thread statT(std::bind(&AuthWebServer::statThread, this));
+ statT.detach();
}
void AuthWebServer::statThread()
}
}
-void *AuthWebServer::statThreadHelper(void *p)
-{
- AuthWebServer *self=static_cast<AuthWebServer *>(p);
- self->statThread();
- return 0; // never reached
-}
-
-void *AuthWebServer::webThreadHelper(void *p)
-{
- AuthWebServer *self=static_cast<AuthWebServer *>(p);
- self->webThread();
- return 0; // never reached
-}
-
static string htmlescape(const string &s) {
string result;
for(string::const_iterator it=s.begin(); it!=s.end(); ++it) {
return result;
}
-void printtable(ostringstream &ret, const string &ringname, const string &title, int limit=10)
+static void printtable(ostringstream &ret, const string &ringname, const string &title, int limit=10)
{
int tot=0;
int entries=0;
+ "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 (zonemaster.size()) {
- di.backend->setMaster(zonename, boost::join(zonemaster, ","));
+ if (document["account"].is_string()) {
+ account = document["account"].string_value();
+ } else {
+ account = boost::none;
}
- if (document["kind"].is_string()) {
- di.backend->setKind(zonename, DomainInfo::stringToKind(stringFromJson(document, "kind")));
+}
+
+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 (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.");
+ }
}
}
"NOTIFY-DNSUPDATE",
"ALSO-NOTIFY",
"AXFR-MASTER-TSIG",
- "GSS-ALLOW-AXFR-PRINCIPAL",
- "GSS-ACCEPTOR-PRINCIPAL",
"IXFR",
"LUA-AXFR-SCRIPT",
"NSEC3NARROW",
}
}
-/** Throws ApiException if records which violate RRset contraints are present.
+/** Throws ApiException if records which violate RRset constraints are present.
* NOTE: sorts records in-place.
*
* Constraints being checked:
}
}
+ 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))
if(di.masters.empty())
throw ApiException("Domain '"+zonename.toString()+"' is not a slave domain (or has no master defined)");
- random_shuffle(di.masters.begin(), di.masters.end());
+ shuffle(di.masters.begin(), di.masters.end(), pdns::dns_random_engine());
Communicator.addSuckRequest(zonename, di.masters.front());
resp->setSuccessResult("Added retrieval request for '"+zonename.toString()+"' from master "+di.masters.front().toLogString());
}
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).
B.getAllDomains(&domains, true);
- for(const DomainInfo di: domains)
+ for(const DomainInfo& di: domains)
{
if ((objectType == ObjectType::ALL || objectType == ObjectType::ZONE) && ents < maxEnts && sm.match(di.zone)) {
doc.push_back(Json::object {
resp->setBody(doc);
}
-void apiServerCacheFlush(HttpRequest* req, HttpResponse* resp) {
+static void apiServerCacheFlush(HttpRequest* req, HttpResponse* resp) {
if(req->method != "PUT")
throw HttpMethodNotAllowedException();
});
}
+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";
d_ws->registerApiHandler("/api", &apiDiscovery);
}
if (::arg().mustDo("webserver")) {
- d_ws->registerWebHandler("/style.css", boost::bind(&AuthWebServer::cssfunction, this, _1, _2));
- d_ws->registerWebHandler("/", boost::bind(&AuthWebServer::indexfunction, this, _1, _2));
+ 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>
static string makePercentage(const double& val);
private:
- static void *webThreadHelper(void *);
- static void *statThreadHelper(void *p);
void indexfunction(HttpRequest* req, HttpResponse* resp);
void cssfunction(HttpRequest* req, HttpResponse* resp);
void jsonstat(HttpRequest* req, HttpResponse* resp);
void printargs(ostringstream &ret);
void webThread();
void statThread();
- pthread_t d_tid;
time_t d_start;
double d_min10, d_min5, d_min1;
DNSName canon = apiNameToDNSName(req->getvars["domain"]);
bool subtree = (req->getvars.count("subtree") > 0 && req->getvars["subtree"].compare("true") == 0);
- int count = broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, canon, subtree, 0xffff));
- count += broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, canon, subtree, 0xffff));
- count += broadcastAccFunction<uint64_t>(boost::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." }
auto zone = luaconf->dfe.getZone(i);
if (zone == nullptr)
continue;
- auto name = zone->getName();
- auto stats = getRPZZoneStats(*name);
+ const auto& name = zone->getName();
+ auto stats = getRPZZoneStats(name);
if (stats == nullptr)
continue;
Json::object zoneInfo = {
{"last_update", (double)stats->d_lastUpdate},
{"serial", (double)stats->d_serial},
};
- ret[*name] = zoneInfo;
+ ret[name] = zoneInfo;
}
resp->setBody(ret);
}
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;
d_ws->bind();
// legacy dispatch
- d_ws->registerApiHandler("/jsonstat", boost::bind(&RecursorWebServer::jsonstat, this, _1, _2), true);
+ d_ws->registerApiHandler("/jsonstat", std::bind(&RecursorWebServer::jsonstat, this, std::placeholders::_1, std::placeholders::_2), true);
d_ws->registerApiHandler("/api/v1/servers/localhost/cache/flush", &apiServerCacheFlush);
d_ws->registerApiHandler("/api/v1/servers/localhost/config/allow-from", &apiServerConfigAllowFrom);
d_ws->registerApiHandler("/api/v1/servers/localhost/config", &apiServerConfig);
void AsyncServer::asyncWaitForConnections(FDMultiplexer* fdm, const newconnectioncb_t& callback)
{
d_asyncNewConnectionCallback = callback;
- fdm->addReadFD(d_server_socket.getHandle(), boost::bind(&AsyncServer::newConnection, this));
+ fdm->addReadFD(d_server_socket.getHandle(), std::bind(&AsyncServer::newConnection, this));
}
void AsyncServer::newConnection()
// This is an entry point from FDM, so it needs to catch everything.
void AsyncWebServer::serveConnection(std::shared_ptr<Socket> client) const {
+ if (!client->acl(d_acl)) {
+ return;
+ }
+
const string logprefix = d_logprefix + to_string(getUniqueID()) + " ";
HttpRequest req(logprefix);
auto server = std::dynamic_pointer_cast<AsyncServer>(d_server);
if (!server)
return;
- server->asyncWaitForConnections(d_fdm, boost::bind(&AsyncWebServer::serveConnection, this, _1));
+ server->asyncWaitForConnections(d_fdm, std::bind(&AsyncWebServer::serveConnection, this, std::placeholders::_1));
}
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);
map<DNSName,bool> g_recorddata;
map<DNSName, map<string, bool> > g_recordttl;
-std::string encode_non_ascii( const std::string &input ) {
+static std::string encode_non_ascii( const std::string &input ) {
std::ostringstream out;
for ( auto i : input ) {
trim_left(content);
}
- bool auth = true;
- if(qtype == "NS" && !pdns_iequals(qname, zname)) {
- auth=false;
- }
-
- if(g_mode==MYSQL || g_mode==SQLITE) {
- cout<<"insert into records (domain_id, name, type,content,ttl,prio,disabled) select id ,"<<
- sqlstr(toLower(qname))<<", "<<
- sqlstr(qtype)<<", "<<
- sqlstr(stripDotContent(content))<<", "<<ttl<<", "<<prio<<", "<<disabled<<
- " from domains where name="<<toLower(sqlstr(zname))<<";\n";
-
- if(!recordcomment.empty()) {
- cout<<"insert into comments (domain_id,name,type,modified_at, comment) select id, "<<toLower(sqlstr(stripDot(qname)))<<", "<<sqlstr(qtype)<<", "<<time(0)<<", "<<sqlstr(recordcomment)<<" from domains where name="<<toLower(sqlstr(zname))<<";\n";
- }
- }
- else if(g_mode==POSTGRES) {
- cout<<"insert into records (domain_id, name, ordername, auth, type,content,ttl,prio,disabled) select id ,"<<
- sqlstr(toLower(qname))<<", "<<
- sqlstr(DNSName(qname).makeRelative(DNSName(zname)).makeLowerCase().labelReverse().toString(" ", false))<<", '"<< (auth ? 't' : 'f') <<"', "<<
- sqlstr(qtype)<<", "<<
- sqlstr(stripDotContent(content))<<", "<<ttl<<", "<<prio<<", '"<<(disabled ? 't': 'f') <<
- "' from domains where name="<<toLower(sqlstr(zname))<<";\n";
+ cout<<"insert into records (domain_id, name, type,content,ttl,prio,disabled) select id ,"<<
+ sqlstr(toLower(qname))<<", "<<
+ sqlstr(qtype)<<", "<<
+ sqlstr(stripDotContent(content))<<", "<<ttl<<", "<<prio<<", "<<(g_mode==POSTGRES ? (disabled ? "'t'" : "'f'") : std::to_string(disabled))<<
+ " from domains where name="<<toLower(sqlstr(zname))<<";\n";
+
+ if(!recordcomment.empty()) {
+ cout<<"insert into comments (domain_id,name,type,modified_at, comment) select id, "<<toLower(sqlstr(stripDot(qname)))<<", "<<sqlstr(qtype)<<", "<<time(0)<<", "<<sqlstr(recordcomment)<<" from domains where name="<<toLower(sqlstr(zname))<<";\n";
}
}
return true;
}
-void chopComment(string& line)
+static void chopComment(string& line)
{
if(line.find(';')==string::npos)
return;
line.resize(pos);
}
-bool findAndElide(string& line, char c)
+static bool findAndElide(string& line, char c)
{
string::size_type pos, len = line.length();
bool inQuote=false;
"""
Try to get get a key that does not exist
"""
- name = "idontexist"
+ name = "idonotexist"
r = self.session.get(self.url(
"/api/v1/servers/localhost/tsigkeys/" + name + '.'),
headers={'accept': 'application/json'})
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 = {
bind_dnssec_db=bind_dnssec_db))
pdnsconf.write(cls._config_template % params)
+ os.system("sqlite3 ./configs/auth/powerdns.sqlite < ../modules/gsqlite3backend/schema.sqlite3.sql")
+
pdnsutilCmd = [os.environ['PDNSUTIL'],
'--config-dir=%s' % confdir,
'create-bind-db',
@classmethod
def generateAllAuthConfig(cls, confdir):
- if cls._zones:
- cls.generateAuthConfig(confdir)
- cls.generateAuthNamedConf(confdir, cls._zones.keys())
+ cls.generateAuthConfig(confdir)
+ cls.generateAuthNamedConf(confdir, cls._zones.keys())
- for zonename, zonecontent in cls._zones.items():
- cls.generateAuthZone(confdir,
- zonename,
- zonecontent)
- if cls._zone_keys.get(zonename, None):
- cls.secureZone(confdir, zonename, cls._zone_keys.get(zonename))
+ for zonename, zonecontent in cls._zones.items():
+ cls.generateAuthZone(confdir,
+ zonename,
+ zonecontent)
+ if cls._zone_keys.get(zonename, None):
+ cls.secureZone(confdir, zonename, cls._zone_keys.get(zonename))
@classmethod
def startAuth(cls, confdir, ipaddress):
if timeout:
sock.settimeout(timeout)
- sock.connect(("127.0.0.1", cls._recursorPort))
+ sock.connect(("127.0.0.1", cls._authPort))
try:
wire = query.to_wire()
message = dns.message.from_wire(data)
return message
-
@classmethod
- def sendTCPQuery(cls, query, timeout=2.0):
+ def sendTCPQueryMultiResponse(cls, query, timeout=2.0, count=1):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if timeout:
sock.settimeout(timeout)
wire = query.to_wire()
sock.send(struct.pack("!H", len(wire)))
sock.send(wire)
- data = sock.recv(2)
- if data:
- (datalen,) = struct.unpack("!H", data)
- data = sock.recv(datalen)
except socket.timeout as e:
- print("Timeout: %s" % (str(e)))
- data = None
+ raise Exception("Timeout: %s" % (str(e)))
except socket.error as e:
- print("Network error: %s" % (str(e)))
- data = None
- finally:
- sock.close()
+ raise Exception("Network error: %s" % (str(e)))
- message = None
- if data:
- message = dns.message.from_wire(data)
- return message
+ messages = []
+ for i in range(count):
+ try:
+ data = sock.recv(2)
+ print("got data", repr(data))
+ if data:
+ (datalen,) = struct.unpack("!H", data)
+ data = sock.recv(datalen)
+ messages.append(dns.message.from_wire(data))
+ else:
+ break
+ except socket.timeout as e:
+ raise Exception("Timeout: %s" % (str(e)))
+ except socket.error as e:
+ raise Exception("Network error: %s" % (str(e)))
+
+ return messages
def setUp(self):
# This function is called before every tests
nose>=1.3.7
Twisted>0.15.0
requests>=2.18.4
+git+https://github.com/PowerDNS/xfrserver.git@0.2
--- /dev/null
+import dns
+import os
+import subprocess
+import time
+
+from authtests import AuthTest
+from xfrserver.xfrserver import AXFRServer
+
+zones = {
+ 1: ["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 1 2 3 4 5
+@ 4242 NS ns1.example.
+@ 4242 NS ns2.example.
+ns1.example. 4242 A 192.0.2.1
+ns2.example. 4242 A 192.0.2.2
+"""],
+ 2: ["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 2 2 3 4 5
+@ 4242 NS ns1.example.
+@ 4242 NS ns2.example.
+ns1.example. 4242 A 192.0.2.1
+ns2.example. 4242 A 192.0.2.2
+newrecord.example. 8484 A 192.0.2.42
+"""],
+ 3: ["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 3 2 3 4 5""","""
+@ 86400 SOA foo bar 2 2 3 4 5""","""
+@ 86400 SOA foo bar 3 2 3 4 5""","""
+@ 4242 NS ns3.example.
+"""],
+ 5: ["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 5 2 3 4 5""","""
+@ 86400 SOA foo bar 3 2 3 4 5""","""
+@ 86400 SOA foo bar 4 2 3 4 5""","""
+@ 86400 SOA foo bar 4 2 3 4 5""","""
+@ 86400 SOA foo bar 5 2 3 4 5""","""
+@ 4242 NS ns5.example.
+"""],
+ 8: ["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 8 2 3 4 5""","""
+@ 86400 SOA foo bar 5 2 3 4 5""","""
+@ 86400 SOA foo bar 6 2 3 4 5""","""
+@ 86400 SOA foo bar 6 2 3 4 5""","""
+@ 86400 SOA foo bar 7 2 3 4 5""","""
+@ 86400 SOA foo bar 7 2 3 4 5""","""
+@ 86400 SOA foo bar 8 2 3 4 5""","""
+"""]
+
+
+}
+
+
+xfrServerPort = 4244
+xfrServer = AXFRServer(xfrServerPort, zones)
+
+class TestIXFR(AuthTest):
+ _config_template = """
+launch=gsqlite3 bind
+gsqlite3-database=configs/auth/powerdns.sqlite
+gsqlite3-dnssec
+slave
+slave-cycle-interval=1
+query-cache-ttl=20
+negquery-cache-ttl=60
+"""
+
+ _zones = {}
+ global xfrServerPort
+ _xfrDone = 0
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestIXFR, cls).setUpClass()
+ os.system("$PDNSUTIL --config-dir=configs/auth create-slave-zone example. 127.0.0.1:%s" % (xfrServerPort,))
+ os.system("$PDNSUTIL --config-dir=configs/auth set-meta example. IXFR 1")
+
+ def waitUntilCorrectSerialIsLoaded(self, serial, timeout=10):
+ global xfrServer
+
+ xfrServer.moveToSerial(serial)
+
+ attempts = 0
+ while attempts < timeout:
+ print('attempts=%s timeout=%s' % (attempts, timeout))
+ servedSerial = xfrServer.getServedSerial()
+ print('servedSerial=%s' % servedSerial)
+ if servedSerial > serial:
+ raise AssertionError("Expected serial %d, got %d" % (serial, servedSerial))
+ if servedSerial == serial:
+ self._xfrDone = self._xfrDone + 1
+ time.sleep(1)
+ return
+
+ attempts = attempts + 1
+ time.sleep(1)
+
+ raise AssertionError("Waited %d seconds for the serial to be updated to %d but the last served serial is still %d" % (timeout, serial, servedSerial))
+
+ def checkFullZone(self, serial, data=None):
+ global zones
+
+ # FIXME: 90% duplication from _getRecordsForSerial
+ zone = []
+ if not data:
+ data = zones[serial]
+ for i in dns.zone.from_text('\n'.join(data), relativize=False).iterate_rdatasets():
+ n, rds = i
+ rrs=dns.rrset.RRset(n, rds.rdclass, rds.rdtype)
+ rrs.update(rds)
+ zone.append(rrs)
+
+ expected =[[zone[0]], sorted(zone[1:], key=lambda rrset: (rrset.name, rrset.rdtype)), [zone[0]]] # AXFRs are SOA-wrapped
+
+ query = dns.message.make_query('example.', 'AXFR')
+ res = self.sendTCPQueryMultiResponse(query, count=len(expected))
+ answers = [r.answer for r in res]
+ answers[1].sort(key=lambda rrset: (rrset.name, rrset.rdtype))
+ self.assertEqual(answers, expected)
+
+ def checkIXFR(self, fromserial, toserial):
+ global zones, xfrServer
+
+ ixfr = []
+ soa1 = xfrServer._getSOAForSerial(fromserial)
+ soa2 = xfrServer._getSOAForSerial(toserial)
+ newrecord = [r for r in xfrServer._getRecordsForSerial(toserial) if r.name==dns.name.from_text('newrecord.example.')]
+ query = dns.message.make_query('example.', 'IXFR')
+ query.authority = [soa1]
+
+ expected = [[soa2], [soa1], [soa2], newrecord, [soa2]]
+ res = self.sendTCPQueryMultiResponse(query, count=len(expected))
+ answers = [r.answer for r in res]
+
+ # answers[1].sort(key=lambda rrset: (rrset.name, rrset.rdtype))
+ self.assertEqual(answers, expected)
+ # check the TTLs
+ answerPos = 0
+ for expectedAnswer in expected:
+ pos = 0
+ for rec in expectedAnswer:
+ self.assertEquals(rec.ttl, answers[answerPos][pos].ttl)
+ pos = pos + 1
+ answerPos = answerPos + 1
+
+ def test_a_XFR(self):
+ print("x1")
+ self.waitUntilCorrectSerialIsLoaded(1)
+ print("x2")
+ self.checkFullZone(1)
+ print("x3")
+
+ self.waitUntilCorrectSerialIsLoaded(2)
+ print("x4")
+ self.checkFullZone(2)
+ print("x5")
+
+ self.waitUntilCorrectSerialIsLoaded(3)
+ print("x7")
+ self.checkFullZone(3, data=["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 3 2 3 4 5
+@ 4242 NS ns1.example.
+@ 4242 NS ns2.example.
+@ 4242 NS ns3.example.
+ns1.example. 4242 A 192.0.2.1
+ns2.example. 4242 A 192.0.2.2
+newrecord.example. 8484 A 192.0.2.42
+"""])
+ print("x8")
+
+ self.waitUntilCorrectSerialIsLoaded(5)
+ print("x7")
+ self.checkFullZone(5, data=["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 5 2 3 4 5
+@ 4242 NS ns1.example.
+@ 4242 NS ns2.example.
+@ 4242 NS ns3.example.
+@ 4242 NS ns5.example.
+ns1.example. 4242 A 192.0.2.1
+ns2.example. 4242 A 192.0.2.2
+newrecord.example. 8484 A 192.0.2.42
+"""])
+ print("x8")
+
+
+ # _b_ because we expect post-XFR testing state
+ def test_b_UDP_SOA_existing(self):
+ query = dns.message.make_query('example.', 'SOA')
+ expected = dns.message.make_response(query)
+ expected.answer.append(xfrServer._getSOAForSerial(5))
+ expected.flags |= dns.flags.AA
+
+ response = self.sendUDPQuery(query)
+
+ self.assertEquals(expected, response)
+ # check the TTLs
+ pos = 0
+ for rec in expected.answer:
+ self.assertEquals(rec.ttl, response.answer[pos].ttl)
+ pos = pos + 1
+
+ def test_b_UDP_SOA_not_loaded(self):
+ query = dns.message.make_query('example2.', 'SOA')
+ expected = dns.message.make_response(query)
+ expected.set_rcode(dns.rcode.REFUSED)
+
+ response = self.sendUDPQuery(query)
+ self.assertEquals(expected, response)
+
+ def test_b_UDP_SOA_not_configured(self):
+ query = dns.message.make_query('example3.', 'SOA')
+ expected = dns.message.make_response(query)
+ expected.set_rcode(dns.rcode.REFUSED)
+
+ response = self.sendUDPQuery(query)
+ self.assertEquals(expected, response)
+
+ def test_d_XFR(self):
+ self.waitUntilCorrectSerialIsLoaded(8)
+ print("x7")
+ self.checkFullZone(7, data=["""
+$ORIGIN example.""","""
+@ 86400 SOA foo bar 8 2 3 4 5
+@ 4242 NS ns1.example.
+@ 4242 NS ns2.example.
+@ 4242 NS ns3.example.
+@ 4242 NS ns5.example.
+ns1.example. 4242 A 192.0.2.1
+ns2.example. 4242 A 192.0.2.2
+newrecord.example. 8484 A 192.0.2.42
+"""])
+ print("x8")
+ ret = subprocess.check_output([os.environ['PDNSUTIL'],
+ '--config-dir=configs/auth',
+ 'list-zone', 'example'], stderr=subprocess.STDOUT)
+ rets = ret.split('\n')
+
+ self.assertEqual(1, sum('SOA' in l for l in ret.split('\n')))
\ No newline at end of file
any IN LUA A "'192.0.2.1'"
any IN TXT "hello there"
+resolve IN LUA A ";local r=resolve('localhost', 1) local t={{}} for _,v in ipairs(r) do table.insert(t, v:toString()) end return t"
+
+*.createforward IN LUA A "createForward()"
+
""",
}
_web_rrsets = []
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertAnyRRsetInAnswer(res, expected)
- # the first IP should not be up so only second shoud be returned
+ # the first IP should not be up so only second should be returned
expected = [expected[1]]
res = self.sendUDPQuery(query)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertRcodeEqual(res, dns.rcode.NOERROR)
self.assertEqual(self.sortRRsets(res.answer), self.sortRRsets(response.answer))
+ def testResolve(self):
+ """
+ Test resolve() function
+ """
+ name = 'resolve.example.org.'
+
+ 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, '127.0.0.1'))
+
+ res = self.sendUDPQuery(query)
+ 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)
--- /dev/null
+#!/usr/bin/env python
+
+import copy
+import socket
+import struct
+
+class ProxyProtocol(object):
+ MAGIC = b'\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A'
+ # Header is magic + versioncommand (1) + family (1) + content length (2)
+ HEADER_SIZE = len(MAGIC) + 1 + 1 + 2
+ PORT_SIZE = 2
+
+ def consumed(self):
+ return self.offset
+
+ def parseHeader(self, data):
+ if len(data) < self.HEADER_SIZE:
+ return False
+
+ if data[:len(self.MAGIC)] != self.MAGIC:
+ return False
+
+ value = struct.unpack('!B', bytes(bytearray([data[12]])))[0]
+ self.version = value >> 4
+ if self.version != 0x02:
+ return False
+
+ self.command = value & ~0x20
+ self.local = False
+ self.offset = self.HEADER_SIZE
+
+ if self.command == 0x00:
+ self.local = True
+ elif self.command == 0x01:
+ value = struct.unpack('!B', bytes(bytearray([data[13]])))[0]
+ self.family = value >> 4
+ if self.family == 0x01:
+ self.addrSize = 4
+ elif self.family == 0x02:
+ self.addrSize = 16
+ else:
+ return False
+
+ self.protocol = value & ~0xF0
+ if self.protocol == 0x01:
+ self.tcp = True
+ elif self.protocol == 0x02:
+ self.tcp = False
+ else:
+ return False
+ else:
+ return False
+
+ self.contentLen = struct.unpack("!H", data[14:16])[0]
+
+ if not self.local:
+ if self.contentLen < (self.addrSize * 2 + self.PORT_SIZE * 2):
+ return False
+
+ return True
+
+ def getAddr(self, data):
+ if len(data) < (self.consumed() + self.addrSize):
+ return False
+
+ value = None
+ if self.family == 0x01:
+ value = socket.inet_ntop(socket.AF_INET, data[self.offset:self.offset + self.addrSize])
+ else:
+ value = socket.inet_ntop(socket.AF_INET6, data[self.offset:self.offset + self.addrSize])
+
+ self.offset = self.offset + self.addrSize
+ return value
+
+ def getPort(self, data):
+ if len(data) < (self.consumed() + self.PORT_SIZE):
+ return False
+
+ value = struct.unpack('!H', data[self.offset:self.offset + self.PORT_SIZE])[0]
+ self.offset = self.offset + self.PORT_SIZE
+ return value
+
+ def parseAddressesAndPorts(self, data):
+ if self.local:
+ return True
+
+ if len(data) < (self.consumed() + self.addrSize * 2 + self.PORT_SIZE * 2):
+ return False
+
+ self.source = self.getAddr(data)
+ self.destination = self.getAddr(data)
+ self.sourcePort = self.getPort(data)
+ self.destinationPort = self.getPort(data)
+ return True
+
+ def parseAdditionalValues(self, data):
+ self.values = []
+ if self.local:
+ return True
+
+ if len(data) < (self.HEADER_SIZE + self.contentLen):
+ return False
+
+ remaining = self.HEADER_SIZE + self.contentLen - self.consumed()
+ if len(data) < remaining:
+ return False
+
+ while remaining >= 3:
+ valueType = struct.unpack("!B", bytes(bytearray([data[self.offset]])))[0]
+ self.offset = self.offset + 1
+ valueLen = struct.unpack("!H", data[self.offset:self.offset+2])[0]
+ self.offset = self.offset + 2
+
+ remaining = remaining - 3
+ if valueLen > 0:
+ if valueLen > remaining:
+ return False
+ self.values.append([valueType, data[self.offset:self.offset+valueLen]])
+ self.offset = self.offset + valueLen
+ remaining = remaining - valueLen
+
+ else:
+ self.values.append([valueType, ""])
+
+ return True
+
+ @classmethod
+ def getPayload(cls, local, tcp, v6, source, destination, sourcePort, destinationPort, values):
+ payload = copy.deepcopy(cls.MAGIC)
+ version = 0x02
+
+ if local:
+ command = 0x00
+ else:
+ command = 0x01
+
+ value = struct.pack('!B', (version << 4) + command)
+ payload = payload + value
+
+ addrSize = 0
+ family = 0x00
+ protocol = 0x00
+ if not local:
+ if tcp:
+ protocol = 0x01
+ else:
+ protocol = 0x02
+ # sorry but compatibility with python 2 is awful for this,
+ # not going to waste time on it
+ if not v6:
+ family = 0x01
+ addrSize = 4
+ else:
+ family = 0x02
+ addrSize = 16
+
+ value = struct.pack('!B', (family << 4) + protocol)
+ payload = payload + value
+
+ contentSize = 0
+ if not local:
+ contentSize = contentSize + addrSize * 2 + cls.PORT_SIZE *2
+
+ valuesSize = 0
+ for value in values:
+ valuesSize = valuesSize + 3 + len(value[1])
+
+ contentSize = contentSize + valuesSize
+
+ value = struct.pack('!H', contentSize)
+ payload = payload + value
+
+ if not local:
+ if family == 0x01:
+ af = socket.AF_INET
+ else:
+ af = socket.AF_INET6
+
+ value = socket.inet_pton(af, source)
+ payload = payload + value
+ value = socket.inet_pton(af, destination)
+ payload = payload + value
+ value = struct.pack('!H', sourcePort)
+ payload = payload + value
+ value = struct.pack('!H', destinationPort)
+ payload = payload + value
+
+ for value in values:
+ valueType = struct.pack('!B', value[0])
+ valueLen = struct.pack('!H', len(value[1]))
+ payload = payload + valueType + valueLen + value[1]
+
+ return payload
_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__))
--- /dev/null
+../regression-tests.common/proxyprotocol.py
\ No newline at end of file
def testServersIDontExist(self):
"""
- API: /api/v1/servers/idontexist (should be 404)
+ API: /api/v1/servers/idonotexist (should be 404)
"""
headers = {'x-api-key': self._webServerAPIKey}
- url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/idontexist'
+ url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/idonotexist'
r = requests.get(url, headers=headers, timeout=self._webTimeout)
self.assertEquals(r.status_code, 404)
'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)
import time
import dns
import clientsubnetoption
+import cookiesoption
from dnsdisttests import DNSDistTest
class TestCaching(DNSDistTest):
self.assertEquals(total, 1)
+ def testCacheDifferentCookies(self):
+ """
+ Cache: The content of cookies should be ignored by the cache
+ """
+ ttl = 600
+ name = 'cache-different-cookies.cache.tests.powerdns.com.'
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ # first query to fill the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ eco = cookiesoption.CookiesOption(b'badc0fee', b'badc0fee')
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ # second query should be served from the cache
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ receivedResponse.id = response.id
+ self.assertEquals(receivedResponse, response)
+
+ def testCacheCookies(self):
+ """
+ Cache: A query with a cookie should not match one without any cookie
+ """
+ ttl = 600
+ name = 'cache-cookie.cache.tests.powerdns.com.'
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ # first query to fill the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+ # second query should NOT be served from the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ def testCacheSameCookieDifferentECS(self):
+ """
+ Cache: The content of cookies should be ignored by the cache but not the ECS one
+ """
+ ttl = 600
+ name = 'cache-different-cookies-different-ecs.cache.tests.powerdns.com.'
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+class TestCachingHashingCookies(DNSDistTest):
+
+ _config_template = """
+ pc = newPacketCache(100, {maxTTL=86400, minTTL=1, cookieHashing=true})
+ getPool(""):setCache(pc)
+ newServer{address="127.0.0.1:%d"}
+ """
+
+ def testCached(self):
+ """
+ Cache: Served from cache
+
+ dnsdist is configured to cache entries, we are sending several
+ identical requests and checking that the backend only receive
+ the first one.
+ """
+ numberOfQueries = 10
+ name = 'cached.cache.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'AAAA', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ # first query to fill the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ for _ in range(numberOfQueries):
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, response)
+
+ total = 0
+ for key in self._responsesCounter:
+ total += self._responsesCounter[key]
+ TestCaching._responsesCounter[key] = 0
+
+ self.assertEquals(total, 1)
+
+ # TCP should not be cached
+ # first query to fill the cache
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ for _ in range(numberOfQueries):
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, response)
+
+ total = 0
+ for key in self._responsesCounter:
+ total += self._responsesCounter[key]
+ TestCaching._responsesCounter[key] = 0
+
+ self.assertEquals(total, 1)
+
+
+ def testCacheDifferentCookies(self):
+ """
+ Cache: The content of cookies should NOT be ignored by the cache (cookieHashing is set)
+ """
+ ttl = 600
+ name = 'cache-different-cookies.cache-cookie-hashing.tests.powerdns.com.'
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ # first query to fill the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ eco = cookiesoption.CookiesOption(b'badc0fee', b'badc0fee')
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco])
+ differentResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '2001:DB8::1')
+ differentResponse.answer.append(rrset)
+ # second query should NOT be served from the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, differentResponse)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, differentResponse)
+ self.assertNotEquals(receivedResponse, response)
+
+ def testCacheCookies(self):
+ """
+ Cache: A query with a cookie should not match one without any cookie (cookieHashing=true)
+ """
+ ttl = 600
+ name = 'cache-cookie.cache-cookie-hashing.tests.powerdns.com.'
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ # first query to fill the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+ # second query should NOT be served from the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ def testCacheSameCookieDifferentECS(self):
+ """
+ Cache: The content of cookies should NOT be ignored by the cache (cookieHashing=true), even with ECS there
+ """
+ ttl = 600
+ name = 'cache-different-cookies-different-ecs.cache-cookie-hashing.tests.powerdns.com.'
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
+ eco = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
+ query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, payload=4096, options=[eco,ecso])
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(receivedResponse, response)
+
class TestTempFailureCacheTTLAction(DNSDistTest):
_config_template = """
time.sleep(self._negCacheTTL + 1)
- # we should not have cached for longer than the negativel TTL
+ # we should not have cached for longer than the negative TTL
# so it should be a miss
(receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
self.assertTrue(receivedQuery)
Cache: Collision with no ECS parsing
"""
name = 'collision-no-ecs-parsing.cache.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('10.0.188.3', 32)
+ ecso = clientsubnetoption.ClientSubnetOption('10.0.226.63', 32)
query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
query.flags = dns.flags.RD
response = dns.message.make_response(query)
# second query will hash to the same key, triggering a collision which
# will not be detected because the qname, qtype, qclass and flags will
# match and EDNS Client Subnet parsing has not been enabled
- ecso2 = clientsubnetoption.ClientSubnetOption('10.0.192.138', 32)
+ ecso2 = clientsubnetoption.ClientSubnetOption('10.1.60.19', 32)
query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
query2.flags = dns.flags.RD
(_, receivedResponse) = self.sendUDPQuery(query2, response=None, useQueue=False)
Cache: Collision with ECS parsing
"""
name = 'collision-with-ecs-parsing.cache.tests.powerdns.com.'
- ecso = clientsubnetoption.ClientSubnetOption('10.0.115.61', 32)
+ ecso = clientsubnetoption.ClientSubnetOption('10.0.150.206', 32)
query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512)
query.flags = dns.flags.RD
response = dns.message.make_response(query)
# second query will hash to the same key, triggering a collision which
# _will_ be detected this time because the qname, qtype, qclass and flags will
# match but EDNS Client Subnet parsing is now enabled and will detect the issue
- ecso2 = clientsubnetoption.ClientSubnetOption('10.0.143.21', 32)
+ ecso2 = clientsubnetoption.ClientSubnetOption('10.0.212.51', 32)
query2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512)
query2.flags = dns.flags.RD
response2 = dns.message.make_response(query2)
self.assertEquals(self._rcode, 200)
self.assertTrue('content-type: text/plain' in self._response_headers.decode())
+class TestDOHForwardedFor(DNSDistDOHTest):
+
+ _serverKey = 'server.key'
+ _serverCert = 'server.chain'
+ _serverName = 'tls.tests.dnsdist.org'
+ _caCert = 'ca.pem'
+ _dohServerPort = 8443
+ _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _config_template = """
+ newServer{address="127.0.0.1:%s"}
+
+ setACL('192.0.2.1/32')
+ addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/" }, {trustForwardedForHeader=true})
+ """
+ _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey']
+
+ def testDOHAllowedForwarded(self):
+ """
+ DOH with X-Forwarded-For allowed
+ """
+ name = 'allowed.forwarded.doh.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query.id = 0
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery.id = 0
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, customHeaders=['x-forwarded-for: 127.0.0.1:42, 127.0.0.1, 192.0.2.1:4200'])
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ def testDOHDeniedForwarded(self):
+ """
+ DOH with X-Forwarded-For not allowed
+ """
+ name = 'not-allowed.forwarded.doh.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query.id = 0
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery.id = 0
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, useQueue=False, rawResponse=True, customHeaders=['x-forwarded-for: 127.0.0.1:42, 127.0.0.1'])
+
+ self.assertEquals(self._rcode, 403)
+ self.assertEquals(receivedResponse, b'dns query not allowed because of ACL')
+
+class TestDOHForwardedForNoTrusted(DNSDistDOHTest):
+
+ _serverKey = 'server.key'
+ _serverCert = 'server.chain'
+ _serverName = 'tls.tests.dnsdist.org'
+ _caCert = 'ca.pem'
+ _dohServerPort = 8443
+ _dohBaseURL = ("https://%s:%d/" % (_serverName, _dohServerPort))
+ _config_template = """
+ newServer{address="127.0.0.1:%s"}
+
+ setACL('192.0.2.1/32')
+ addDOHLocal("127.0.0.1:%s", "%s", "%s", { "/" })
+ """
+ _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey']
+
+ def testDOHForwardedUntrusted(self):
+ """
+ DOH with X-Forwarded-For not trusted
+ """
+ name = 'not-trusted.forwarded.doh.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+ query.id = 0
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery.id = 0
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, useQueue=False, rawResponse=True, customHeaders=['x-forwarded-for: 192.0.2.1:4200'])
+
+ self.assertEquals(self._rcode, 403)
+ self.assertEquals(receivedResponse, b'dns query not allowed because of ACL')
--- /dev/null
+#!/usr/bin/env python
+
+import dns
+import socket
+import struct
+import sys
+import threading
+
+from dnsdisttests import DNSDistTest
+from proxyprotocol import ProxyProtocol
+
+# Python2/3 compatibility hacks
+try:
+ from queue import Queue
+except ImportError:
+ from Queue import Queue
+
+def ProxyProtocolUDPResponder(port, fromQueue, toQueue):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ try:
+ sock.bind(("127.0.0.1", port))
+ except socket.error as e:
+ print("Error binding in the Proxy Protocol UDP responder: %s" % str(e))
+ sys.exit(1)
+
+ while True:
+ data, addr = sock.recvfrom(4096)
+
+ proxy = ProxyProtocol()
+ if len(data) < proxy.HEADER_SIZE:
+ continue
+
+ if not proxy.parseHeader(data):
+ continue
+
+ if proxy.local:
+ # likely a healthcheck
+ data = data[proxy.HEADER_SIZE:]
+ request = dns.message.from_wire(data)
+ response = dns.message.make_response(request)
+ wire = response.to_wire()
+ sock.settimeout(2.0)
+ sock.sendto(wire, addr)
+ sock.settimeout(None)
+
+ continue
+
+ payload = data[:(proxy.HEADER_SIZE + proxy.contentLen)]
+ dnsData = data[(proxy.HEADER_SIZE + proxy.contentLen):]
+ toQueue.put([payload, dnsData], True, 2.0)
+ # computing the correct ID for the response
+ request = dns.message.from_wire(dnsData)
+ response = fromQueue.get(True, 2.0)
+ response.id = request.id
+
+ sock.settimeout(2.0)
+ sock.sendto(response.to_wire(), addr)
+ sock.settimeout(None)
+
+ sock.close()
+
+def ProxyProtocolTCPResponder(port, fromQueue, toQueue):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+ try:
+ sock.bind(("127.0.0.1", port))
+ except socket.error as e:
+ print("Error binding in the TCP responder: %s" % str(e))
+ sys.exit(1)
+
+ sock.listen(100)
+ while True:
+ (conn, _) = sock.accept()
+ conn.settimeout(5.0)
+ # try to read the entire Proxy Protocol header
+ proxy = ProxyProtocol()
+ header = conn.recv(proxy.HEADER_SIZE)
+ if not header:
+ conn.close()
+ continue
+
+ if not proxy.parseHeader(header):
+ conn.close()
+ continue
+
+ proxyContent = conn.recv(proxy.contentLen)
+ if not proxyContent:
+ conn.close()
+ continue
+
+ payload = header + proxyContent
+ while True:
+ try:
+ data = conn.recv(2)
+ except socket.timeout:
+ data = None
+
+ if not data:
+ conn.close()
+ break
+
+ (datalen,) = struct.unpack("!H", data)
+ data = conn.recv(datalen)
+
+ toQueue.put([payload, data], True, 2.0)
+
+ response = fromQueue.get(True, 2.0)
+ if not response:
+ conn.close()
+ break
+
+ # computing the correct ID for the response
+ request = dns.message.from_wire(data)
+ response.id = request.id
+
+ wire = response.to_wire()
+ conn.send(struct.pack("!H", len(wire)))
+ conn.send(wire)
+
+ conn.close()
+
+ sock.close()
+
+toProxyQueue = Queue()
+fromProxyQueue = Queue()
+proxyResponderPort = 5470
+
+udpResponder = threading.Thread(name='UDP Proxy Protocol Responder', target=ProxyProtocolUDPResponder, args=[proxyResponderPort, toProxyQueue, fromProxyQueue])
+udpResponder.setDaemon(True)
+udpResponder.start()
+tcpResponder = threading.Thread(name='TCP Proxy Protocol Responder', target=ProxyProtocolTCPResponder, args=[proxyResponderPort, toProxyQueue, fromProxyQueue])
+tcpResponder.setDaemon(True)
+tcpResponder.start()
+
+class ProxyProtocolTest(DNSDistTest):
+ _proxyResponderPort = proxyResponderPort
+ _config_params = ['_proxyResponderPort']
+
+ def checkMessageProxyProtocol(self, receivedProxyPayload, source, destination, isTCP, values=[]):
+ proxy = ProxyProtocol()
+ self.assertTrue(proxy.parseHeader(receivedProxyPayload))
+ self.assertEquals(proxy.version, 0x02)
+ self.assertEquals(proxy.command, 0x01)
+ self.assertEquals(proxy.family, 0x01)
+ if not isTCP:
+ self.assertEquals(proxy.protocol, 0x02)
+ else:
+ self.assertEquals(proxy.protocol, 0x01)
+ self.assertGreater(proxy.contentLen, 0)
+
+ self.assertTrue(proxy.parseAddressesAndPorts(receivedProxyPayload))
+ self.assertEquals(proxy.source, source)
+ self.assertEquals(proxy.destination, destination)
+ #self.assertEquals(proxy.sourcePort, sourcePort)
+ self.assertEquals(proxy.destinationPort, self._dnsDistPort)
+
+ self.assertTrue(proxy.parseAdditionalValues(receivedProxyPayload))
+ proxy.values.sort()
+ values.sort()
+ self.assertEquals(proxy.values, values)
+
+class TestProxyProtocol(ProxyProtocolTest):
+ """
+ dnsdist is configured to prepend a Proxy Protocol header to the query
+ """
+
+ _config_template = """
+ newServer{address="127.0.0.1:%d", useProxyProtocol=true}
+
+ function addValues(dq)
+ local values = { [0]="foo", [42]="bar" }
+ dq:setProxyProtocolValues(values)
+ return DNSAction.None
+ end
+
+ addAction("values-lua.proxy.tests.powerdns.com.", LuaAction(addValues))
+ addAction("values-action.proxy.tests.powerdns.com.", SetProxyProtocolValuesAction({ ["1"]="dnsdist", ["255"]="proxy-protocol"}))
+ """
+ _config_params = ['_proxyResponderPort']
+
+ def testProxyUDP(self):
+ """
+ Proxy Protocol: no value (UDP)
+ """
+ name = 'simple-udp.proxy.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+
+ toProxyQueue.put(response, True, 2.0)
+
+ data = query.to_wire()
+ self._sock.send(data)
+ receivedResponse = None
+ try:
+ self._sock.settimeout(2.0)
+ data = self._sock.recv(4096)
+ except socket.timeout:
+ print('timeout')
+ data = None
+ if data:
+ receivedResponse = dns.message.from_wire(data)
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEquals(receivedQuery, query)
+ self.assertEquals(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', False)
+
+ def testProxyTCP(self):
+ """
+ Proxy Protocol: no value (TCP)
+ """
+ name = 'simple-tcp.proxy.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+
+ toProxyQueue.put(response, True, 2.0)
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print('timeout')
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEquals(receivedQuery, query)
+ self.assertEquals(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True)
+
+ def testProxyUDPWithValuesFromLua(self):
+ """
+ Proxy Protocol: values from Lua (UDP)
+ """
+ name = 'values-lua.proxy.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+
+ toProxyQueue.put(response, True, 2.0)
+
+ data = query.to_wire()
+ self._sock.send(data)
+ receivedResponse = None
+ try:
+ self._sock.settimeout(2.0)
+ data = self._sock.recv(4096)
+ except socket.timeout:
+ print('timeout')
+ data = None
+ if data:
+ receivedResponse = dns.message.from_wire(data)
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEquals(receivedQuery, query)
+ self.assertEquals(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', False, [ [0, b'foo'] , [ 42, b'bar'] ])
+
+ def testProxyTCPWithValuesFromLua(self):
+ """
+ Proxy Protocol: values from Lua (TCP)
+ """
+ name = 'values-lua.proxy.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+
+ toProxyQueue.put(response, True, 2.0)
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print('timeout')
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEquals(receivedQuery, query)
+ self.assertEquals(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [0, b'foo'] , [ 42, b'bar'] ])
+
+ def testProxyUDPWithValuesFromAction(self):
+ """
+ Proxy Protocol: values from Action (UDP)
+ """
+ name = 'values-action.proxy.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+
+ toProxyQueue.put(response, True, 2.0)
+
+ data = query.to_wire()
+ self._sock.send(data)
+ receivedResponse = None
+ try:
+ self._sock.settimeout(2.0)
+ data = self._sock.recv(4096)
+ except socket.timeout:
+ print('timeout')
+ data = None
+ if data:
+ receivedResponse = dns.message.from_wire(data)
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEquals(receivedQuery, query)
+ self.assertEquals(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', False, [ [1, b'dnsdist'] , [ 255, b'proxy-protocol'] ])
+
+ def testProxyTCPWithValuesFromAction(self):
+ """
+ Proxy Protocol: values from Action (TCP)
+ """
+ name = 'values-action.proxy.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+
+ toProxyQueue.put(response, True, 2.0)
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print('timeout')
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEquals(receivedQuery, query)
+ self.assertEquals(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [ [1, b'dnsdist'] , [ 255, b'proxy-protocol'] ])
+
+ def testProxyTCPSeveralQueriesOnSameConnection(self):
+ """
+ Proxy Protocol: Several queries on the same TCP connection
+ """
+ name = 'several-queries-same-conn.proxy.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+
+ conn = self.openTCPConnection(2.0)
+ data = query.to_wire()
+
+ for idx in range(10):
+ toProxyQueue.put(response, True, 2.0)
+ self.sendTCPQueryOverConnection(conn, data, rawQuery=True)
+ receivedResponse = None
+ try:
+ receivedResponse = self.recvTCPResponseOverConnection(conn)
+ except socket.timeout:
+ print('timeout')
+
+ (receivedProxyPayload, receivedDNSData) = fromProxyQueue.get(True, 2.0)
+ self.assertTrue(receivedProxyPayload)
+ self.assertTrue(receivedDNSData)
+ self.assertTrue(receivedResponse)
+
+ receivedQuery = dns.message.from_wire(receivedDNSData)
+ receivedQuery.id = query.id
+ receivedResponse.id = response.id
+ self.assertEquals(receivedQuery, query)
+ self.assertEquals(receivedResponse, response)
+ self.checkMessageProxyProtocol(receivedProxyPayload, '127.0.0.1', '127.0.0.1', True, [])
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEquals(receivedResponse, None)
+class TestRoutingLuaFFIPerThreadRoundRobinLB(DNSDistTest):
+
+ _testServer2Port = 5351
+ _config_params = ['_testServerPort', '_testServer2Port']
+ _config_template = """
+ setServerPolicyLuaFFIPerThread("luaffiroundrobin", [[
+ local ffi = require("ffi")
+ local C = ffi.C
+
+ local counter = 0
+ return function(servers_list, dq)
+ counter = counter + 1
+ return (counter %% tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list)))
+ end
+ ]])
+
+ s1 = newServer{address="127.0.0.1:%s"}
+ s1:setUp()
+ s2 = newServer{address="127.0.0.1:%s"}
+ s2:setUp()
+ """
+
+ @classmethod
+ def startResponders(cls):
+ print("Launching responders..")
+ cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.start()
+ cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2.setDaemon(True)
+ cls._UDPResponder2.start()
+
+ cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder.setDaemon(True)
+ cls._TCPResponder.start()
+
+ cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2.setDaemon(True)
+ cls._TCPResponder2.start()
+
+ def testRR(self):
+ """
+ Routing: Round Robin
+
+ Send 10 A queries to "rr.routing.tests.powerdns.com.",
+ check that dnsdist routes half of it to each backend.
+ """
+ numberOfQueries = 10
+ name = 'rr.routing.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ # the round robin counter is shared for UDP and TCP,
+ # so we need to do UDP then TCP to have a clean count
+ for _ in range(numberOfQueries):
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ for _ in range(numberOfQueries):
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ for key in self._responsesCounter:
+ value = self._responsesCounter[key]
+ self.assertEquals(value, numberOfQueries / 2)
+
class TestRoutingOrder(DNSDistTest):
_testServer2Port = 5351
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
a98864b315f16bcf49ce577426063c42 ../regression-tests/zones/cdnskey-cds-test.com
9aeed2c26d0c3ba3baf22dfa9568c451 ../regression-tests/zones/2.0.192.in-addr.arpa
99c73e8b5db5781fec1ac3fa6a2662a9 ../regression-tests/zones/cryptokeys.org
+1f9e19be0cff67330f3a0a5347654f91 ../regression-tests/zones/hiddencryptokeys.org
52a95993ada0b4ed986a2fe6463a27e0 ../modules/tinydnsbackend/data.cdb
def testSecureCNAMEWildCardNXDOMAIN(self):
# the answer to this query reaches the UDP truncation threshold, so let's use TCP
res = self.sendQuery('something.cnamewildcardnxdomain.secure.example.', 'A', useTCP=True)
- expectedCNAME = dns.rrset.from_text('something.cnamewildcardnxdomain.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'doesntexist.secure.example.')
+ expectedCNAME = dns.rrset.from_text('something.cnamewildcardnxdomain.secure.example.', 0, dns.rdataclass.IN, 'CNAME', 'doesnotexist.secure.example.')
self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
self.assertMatchingRRSIGInAnswer(res, expectedCNAME)
--- /dev/null
+../regression-tests.common/proxyprotocol.py
\ No newline at end of file
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
threads=1
loglevel=9
disable-syslog=yes
+log-common-errors=yes
"""
_config_template = """
"""
_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"
delay2.example. 3600 IN NS ns1.delay2.example.
ns1.delay2.example. 3600 IN A {prefix}.17
delay2.example. 3600 IN DS 42043 13 2 60a047b87740c8564c21d5fd34626c10a77a6c41e3b34564230119c2f13937b8
+
+cname-nxd.example. 3600 IN CNAME cname-nxd-target.example.
+cname-nxd-target.example. 3600 IN A 192.0.2.100
+cname-nodata.example. 3600 IN CNAME cname-nodata-target.example.
+cname-nodata-target.example. 3600 IN A 192.0.2.101
+cname-custom-a.example. 3600 IN CNAME cname-custom-a-target.example.
+cname-custom-a-target.example. 3600 IN A 192.0.2.102
""",
'secure.example': """
secure.example. 3600 IN SOA {soa}
*.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
-*.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesntexist.secure.example.
+*.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
'18': {'threads': 1,
'zones': ['example']}
}
+ # Other IPs used:
+ # 2: test_Interop.py
+ # 3-7: free?
+ # 19: free?
+ # 20: free?
+ # 21: test_ECS.py
+ # 22: test_EDNSBuffer.py
+ # 23: test_Lua.py
+ # 24: test_RoutingTag.py
_auth_cmd = ['authbind',
os.environ['PDNS']]
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
--- /dev/null
+import dns
+import os
+
+from recursortests import RecursorTest
+
+class DNS64RecursorTest(RecursorTest):
+
+ _confdir = 'DNS64'
+ _config_template = """
+ auth-zones=example.dns64=configs/%s/example.dns64.zone
+ auth-zones+=in-addr.arpa=configs/%s/in-addr.arpa.zone
+ auth-zones+=ip6.arpa=configs/%s/ip6.arpa.zone
+
+ dns64-prefix=64:ff9b::/96
+ """ % (_confdir, _confdir, _confdir)
+
+ @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()
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ authzonepath = os.path.join(confdir, 'example.dns64.zone')
+ with open(authzonepath, 'w') as authzone:
+ authzone.write("""$ORIGIN example.dns64
+@ 3600 IN SOA {soa}
+www 3600 IN A 192.0.2.42
+www 3600 IN TXT "does exist"
+aaaa 3600 IN AAAA 2001:db8::1
+""".format(soa=cls._SOA))
+
+ authzonepath = os.path.join(confdir, 'in-addr.arpa.zone')
+ with open(authzonepath, 'w') as authzone:
+ authzone.write("""$ORIGIN in-addr.arpa
+@ 3600 IN SOA {soa}
+42.2.0.192 IN PTR www.example.dns64.
+""".format(soa=cls._SOA))
+
+ authzonepath = os.path.join(confdir, 'ip6.arpa.zone')
+ with open(authzonepath, 'w') as authzone:
+ authzone.write("""$ORIGIN ip6.arpa
+@ 3600 IN SOA {soa}
+1.0.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 IN PTR aaaa.example.dns64.
+""".format(soa=cls._SOA))
+
+ super(DNS64RecursorTest, cls).generateRecursorConfig(confdir)
+
+ # this type (A) exists for this name
+ def testExistingA(self):
+ qname = 'www.example.dns64.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ # there is no A record, we should get a NODATA
+ def testNonExistingA(self):
+ qname = 'aaaa.example.dns64.'
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEquals(len(res.answer), 0)
+
+ # this type (AAAA) does not exist for this name but there is an A record, we should get a DNS64-wrapped AAAA
+ def testNonExistingAAAA(self):
+ qname = 'www.example.dns64.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'AAAA', '64:ff9b::c000:22a')
+
+ query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ # this type (AAAA) does not exist for this name and there is no A record either, we should get a NXDomain
+ def testNonExistingAAAA(self):
+ qname = 'nxd.example.dns64.'
+
+ query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
+
+ # there is an AAAA record, we should get it
+ def testExistingAAAA(self):
+ qname = 'aaaa.example.dns64.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'AAAA', '2001:db8::1')
+
+ query = dns.message.make_query(qname, 'AAAA', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ # there is a TXT record, we should get it
+ def testExistingTXT(self):
+ qname = 'www.example.dns64.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'TXT', '"does exist"')
+
+ query = dns.message.make_query(qname, 'TXT', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ # the PTR records for the DNS64 prefix should be generated
+ def testNonExistingPTR(self):
+ qname = 'a.2.2.0.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.b.9.f.f.4.6.0.0.ip6.arpa.'
+ expectedCNAME = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'CNAME', '42.2.0.192.in-addr.arpa.')
+ expected = dns.rrset.from_text('42.2.0.192.in-addr.arpa.', 0, dns.rdataclass.IN, 'PTR', 'www.example.dns64.')
+
+ query = dns.message.make_query(qname, 'PTR', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ print(res)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expectedCNAME)
+ self.assertRRsetInAnswer(res, expected)
+
+ # but not for other prefixes
+ def testExistingPTR(self):
+ qname = '1.0.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.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'PTR', 'aaaa.example.dns64.')
+
+ query = dns.message.make_query(qname, 'PTR', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
import threading
import time
import clientsubnetoption
-from recursortests import RecursorTest
+import unittest
+from recursortests import RecursorTest, have_ipv6
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
nameECSInvalidScope = 'invalid-scope.ecs-echo.example.'
ttlECS = 60
ecsReactorRunning = False
+ecsReactorv6Running = False
class ECSTest(RecursorTest):
_config_template_default = """
@classmethod
def startResponders(cls):
global ecsReactorRunning
+ global ecsReactorv6Running
print("Launching responders..")
address = cls._PREFIX + '.21'
reactor.listenUDP(port, UDPECSResponder(), interface=address)
ecsReactorRunning = True
+ if not ecsReactorv6Running and have_ipv6():
+ reactor.listenUDP(53000, UDPECSResponder(), interface='::1')
+ ecsReactorv6Running = True
+
if not reactor.running:
cls._UDPResponder = threading.Thread(name='UDP Responder', target=reactor.run, args=(False,))
cls._UDPResponder.setDaemon(True)
query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected, ttlECS)
+@unittest.skipIf(not have_ipv6(), "No IPv6")
class testIncomingECSByNameV6(ECSTest):
_confdir = 'ECSIncomingByNameV6'
ecs-ipv6-bits=128
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
-forward-zones=ecs-echo.example=%s.21
-query-local-address6=::1
- """ % (os.environ['PREFIX'])
+query-local-address=::1
+forward-zones=ecs-echo.example=[::1]:53000
+ """
def testSendECS(self):
expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '2001:db8::1/128')
self.sendECSQuery(query, expected, ttlECS)
def testRequireNoECS(self):
- # we should get ::1/128 because neither ecs-scope-zero-addr nor query-local-address are set,
- # but query-local-address6 is set to ::1
+ # we should get ::1/128 because ecs-scope-zero-addr is unset and query-local-address is set to ::1
expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "::1/128")
ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
self.assertEqual(res.answer[0].ttl, 1)
def testIPFilterHeader(self):
- query = dns.message.make_query('ipfiler.luahooks.example.', 'A', 'IN')
+ query = dns.message.make_query('ipfilter.luahooks.example.', 'A', 'IN')
query.flags |= dns.flags.AD
for method in ("sendUDPQuery", "sendTCPQuery"):
--- /dev/null
+import clientsubnetoption
+import cookiesoption
+import dns
+import os
+import requests
+
+from recursortests import RecursorTest
+
+class PacketCacheRecursorTest(RecursorTest):
+
+ _confdir = 'PacketCache'
+ _wsPort = 8042
+ _wsTimeout = 2
+ _wsPassword = 'secretpassword'
+ _apiKey = 'secretapikey'
+ _config_template = """
+ packetcache-ttl=60
+ auth-zones=example=configs/%s/example.zone
+ webserver=yes
+ webserver-port=%d
+ webserver-address=127.0.0.1
+ webserver-password=%s
+ api-key=%s
+ """ % (_confdir, _wsPort, _wsPassword, _apiKey)
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ authzonepath = os.path.join(confdir, 'example.zone')
+ with open(authzonepath, 'w') as authzone:
+ authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+a 3600 IN A 192.0.2.42
+b 3600 IN A 192.0.2.42
+c 3600 IN A 192.0.2.42
+d 3600 IN A 192.0.2.42
+e 3600 IN A 192.0.2.42
+""".format(soa=cls._SOA))
+ super(PacketCacheRecursorTest, cls).generateRecursorConfig(confdir)
+
+ @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()
+
+ def checkPacketCacheMetrics(self, expectedHits, expectedMisses):
+ 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())
+ content = r.json()
+ foundHits = False
+ foundMisses = True
+ for entry in content:
+ if entry['name'] == 'packetcache-hits':
+ foundHits = True
+ self.assertEquals(int(entry['value']), expectedHits)
+ elif entry['name'] == 'packetcache-misses':
+ foundMisses = True
+ self.assertEquals(int(entry['value']), expectedMisses)
+
+ self.assertTrue(foundHits)
+ self.assertTrue(foundMisses)
+
+ def testPacketCache(self):
+ # first query, no cookie
+ qname = 'a.example.'
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ self.checkPacketCacheMetrics(0, 1)
+
+ # we should get a hit over UDP this time
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkPacketCacheMetrics(1, 1)
+
+ eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
+ eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
+ ecso1 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
+ ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
+
+ # we add a cookie, should not match anymore
+ query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkPacketCacheMetrics(1, 2)
+
+ # same cookie, should match
+ query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1])
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkPacketCacheMetrics(2, 2)
+
+ # different cookie, should still match
+ query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2])
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkPacketCacheMetrics(3, 2)
+
+ # first cookie but with an ECS option, should not match
+ query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso1])
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkPacketCacheMetrics(3, 3)
+
+ # different cookie but same ECS option, should match
+ query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2, ecso1])
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkPacketCacheMetrics(4, 3)
+
+ # first cookie but different ECS option, should still match (we ignore EDNS Client Subnet
+ # in the recursor's packet cache, but ECS-specific responses are not cached
+ query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso2])
+ res = self.sendUDPQuery(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.checkPacketCacheMetrics(5, 3)
self.assertEquals(record.ttl, rttl)
self.assertTrue(record.HasField('rdata'))
- def checkProtobufPolicy(self, msg, policyType, reason):
+ def checkProtobufPolicy(self, msg, policyType, reason, trigger, hit):
self.assertEquals(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
self.assertTrue(msg.response.HasField('appliedPolicyType'))
self.assertTrue(msg.response.HasField('appliedPolicy'))
+ self.assertTrue(msg.response.HasField('appliedPolicyTrigger'))
+ self.assertTrue(msg.response.HasField('appliedPolicyHit'))
self.assertEquals(msg.response.appliedPolicy, reason)
self.assertEquals(msg.response.appliedPolicyType, policyType)
+ self.assertEquals(msg.response.appliedPolicyTrigger, trigger)
+ self.assertEquals(msg.response.appliedPolicyHit, hit)
def checkProtobufTags(self, msg, tags):
+ print(tags)
+ print('---')
+ print(msg.response.tags)
self.assertEquals(len(msg.response.tags), len(tags))
for tag in msg.response.tags:
self.assertTrue(tag in tags)
# check the protobuf messages corresponding to the UDP query and answer
msg = self.getFirstProtobufMessage()
self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
- self.checkProtobufTags(msg, [self._tag_from_gettag])
+ self.checkProtobufTags(msg, [ self._tag_from_gettag ])
# then the response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
# we have max-cache-ttl set to 15
self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
- tags = [self._tag_from_gettag] + self._tags
+ tags = [ self._tag_from_gettag ] + self._tags
self.checkProtobufTags(msg, tags)
self.checkNoRemainingMessage()
# then the response
msg = self.getFirstProtobufMessage()
self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
- self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.')
+ self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.', '*.test.example.', 'sub.test.example')
+ self.assertEquals(len(msg.response.rrs), 1)
+ rr = msg.response.rrs[0]
+ # we have max-cache-ttl set to 15
+ self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
+ self.assertEquals(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
+ self.checkNoRemainingMessage()
+
+class ProtobufRPZTagsTest(TestRecursorProtobuf):
+ """
+ This test makes sure that we correctly export the RPZ tags in our protobuf messages
+ """
+
+ _confdir = 'ProtobufRPZTags'
+ _config_template = """
+auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
+ _tags = ['tag1', 'tag2']
+ _tags_from_gettag = ['tag1-from-gettag', 'tag2-from-gettag']
+ _tags_from_rpz = ['tag1-from-rpz', 'tag2-from-rpz' ]
+ _lua_config_file = """
+ protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, tags={'tag1', 'tag2'} } )
+ rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", tags={ '%s', '%s'} })
+ """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir, _tags_from_rpz[0], _tags_from_rpz[1])
+ _lua_dns_script_file = """
+ function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
+ return 0, { '%s', '%s' }
+ end
+ function preresolve(dq)
+ dq:addPolicyTag('%s')
+ dq:addPolicyTag('%s')
+ return false
+ end
+ """ % (_tags_from_gettag[0], _tags_from_gettag[1], _tags[0], _tags[1])
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ authzonepath = os.path.join(confdir, 'example.rpz.zone')
+ with open(authzonepath, 'w') as authzone:
+ authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+sub.test 3600 IN A 192.0.2.42
+""".format(soa=cls._SOA))
+
+ rpzFilePath = os.path.join(confdir, 'zone.rpz')
+ with open(rpzFilePath, 'w') as rpzZone:
+ rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
+""".format(soa=cls._SOA))
+
+ super(ProtobufRPZTagsTest, cls).generateRecursorConfig(confdir)
+
+ def testA(self):
+ name = 'sub.test.example.'
+ expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
+ query = dns.message.make_query(name, 'A', want_dnssec=True)
+ query.flags |= dns.flags.CD
+ res = self.sendUDPQuery(query)
+ self.assertRRsetInAnswer(res, expected)
+
+ # check the protobuf messages corresponding to the UDP query and answer
+ msg = self.getFirstProtobufMessage()
+ self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
+
+ # then the response
+ msg = self.getFirstProtobufMessage()
+ self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
+ self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.', '*.test.example.', 'sub.test.example')
+ self.checkProtobufTags(msg, self._tags + self._tags_from_gettag + self._tags_from_rpz)
self.assertEquals(len(msg.response.rrs), 1)
rr = msg.response.rrs[0]
# we have max-cache-ttl set to 15
--- /dev/null
+import dns
+import os
+import socket
+import struct
+import sys
+import time
+
+try:
+ range = xrange
+except NameError:
+ pass
+
+from recursortests import RecursorTest
+from proxyprotocol import ProxyProtocol
+
+class ProxyProtocolRecursorTest(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()
+
+ @classmethod
+ def sendUDPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
+ queryPayload = query.to_wire()
+ ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
+ payload = ppPayload + queryPayload
+
+ if timeout:
+ cls._sock.settimeout(timeout)
+
+ try:
+ cls._sock.send(payload)
+ data = cls._sock.recv(4096)
+ except socket.timeout:
+ data = None
+ finally:
+ if timeout:
+ cls._sock.settimeout(None)
+
+ message = None
+ if data:
+ message = dns.message.from_wire(data)
+ return message
+
+ @classmethod
+ def sendTCPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
+ queryPayload = query.to_wire()
+ ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ if timeout:
+ sock.settimeout(timeout)
+
+ sock.connect(("127.0.0.1", cls._recursorPort))
+
+ try:
+ sock.send(ppPayload)
+ sock.send(struct.pack("!H", len(queryPayload)))
+ sock.send(queryPayload)
+ data = sock.recv(2)
+ if data:
+ (datalen,) = struct.unpack("!H", data)
+ data = sock.recv(datalen)
+ except socket.timeout as e:
+ print("Timeout: %s" % (str(e)))
+ data = None
+ except socket.error as e:
+ print("Network error: %s" % (str(e)))
+ data = None
+ finally:
+ sock.close()
+
+ message = None
+ if data:
+ message = dns.message.from_wire(data)
+ return message
+
+class ProxyProtocolAllowedRecursorTest(ProxyProtocolRecursorTest):
+ _confdir = 'ProxyProtocol'
+ _lua_dns_script_file = """
+
+ function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp, proxyProtocolValues)
+ local remoteaddr = remote:toStringWithPort()
+ local localaddr = localip:toStringWithPort()
+ local foundFoo = false
+ local foundBar = false
+
+ if remoteaddr ~= '127.0.0.42:0' and remoteaddr ~= '[::42]:0' then
+ pdnslog('gettag: invalid source '..remoteaddr)
+ return 1
+ end
+ if localaddr ~= '255.255.255.255:65535' and localaddr ~= '[2001:db8::ff]:65535' then
+ pdnslog('gettag: invalid dest '..localaddr)
+ return 2
+ end
+
+ for k,v in pairs(proxyProtocolValues) do
+ local type = v:getType()
+ local content = v:getContent()
+ if type == 0 and content == 'foo' then
+ foundFoo = true
+ end
+ if type == 255 and content == 'bar' then
+ foundBar = true
+ end
+ end
+
+ if not foundFoo or not foundBar then
+ pdnslog('gettag: TLV not found')
+ return 3
+ end
+
+ return 42
+ end
+
+ function preresolve(dq)
+ local foundFoo = false
+ local foundBar = false
+ local values = dq:getProxyProtocolValues()
+ for k,v in pairs(values) do
+ local type = v:getType()
+ local content = v:getContent()
+ if type == 0 and content == 'foo' then
+ foundFoo = true
+ end
+ if type == 255 and content == 'bar' then
+ foundBar = true
+ end
+ end
+
+ if not foundFoo or not foundBar then
+ pdnslog('TLV not found')
+ dq:addAnswer(pdns.A, '192.0.2.255', 60)
+ return true
+ end
+
+ local remoteaddr = dq.remoteaddr:toStringWithPort()
+ local localaddr = dq.localaddr:toStringWithPort()
+
+ if remoteaddr ~= '127.0.0.42:0' and remoteaddr ~= '[::42]:0' then
+ pdnslog('invalid source '..remoteaddr)
+ dq:addAnswer(pdns.A, '192.0.2.128', 60)
+ return true
+ end
+ if localaddr ~= '255.255.255.255:65535' and localaddr ~= '[2001:db8::ff]:65535' then
+ pdnslog('invalid dest '..localaddr)
+ dq:addAnswer(pdns.A, '192.0.2.129', 60)
+ return true
+ end
+
+ if dq.tag ~= 42 then
+ pdnslog('invalid tag '..dq.tag)
+ dq:addAnswer(pdns.A, '192.0.2.130', 60)
+ return true
+ end
+
+ dq:addAnswer(pdns.A, '192.0.2.1', 60)
+ return true
+ end
+ """
+
+ _config_template = """
+ proxy-protocol-from=127.0.0.1
+ proxy-protocol-maximum-size=512
+ allow-from=127.0.0.0/24, ::1/128, ::42/128
+""" % ()
+
+ def testLocalProxyProtocol(self):
+ qname = 'local.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.255')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ queryPayload = query.to_wire()
+ ppPayload = ProxyProtocol.getPayload(True, False, False, None, None, None, None, [])
+ payload = ppPayload + queryPayload
+
+ # UDP
+ self._sock.settimeout(2.0)
+
+ try:
+ self._sock.send(payload)
+ data = self._sock.recv(4096)
+ except socket.timeout:
+ data = None
+ finally:
+ self._sock.settimeout(None)
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ # TCP
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(2.0)
+ sock.connect(("127.0.0.1", self._recursorPort))
+
+ try:
+ sock.send(ppPayload)
+ sock.send(struct.pack("!H", len(queryPayload)))
+ sock.send(queryPayload)
+ data = sock.recv(2)
+ if data:
+ (datalen,) = struct.unpack("!H", data)
+ data = sock.recv(datalen)
+ except socket.timeout as e:
+ print("Timeout: %s" % (str(e)))
+ data = None
+ except socket.error as e:
+ print("Network error: %s" % (str(e)))
+ data = None
+ finally:
+ sock.close()
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testInvalidMagicProxyProtocol(self):
+ qname = 'invalid-magic.proxy-protocol.recursor-tests.powerdns.com.'
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ queryPayload = query.to_wire()
+ ppPayload = ProxyProtocol.getPayload(True, False, False, None, None, None, None, [])
+ ppPayload = b'\x00' + ppPayload[1:]
+ payload = ppPayload + queryPayload
+
+ # UDP
+ self._sock.settimeout(2.0)
+
+ try:
+ self._sock.send(payload)
+ data = self._sock.recv(4096)
+ except socket.timeout:
+ data = None
+ finally:
+ self._sock.settimeout(None)
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertEqual(res, None)
+
+ # TCP
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(2.0)
+ sock.connect(("127.0.0.1", self._recursorPort))
+
+ try:
+ sock.send(ppPayload)
+ sock.send(struct.pack("!H", len(queryPayload)))
+ sock.send(queryPayload)
+ data = sock.recv(2)
+ if data:
+ (datalen,) = struct.unpack("!H", data)
+ data = sock.recv(datalen)
+ except socket.timeout as e:
+ print("Timeout: %s" % (str(e)))
+ data = None
+ except socket.error as e:
+ print("Network error: %s" % (str(e)))
+ data = None
+ finally:
+ sock.close()
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertEqual(res, None)
+
+ def testTCPOneByteAtATimeProxyProtocol(self):
+ qname = 'tcp-one-byte-at-a-time.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ queryPayload = query.to_wire()
+ ppPayload = ProxyProtocol.getPayload(False, True, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+
+ # TCP
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(2.0)
+ sock.connect(("127.0.0.1", self._recursorPort))
+
+ try:
+ for i in range(len(ppPayload)):
+ sock.send(ppPayload[i:i+1])
+ time.sleep(0.01)
+ value = struct.pack("!H", len(queryPayload))
+ for i in range(len(value)):
+ sock.send(value[i:i+1])
+ time.sleep(0.01)
+ for i in range(len(queryPayload)):
+ sock.send(queryPayload[i:i+1])
+ time.sleep(0.01)
+
+ data = sock.recv(2)
+ if data:
+ (datalen,) = struct.unpack("!H", data)
+ data = sock.recv(datalen)
+ except socket.timeout as e:
+ print("Timeout: %s" % (str(e)))
+ data = None
+ except socket.error as e:
+ print("Network error: %s" % (str(e)))
+ data = None
+ finally:
+ sock.close()
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testTooLargeProxyProtocol(self):
+ # the total payload (proxy protocol + DNS) is larger than proxy-protocol-maximum-size
+ # so it should be dropped
+ qname = 'too-large.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ queryPayload = query.to_wire()
+ ppPayload = ProxyProtocol.getPayload(False, True, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [1, b'A'*512], [ 255, b'bar'] ])
+ payload = ppPayload + queryPayload
+
+ # UDP
+ self._sock.settimeout(2.0)
+
+ try:
+ self._sock.send(payload)
+ data = self._sock.recv(4096)
+ except socket.timeout:
+ data = None
+ finally:
+ self._sock.settimeout(None)
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertEqual(res, None)
+
+ # TCP
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(2.0)
+ sock.connect(("127.0.0.1", self._recursorPort))
+
+ try:
+ sock.send(ppPayload)
+ sock.send(struct.pack("!H", len(queryPayload)))
+ sock.send(queryPayload)
+
+ data = sock.recv(2)
+ if data:
+ (datalen,) = struct.unpack("!H", data)
+ data = sock.recv(datalen)
+ except socket.timeout as e:
+ print("Timeout: %s" % (str(e)))
+ data = None
+ except socket.error as e:
+ print("Network error: %s" % (str(e)))
+ data = None
+ finally:
+ sock.close()
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertEqual(res, None)
+
+ def testNoHeaderProxyProtocol(self):
+ qname = 'no-header.proxy-protocol.recursor-tests.powerdns.com.'
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertEqual(res, None)
+
+ def testIPv4ProxyProtocol(self):
+ qname = 'ipv4.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testIPv4NoValuesProxyProtocol(self):
+ qname = 'ipv4-no-values.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.255')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testIPv4ProxyProtocolNotAuthorized(self):
+ qname = 'ipv4-not-authorized.proxy-protocol.recursor-tests.powerdns.com.'
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, False, '192.0.2.255', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ self.assertEqual(res, None)
+
+ def testIPv6ProxyProtocol(self):
+ qname = 'ipv6.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, True, '::42', '2001:db8::ff', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testIPv6NoValuesProxyProtocol(self):
+ qname = 'ipv6-no-values.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.255')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, True, '::42', '2001:db8::ff', 0, 65535)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testIPv6ProxyProtocolNotAuthorized(self):
+ qname = 'ipv6-not-authorized.proxy-protocol.recursor-tests.powerdns.com.'
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, True, '2001:db8::1', '2001:db8::ff', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ self.assertEqual(res, None)
+
+ def testIPv6ProxyProtocolSeveralQueriesOverTCP(self):
+ qname = 'several-queries-tcp.proxy-protocol.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ queryPayload = query.to_wire()
+ ppPayload = ProxyProtocol.getPayload(False, True, True, '::42', '2001:db8::ff', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ payload = ppPayload + queryPayload
+
+ # TCP
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(2.0)
+ sock.connect(("127.0.0.1", self._recursorPort))
+
+ sock.send(ppPayload)
+
+ count = 0
+ for idx in range(5):
+ try:
+ sock.send(struct.pack("!H", len(queryPayload)))
+ sock.send(queryPayload)
+
+ data = sock.recv(2)
+ if data:
+ (datalen,) = struct.unpack("!H", data)
+ data = sock.recv(datalen)
+ except socket.timeout as e:
+ print("Timeout: %s" % (str(e)))
+ data = None
+ break
+ except socket.error as e:
+ print("Network error: %s" % (str(e)))
+ data = None
+ break
+
+ res = None
+ if data:
+ res = dns.message.from_wire(data)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ count = count + 1
+
+ self.assertEqual(count, 5)
+ sock.close()
+
+class ProxyProtocolAllowedFFIRecursorTest(ProxyProtocolAllowedRecursorTest):
+ # same tests than ProxyProtocolAllowedRecursorTest but with the Lua FFI interface instead of the regular one
+ _confdir = 'ProxyProtocolFFI'
+ _lua_dns_script_file = """
+ local ffi = require("ffi")
+
+ ffi.cdef[[
+ typedef struct pdns_ffi_param pdns_ffi_param_t;
+
+ typedef struct pdns_proxyprotocol_value {
+ uint8_t type;
+ uint16_t len;
+ const void* data;
+ } pdns_proxyprotocol_value_t;
+
+ size_t pdns_ffi_param_get_proxy_protocol_values(pdns_ffi_param_t* ref, const pdns_proxyprotocol_value_t** out);
+ const char* pdns_ffi_param_get_remote(pdns_ffi_param_t* ref);
+ const char* pdns_ffi_param_get_local(pdns_ffi_param_t* ref);
+ uint16_t pdns_ffi_param_get_remote_port(const pdns_ffi_param_t* ref);
+ uint16_t pdns_ffi_param_get_local_port(const pdns_ffi_param_t* ref);
+
+ void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag);
+ ]]
+
+ function gettag_ffi(obj)
+ local remoteaddr = ffi.string(ffi.C.pdns_ffi_param_get_remote(obj))
+ local localaddr = ffi.string(ffi.C.pdns_ffi_param_get_local(obj))
+ local foundFoo = false
+ local foundBar = false
+
+ if remoteaddr ~= '127.0.0.42' and remoteaddr ~= '::42' then
+ pdnslog('gettag-ffi: invalid source '..remoteaddr)
+ ffi.C.pdns_ffi_param_set_tag(obj, 1)
+ return
+ end
+ if localaddr ~= '255.255.255.255' and localaddr ~= '2001:db8::ff' then
+ pdnslog('gettag-ffi: invalid dest '..localaddr)
+ ffi.C.pdns_ffi_param_set_tag(obj, 2)
+ return
+ end
+
+ if ffi.C.pdns_ffi_param_get_remote_port(obj) ~= 0 then
+ pdnslog('gettag-ffi: invalid source port '..ffi.C.pdns_ffi_param_get_remote_port(obj))
+ ffi.C.pdns_ffi_param_set_tag(obj, 1)
+ return
+ end
+
+ if ffi.C.pdns_ffi_param_get_local_port(obj) ~= 65535 then
+ pdnslog('gettag-ffi: invalid source port '..ffi.C.pdns_ffi_param_get_local_port(obj))
+ ffi.C.pdns_ffi_param_set_tag(obj, 2)
+ return
+ end
+
+ local ret_ptr = ffi.new("const pdns_proxyprotocol_value_t *[1]")
+ local ret_ptr_param = ffi.cast("const pdns_proxyprotocol_value_t **", ret_ptr)
+ local values_count = ffi.C.pdns_ffi_param_get_proxy_protocol_values(obj, ret_ptr_param)
+
+ if values_count > 0 then
+ for i = 0,tonumber(values_count)-1 do
+ local type = ret_ptr[0][i].type
+ local content = ffi.string(ret_ptr[0][i].data, ret_ptr[0][i].len)
+ if type == 0 and content == 'foo' then
+ foundFoo = true
+ end
+ if type == 255 and content == 'bar' then
+ foundBar = true
+ end
+ end
+ end
+
+ if not foundFoo or not foundBar then
+ pdnslog('gettag-ffi: TLV not found')
+ ffi.C.pdns_ffi_param_set_tag(obj, 3)
+ return
+ end
+
+ ffi.C.pdns_ffi_param_set_tag(obj, 42)
+ end
+
+ function preresolve(dq)
+ local foundFoo = false
+ local foundBar = false
+ local values = dq:getProxyProtocolValues()
+ for k,v in pairs(values) do
+ local type = v:getType()
+ local content = v:getContent()
+ if type == 0 and content == 'foo' then
+ foundFoo = true
+ end
+ if type == 255 and content == 'bar' then
+ foundBar = true
+ end
+ end
+
+ if not foundFoo or not foundBar then
+ pdnslog('TLV not found')
+ dq:addAnswer(pdns.A, '192.0.2.255', 60)
+ return true
+ end
+
+ local remoteaddr = dq.remoteaddr:toStringWithPort()
+ local localaddr = dq.localaddr:toStringWithPort()
+
+ if remoteaddr ~= '127.0.0.42:0' and remoteaddr ~= '[::42]:0' then
+ pdnslog('invalid source '..remoteaddr)
+ dq:addAnswer(pdns.A, '192.0.2.128', 60)
+ return true
+ end
+ if localaddr ~= '255.255.255.255:65535' and localaddr ~= '[2001:db8::ff]:65535' then
+ pdnslog('invalid dest '..localaddr)
+ dq:addAnswer(pdns.A, '192.0.2.129', 60)
+ return true
+ end
+
+ if dq.tag ~= 42 then
+ pdnslog('invalid tag '..dq.tag)
+ dq:addAnswer(pdns.A, '192.0.2.130', 60)
+ return true
+ end
+
+ dq:addAnswer(pdns.A, '192.0.2.1', 60)
+ return true
+ end
+ """
+
+class ProxyProtocolNotAllowedRecursorTest(ProxyProtocolRecursorTest):
+ _confdir = 'ProxyProtocolNotAllowed'
+ _lua_dns_script_file = """
+
+ function preresolve(dq)
+ dq:addAnswer(pdns.A, '192.0.2.1', 60)
+ return true
+ end
+ """
+
+ _config_template = """
+ proxy-protocol-from=192.0.2.1/32
+ allow-from=127.0.0.0/24, ::1/128
+""" % ()
+
+ def testNoHeaderProxyProtocol(self):
+ qname = 'no-header.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testIPv4ProxyProtocol(self):
+ qname = 'ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ self.assertEqual(res, None)
return False
if newSerial != self._currentSerial + 1:
- raise AssertionError("Asking the RPZ server to server serial %d, already serving %d" % (newSerial, self._currentSerial))
+ raise AssertionError("Asking the RPZ server to serve serial %d, already serving %d" % (newSerial, self._currentSerial))
self._targetSerial = newSerial
return True
elif message.question[0].rdtype == dns.rdatatype.IXFR:
oldSerial = message.authority[0][0].serial
- if oldSerial != self._currentSerial:
+ # special case for the 9th update, which might get skipped
+ if oldSerial != self._currentSerial and self._currentSerial != 9:
print('Received an IXFR query with an unexpected serial %d, expected %d' % (oldSerial, self._currentSerial))
return (None, self._currentSerial)
dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
]
+ elif newSerial == 9:
+ # IXFR inserting a duplicate, we should not crash and skip it
+ records = [
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
+ dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
+ ]
+ elif newSerial == 10:
+ # full AXFR to make sure we are removing the duplicate, adding a record, to check that the update was correctly applied
+ records = [
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
+ ]
+ elif newSerial == 11:
+ # IXFR with two deltas, the first one adding a 'g' and the second one removing 'f'
+ records = [
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('g.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1))
+ ]
+ # this one has two updates in one
+ newSerial = newSerial + 1
+ self._targetSerial = self._targetSerial + 1
response.answer = records
return (newSerial, response)
self.checkNXD('tc.example.')
self.checkNXD('drop.example.')
+ # 9th zone is a duplicate, it might get skipped
+ global rpzServer
+ rpzServer.moveToSerial(9)
+ time.sleep(3)
+ self.waitUntilCorrectSerialIsLoaded(10)
+ self.checkRPZStats(10, 1, 4, self._xfrDone)
+ self.checkNotBlocked('a.example.')
+ self.checkNotBlocked('b.example.')
+ self.checkNotBlocked('c.example.')
+ self.checkNotBlocked('d.example.')
+ self.checkNotBlocked('e.example.')
+ self.checkBlocked('f.example.')
+ self.checkNXD('tc.example.')
+ self.checkNXD('drop.example.')
+
+ # the next update will update the zone twice
+ rpzServer.moveToSerial(11)
+ time.sleep(3)
+ self.waitUntilCorrectSerialIsLoaded(12)
+ self.checkRPZStats(12, 1, 4, self._xfrDone)
+ self.checkNotBlocked('a.example.')
+ self.checkNotBlocked('b.example.')
+ self.checkNotBlocked('c.example.')
+ self.checkNotBlocked('d.example.')
+ self.checkNotBlocked('e.example.')
+ self.checkNXD('f.example.')
+ self.checkBlocked('g.example.')
+ self.checkNXD('tc.example.')
+ self.checkNXD('drop.example.')
+
class RPZFileRecursorTest(RPZRecursorTest):
"""
This test makes sure that we correctly load RPZ zones from a file
super(RPZFileDefaultPolNotOverrideLocalRecursorTest, cls).generateRecursorConfig(confdir)
def testRPZ(self):
- # local data entries will not be overridden by the default polic
+ # local data entries will not be overridden by the default policy
self.checkCustom('a.example.', 'A', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42', '192.0.2.43'))
self.checkCustom('a.example.', 'TXT', dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'TXT', '"some text"'))
# will be blocked because the default policy does not override local data entries
# We only test once because after that the answer is cached, so the NS is not contacted
# and the whitelist is not applied (yes, NSIP and NSDNAME are brittle).
self.checkCustom('nsip.delegated.example.', 'A', dns.rrset.from_text('nsip.delegated.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1'))
+
+
+class RPZResponseIPCNameChainCustomTest(RPZRecursorTest):
+ """
+ This test makes sure that the recursor applies response IP rules to records in a CNAME chain,
+ and resolves the target of a custom CNAME.
+ """
+
+ _confdir = 'RPZResponseIPCNameChainCustom'
+ _lua_config_file = """
+ rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
+ """ % (_confdir)
+ _config_template = """
+auth-zones=example=configs/%s/example.zone
+forward-zones=delegated.example=127.0.0.1:%d
+""" % (_confdir, rpzAuthServerPort)
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ authzonepath = os.path.join(confdir, 'example.zone')
+ with open(authzonepath, 'w') as authzone:
+ authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+name IN CNAME cname
+cname IN A 192.0.2.255
+custom-target IN A 192.0.2.254
+""".format(soa=cls._SOA))
+
+ rpzFilePath = os.path.join(confdir, 'zone.rpz')
+ with open(rpzFilePath, 'w') as rpzZone:
+ rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+cname.example IN CNAME custom-target.example.
+custom-target.example IN A 192.0.2.253
+""".format(soa=cls._SOA))
+
+ super(RPZResponseIPCNameChainCustomTest, cls).generateRecursorConfig(confdir)
+
+ def testRPZChain(self):
+ # we request the A record for 'name.example.', which is a CNAME to 'cname.example'
+ # this one does exist but we have a RPZ rule that should be triggered,
+ # replacing the 'real' CNAME by a CNAME to 'custom-target.example.'
+ # There is a RPZ rule for that name but it should not be triggered, since
+ # the RPZ specs state "Recall that only one policy rule, from among all those matched at all
+ # stages of resolving a CNAME or DNAME chain, can affect the final
+ # response; this is true even if the selected rule has a PASSTHRU
+ # action" in 5.1 "CNAME or DNAME Chain Position" Precedence Rule
+
+ # two times to check the cache
+ for _ in range(2):
+ query = dns.message.make_query('name.example.', 'A', want_dnssec=True)
+ query.flags |= dns.flags.CD
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, dns.rrset.from_text('name.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname.example.'))
+ self.assertRRsetInAnswer(res, dns.rrset.from_text('cname.example.', 0, dns.rdataclass.IN, 'CNAME', 'custom-target.example.'))
+ self.assertRRsetInAnswer(res, dns.rrset.from_text('custom-target.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.254'))
+
+
+class RPZCNameChainCustomTest(RPZRecursorTest):
+ """
+ This test makes sure that the recursor applies QName rules to names in a CNAME chain.
+ No forward or internal auth zones here, as we want to test the real resolution
+ (with QName Minimization).
+ """
+
+ _PREFIX = os.environ['PREFIX']
+ _confdir = 'RPZCNameChainCustom'
+ _lua_config_file = """
+ rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
+ """ % (_confdir)
+ _config_template = ""
+
+ @classmethod
+ def setUpClass(cls):
+
+ cls.setUpSockets()
+ cls.startResponders()
+
+ confdir = os.path.join('configs', cls._confdir)
+ cls.createConfigDir(confdir)
+
+ cls.generateAllAuthConfig(confdir)
+ cls.startAuth(os.path.join(confdir, "auth-8"), cls._PREFIX + '.8')
+ cls.startAuth(os.path.join(confdir, "auth-10"), cls._PREFIX + '.10')
+
+ cls.generateRecursorConfig(confdir)
+ cls.startRecursor(confdir, cls._recursorPort)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.tearDownAuth()
+ cls.tearDownRecursor()
+
+ @classmethod
+ def generateRecursorConfig(cls, confdir):
+ rpzFilePath = os.path.join(confdir, 'zone.rpz')
+ with open(rpzFilePath, 'w') as rpzZone:
+ rpzZone.write("""$ORIGIN zone.rpz.
+@ 3600 IN SOA {soa}
+32.100.2.0.192.rpz-ip IN CNAME .
+32.101.2.0.192.rpz-ip IN CNAME *.
+32.102.2.0.192.rpz-ip IN A 192.0.2.103
+""".format(soa=cls._SOA))
+
+ super(RPZCNameChainCustomTest, cls).generateRecursorConfig(confdir)
+
+ def testRPZChainNXD(self):
+ # we should match the A at the end of the CNAME chain and
+ # trigger a NXD
+
+ # two times to check the cache
+ for _ in range(2):
+ query = dns.message.make_query('cname-nxd.example.', 'A', want_dnssec=True)
+ query.flags |= dns.flags.CD
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
+ self.assertEquals(len(res.answer), 0)
+
+ def testRPZChainNODATA(self):
+ # we should match the A at the end of the CNAME chain and
+ # trigger a NODATA
+
+ # two times to check the cache
+ for _ in range(2):
+ query = dns.message.make_query('cname-nodata.example.', 'A', want_dnssec=True)
+ query.flags |= dns.flags.CD
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertEquals(len(res.answer), 0)
+
+ def testRPZChainCustom(self):
+ # we should match the A at the end of the CNAME chain and
+ # get a custom A, replacing the existing one
+
+ # two times to check the cache
+ for _ in range(2):
+ query = dns.message.make_query('cname-custom-a.example.', 'A', want_dnssec=True)
+ query.flags |= dns.flags.CD
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ # the original CNAME record is signed
+ self.assertEquals(len(res.answer), 3)
+ self.assertRRsetInAnswer(res, dns.rrset.from_text('cname-custom-a.example.', 0, dns.rdataclass.IN, 'CNAME', 'cname-custom-a-target.example.'))
+ self.assertRRsetInAnswer(res, dns.rrset.from_text('cname-custom-a-target.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.103'))
from recursortests import RecursorTest
-class testReadTrustAnchorsFronFile(RecursorTest):
+class testReadTrustAnchorsFromFile(RecursorTest):
_confdir = 'ReadTAsFromFile'
_config_template = """dnssec=validate"""
--- /dev/null
+import dns
+import os
+import socket
+import struct
+import threading
+import time
+import clientsubnetoption
+import subprocess
+from recursortests import RecursorTest
+from twisted.internet.protocol import DatagramProtocol
+from twisted.internet import reactor
+
+emptyECSText = 'No ECS received'
+nameECS = 'ecs-echo.example.'
+nameECSInvalidScope = 'invalid-scope.ecs-echo.example.'
+ttlECS = 60
+routingReactorRunning = False
+
+class RoutingTagTest(RecursorTest):
+ _config_template_default = """
+daemon=no
+trace=yes
+dont-query=
+ecs-add-for=0.0.0.0/0
+local-address=127.0.0.1
+packetcache-ttl=0
+packetcache-servfail-ttl=0
+max-cache-ttl=600
+threads=1
+loglevel=9
+disable-syslog=yes
+"""
+
+ def sendECSQuery(self, query, expected, expectedFirstTTL=None):
+ res = self.sendUDPQuery(query)
+
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ # this will break if you are not looking for the first RR, sorry!
+ if expectedFirstTTL is not None:
+ self.assertEqual(res.answer[0].ttl, expectedFirstTTL)
+ else:
+ expectedFirstTTL = res.answer[0].ttl
+
+ # wait one second, check that the TTL has been
+ # decreased indicating a cache hit
+ time.sleep(1)
+
+ res = self.sendUDPQuery(query)
+
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ self.assertLess(res.answer[0].ttl, expectedFirstTTL)
+
+ def checkECSQueryHit(self, query, expected):
+ res = self.sendUDPQuery(query)
+
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+ # this will break if you are not looking for the first RR, sorry!
+ self.assertLess(res.answer[0].ttl, ttlECS)
+
+ def setRoutingTag(self, tag):
+ # This value is picked up by the gettag()
+ file = open('tagfile', 'w')
+ if tag:
+ file.write(tag)
+ file.close();
+
+ @classmethod
+ def startResponders(cls):
+ global routingReactorRunning
+ print("Launching responders..")
+
+ address = cls._PREFIX + '.24'
+ port = 53
+
+ if not routingReactorRunning:
+ reactor.listenUDP(port, UDPRoutingResponder(), interface=address)
+ routingReactorRunning = True
+
+ if not reactor.running:
+ cls._UDPResponder = threading.Thread(name='UDP Routing Responder', target=reactor.run, args=(False,))
+ cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.start()
+
+ @classmethod
+ def setUpClass(cls):
+ cls.setUpSockets()
+
+ cls.startResponders()
+
+ confdir = os.path.join('configs', cls._confdir)
+ cls.createConfigDir(confdir)
+
+ cls.generateRecursorConfig(confdir)
+ cls.startRecursor(confdir, cls._recursorPort)
+
+ print("Launching tests..")
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.tearDownRecursor()
+ os.unlink('tagfile')
+
+class testRoutingTag(RoutingTagTest):
+ _confdir = 'RoutingTag'
+
+ _config_template = """
+log-common-errors=yes
+use-incoming-edns-subnet=yes
+edns-subnet-whitelist=ecs-echo.example.
+forward-zones=ecs-echo.example=%s.24
+ """ % (os.environ['PREFIX'])
+ _lua_dns_script_file = """
+
+function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp, proxyProtocolValues)
+ local rtag
+ for line in io.lines('tagfile') do
+ rtag = line
+ break
+ end
+ return 0, nil, nil, nil, nil, nil, rtag
+end
+"""
+
+ def testSendECS(self):
+ # First send an ECS query with routingTag
+ self.setRoutingTag('foo')
+ expected1 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.sendECSQuery(query, expected1)
+
+ # Now check a cache hit with the same routingTag (but no ECS)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.checkECSQueryHit(query, expected1)
+
+ expected2 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
+ # And see if a different tag does *not* hit the first one
+ self.setRoutingTag('bar')
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.sendECSQuery(query, expected2)
+
+ # And see if a *no* tag does *not* hit the first one
+ expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
+ self.setRoutingTag(None)
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.sendECSQuery(query, expected3)
+
+ # And see if an unknown tag from the same subnet does hit the last
+ self.setRoutingTag('baz')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.3.2', 32)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.checkECSQueryHit(query, expected3)
+
+ # And a no tag and no subnet query does hit the general case
+ self.setRoutingTag(None)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.sendECSQuery(query, expected2)
+
+ # And a unknown tag and no subnet query does hit the general case
+ self.setRoutingTag('bag')
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.sendECSQuery(query, expected2)
+
+ #return # remove this line to peek at cache
+ rec_controlCmd = [os.environ['RECCONTROL'],
+ '--config-dir=%s' % 'configs/' + self._confdir,
+ 'dump-cache x']
+ try:
+ expected = 'dumped 7 records\n'
+ ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
+ self.assertEqual(ret, expected)
+
+ except subprocess.CalledProcessError as e:
+ print(e.output)
+ raise
+
+class testRoutingTagFFI(RoutingTagTest):
+ _confdir = 'RoutingTagFFI'
+
+ _config_template = """
+log-common-errors=yes
+use-incoming-edns-subnet=yes
+edns-subnet-whitelist=ecs-echo.example.
+forward-zones=ecs-echo.example=%s.24
+ """ % (os.environ['PREFIX'])
+ _lua_dns_script_file = """
+
+local ffi = require("ffi")
+ffi.cdef[[
+ typedef struct pdns_ffi_param pdns_ffi_param_t;
+
+ const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
+ void pdns_ffi_param_set_routingtag(pdns_ffi_param_t* ref, const char* rtag);
+]]
+
+function gettag_ffi(obj)
+ for line in io.lines('tagfile') do
+ local rtag = ffi.string(line)
+ ffi.C.pdns_ffi_param_set_routingtag(obj, rtag)
+ break
+ end
+ return 0
+end
+"""
+ def testSendECS(self):
+ # First send an ECS query with routingTag
+ self.setRoutingTag('foo')
+ expected1 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.sendECSQuery(query, expected1)
+
+ # Now check a cache hit with the same routingTag (but no ECS)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.checkECSQueryHit(query, expected1)
+
+ expected2 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
+ # And see if a different tag does *not* hit the first one
+ self.setRoutingTag('bar')
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.sendECSQuery(query, expected2)
+
+ # And see if a *no* tag does *not* hit the first one
+ expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
+ self.setRoutingTag(None)
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.sendECSQuery(query, expected3)
+
+ # And see if an unknown tag from the same subnet does hit the last
+ self.setRoutingTag('baz')
+ ecso = clientsubnetoption.ClientSubnetOption('192.0.3.2', 32)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
+ self.checkECSQueryHit(query, expected3)
+
+ # And a no tag and no subnet query does hit the general case
+ self.setRoutingTag(None)
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.sendECSQuery(query, expected2)
+
+ # And a unknown tag and no subnet query does hit the general case
+ self.setRoutingTag('bag')
+ query = dns.message.make_query(nameECS, 'TXT', 'IN')
+ self.sendECSQuery(query, expected2)
+
+ return #remove this line to peek at cache
+ rec_controlCmd = [os.environ['RECCONTROL'],
+ '--config-dir=%s' % 'configs/' + self._confdir,
+ 'dump-cache y']
+ try:
+ expected = 'dumped 6 records\n'
+ ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
+ self.assertEqual(ret, expected)
+
+ except subprocess.CalledProcessError as e:
+ print(e.output)
+ raise
+
+class UDPRoutingResponder(DatagramProtocol):
+ @staticmethod
+ def ipToStr(option):
+ if option.family == clientsubnetoption.FAMILY_IPV4:
+ ip = socket.inet_ntop(socket.AF_INET, struct.pack('!L', option.ip))
+ elif option.family == clientsubnetoption.FAMILY_IPV6:
+ ip = socket.inet_ntop(socket.AF_INET6,
+ struct.pack('!QQ',
+ option.ip >> 64,
+ option.ip & (2 ** 64 - 1)))
+ return ip
+
+ def datagramReceived(self, datagram, address):
+ request = dns.message.from_wire(datagram)
+
+ response = dns.message.make_response(request)
+ response.flags |= dns.flags.AA
+ ecso = None
+
+ if (request.question[0].name == dns.name.from_text(nameECS) or request.question[0].name == dns.name.from_text(nameECSInvalidScope)) and request.question[0].rdtype == dns.rdatatype.TXT:
+
+ text = emptyECSText
+ for option in request.options:
+ if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(option, clientsubnetoption.ClientSubnetOption):
+ text = self.ipToStr(option) + '/' + str(option.mask)
+
+ # Send a scope more specific than the received source for nameECSInvalidScope
+ if request.question[0].name == dns.name.from_text(nameECSInvalidScope):
+ ecso = clientsubnetoption.ClientSubnetOption("192.0.42.42", 32, 32)
+ else:
+ ecso = clientsubnetoption.ClientSubnetOption(self.ipToStr(option), option.mask, option.mask)
+
+ answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, 'TXT', text)
+ response.answer.append(answer)
+
+ elif request.question[0].name == dns.name.from_text(nameECS) and request.question[0].rdtype == dns.rdatatype.NS:
+ answer = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'NS', 'ns1.ecs-echo.example.')
+ response.answer.append(answer)
+ additional = dns.rrset.from_text('ns1.ecs-echo.example.', 15, dns.rdataclass.IN, 'A', os.environ['PREFIX'] + '.24')
+ response.additional.append(additional)
+
+ if ecso:
+ response.options = [ecso]
+
+ self.transport.write(response.to_wire(), address)
==> www.trillian.example.net has no RPZ policy attached, so lookup should succeed
Reply to question for qname='www.trillian.example.net.', qtype=A
Rcode: 0 (No Error), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
-0 www.trillian.example.net. IN CNAME 15 www2.arthur.example.net.
-0 www2.arthur.example.net. IN A 15 192.0.2.6
+0 www.trillian.example.net. IN CNAME 15 www3.arthur.example.net.
+0 www3.arthur.example.net. IN A 15 192.0.2.6
==> www.hijackme.example.net is served on ns.hijackme.example.net, which should be NXDOMAIN
Reply to question for qname='www.hijackme.example.net.', qtype=A
Rcode: 3 (Non-Existent domain), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
Reply to question for qname='not-rpz.example.net.', qtype=A
Rcode: 3 (Non-Existent domain), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
0 not-rpz.example.net. IN CNAME 5 rpz-not.com.
+1 . IN SOA 15 ns.example.net. hostmaster.example.net. 1 3600 1800 1209600 300
==> echo-me.wildcard-target.example.net is an RPZ wildcard target
Reply to question for qname='echo-me.wildcard-target.example.net.', qtype=A
Rcode: 3 (Non-Existent domain), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
0 echo-me.wildcard-target.example.net. IN CNAME 7200 echo-me.wildcard-target.example.net.walled-garden.example.net.
+1 example.net. IN SOA 15 ns.example.net. hostmaster.example.net. 1 3600 1800 1209600 300
ns2.arthur.example.net. 3600 IN A $PREFIX.13
www.arthur.example.net. 3600 IN A 192.0.2.2
www2.arthur.example.net. 3600 IN A 192.0.2.6
+www3.arthur.example.net. 3600 IN A 192.0.2.6
mail.arthur.example.net. 3600 IN A 192.0.2.3
big.arthur.example.net. 3600 IN TXT "the quick brown fox jumps over the lazy dog"
big.arthur.example.net. 3600 IN TXT "The quick brown fox jumps over the lazy dog"
trillian.example.net. 3600 IN SOA $SOA
trillian.example.net. 3600 IN NS ns.trillian.example.net.
ns.trillian.example.net. 3600 IN A $PREFIX.16
-www.trillian.example.net. 3600 IN CNAME www2.arthur.example.net.
+www.trillian.example.net. 3600 IN CNAME www3.arthur.example.net.
EOF
cat > $PREFIX.16/prequery.lua <<EOF
then
dnspacket:setRcode(pdns.NXDOMAIN)
ret = {}
- ret[1] = newDR(newDN(qname), "CNAME", 3600, "www2.arthur.example.net", 1)
+ ret[1] = newDR(newDN(qname), "CNAME", 3600, "www3.arthur.example.net", 1)
ret[2] = newDR(newDN(""), "SOA", 3600, "$SOA", 2)
dnspacket:addRecords(ret)
return true
socket-dir=/tmp/recursor-service3
lua-config-file=$(pwd)/recursor-service3/config.lua
lua-dns-script=$(pwd)/recursor-service3/script.lua
+security-poll-suffix=
EOF
arthur.example.net CNAME . ; NXDOMAIN on apex
*.arthur.example.net CNAME *. ; NODATA for everything below the apex
-srv.arthur.example.net CNAME rpz-passthru. ; Allow this name though
+www3.arthur.example.net CNAME rpz-passthru. ; Allow this name through (so that the CNAME from www.trillian.example.net is not blocked)
+srv.arthur.example.net CNAME rpz-passthru. ; Allow this name through
www.example.net CNAME www2.example.net. ; Local-Data Action
www3.example.net CNAME www4.example.net. ; Local-Data Action (to be changed in preresolve)
www5.example.net A 192.0.2.15 ; Override www5.example.net.
@ SOA $SOA
@ NS ns.example.net.
-defpol-with-ttl.example.net 50 IN A 192.0.2.35 ; will be overriden by the default policy and the default TTL
+defpol-with-ttl.example.net 50 IN A 192.0.2.35 ; will be overridden by the default policy and the default TTL
EOF
@ SOA $SOA
@ NS ns.example.net.
-defpol-with-ttl-capped.example.net 100 IN A 192.0.2.35 ; will be overriden by the default policy and the default TTL (but capped by maxTTL)
+defpol-with-ttl-capped.example.net 100 IN A 192.0.2.35 ; will be overridden by the default policy and the default TTL (but capped by maxTTL)
EOF
@ SOA $SOA
@ NS ns.example.net.
-defpol-without-ttl.example.net A 192.0.2.35 ; will be overriden by the default policy, but with the zone's TTL
+defpol-without-ttl.example.net A 192.0.2.35 ; will be overridden by the default policy, but with the zone's TTL
EOF
@ SOA $SOA
@ NS ns.example.net.
-defpol-without-ttl-capped.example.net A 192.0.2.35 ; will be overriden by the default policy, but with the zone's TTL capped by maxTTL
+defpol-without-ttl-capped.example.net A 192.0.2.35 ; will be overridden by the default policy, but with the zone's TTL capped by maxTTL
EOF
if dq.qname:equal("android.marvin.example.net") then
dq.wantsRPZ = false -- disable RPZ
end
- if dq.appliedPolicy.policyKind == pdns.policykinds.Custom then
- if dq.qname:equal("www3.example.net") then
- dq.appliedPolicy.policyCustom = "www2.example.net"
+ return false
+end
+
+function policyEventFilter(event)
+ if event.appliedPolicy.policyKind == pdns.policykinds.Custom then
+ if event.qname:equal("www3.example.net") then
+ event.appliedPolicy.policyCustom = "www2.example.net"
+ return false
end
end
return false
-0 www.trillian.example.net. IN CNAME 3600 www2.arthur.example.net.
-0 www2.arthur.example.net. IN A 3600 192.0.2.6
+0 www.trillian.example.net. IN CNAME 3600 www3.arthur.example.net.
+0 www3.arthur.example.net. IN A 3600 192.0.2.6
Rcode: 0 (No Error), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
Reply to question for qname='www.trillian.example.net.', qtype=A
Postgres:
```sh
-docker run -p 5432:5432 --rm -d postgres
+docker run -p 5432:5432 --rm -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres
GPGSQLUSER=postgres PGHOST=127.0.0.1 ./start-test-stop 5300 gpgsql
```
case $context in
- bind)
- cat > pdns-bind.conf << __EOF__
+ bind)
+ cat > pdns-bind.conf << __EOF__
module-dir=./modules
launch=bind
bind-config=./named.conf
bind-ignore-broken-records=yes
__EOF__
- $RUNWRAPPER $PDNS --daemon=no --local-address=$address --local-port=$port --config-dir=. \
- --config-name=bind --socket-dir=./ --no-shuffle \
- --cache-ttl=$cachettl --dname-processing \
- --disable-axfr-rectify=yes &
- skipreasons="nodnssec nodyndns nometa noalias"
- bindwait bind
- ;;
+ $RUNWRAPPER $PDNS --daemon=no --local-address=$address --local-port=$port --config-dir=. \
+ --config-name=bind --socket-dir=./ --no-shuffle \
+ --cache-ttl=$cachettl --dname-processing \
+ --disable-axfr-rectify=yes &
+ skipreasons="nodnssec nodyndns nometa noalias"
+ bindwait bind
+ ;;
- bind-dnssec | bind-dnssec-nsec3 | bind-hybrid-nsec3 | bind-dnssec-nsec3-optout | bind-dnssec-nsec3-narrow)
- rm -f dnssec.sqlite3
- cat > pdns-bind.conf << __EOF__
+ bind-dnssec | bind-dnssec-nsec3 | bind-hybrid-nsec3 | bind-dnssec-nsec3-optout | bind-dnssec-nsec3-narrow)
+ rm -f dnssec.sqlite3
+ cat > pdns-bind.conf << __EOF__
module-dir=./modules
launch=bind
bind-config=./named.conf
bind-ignore-broken-records=yes
__EOF__
- if [ $context = bind-hybrid-nsec3 ]
- then
- [ -z "$GMYSQLDB" ] && GMYSQLDB=pdnstest
- [ -z "$GMYSQLUSER" ] && GMYSQLUSER=root
- [ -z "$GMYSQLHOST" ] && GMYSQLHOST=localhost
- [ -z "$GMYSQLPASSWD" ] && GMYSQLPASSWD=''
+ if [ $context = bind-hybrid-nsec3 ]
+ then
+ [ -z "$GMYSQLDB" ] && GMYSQLDB=pdnstest
+ [ -z "$GMYSQLUSER" ] && GMYSQLUSER=root
+ [ -z "$GMYSQLHOST" ] && GMYSQLHOST=localhost
+ [ -z "$GMYSQLPASSWD" ] && GMYSQLPASSWD=''
- mysqladmin --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" --force drop "$GMYSQLDB" \
- || echo ignoring mysqladmin drop failure
- mysqladmin --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" create "$GMYSQLDB"
- mysql --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" \
- "$GMYSQLDB" < ../modules/gmysqlbackend/schema.mysql.sql
+ mysqladmin --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" --force drop "$GMYSQLDB" \
+ || echo ignoring mysqladmin drop failure
+ mysqladmin --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" create "$GMYSQLDB"
+ mysql --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" \
+ "$GMYSQLDB" < ../modules/gmysqlbackend/schema.mysql.sql
- cat >> pdns-bind.conf << __EOF__
+ cat >> pdns-bind.conf << __EOF__
bind-hybrid
launch+=gmysql
gmysql-dbname=$GMYSQLDB
gmysql-password=$GMYSQLPASSWD
gmysql-dnssec
__EOF__
- else
- echo "bind-dnssec-db=./dnssec.sqlite3" >> pdns-bind.conf
- $PDNSUTIL --config-dir=. --config-name=bind create-bind-db dnssec.sqlite3
- fi
+ else
+ echo "bind-dnssec-db=./dnssec.sqlite3" >> pdns-bind.conf
+ $PDNSUTIL --config-dir=. --config-name=bind create-bind-db dnssec.sqlite3
+ fi
- for zone in $(grep 'zone ' named.conf | cut -f2 -d\")
- do
- if [ $context = bind-hybrid-nsec3 ]
- then
- mysql --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" \
- "$GMYSQLDB" -e "INSERT INTO domains (name, type, master) VALUES('$zone','SLAVE','127.0.0.1:$port')"
- fi
- if [ $zone != insecure.dnssec-parent.com ]
- then
- securezone $zone bind
- if [ $context = bind-dnssec-nsec3 ] || [ $context = bind-dnssec-nsec3-optout ] || [ $context = bind-hybrid-nsec3 ]
- then
- $PDNSUTIL --config-dir=. --config-name=bind set-nsec3 $zone "1 $optout 1 abcd" 2>&1
- elif [ $context = bind-dnssec-nsec3-narrow ]
- then
- $PDNSUTIL --config-dir=. --config-name=bind set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
- fi
+ for zone in $(grep 'zone ' named.conf | cut -f2 -d\")
+ do
+ if [ $context = bind-hybrid-nsec3 ]
+ then
+ mysql --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" \
+ "$GMYSQLDB" -e "INSERT INTO domains (name, type, master) VALUES('$zone','SLAVE','127.0.0.1:$port')"
+ fi
+ if [ $zone != insecure.dnssec-parent.com ]
+ then
+ securezone $zone bind
+ if [ $zone = hiddencryptokeys.org ]
+ then
+ keyid=$($PDNSUTIL --config-dir=. --config-name=bind list-keys $zone | grep hiddencryptokeys.org | awk '{ print $5 }')
+ $PDNSUTIL --config-dir=. --config-name=bind unpublish-zone-key $zone $keyid
+ fi
+ if [ $context = bind-dnssec-nsec3 ] || [ $context = bind-dnssec-nsec3-optout ] || [ $context = bind-hybrid-nsec3 ]
+ then
+ $PDNSUTIL --config-dir=. --config-name=bind set-nsec3 $zone "1 $optout 1 abcd" 2>&1
+ elif [ $context = bind-dnssec-nsec3-narrow ]
+ then
+ $PDNSUTIL --config-dir=. --config-name=bind set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
+ fi
if [ $zone = cryptokeys.org ]
then
$PDNSUTIL --config-dir=. --config-name=bind add-zone-key $zone zsk 384 active unpublished ecdsa384
$PDNSUTIL --config-dir=. --config-name=bind add-zone-key $zone zsk 2048 inactive published rsasha512
$PDNSUTIL --config-dir=. --config-name=bind add-zone-key $zone zsk 2048 inactive unpublished rsasha256
fi
- fi
- if [ "$zone" = "tsig.com" ]; then
- $PDNSUTIL --config-dir=. --config-name=bind import-tsig-key test $ALGORITHM $KEY
- $PDNSUTIL --config-dir=. --config-name=bind activate-tsig-key tsig.com test master
- fi
- done
+ fi
+ if [ "$zone" = "tsig.com" ]; then
+ $PDNSUTIL --config-dir=. --config-name=bind import-tsig-key test $ALGORITHM $KEY
+ $PDNSUTIL --config-dir=. --config-name=bind activate-tsig-key tsig.com test master
+ fi
+ done
- if [ $context = bind-dnssec-nsec3 ] || [ $context = bind-hybrid-nsec3 ]
- then
- extracontexts="bind dnssec nsec3"
- skipreasons="nsec3 nodyndns noalias"
- elif [ $context = bind-dnssec-nsec3-optout ]
- then
- extracontexts="bind dnssec nsec3 nsec3-optout"
- skipreasons="optout nodyndns noalias"
- elif [ $context = bind-dnssec-nsec3-narrow ]
- then
- extracontexts="bind dnssec narrow"
- skipreasons="narrow nodyndns noalias"
- else
- extracontexts="bind dnssec"
- skipreasons="nodyndns noalias nsec"
- fi
+ if [ $context = bind-dnssec-nsec3 ] || [ $context = bind-hybrid-nsec3 ]
+ then
+ extracontexts="bind dnssec nsec3"
+ skipreasons="nsec3 nodyndns noalias"
+ elif [ $context = bind-dnssec-nsec3-optout ]
+ then
+ extracontexts="bind dnssec nsec3 nsec3-optout"
+ skipreasons="optout nodyndns noalias"
+ elif [ $context = bind-dnssec-nsec3-narrow ]
+ then
+ extracontexts="bind dnssec narrow"
+ skipreasons="narrow nodyndns noalias"
+ else
+ extracontexts="bind dnssec"
+ skipreasons="nodyndns noalias nsec"
+ fi
- $RUNWRAPPER $PDNS --daemon=no --local-address=$address --local-port=$port --config-dir=. \
- --config-name=bind --socket-dir=./ --no-shuffle \
- --cache-ttl=$cachettl --dname-processing \
- --disable-axfr-rectify=yes $lua_prequery &
+ $RUNWRAPPER $PDNS --daemon=no --local-address=$address --local-port=$port --config-dir=. \
+ --config-name=bind --socket-dir=./ --no-shuffle \
+ --cache-ttl=$cachettl --dname-processing \
+ --disable-axfr-rectify=yes $lua_prequery &
- bindwait bind
- ;;
+ bindwait bind
+ ;;
- *)
- nocontext=yes
+ *)
+ nocontext=yes
esac
source ./backends/gsql-common
case $context in
- gmysql-nodnssec | gmysql | gmysql-nsec3 | gmysql-nsec3-optout | gmysql-nsec3-narrow)
+ gmysql-nodnssec | gmysql | gmysql-nsec3 | gmysql-nsec3-optout | gmysql-nsec3-narrow | gmysql_sp)
[ -z "$GMYSQLDB" ] && GMYSQLDB=pdnstest
[ -z "$GMYSQLUSER" ] && GMYSQLUSER=root
[ -z "$GMYSQLHOST" ] && GMYSQLHOST=localhost
*)
nocontext=yes
esac
+
+if [[ "$context" = "gmysql_sp" ]]; then
+ cat >> pdns-gmysql.conf << __EOF__
+gmysql-basic-query=CALL basic_query(?, ?)
+__EOF__
+ mysql --user="$GMYSQLUSER" --password="$GMYSQLPASSWD" --host="$GMYSQLHOST" \
+ "$GMYSQLDB" << __EOF__
+DELIMITER //
+CREATE PROCEDURE basic_query(incoming_type varchar(10), incoming_name varchar(255))
+BEGIN
+SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE disabled=0 and type=incoming_type and name=incoming_name;
+END//
+__EOF__
+fi
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
gsql_master()
{
- backend=$1
- skipreasons=$2
+ backend=$1
+ skipreasons=$2
- real_backend=$backend
- if `echo $backend | grep -q '_'`; then
- real_backend=$(echo $backend | awk -F '_' '{print $1}')
- fi
+ real_backend=$backend
+ if `echo $backend | grep -q '_'`; then
+ real_backend=$(echo $backend | awk -F '_' '{print $1}')
+ fi
- if [ $context != ${backend}-nodnssec ]
- then
- echo "${real_backend}-dnssec" >> pdns-${backend}.conf
- fi
+ if [ $context != ${backend}-nodnssec ]
+ then
+ echo "${real_backend}-dnssec" >> pdns-${backend}.conf
+ fi
- for zone in $(grep 'zone ' named.conf | cut -f2 -d\")
- do
- if [ $context != ${backend}-nodnssec ] && [ $zone != insecure.dnssec-parent.com ]
- then
- if [ $context = ${backend}-nsec3 ] || [ $context = ${backend}-nsec3-optout ]
- then
- $PDNSUTIL --config-dir=. --config-name=$backend set-nsec3 $zone "1 $optout 1 abcd" 2>&1
- elif [ $context = ${backend}-nsec3-narrow ]
- then
- $PDNSUTIL --config-dir=. --config-name=$backend set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
- fi
- securezone $zone ${backend}
+ for zone in $(grep 'zone ' named.conf | cut -f2 -d\")
+ do
+ if [ $context != ${backend}-nodnssec ] && [ $zone != insecure.dnssec-parent.com ]
+ then
+ if [ $context = ${backend}-nsec3 ] || [ $context = ${backend}-nsec3-optout ]
+ then
+ $PDNSUTIL --config-dir=. --config-name=$backend set-nsec3 $zone "1 $optout 1 abcd" 2>&1
+ elif [ $context = ${backend}-nsec3-narrow ]
+ then
+ $PDNSUTIL --config-dir=. --config-name=$backend set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
+ fi
+ securezone $zone ${backend}
+ if [ $zone = hiddencryptokeys.org ]
+ then
+ keyid=$($PDNSUTIL --config-dir=. --config-name=$backend list-keys $zone | grep hiddencryptokeys.org | awk '{ print $5 }')
+ $PDNSUTIL --config-dir=. --config-name=$backend unpublish-zone-key $zone $keyid
+ fi
if [ $zone = cryptokeys.org ]
then
$PDNSUTIL --config-dir=. --config-name=$backend add-zone-key $zone zsk 384 active unpublished ecdsa384
$PDNSUTIL --config-dir=. --config-name=$backend add-zone-key $zone zsk 2048 inactive published rsasha512
$PDNSUTIL --config-dir=. --config-name=$backend add-zone-key $zone zsk 2048 inactive unpublished rsasha256
fi
- else
- $PDNSUTIL --config-dir=. --config-name=$backend rectify-zone $zone 2>&1
- fi
- if [ "$zone" = "tsig.com" ]; then
- $PDNSUTIL --config-dir=. --config-name=$backend import-tsig-key test $ALGORITHM $KEY
- $PDNSUTIL --config-dir=. --config-name=$backend activate-tsig-key tsig.com test master
- fi
- done
+ else
+ $PDNSUTIL --config-dir=. --config-name=$backend rectify-zone $zone 2>&1
+ fi
+ if [ "$zone" = "tsig.com" ]; then
+ $PDNSUTIL --config-dir=. --config-name=$backend import-tsig-key test $ALGORITHM $KEY
+ $PDNSUTIL --config-dir=. --config-name=$backend activate-tsig-key tsig.com test master
+ fi
+ done
- $RUNWRAPPER $PDNS --daemon=no --local-address=$address --local-port=$port --config-dir=. \
- --config-name=$backend --socket-dir=./ --no-shuffle \
- --dnsupdate=yes --resolver=$RESOLVERIP --outgoing-axfr-expand-alias=yes \
- --expand-alias=yes \
- --cache-ttl=$cachettl --dname-processing \
- --disable-axfr-rectify=yes $lua_prequery &
+ $RUNWRAPPER $PDNS --daemon=no --local-address=$address --local-port=$port --config-dir=. \
+ --config-name=$backend --socket-dir=./ --no-shuffle \
+ --dnsupdate=yes --resolver=$RESOLVERIP --outgoing-axfr-expand-alias=yes \
+ --expand-alias=yes \
+ --cache-ttl=$cachettl --dname-processing \
+ --disable-axfr-rectify=yes $lua_prequery &
- if [ $context = ${backend}-nsec3 ]
- then
- extracontexts="dnssec nsec3"
- skipreasons="$skipreasons nsec3"
- elif [ $context = ${backend}-nsec3-optout ]
- then
- extracontexts="dnssec nsec3 nsec3-optout"
- skipreasons="$skipreasons optout"
- elif [ $context = ${backend}-nsec3-narrow ]
- then
- extracontexts="dnssec narrow"
- skipreasons="$skipreasons narrow"
- elif [ $context = ${backend}-nodnssec ]
- then
- skipreasons="$skipreasons nodnssec"
- else
- extracontexts="dnssec"
- skipreasons="$skipreasons nsec"
- fi
+ if [ $context = ${backend}-nsec3 ]
+ then
+ extracontexts="dnssec nsec3"
+ skipreasons="$skipreasons nsec3"
+ elif [ $context = ${backend}-nsec3-optout ]
+ then
+ extracontexts="dnssec nsec3 nsec3-optout"
+ skipreasons="$skipreasons optout"
+ elif [ $context = ${backend}-nsec3-narrow ]
+ then
+ extracontexts="dnssec narrow"
+ skipreasons="$skipreasons narrow"
+ elif [ $context = ${backend}-nodnssec ]
+ then
+ skipreasons="$skipreasons nodnssec"
+ else
+ extracontexts="dnssec"
+ skipreasons="$skipreasons nsec"
+ fi
}
$PDNSUTIL --config-dir=. --config-name=lmdb set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
fi
securezone $zone lmdb
+ if [ $zone = hiddencryptokeys.org ]
+ then
+ keyid=$($PDNSUTIL --config-dir=. --config-name=lmdb list-keys $zone | grep hiddencryptokeys.org | awk '{ print $5 }')
+ $PDNSUTIL --config-dir=. --config-name=lmdb unpublish-zone-key $zone $keyid
+ fi
if [ $zone = cryptokeys.org ]
then
$PDNSUTIL --config-dir=. --config-name=lmdb add-zone-key $zone zsk 384 active unpublished ecdsa384
file "cryptokeys.org";
};
+zone "hiddencryptokeys.org"{
+ type master;
+ file "hiddencryptokeys.org";
+};
+
limit=$2
[ -z "$limit" ] && limit=100000
threads=$3
-[ -z "$threads" ] && threads=2
+[ -z "$threads" ] && threads=8
mthreads=$4
-[ -z "$mthreads" ] && mthreads=100
+[ -z "$mthreads" ] && mthreads=2048
shards=$5
[ -z "$shards" ] && shards=1024
if [ $IPv6 = 1 ]
then
- QLA6="::"
+ QLA6=" ::"
else
QLA6=""
fi
<measurement><name>system CPU seconds</name><value>%S</value></measurement>
<measurement><name>wallclock seconds</name><value>%e</value></measurement>
<measurement><name>%% CPU used</name><value>%P</value></measurement>
-' ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address6="${QLA6}" --threads=$threads --cache-shards=$shards --disable-packetcache > recursor.log 2>&1 &
+' ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address="0.0.0.0${QLA6}" --threads=$threads --record-cache-shards=$shards --disable-packetcache > recursor.log 2>&1 &
sleep 3
# warm up the cache
if [ $IPv6 = 1 ]
then
- QLA6="::"
+ QLA6=" ::"
else
QLA6=""
fi
rm -f recursor.pid pdns_recursor.pid
-${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --local-address=0.0.0.0 --allow-from=0.0.0.0/0 --query-local-address6="${QLA6}" > recursor.log 2>&1 &
+${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --local-address=0.0.0.0 --allow-from=0.0.0.0/0 --query-local-address="0.0.0.0${QLA6}" > recursor.log 2>&1 &
sleep 3
./dnsbulktest -qe 37.252.127.190 $port $limit < ${CSV} > bulktest.results
kill $(cat pdns_recursor.pid)
context is one of:
bind bind-dnssec bind-dnssec-nsec3 bind-dnssec-nsec3-optout bind-dnssec-nsec3-narrow
geoip geoip-nsec3-narrow
-gmysql-nodnssec gmysql gmysql-nsec3 gmysql-nsec3-optout gmysql-nsec3-narrow
+gmysql-nodnssec gmysql gmysql-nsec3 gmysql-nsec3-optout gmysql-nsec3-narrow gmysql_sp
godbc_mssql-nodnssec godbc_mssql godbc_mssql-nsec3 godbc_mssql-nsec3-optout godbc_mssql-nsec3-narrow
godbc_sqlite3-nodnssec godbc_sqlite3 godbc_sqlite3-nsec3 godbc_sqlite3-nsec3-optout godbc_sqlite3-narrow
gpgsql-nodnssec gpgsql gpgsql-nsec3 gpgsql-nsec3-optout gpgsql-nsec3-narrow
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
}
#!/bin/sh
cleandig cryptokeys.org DNSKEY dnssec
+cleandig hiddencryptokeys.org DNSKEY dnssec
2 . IN OPT 32768
Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
Reply to question for qname='cryptokeys.org.', qtype=DNSKEY
+1 hiddencryptokeys.org. IN NSEC 3600 hiddencryptokeys.org. A NS SOA RRSIG NSEC
+1 hiddencryptokeys.org. IN RRSIG 3600 NSEC 13 2 3600 [expiry] [inception] [keytag] hiddencryptokeys.org. ...
+1 hiddencryptokeys.org. IN RRSIG 3600 SOA 13 2 3600 [expiry] [inception] [keytag] hiddencryptokeys.org. ...
+1 hiddencryptokeys.org. IN SOA 3600 cryptokeys.ds9a.nl. ahu.ds9a.nl. 2009071301 14400 3600 604800 3600
+2 . IN OPT 32768
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='hiddencryptokeys.org.', qtype=DNSKEY
--- /dev/null
+0 cryptokeys.org. IN DNSKEY 3600 256 3 10 ...
+0 cryptokeys.org. IN DNSKEY 3600 257 3 13 ...
+0 cryptokeys.org. IN RRSIG 3600 DNSKEY 13 2 3600 [expiry] [inception] [keytag] cryptokeys.org. ...
+0 cryptokeys.org. IN RRSIG 3600 DNSKEY 14 2 3600 [expiry] [inception] [keytag] cryptokeys.org. ...
+2 . IN OPT 32768
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='cryptokeys.org.', qtype=DNSKEY
+1 hiddencryptokeys.org. IN RRSIG 3600 SOA 13 2 3600 [expiry] [inception] [keytag] hiddencryptokeys.org. ...
+1 hiddencryptokeys.org. IN SOA 3600 cryptokeys.ds9a.nl. ahu.ds9a.nl. 2009071301 14400 3600 604800 3600
+1 vd844e5oi5854h79fnaa0f80nqo8brf0.hiddencryptokeys.org. IN NSEC3 3600 1 [flags] 1 abcd VD844E5OI5854H79FNAA0F80NQO8BRF1 A NS SOA RRSIG NSEC3PARAM
+1 vd844e5oi5854h79fnaa0f80nqo8brf0.hiddencryptokeys.org. IN RRSIG 3600 NSEC3 13 3 3600 [expiry] [inception] [keytag] hiddencryptokeys.org. ...
+2 . IN OPT 32768
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='hiddencryptokeys.org.', qtype=DNSKEY
--- /dev/null
+0 cryptokeys.org. IN DNSKEY 3600 256 3 10 ...
+0 cryptokeys.org. IN DNSKEY 3600 257 3 13 ...
+0 cryptokeys.org. IN RRSIG 3600 DNSKEY 13 2 3600 [expiry] [inception] [keytag] cryptokeys.org. ...
+0 cryptokeys.org. IN RRSIG 3600 DNSKEY 14 2 3600 [expiry] [inception] [keytag] cryptokeys.org. ...
+2 . IN OPT 32768
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='cryptokeys.org.', qtype=DNSKEY
+1 hiddencryptokeys.org. IN RRSIG 3600 SOA 13 2 3600 [expiry] [inception] [keytag] hiddencryptokeys.org. ...
+1 hiddencryptokeys.org. IN SOA 3600 cryptokeys.ds9a.nl. ahu.ds9a.nl. 2009071301 14400 3600 604800 3600
+1 vd844e5oi5854h79fnaa0f80nqo8brf0.hiddencryptokeys.org. IN NSEC3 3600 1 [flags] 1 abcd VD844E5OI5854H79FNAA0F80NQO8BRF0 A NS SOA RRSIG NSEC3PARAM
+1 vd844e5oi5854h79fnaa0f80nqo8brf0.hiddencryptokeys.org. IN RRSIG 3600 NSEC3 13 3 3600 [expiry] [inception] [keytag] hiddencryptokeys.org. ...
+2 . IN OPT 32768
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='hiddencryptokeys.org.', qtype=DNSKEY
-Check NSECx response for wildcards no data asnwers (mode 2)
+Check NSECx response for wildcards no data answers (mode 2)
-Check NSECx response for wildcard asnwers (mode 3)
+Check NSECx response for wildcard answers (mode 3)
#!/usr/bin/env bash
-for zone in $(grep 'zone ' named.conf | cut -f2 -d\" | grep -v '^\(cryptokeys.org\|example.com\|nztest.com\|insecure.dnssec-parent.com\)$')
+for zone in $(grep 'zone ' named.conf | cut -f2 -d\" | grep -v '^\(hiddencryptokeys.org\|cryptokeys.org\|example.com\|nztest.com\|insecure.dnssec-parent.com\)$')
do
TFILE=$(mktemp tmp.XXXXXXXXXX)
drill -p $port axfr $zone @$nameserver | ldns-read-zone -z -u CDS -u CDNSKEY > $TFILE
--- /dev/null
+hiddencryptokeys.org. 3600 IN SOA cryptokeys.ds9a.nl. ahu.ds9a.nl. (
+ 2009071301 ; serial
+ 14400 ; refresh (2 hours 30 minutes)
+ 3600 ; retry (7 minutes 30 seconds)
+ 604800 ; expire (1 week)
+ 3600 ; minimum (7 minutes 30 seconds)
+ )
+ 3600 NS cryptokeys.ds9a.nl.
+ 3600 NS cryptokeys.ds9a.nl.
+ 3600 A 212.123.148.70