]>
git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.auth-py/authtests.py
15 from pprint
import pprint
17 class AuthTest ( unittest
. TestCase
):
19 Setup auth required for the tests
25 _root_DS
= "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
27 # The default SOA for zones in the authoritative servers
28 _SOA
= "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
30 # The definitions of the zones on the authoritative servers, the key is the
31 # zonename and the value is the zonefile content. several strings are replaced:
32 # - {soa} => value of _SOA
33 # - {prefix} value of _PREFIX
36 example.org. 3600 IN SOA {soa}
37 example.org. 3600 IN NS ns1.example.org.
38 example.org. 3600 IN NS ns2.example.org.
39 ns1.example.org. 3600 IN A {prefix} .10
40 ns2.example.org. 3600 IN A {prefix} .11
46 Private-key-format: v1.2
47 Algorithm: 13 (ECDSAP256SHA256)
48 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
52 _auth_cmd
= [ 'authbind' ,
57 _PREFIX
= os
. environ
[ 'PREFIX' ]
61 def createConfigDir ( cls
, confdir
):
63 shutil
. rmtree ( confdir
)
65 if e
. errno
!= errno
. ENOENT
:
67 os
. mkdir ( confdir
, 0755 )
70 def generateAuthZone ( cls
, confdir
, zonename
, zonecontent
):
71 with
open ( os
. path
. join ( confdir
, ' %s .zone' % zonename
), 'w' ) as zonefile
:
72 zonefile
. write ( zonecontent
. format ( prefix
= cls
._ PREFIX
, soa
= cls
._ SOA
))
75 def generateAuthNamedConf ( cls
, confdir
, zones
):
76 with
open ( os
. path
. join ( confdir
, 'named.conf' ), 'w' ) as namedconf
:
81 for zonename
in zones
:
82 zone
= '.' if zonename
== 'ROOT' else zonename
88 };""" % ( zone
, zonename
))
91 def generateAuthConfig ( cls
, confdir
):
92 bind_dnssec_db
= os
. path
. join ( confdir
, 'bind-dnssec.sqlite3' )
94 with
open ( os
. path
. join ( confdir
, 'pdns.conf' ), 'w' ) as pdnsconf
:
96 module-dir=../regression-tests/modules
100 bind-config= {confdir} /named.conf
101 bind-dnssec-db= {bind_dnssec_db}
109 geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
110 edns-subnet-processing=yes
112 resolver= {prefix} .1:5301
114 distributor-threads=1""" . format ( confdir
= confdir
, prefix
= cls
._ PREFIX
,
115 bind_dnssec_db
= bind_dnssec_db
))
117 pdnsutilCmd
= [ os
. environ
[ 'PDNSUTIL' ],
118 '--config-dir= %s ' % confdir
,
122 print ' ' . join ( pdnsutilCmd
)
124 subprocess
. check_output ( pdnsutilCmd
, stderr
= subprocess
. STDOUT
)
125 except subprocess
. CalledProcessError
as e
:
130 def secureZone ( cls
, confdir
, zonename
, key
= None ):
131 zone
= '.' if zonename
== 'ROOT' else zonename
133 pdnsutilCmd
= [ os
. environ
[ 'PDNSUTIL' ],
134 '--config-dir= %s ' % confdir
,
138 keyfile
= os
. path
. join ( confdir
, 'dnssec.key' )
139 with
open ( keyfile
, 'w' ) as fdKeyfile
:
142 pdnsutilCmd
= [ os
. environ
[ 'PDNSUTIL' ],
143 '--config-dir= %s ' % confdir
,
150 print ' ' . join ( pdnsutilCmd
)
152 subprocess
. check_output ( pdnsutilCmd
, stderr
= subprocess
. STDOUT
)
153 except subprocess
. CalledProcessError
as e
:
158 def generateAllAuthConfig ( cls
, confdir
):
160 cls
. generateAuthConfig ( confdir
)
161 cls
. generateAuthNamedConf ( confdir
, cls
._ zones
. keys ())
163 for zonename
, zonecontent
in cls
._ zones
. items ():
164 cls
. generateAuthZone ( confdir
,
167 if cls
._ zone
_ keys
. get ( zonename
, None ):
168 cls
. secureZone ( confdir
, zonename
, cls
._ zone
_ keys
. get ( zonename
))
171 def startAuth ( cls
, confdir
, ipaddress
):
173 print ( "Launching pdns_server.." )
174 authcmd
= list ( cls
._ auth
_ cmd
)
175 authcmd
. append ( '--config-dir= %s ' % confdir
)
176 authcmd
. append ( '--local-address= %s ' % ipaddress
)
177 authcmd
. append ( '--local-port= %s ' % cls
._ authPort
)
178 authcmd
. append ( '--loglevel=9' )
179 authcmd
. append ( '--enable-lua-record' )
180 print ( ' ' . join ( authcmd
))
182 logFile
= os
. path
. join ( confdir
, 'pdns.log' )
183 with
open ( logFile
, 'w' ) as fdLog
:
184 cls
._ auths
[ ipaddress
] = subprocess
. Popen ( authcmd
, close_fds
= True ,
185 stdout
= fdLog
, stderr
= fdLog
,
190 if cls
._ auths
[ ipaddress
]. poll () is not None :
192 cls
._ auths
[ ipaddress
]. kill ()
194 if e
. errno
!= errno
. ESRCH
:
196 with
open ( logFile
, 'r' ) as fdLog
:
198 sys
. exit ( cls
._ auths
[ ipaddress
]. returncode
)
201 def setUpSockets ( cls
):
202 print ( "Setting up UDP socket.." )
203 cls
._ sock
= socket
. socket ( socket
. AF_INET
, socket
. SOCK_DGRAM
)
204 cls
._ sock
. settimeout ( 2.0 )
205 cls
._ sock
. connect (( cls
._ PREFIX
+ ".1" , cls
._ authPort
))
208 def startResponders ( cls
):
215 cls
. startResponders ()
217 confdir
= os
. path
. join ( 'configs' , cls
._ confdir
)
218 cls
. createConfigDir ( confdir
)
220 cls
. generateAllAuthConfig ( confdir
)
221 cls
. startAuth ( confdir
, cls
._ PREFIX
+ ".1" )
223 print ( "Launching tests.." )
226 def tearDownClass ( cls
):
228 cls
. tearDownResponders ()
231 def tearDownResponders ( cls
):
235 def tearDownClass ( cls
):
239 def tearDownAuth ( cls
):
240 if 'PDNSRECURSOR_FAST_TESTS' in os
. environ
:
245 for _
, auth
in cls
._ auths
. items ():
248 if auth
. poll () is None :
250 if auth
. poll () is None :
254 if e
. errno
!= errno
. ESRCH
:
258 def sendUDPQuery ( cls
, query
, timeout
= 2.0 , decode
= True , fwparams
= dict ()):
260 cls
._ sock
. settimeout ( timeout
)
263 cls
._ sock
. send ( query
. to_wire ())
264 data
= cls
._ sock
. recv ( 4096 )
265 except socket
. timeout
:
269 cls
._ sock
. settimeout ( None )
275 message
= dns
. message
. from_wire ( data
, ** fwparams
)
279 def sendTCPQuery ( cls
, query
, timeout
= 2.0 ):
280 sock
= socket
. socket ( socket
. AF_INET
, socket
. SOCK_STREAM
)
282 sock
. settimeout ( timeout
)
284 sock
. connect (( "127.0.0.1" , cls
._ recursorPort
))
287 wire
= query
. to_wire ()
288 sock
. send ( struct
. pack ( "!H" , len ( wire
)))
292 ( datalen
,) = struct
. unpack ( "!H" , data
)
293 data
= sock
. recv ( datalen
)
294 except socket
. timeout
as e
:
295 print ( "Timeout: %s " % ( str ( e
)))
297 except socket
. error
as e
:
298 print ( "Network error: %s " % ( str ( e
)))
305 message
= dns
. message
. from_wire ( data
)
310 def sendTCPQuery ( cls
, query
, timeout
= 2.0 ):
311 sock
= socket
. socket ( socket
. AF_INET
, socket
. SOCK_STREAM
)
313 sock
. settimeout ( timeout
)
315 sock
. connect (( "127.0.0.1" , cls
._ authPort
))
318 wire
= query
. to_wire ()
319 sock
. send ( struct
. pack ( "!H" , len ( wire
)))
323 ( datalen
,) = struct
. unpack ( "!H" , data
)
324 data
= sock
. recv ( datalen
)
325 except socket
. timeout
as e
:
326 print ( "Timeout: %s " % ( str ( e
)))
328 except socket
. error
as e
:
329 print ( "Network error: %s " % ( str ( e
)))
336 message
= dns
. message
. from_wire ( data
)
340 # This function is called before every tests
343 ## Functions for comparisons
344 def assertMessageHasFlags ( self
, msg
, flags
, ednsflags
=[]):
345 """Asserts that msg has all the flags from flags set
347 @param msg: the dns.message.Message to check
348 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
349 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
351 if not isinstance ( msg
, dns
. message
. Message
):
352 raise TypeError ( "msg is not a dns.message.Message" )
354 if isinstance ( flags
, list ):
356 if not isinstance ( elem
, str ):
357 raise TypeError ( "flags is not a list of strings" )
359 raise TypeError ( "flags is not a list of strings" )
361 if isinstance ( ednsflags
, list ):
362 for elem
in ednsflags
:
363 if not isinstance ( elem
, str ):
364 raise TypeError ( "ednsflags is not a list of strings" )
366 raise TypeError ( "ednsflags is not a list of strings" )
368 msgFlags
= dns
. flags
. to_text ( msg
. flags
). split ()
369 missingFlags
= [ flag
for flag
in flags
if flag
not in msgFlags
]
371 msgEdnsFlags
= dns
. flags
. edns_to_text ( msg
. ednsflags
). split ()
372 missingEdnsFlags
= [ ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
374 if len ( missingFlags
) or len ( missingEdnsFlags
) or len ( msgFlags
) > len ( flags
):
375 raise AssertionError ( "Expected flags ' %s ' (EDNS: ' %s '), found ' %s ' (EDNS: ' %s ') in query %s " %
376 ( ' ' . join ( flags
), ' ' . join ( ednsflags
),
377 ' ' . join ( msgFlags
), ' ' . join ( msgEdnsFlags
),
380 def assertMessageIsAuthenticated ( self
, msg
):
381 """Asserts that the message has the AD bit set
383 @param msg: the dns.message.Message to check"""
385 if not isinstance ( msg
, dns
. message
. Message
):
386 raise TypeError ( "msg is not a dns.message.Message" )
388 msgFlags
= dns
. flags
. to_text ( msg
. flags
)
389 self
. assertTrue ( 'AD' in msgFlags
, "No AD flag found in the message for %s " % msg
. question
[ 0 ]. name
)
391 def assertRRsetInAnswer ( self
, msg
, rrset
):
392 """Asserts the rrset (without comparing TTL) exists in the
393 answer section of msg
395 @param msg: the dns.message.Message to check
396 @param rrset: a dns.rrset.RRset object"""
399 if not isinstance ( msg
, dns
. message
. Message
):
400 raise TypeError ( "msg is not a dns.message.Message" )
402 if not isinstance ( rrset
, dns
. rrset
. RRset
):
403 raise TypeError ( "rrset is not a dns.rrset.RRset" )
406 for ans
in msg
. answer
:
407 ret
+= " %s \n " % ans
. to_text ()
408 if ans
. match ( rrset
. name
, rrset
. rdclass
, rrset
. rdtype
, 0 , None ):
409 self
. assertEqual ( ans
, rrset
, "' %s ' != ' %s '" % ( ans
. to_text (), rrset
. to_text ()))
413 raise AssertionError ( "RRset not found in answer \n\n %s " % ret
)
415 def assertAnyRRsetInAnswer ( self
, msg
, rrsets
):
416 """Asserts that any of the supplied rrsets exists (without comparing TTL)
417 in the answer section of msg
419 @param msg: the dns.message.Message to check
420 @param rrsets: an array of dns.rrset.RRset object"""
422 if not isinstance ( msg
, dns
. message
. Message
):
423 raise TypeError ( "msg is not a dns.message.Message" )
427 if not isinstance ( rrset
, dns
. rrset
. RRset
):
428 raise TypeError ( "rrset is not a dns.rrset.RRset" )
429 for ans
in msg
. answer
:
430 if ans
. match ( rrset
. name
, rrset
. rdclass
, rrset
. rdtype
, 0 , None ):
435 raise AssertionError ( "RRset not found in answer \n %s " %
436 " \n " . join (([ ans
. to_text () for ans
in msg
. answer
])))
438 def assertMatchingRRSIGInAnswer ( self
, msg
, coveredRRset
, keys
= None ):
439 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
440 that covers that RRset. If keys is not None, this function will also try to
441 validate the RRset against the RRSIG
443 @param msg: The dns.message.Message to check
444 @param coveredRRset: The RRSet to check for
445 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
447 if not isinstance ( msg
, dns
. message
. Message
):
448 raise TypeError ( "msg is not a dns.message.Message" )
450 if not isinstance ( coveredRRset
, dns
. rrset
. RRset
):
451 raise TypeError ( "coveredRRset is not a dns.rrset.RRset" )
457 for ans
in msg
. answer
:
458 ret
+= ans
. to_text () + " \n "
460 if ans
. match ( coveredRRset
. name
, coveredRRset
. rdclass
, coveredRRset
. rdtype
, 0 , None ):
462 if ans
. match ( coveredRRset
. name
, dns
. rdataclass
. IN
, dns
. rdatatype
. RRSIG
, coveredRRset
. rdtype
, None ):
464 if msgRRSet
and msgRRsigRRSet
:
468 raise AssertionError ( "RRset for ' %s ' not found in answer" % msg
. question
[ 0 ]. to_text ())
470 if not msgRRsigRRSet
:
471 raise AssertionError ( "No RRSIGs found in answer for %s : \n Full answer: \n %s " % ( msg
. question
[ 0 ]. to_text (), ret
))
475 dns
. dnssec
. validate ( msgRRSet
, msgRRsigRRSet
. to_rdataset (), keys
)
476 except dns
. dnssec
. ValidationFailure
as e
:
477 raise AssertionError ( "Signature validation failed for %s : \n %s " % ( msg
. question
[ 0 ]. to_text (), e
))
479 def assertNoRRSIGsInAnswer ( self
, msg
):
480 """Checks if there are _no_ RRSIGs in the answer section of msg"""
482 if not isinstance ( msg
, dns
. message
. Message
):
483 raise TypeError ( "msg is not a dns.message.Message" )
486 for ans
in msg
. answer
:
487 if ans
. rdtype
== dns
. rdatatype
. RRSIG
:
488 ret
+= ans
. name
. to_text () + " \n "
491 raise AssertionError ( "RRSIG found in answers for: \n %s " % ret
)
493 def assertAnswerEmpty ( self
, msg
):
494 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
])))
496 def assertAnswerNotEmpty ( self
, msg
):
497 self
. assertTrue ( len ( msg
. answer
) > 0 , "Answer is empty" )
499 def assertRcodeEqual ( self
, msg
, rcode
):
500 if not isinstance ( msg
, dns
. message
. Message
):
501 raise TypeError ( "msg is not a dns.message.Message but a %s " % type ( msg
))
503 if not isinstance ( rcode
, int ):
504 if isinstance ( rcode
, str ):
505 rcode
= dns
. rcode
. from_text ( rcode
)
507 raise TypeError ( "rcode is neither a str nor int" )
509 if msg
. rcode () != rcode
:
510 msgRcode
= dns
. rcode
._ by
_ value
[ msg
. rcode ()]
511 wantedRcode
= dns
. rcode
._ by
_ value
[ rcode
]
513 raise AssertionError ( "Rcode for %s is %s , expected %s ." % ( msg
. question
[ 0 ]. to_text (), msgRcode
, wantedRcode
))
515 def assertAuthorityHasSOA ( self
, msg
):
516 if not isinstance ( msg
, dns
. message
. Message
):
517 raise TypeError ( "msg is not a dns.message.Message but a %s " % type ( msg
))
520 for rrset
in msg
. authority
:
521 if rrset
. rdtype
== dns
. rdatatype
. SOA
:
526 raise AssertionError ( "No SOA record found in the authority section: \n %s " % msg
. to_text ())