]>
git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.recursor-dnssec/test_Lua.py
1 import clientsubnetoption
8 from twisted
.internet
.protocol
import DatagramProtocol
9 from twisted
.internet
import reactor
11 from recursortests
import RecursorTest
13 class GettagRecursorTest(RecursorTest
):
14 _confdir
= 'LuaGettag'
15 _config_template
= """
17 gettag-needs-edns-options=yes
19 _lua_dns_script_file
= """
20 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
25 -- make sure we can pass data around to the other hooks
26 data['canary'] = 'from-gettag'
28 -- test that the remote addr is valid
29 if remote:toString() ~= '127.0.0.1' then
30 pdnslog("invalid remote")
31 table.insert(tags, 'invalid remote '..remote:toString())
35 -- test that the local addr is valid
36 if localip:toString() ~= '127.0.0.1' then
37 pdnslog("invalid local")
38 table.insert(tags, 'invalid local '..localip:toString())
42 if not ednssubnet:empty() then
43 table.insert(tags, 'edns-subnet-'..ednssubnet:toString())
46 for k,v in pairs(ednsoptions) do
47 table.insert(tags, 'ednsoption-'..k..'-count-'..v:count())
49 local values = v:getValues()
50 for j,l in pairs(values) do
53 -- check that the old interface (before 4.2.0) still works
55 if l:len() ~= v.size then
56 table.insert(tags, 'size obtained via the old edns option interface does not match')
58 value = v:getContent()
60 table.insert(tags, 'content obtained via the old edns option interface does not match')
64 table.insert(tags, 'ednsoption-'..k..'-total-len-'..len)
68 table.insert(tags, 'gettag-tcp')
71 -- test that tags are passed to other hooks
72 table.insert(tags, qname:toString())
73 table.insert(tags, 'gettag-qtype-'..qtype)
78 function preresolve(dq)
80 -- test that we are getting the tags set by gettag()
81 -- and also getting the correct qname
83 for _, tag in pairs(dq:getPolicyTags()) do
84 if dq.qname:equal(tag) then
87 dq:addAnswer(pdns.TXT, '"'..tag..'"')
91 pdnslog("not valid tag found")
92 dq.rcode = pdns.REFUSED
96 if dq.data['canary'] ~= 'from-gettag' then
97 pdnslog("did not get any data from gettag")
98 dq.rcode = pdns.REFUSED
102 if dq.qtype == pdns.A then
103 dq:addAnswer(pdns.A, '192.0.2.1')
104 elseif dq.qtype == pdns.AAAA then
105 dq:addAnswer(pdns.AAAA, '2001:db8::1')
116 confdir
= os
.path
.join('configs', cls
._confdir
)
117 cls
.createConfigDir(confdir
)
118 cls
.generateRecursorConfig(confdir
)
119 cls
.startRecursor(confdir
, cls
._recursorPort
)
122 def tearDownClass(cls
):
123 cls
.tearDownRecursor()
128 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.1'),
129 dns
.rrset
.from_text_list(name
, 0, dns
.rdataclass
.IN
, 'TXT', [ name
, 'gettag-qtype-1'])
131 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
132 query
.flags |
= dns
.flags
.CD
133 res
= self
.sendUDPQuery(query
)
134 self
.assertResponseMatches(query
, expected
, res
)
137 name
= 'gettag-tcpa.lua.'
139 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.1'),
140 dns
.rrset
.from_text_list(name
, 0, dns
.rdataclass
.IN
, 'TXT', [ name
, 'gettag-qtype-1', 'gettag-tcp'])
142 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
143 query
.flags |
= dns
.flags
.CD
144 res
= self
.sendTCPQuery(query
)
145 self
.assertResponseMatches(query
, expected
, res
)
148 name
= 'gettag-aaaa.lua.'
150 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'AAAA', '2001:db8::1'),
151 dns
.rrset
.from_text_list(name
, 0, dns
.rdataclass
.IN
, 'TXT', [ name
, 'gettag-qtype-28'])
153 query
= dns
.message
.make_query(name
, 'AAAA', want_dnssec
=True)
154 query
.flags |
= dns
.flags
.CD
155 res
= self
.sendUDPQuery(query
)
156 self
.assertResponseMatches(query
, expected
, res
)
159 name
= 'gettag-tcpaaaa.lua.'
161 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'AAAA', '2001:db8::1'),
162 dns
.rrset
.from_text_list(name
, 0, dns
.rdataclass
.IN
, 'TXT', [ name
, 'gettag-qtype-28', 'gettag-tcp'])
164 query
= dns
.message
.make_query(name
, 'AAAA', want_dnssec
=True)
165 query
.flags |
= dns
.flags
.CD
166 res
= self
.sendTCPQuery(query
)
167 self
.assertResponseMatches(query
, expected
, res
)
169 def testSubnet(self
):
170 name
= 'gettag-subnet.lua.'
171 subnet
= '192.0.2.255'
173 ecso
= clientsubnetoption
.ClientSubnetOption(subnet
, subnetMask
)
175 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.1'),
176 dns
.rrset
.from_text_list(name
, 0, dns
.rdataclass
.IN
, 'TXT', [name
, 'gettag-qtype-1', 'edns-subnet-' + subnet
+ '/' + str(subnetMask
),
177 'ednsoption-8-count-1', 'ednsoption-8-total-len-8']),
179 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True, options
=[ecso
])
180 query
.flags |
= dns
.flags
.CD
181 res
= self
.sendUDPQuery(query
)
182 self
.assertResponseMatches(query
, expected
, res
)
184 def testEDNSOptions(self
):
185 name
= 'gettag-ednsoptions.lua.'
186 subnet
= '192.0.2.255'
188 ecso
= clientsubnetoption
.ClientSubnetOption(subnet
, subnetMask
)
189 eco1
= cookiesoption
.CookiesOption(b
'deadbeef', b
'deadbeef')
190 eco2
= cookiesoption
.CookiesOption(b
'deadc0de', b
'deadc0de')
193 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.1'),
194 dns
.rrset
.from_text_list(name
, 0, dns
.rdataclass
.IN
, 'TXT', [name
, 'gettag-qtype-1', 'edns-subnet-' + subnet
+ '/' + str(subnetMask
),
195 'ednsoption-10-count-2', 'ednsoption-10-total-len-32',
196 'ednsoption-8-count-1', 'ednsoption-8-total-len-8'
199 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True, options
=[eco1
,ecso
,eco2
])
200 query
.flags |
= dns
.flags
.CD
201 res
= self
.sendUDPQuery(query
)
202 self
.assertResponseMatches(query
, expected
, res
)
204 class GettagRecursorDistributesQueriesTest(GettagRecursorTest
):
205 _confdir
= 'LuaGettagDistributes'
206 _config_template
= """
207 log-common-errors=yes
208 gettag-needs-edns-options=yes
209 pdns-distributes-queries=yes
213 hooksReactorRunning
= False
215 class UDPHooksResponder(DatagramProtocol
):
217 def datagramReceived(self
, datagram
, address
):
218 request
= dns
.message
.from_wire(datagram
)
220 response
= dns
.message
.make_response(request
)
221 response
.flags |
= dns
.flags
.AA
223 if request
.question
[0].name
== dns
.name
.from_text('nxdomain.luahooks.example.'):
224 soa
= dns
.rrset
.from_text('luahooks.example.', 86400, dns
.rdataclass
.IN
, 'SOA', 'ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1')
225 response
.authority
.append(soa
)
226 response
.set_rcode(dns
.rcode
.NXDOMAIN
)
228 elif request
.question
[0].name
== dns
.name
.from_text('nodata.luahooks.example.'):
229 soa
= dns
.rrset
.from_text('luahooks.example.', 86400, dns
.rdataclass
.IN
, 'SOA', 'ns.luahooks.example. hostmaster.luahooks.example. 1 3600 3600 3600 1')
230 response
.authority
.append(soa
)
232 elif request
.question
[0].name
== dns
.name
.from_text('postresolve.luahooks.example.'):
233 answer
= dns
.rrset
.from_text('postresolve.luahooks.example.', 3600, dns
.rdataclass
.IN
, 'A', '192.0.2.1')
234 response
.answer
.append(answer
)
236 self
.transport
.write(response
.to_wire(), address
)
238 class LuaHooksRecursorTest(RecursorTest
):
239 _confdir
= 'LuaHooks'
240 _config_template
= """
241 forward-zones=luahooks.example=%s.23
242 log-common-errors=yes
244 """ % (os
.environ
['PREFIX'])
245 _lua_dns_script_file
= """
247 allowedips = newNMG()
248 allowedips:addMask("%s.0/24")
250 function ipfilter(remoteip, localip, dh)
251 -- allow only 127.0.0.1 and AD=0
252 if allowedips:match(remoteip) and not dh:getAD() then
260 if dq.qtype == pdns.AAAA and dq.qname == newDN("nodata.luahooks.example.") then
261 dq:addAnswer(pdns.AAAA, "2001:DB8::1")
268 function nxdomain(dq)
269 if dq.qtype == pdns.A and dq.qname == newDN("nxdomain.luahooks.example.") then
271 dq:addAnswer(pdns.A, "192.0.2.1")
278 function postresolve(dq)
279 if dq.qtype == pdns.A and dq.qname == newDN("postresolve.luahooks.example.") then
280 local records = dq:getRecords()
281 for k,v in pairs(records) do
282 if v.type == pdns.A and v:getContent() == "192.0.2.1" then
283 v:changeContent("192.0.2.42")
287 dq:setRecords(records)
294 function preoutquery(dq)
295 if dq.remoteaddr:equal(newCA("%s.23")) and dq.qname == newDN("preout.luahooks.example.") and dq.qtype == pdns.A then
296 dq.rcode = -3 -- "kill"
303 """ % (os
.environ
['PREFIX'], os
.environ
['PREFIX'])
306 def startResponders(cls
):
307 global hooksReactorRunning
308 print("Launching responders..")
310 address
= cls
._PREFIX
+ '.23'
313 if not hooksReactorRunning
:
314 reactor
.listenUDP(port
, UDPHooksResponder(), interface
=address
)
315 hooksReactorRunning
= True
317 if not reactor
.running
:
318 cls
._UDPResponder
= threading
.Thread(name
='UDP Hooks Responder', target
=reactor
.run
, args
=(False,))
319 cls
._UDPResponder
.setDaemon(True)
320 cls
._UDPResponder
.start()
326 cls
.startResponders()
328 confdir
= os
.path
.join('configs', cls
._confdir
)
329 cls
.createConfigDir(confdir
)
331 cls
.generateRecursorConfig(confdir
)
332 cls
.startRecursor(confdir
, cls
._recursorPort
)
334 print("Launching tests..")
337 def tearDownClass(cls
):
338 cls
.tearDownRecursor()
340 def testNoData(self
):
341 expected
= dns
.rrset
.from_text('nodata.luahooks.example.', 3600, dns
.rdataclass
.IN
, 'AAAA', '2001:DB8::1')
342 query
= dns
.message
.make_query('nodata.luahooks.example.', 'AAAA', 'IN')
344 for method
in ("sendUDPQuery", "sendTCPQuery"):
345 sender
= getattr(self
, method
)
347 self
.assertRcodeEqual(res
, dns
.rcode
.NOERROR
)
348 self
.assertRRsetInAnswer(res
, expected
)
350 def testVanillaNXD(self
):
351 #expected = dns.rrset.from_text('nxdomain.luahooks.example.', 3600, dns.rdataclass.IN, 'A', '192.0.2.1')
352 query
= dns
.message
.make_query('nxdomain.luahooks.example.', 'AAAA', 'IN')
354 for method
in ("sendUDPQuery", "sendTCPQuery"):
355 sender
= getattr(self
, method
)
357 self
.assertRcodeEqual(res
, dns
.rcode
.NXDOMAIN
)
359 def testHookedNXD(self
):
360 expected
= dns
.rrset
.from_text('nxdomain.luahooks.example.', 3600, dns
.rdataclass
.IN
, 'A', '192.0.2.1')
361 query
= dns
.message
.make_query('nxdomain.luahooks.example.', 'A', 'IN')
363 for method
in ("sendUDPQuery", "sendTCPQuery"):
364 sender
= getattr(self
, method
)
366 self
.assertRcodeEqual(res
, dns
.rcode
.NOERROR
)
367 self
.assertRRsetInAnswer(res
, expected
)
369 def testPostResolve(self
):
370 expected
= dns
.rrset
.from_text('postresolve.luahooks.example.', 1, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
371 query
= dns
.message
.make_query('postresolve.luahooks.example.', 'A', 'IN')
373 for method
in ("sendUDPQuery", "sendTCPQuery"):
374 sender
= getattr(self
, method
)
376 self
.assertRcodeEqual(res
, dns
.rcode
.NOERROR
)
377 self
.assertRRsetInAnswer(res
, expected
)
378 self
.assertEqual(res
.answer
[0].ttl
, 1)
380 def testIPFilterHeader(self
):
381 query
= dns
.message
.make_query('ipfiler.luahooks.example.', 'A', 'IN')
382 query
.flags |
= dns
.flags
.AD
384 for method
in ("sendUDPQuery", "sendTCPQuery"):
385 sender
= getattr(self
, method
)
387 self
.assertEqual(res
, None)
389 def testPreOutInterceptedQuery(self
):
390 query
= dns
.message
.make_query('preout.luahooks.example.', 'A', 'IN')
392 for method
in ("sendUDPQuery", "sendTCPQuery"):
393 sender
= getattr(self
, method
)
395 self
.assertRcodeEqual(res
, dns
.rcode
.SERVFAIL
)
397 def testPreOutNotInterceptedQuery(self
):
398 query
= dns
.message
.make_query('preout.luahooks.example.', 'AAAA', 'IN')
400 for method
in ("sendUDPQuery", "sendTCPQuery"):
401 sender
= getattr(self
, method
)
403 self
.assertRcodeEqual(res
, dns
.rcode
.NOERROR
)
405 class LuaHooksRecursorDistributesTest(LuaHooksRecursorTest
):
406 _confdir
= 'LuaHooksDistributes'
407 _config_template
= """
408 forward-zones=luahooks.example=%s.23
409 log-common-errors=yes
410 pdns-distributes-queries=yes
413 """ % (os
.environ
['PREFIX'])
415 class DNS64Test(RecursorTest
):
416 """Tests the dq.followupAction("getFakeAAAARecords")"""
419 _config_template
= """
421 _lua_dns_script_file
= """
422 prefix = "2001:DB8:64::"
425 if dq.qtype ~= pdns.AAAA then
427 end -- only AAAA records
429 -- don't fake AAAA records if DNSSEC validation failed
430 if dq.validationState == pdns.validationstates.Bogus then
434 dq.followupFunction = "getFakeAAAARecords"
435 dq.followupPrefix = prefix
436 dq.followupName = dq.qname
441 def testAtoAAAA(self
):
443 dns
.rrset
.from_text('ns.secure.example.', 15, dns
.rdataclass
.IN
, 'AAAA', '2001:db8:64::7f00:9')
445 query
= dns
.message
.make_query('ns.secure.example', 'AAAA')
447 for method
in ("sendUDPQuery", "sendTCPQuery"):
448 sender
= getattr(self
, method
)
451 self
.assertRcodeEqual(res
, dns
.rcode
.NOERROR
)
452 self
.assertEqual(len(res
.answer
), 1)
453 self
.assertEqual(len(res
.authority
), 0)
454 self
.assertResponseMatches(query
, expected
, res
)
456 def testAtoCNAMEtoAAAA(self
):
458 dns
.rrset
.from_text('cname-to-insecure.secure.example.', 3600, dns
.rdataclass
.IN
, 'CNAME', 'node1.insecure.example.'),
459 dns
.rrset
.from_text('node1.insecure.example.', 3600, dns
.rdataclass
.IN
, 'AAAA', '2001:db8:64::c000:206')
461 query
= dns
.message
.make_query('cname-to-insecure.secure.example.', 'AAAA')
463 for method
in ("sendUDPQuery", "sendTCPQuery"):
464 sender
= getattr(self
, method
)
467 self
.assertRcodeEqual(res
, dns
.rcode
.NOERROR
)
468 self
.assertEqual(len(res
.answer
), 2)
469 self
.assertEqual(len(res
.authority
), 0)
470 self
.assertResponseMatches(query
, expected
, res
)
473 class PDNSRandomTest(RecursorTest
):
474 """Tests if pdnsrandom works"""
476 _confdir
= 'pdnsrandom'
477 _config_template
= """
479 _lua_dns_script_file
= """
480 function preresolve (dq)
481 dq.rcode = pdns.NOERROR
482 dq:addAnswer(pdns.TXT, pdnsrandom())
487 def testRandom(self
):
488 query
= dns
.message
.make_query('whatever.example.', 'TXT')
492 ret
= self
.sendUDPQuery(query
)
493 ans
.add(ret
.answer
[0])
494 ret
= self
.sendUDPQuery(query
)
495 ans
.add(ret
.answer
[0])
497 self
.assertEqual(len(ans
), 2)