]>
git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.auth-py/authtests.py
3 from __future__
import print_function
16 from pprint
import pprint
18 class AuthTest ( unittest
. TestCase
):
20 Setup auth required for the tests
26 _root_DS
= "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
28 # The default SOA for zones in the authoritative servers
29 _SOA
= "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
31 # The definitions of the zones on the authoritative servers, the key is the
32 # zonename and the value is the zonefile content. several strings are replaced:
33 # - {soa} => value of _SOA
34 # - {prefix} value of _PREFIX
37 example.org. 3600 IN SOA {soa}
38 example.org. 3600 IN NS ns1.example.org.
39 example.org. 3600 IN NS ns2.example.org.
40 ns1.example.org. 3600 IN A {prefix} .10
41 ns2.example.org. 3600 IN A {prefix} .11
47 Private-key-format: v1.2
48 Algorithm: 13 (ECDSAP256SHA256)
49 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
53 _auth_cmd
= [ 'authbind' ,
58 _PREFIX
= os
. environ
[ 'PREFIX' ]
62 def createConfigDir ( cls
, confdir
):
64 shutil
. rmtree ( confdir
)
66 if e
. errno
!= errno
. ENOENT
:
68 os
. mkdir ( confdir
, 0o755 )
71 def generateAuthZone ( cls
, confdir
, zonename
, zonecontent
):
72 with
open ( os
. path
. join ( confdir
, ' %s .zone' % zonename
), 'w' ) as zonefile
:
73 zonefile
. write ( zonecontent
. format ( prefix
= cls
._ PREFIX
, soa
= cls
._ SOA
))
76 def generateAuthNamedConf ( cls
, confdir
, zones
):
77 with
open ( os
. path
. join ( confdir
, 'named.conf' ), 'w' ) as namedconf
:
82 for zonename
in zones
:
83 zone
= '.' if zonename
== 'ROOT' else zonename
89 };""" % ( zone
, zonename
))
92 def generateAuthConfig ( cls
, confdir
):
93 bind_dnssec_db
= os
. path
. join ( confdir
, 'bind-dnssec.sqlite3' )
95 with
open ( os
. path
. join ( confdir
, 'pdns.conf' ), 'w' ) as pdnsconf
:
97 module-dir=../regression-tests/modules
101 bind-config= {confdir} /named.conf
102 bind-dnssec-db= {bind_dnssec_db}
110 geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
111 edns-subnet-processing=yes
113 resolver= {prefix} .1:5301
115 distributor-threads=1""" . format ( confdir
= confdir
, prefix
= cls
._ PREFIX
,
116 bind_dnssec_db
= bind_dnssec_db
))
118 pdnsutilCmd
= [ os
. environ
[ 'PDNSUTIL' ],
119 '--config-dir= %s ' % confdir
,
123 print ( ' ' . join ( pdnsutilCmd
))
125 subprocess
. check_output ( pdnsutilCmd
, stderr
= subprocess
. STDOUT
)
126 except subprocess
. CalledProcessError
as e
:
131 def secureZone ( cls
, confdir
, zonename
, key
= None ):
132 zone
= '.' if zonename
== 'ROOT' else zonename
134 pdnsutilCmd
= [ os
. environ
[ 'PDNSUTIL' ],
135 '--config-dir= %s ' % confdir
,
139 keyfile
= os
. path
. join ( confdir
, 'dnssec.key' )
140 with
open ( keyfile
, 'w' ) as fdKeyfile
:
143 pdnsutilCmd
= [ os
. environ
[ 'PDNSUTIL' ],
144 '--config-dir= %s ' % confdir
,
151 print ( ' ' . join ( pdnsutilCmd
))
153 subprocess
. check_output ( pdnsutilCmd
, stderr
= subprocess
. STDOUT
)
154 except subprocess
. CalledProcessError
as e
:
159 def generateAllAuthConfig ( cls
, confdir
):
161 cls
. generateAuthConfig ( confdir
)
162 cls
. generateAuthNamedConf ( confdir
, cls
._ zones
. keys ())
164 for zonename
, zonecontent
in cls
._ zones
. items ():
165 cls
. generateAuthZone ( confdir
,
168 if cls
._ zone
_ keys
. get ( zonename
, None ):
169 cls
. secureZone ( confdir
, zonename
, cls
._ zone
_ keys
. get ( zonename
))
172 def startAuth ( cls
, confdir
, ipaddress
):
174 print ( "Launching pdns_server.." )
175 authcmd
= list ( cls
._ auth
_ cmd
)
176 authcmd
. append ( '--config-dir= %s ' % confdir
)
177 authcmd
. append ( '--local-address= %s ' % ipaddress
)
178 authcmd
. append ( '--local-port= %s ' % cls
._ authPort
)
179 authcmd
. append ( '--loglevel=9' )
180 authcmd
. append ( '--enable-lua-record' )
181 print ( ' ' . join ( authcmd
))
183 logFile
= os
. path
. join ( confdir
, 'pdns.log' )
184 with
open ( logFile
, 'w' ) as fdLog
:
185 cls
._ auths
[ ipaddress
] = subprocess
. Popen ( authcmd
, close_fds
= True ,
186 stdout
= fdLog
, stderr
= fdLog
,
191 if cls
._ auths
[ ipaddress
]. poll () is not None :
193 cls
._ auths
[ ipaddress
]. kill ()
195 if e
. errno
!= errno
. ESRCH
:
197 with
open ( logFile
, 'r' ) as fdLog
:
199 sys
. exit ( cls
._ auths
[ ipaddress
]. returncode
)
202 def setUpSockets ( cls
):
203 print ( "Setting up UDP socket.." )
204 cls
._ sock
= socket
. socket ( socket
. AF_INET
, socket
. SOCK_DGRAM
)
205 cls
._ sock
. settimeout ( 2.0 )
206 cls
._ sock
. connect (( cls
._ PREFIX
+ ".1" , cls
._ authPort
))
209 def startResponders ( cls
):
216 cls
. startResponders ()
218 confdir
= os
. path
. join ( 'configs' , cls
._ confdir
)
219 cls
. createConfigDir ( confdir
)
221 cls
. generateAllAuthConfig ( confdir
)
222 cls
. startAuth ( confdir
, cls
._ PREFIX
+ ".1" )
224 print ( "Launching tests.." )
227 def tearDownClass ( cls
):
229 cls
. tearDownResponders ()
232 def tearDownResponders ( cls
):
236 def tearDownClass ( cls
):
240 def tearDownAuth ( cls
):
241 if 'PDNSRECURSOR_FAST_TESTS' in os
. environ
:
246 for _
, auth
in cls
._ auths
. items ():
249 if auth
. poll () is None :
251 if auth
. poll () is None :
255 if e
. errno
!= errno
. ESRCH
:
259 def sendUDPQuery ( cls
, query
, timeout
= 2.0 , decode
= True , fwparams
= dict ()):
261 cls
._ sock
. settimeout ( timeout
)
264 cls
._ sock
. send ( query
. to_wire ())
265 data
= cls
._ sock
. recv ( 4096 )
266 except socket
. timeout
:
270 cls
._ sock
. settimeout ( None )
276 message
= dns
. message
. from_wire ( data
, ** fwparams
)
280 def sendTCPQuery ( cls
, query
, timeout
= 2.0 ):
281 sock
= socket
. socket ( socket
. AF_INET
, socket
. SOCK_STREAM
)
283 sock
. settimeout ( timeout
)
285 sock
. connect (( "127.0.0.1" , cls
._ recursorPort
))
288 wire
= query
. to_wire ()
289 sock
. send ( struct
. pack ( "!H" , len ( wire
)))
293 ( datalen
,) = struct
. unpack ( "!H" , data
)
294 data
= sock
. recv ( datalen
)
295 except socket
. timeout
as e
:
296 print ( "Timeout: %s " % ( str ( e
)))
298 except socket
. error
as e
:
299 print ( "Network error: %s " % ( str ( e
)))
306 message
= dns
. message
. from_wire ( data
)
311 def sendTCPQuery ( cls
, query
, timeout
= 2.0 ):
312 sock
= socket
. socket ( socket
. AF_INET
, socket
. SOCK_STREAM
)
314 sock
. settimeout ( timeout
)
316 sock
. connect (( "127.0.0.1" , cls
._ authPort
))
319 wire
= query
. to_wire ()
320 sock
. send ( struct
. pack ( "!H" , len ( wire
)))
324 ( datalen
,) = struct
. unpack ( "!H" , data
)
325 data
= sock
. recv ( datalen
)
326 except socket
. timeout
as e
:
327 print ( "Timeout: %s " % ( str ( e
)))
329 except socket
. error
as e
:
330 print ( "Network error: %s " % ( str ( e
)))
337 message
= dns
. message
. from_wire ( data
)
341 # This function is called before every tests
344 ## Functions for comparisons
345 def assertMessageHasFlags ( self
, msg
, flags
, ednsflags
=[]):
346 """Asserts that msg has all the flags from flags set
348 @param msg: the dns.message.Message to check
349 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
350 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
352 if not isinstance ( msg
, dns
. message
. Message
):
353 raise TypeError ( "msg is not a dns.message.Message" )
355 if isinstance ( flags
, list ):
357 if not isinstance ( elem
, str ):
358 raise TypeError ( "flags is not a list of strings" )
360 raise TypeError ( "flags is not a list of strings" )
362 if isinstance ( ednsflags
, list ):
363 for elem
in ednsflags
:
364 if not isinstance ( elem
, str ):
365 raise TypeError ( "ednsflags is not a list of strings" )
367 raise TypeError ( "ednsflags is not a list of strings" )
369 msgFlags
= dns
. flags
. to_text ( msg
. flags
). split ()
370 missingFlags
= [ flag
for flag
in flags
if flag
not in msgFlags
]
372 msgEdnsFlags
= dns
. flags
. edns_to_text ( msg
. ednsflags
). split ()
373 missingEdnsFlags
= [ ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
375 if len ( missingFlags
) or len ( missingEdnsFlags
) or len ( msgFlags
) > len ( flags
):
376 raise AssertionError ( "Expected flags ' %s ' (EDNS: ' %s '), found ' %s ' (EDNS: ' %s ') in query %s " %
377 ( ' ' . join ( flags
), ' ' . join ( ednsflags
),
378 ' ' . join ( msgFlags
), ' ' . join ( msgEdnsFlags
),
381 def assertMessageIsAuthenticated ( self
, msg
):
382 """Asserts that the message has the AD bit set
384 @param msg: the dns.message.Message to check"""
386 if not isinstance ( msg
, dns
. message
. Message
):
387 raise TypeError ( "msg is not a dns.message.Message" )
389 msgFlags
= dns
. flags
. to_text ( msg
. flags
)
390 self
. assertTrue ( 'AD' in msgFlags
, "No AD flag found in the message for %s " % msg
. question
[ 0 ]. name
)
392 def assertRRsetInAnswer ( self
, msg
, rrset
):
393 """Asserts the rrset (without comparing TTL) exists in the
394 answer section of msg
396 @param msg: the dns.message.Message to check
397 @param rrset: a dns.rrset.RRset object"""
400 if not isinstance ( msg
, dns
. message
. Message
):
401 raise TypeError ( "msg is not a dns.message.Message" )
403 if not isinstance ( rrset
, dns
. rrset
. RRset
):
404 raise TypeError ( "rrset is not a dns.rrset.RRset" )
407 for ans
in msg
. answer
:
408 ret
+= " %s \n " % ans
. to_text ()
409 if ans
. match ( rrset
. name
, rrset
. rdclass
, rrset
. rdtype
, 0 , None ):
410 self
. assertEqual ( ans
, rrset
, "' %s ' != ' %s '" % ( ans
. to_text (), rrset
. to_text ()))
414 raise AssertionError ( "RRset not found in answer \n\n %s " % ret
)
416 def assertAnyRRsetInAnswer ( self
, msg
, rrsets
):
417 """Asserts that any of the supplied rrsets exists (without comparing TTL)
418 in the answer section of msg
420 @param msg: the dns.message.Message to check
421 @param rrsets: an array of dns.rrset.RRset object"""
423 if not isinstance ( msg
, dns
. message
. Message
):
424 raise TypeError ( "msg is not a dns.message.Message" )
428 if not isinstance ( rrset
, dns
. rrset
. RRset
):
429 raise TypeError ( "rrset is not a dns.rrset.RRset" )
430 for ans
in msg
. answer
:
431 if ans
. match ( rrset
. name
, rrset
. rdclass
, rrset
. rdtype
, 0 , None ):
436 raise AssertionError ( "RRset not found in answer \n %s " %
437 " \n " . join (([ ans
. to_text () for ans
in msg
. answer
])))
439 def assertMatchingRRSIGInAnswer ( self
, msg
, coveredRRset
, keys
= None ):
440 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
441 that covers that RRset. If keys is not None, this function will also try to
442 validate the RRset against the RRSIG
444 @param msg: The dns.message.Message to check
445 @param coveredRRset: The RRSet to check for
446 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
448 if not isinstance ( msg
, dns
. message
. Message
):
449 raise TypeError ( "msg is not a dns.message.Message" )
451 if not isinstance ( coveredRRset
, dns
. rrset
. RRset
):
452 raise TypeError ( "coveredRRset is not a dns.rrset.RRset" )
458 for ans
in msg
. answer
:
459 ret
+= ans
. to_text () + " \n "
461 if ans
. match ( coveredRRset
. name
, coveredRRset
. rdclass
, coveredRRset
. rdtype
, 0 , None ):
463 if ans
. match ( coveredRRset
. name
, dns
. rdataclass
. IN
, dns
. rdatatype
. RRSIG
, coveredRRset
. rdtype
, None ):
465 if msgRRSet
and msgRRsigRRSet
:
469 raise AssertionError ( "RRset for ' %s ' not found in answer" % msg
. question
[ 0 ]. to_text ())
471 if not msgRRsigRRSet
:
472 raise AssertionError ( "No RRSIGs found in answer for %s : \n Full answer: \n %s " % ( msg
. question
[ 0 ]. to_text (), ret
))
476 dns
. dnssec
. validate ( msgRRSet
, msgRRsigRRSet
. to_rdataset (), keys
)
477 except dns
. dnssec
. ValidationFailure
as e
:
478 raise AssertionError ( "Signature validation failed for %s : \n %s " % ( msg
. question
[ 0 ]. to_text (), e
))
480 def assertNoRRSIGsInAnswer ( self
, msg
):
481 """Checks if there are _no_ RRSIGs in the answer section of msg"""
483 if not isinstance ( msg
, dns
. message
. Message
):
484 raise TypeError ( "msg is not a dns.message.Message" )
487 for ans
in msg
. answer
:
488 if ans
. rdtype
== dns
. rdatatype
. RRSIG
:
489 ret
+= ans
. name
. to_text () + " \n "
492 raise AssertionError ( "RRSIG found in answers for: \n %s " % ret
)
494 def assertAnswerEmpty ( self
, msg
):
495 self
. assertTrue ( len ( msg
. answer
) == 0 , "Data found in the the answer section for %s : \n %s " % ( msg
. question
[ 0 ]. to_text (), ' \n ' . join ([ i
. to_text () for i
in msg
. answer
])))
497 def assertAnswerNotEmpty ( self
, msg
):
498 self
. assertTrue ( len ( msg
. answer
) > 0 , "Answer is empty" )
500 def assertRcodeEqual ( self
, msg
, rcode
):
501 if not isinstance ( msg
, dns
. message
. Message
):
502 raise TypeError ( "msg is not a dns.message.Message but a %s " % type ( msg
))
504 if not isinstance ( rcode
, int ):
505 if isinstance ( rcode
, str ):
506 rcode
= dns
. rcode
. from_text ( rcode
)
508 raise TypeError ( "rcode is neither a str nor int" )
510 if msg
. rcode () != rcode
:
511 msgRcode
= dns
. rcode
._ by
_ value
[ msg
. rcode ()]
512 wantedRcode
= dns
. rcode
._ by
_ value
[ rcode
]
514 raise AssertionError ( "Rcode for %s is %s , expected %s ." % ( msg
. question
[ 0 ]. to_text (), msgRcode
, wantedRcode
))
516 def assertAuthorityHasSOA ( self
, msg
):
517 if not isinstance ( msg
, dns
. message
. Message
):
518 raise TypeError ( "msg is not a dns.message.Message but a %s " % type ( msg
))
521 for rrset
in msg
. authority
:
522 if rrset
. rdtype
== dns
. rdatatype
. SOA
:
527 raise AssertionError ( "No SOA record found in the authority section: \n %s " % msg
. to_text ())