]> git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.recursor-dnssec/test_Lua.py
Make sure we can install unsigned packages.
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / test_Lua.py
1 import clientsubnetoption
2 import cookiesoption
3 import dns
4 import os
5 import threading
6 import time
7
8 from twisted.internet.protocol import DatagramProtocol
9 from twisted.internet import reactor
10
11 from recursortests import RecursorTest
12
13 class GettagRecursorTest(RecursorTest):
14 _confdir = 'LuaGettag'
15 _config_template = """
16 log-common-errors=yes
17 gettag-needs-edns-options=yes
18 """
19 _lua_dns_script_file = """
20 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
21
22 local tags = {}
23 local data = {}
24
25 -- make sure we can pass data around to the other hooks
26 data['canary'] = 'from-gettag'
27
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())
32 return 1, tags, data
33 end
34
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())
39 return 1, tags, data
40 end
41
42 if not ednssubnet:empty() then
43 table.insert(tags, 'edns-subnet-'..ednssubnet:toString())
44 end
45
46 for k,v in pairs(ednsoptions) do
47 table.insert(tags, 'ednsoption-'..k..'-count-'..v:count())
48 local len = 0
49 local values = v:getValues()
50 for j,l in pairs(values) do
51 len = len + l:len()
52
53 -- check that the old interface (before 4.2.0) still works
54 if j == 0 then
55 if l:len() ~= v.size then
56 table.insert(tags, 'size obtained via the old edns option interface does not match')
57 end
58 value = v:getContent()
59 if value ~= l then
60 table.insert(tags, 'content obtained via the old edns option interface does not match')
61 end
62 end
63 end
64 table.insert(tags, 'ednsoption-'..k..'-total-len-'..len)
65 end
66
67 if tcp then
68 table.insert(tags, 'gettag-tcp')
69 end
70
71 -- test that tags are passed to other hooks
72 table.insert(tags, qname:toString())
73 table.insert(tags, 'gettag-qtype-'..qtype)
74
75 return 0, tags, data
76 end
77
78 function preresolve(dq)
79
80 -- test that we are getting the tags set by gettag()
81 -- and also getting the correct qname
82 local found = false
83 for _, tag in pairs(dq:getPolicyTags()) do
84 if dq.qname:equal(tag) then
85 found = true
86 end
87 dq:addAnswer(pdns.TXT, '"'..tag..'"')
88 end
89
90 if not found then
91 pdnslog("not valid tag found")
92 dq.rcode = pdns.REFUSED
93 return true
94 end
95
96 if dq.data['canary'] ~= 'from-gettag' then
97 pdnslog("did not get any data from gettag")
98 dq.rcode = pdns.REFUSED
99 return true
100 end
101
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')
106 end
107
108 return true
109 end
110 """
111
112 @classmethod
113 def setUpClass(cls):
114
115 cls.setUpSockets()
116 confdir = os.path.join('configs', cls._confdir)
117 cls.createConfigDir(confdir)
118 cls.generateRecursorConfig(confdir)
119 cls.startRecursor(confdir, cls._recursorPort)
120
121 @classmethod
122 def tearDownClass(cls):
123 cls.tearDownRecursor()
124
125 def testA(self):
126 name = 'gettag.lua.'
127 expected = [
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'])
130 ]
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)
135
136 def testTCPA(self):
137 name = 'gettag-tcpa.lua.'
138 expected = [
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'])
141 ]
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)
146
147 def testAAAA(self):
148 name = 'gettag-aaaa.lua.'
149 expected = [
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'])
152 ]
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)
157
158 def testAAAA(self):
159 name = 'gettag-tcpaaaa.lua.'
160 expected = [
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'])
163 ]
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)
168
169 def testSubnet(self):
170 name = 'gettag-subnet.lua.'
171 subnet = '192.0.2.255'
172 subnetMask = 32
173 ecso = clientsubnetoption.ClientSubnetOption(subnet, subnetMask)
174 expected = [
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']),
178 ]
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)
183
184 def testEDNSOptions(self):
185 name = 'gettag-ednsoptions.lua.'
186 subnet = '192.0.2.255'
187 subnetMask = 32
188 ecso = clientsubnetoption.ClientSubnetOption(subnet, subnetMask)
189 eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef')
190 eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de')
191
192 expected = [
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'
197 ]),
198 ]
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)
203
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
210 threads=2
211 """
212
213 hooksReactorRunning = False
214
215 class UDPHooksResponder(DatagramProtocol):
216
217 def datagramReceived(self, datagram, address):
218 request = dns.message.from_wire(datagram)
219
220 response = dns.message.make_response(request)
221 response.flags |= dns.flags.AA
222
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)
227
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)
231
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)
235
236 self.transport.write(response.to_wire(), address)
237
238 class LuaHooksRecursorTest(RecursorTest):
239 _confdir = 'LuaHooks'
240 _config_template = """
241 forward-zones=luahooks.example=%s.23
242 log-common-errors=yes
243 quiet=no
244 """ % (os.environ['PREFIX'])
245 _lua_dns_script_file = """
246
247 allowedips = newNMG()
248 allowedips:addMask("%s.0/24")
249
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
253 return false
254 end
255
256 return true
257 end
258
259 function nodata(dq)
260 if dq.qtype == pdns.AAAA and dq.qname == newDN("nodata.luahooks.example.") then
261 dq:addAnswer(pdns.AAAA, "2001:DB8::1")
262 return true
263 end
264
265 return false
266 end
267
268 function nxdomain(dq)
269 if dq.qtype == pdns.A and dq.qname == newDN("nxdomain.luahooks.example.") then
270 dq.rcode=0
271 dq:addAnswer(pdns.A, "192.0.2.1")
272 return true
273 end
274
275 return false
276 end
277
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")
284 v.ttl=1
285 end
286 end
287 dq:setRecords(records)
288 return true
289 end
290
291 return false
292 end
293
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"
297 return true
298 end
299
300 return false
301 end
302
303 """ % (os.environ['PREFIX'], os.environ['PREFIX'])
304
305 @classmethod
306 def startResponders(cls):
307 global hooksReactorRunning
308 print("Launching responders..")
309
310 address = cls._PREFIX + '.23'
311 port = 53
312
313 if not hooksReactorRunning:
314 reactor.listenUDP(port, UDPHooksResponder(), interface=address)
315 hooksReactorRunning = True
316
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()
321
322 @classmethod
323 def setUpClass(cls):
324 cls.setUpSockets()
325
326 cls.startResponders()
327
328 confdir = os.path.join('configs', cls._confdir)
329 cls.createConfigDir(confdir)
330
331 cls.generateRecursorConfig(confdir)
332 cls.startRecursor(confdir, cls._recursorPort)
333
334 print("Launching tests..")
335
336 @classmethod
337 def tearDownClass(cls):
338 cls.tearDownRecursor()
339
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')
343
344 for method in ("sendUDPQuery", "sendTCPQuery"):
345 sender = getattr(self, method)
346 res = sender(query)
347 self.assertRcodeEqual(res, dns.rcode.NOERROR)
348 self.assertRRsetInAnswer(res, expected)
349
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')
353
354 for method in ("sendUDPQuery", "sendTCPQuery"):
355 sender = getattr(self, method)
356 res = sender(query)
357 self.assertRcodeEqual(res, dns.rcode.NXDOMAIN)
358
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')
362
363 for method in ("sendUDPQuery", "sendTCPQuery"):
364 sender = getattr(self, method)
365 res = sender(query)
366 self.assertRcodeEqual(res, dns.rcode.NOERROR)
367 self.assertRRsetInAnswer(res, expected)
368
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')
372
373 for method in ("sendUDPQuery", "sendTCPQuery"):
374 sender = getattr(self, method)
375 res = sender(query)
376 self.assertRcodeEqual(res, dns.rcode.NOERROR)
377 self.assertRRsetInAnswer(res, expected)
378 self.assertEqual(res.answer[0].ttl, 1)
379
380 def testIPFilterHeader(self):
381 query = dns.message.make_query('ipfilter.luahooks.example.', 'A', 'IN')
382 query.flags |= dns.flags.AD
383
384 for method in ("sendUDPQuery", "sendTCPQuery"):
385 sender = getattr(self, method)
386 res = sender(query)
387 self.assertEqual(res, None)
388
389 def testPreOutInterceptedQuery(self):
390 query = dns.message.make_query('preout.luahooks.example.', 'A', 'IN')
391
392 for method in ("sendUDPQuery", "sendTCPQuery"):
393 sender = getattr(self, method)
394 res = sender(query)
395 self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
396
397 def testPreOutNotInterceptedQuery(self):
398 query = dns.message.make_query('preout.luahooks.example.', 'AAAA', 'IN')
399
400 for method in ("sendUDPQuery", "sendTCPQuery"):
401 sender = getattr(self, method)
402 res = sender(query)
403 self.assertRcodeEqual(res, dns.rcode.NOERROR)
404
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
411 threads=2
412 quiet=no
413 """ % (os.environ['PREFIX'])
414
415 class DNS64Test(RecursorTest):
416 """Tests the dq.followupAction("getFakeAAAARecords")"""
417
418 _confdir = 'dns64'
419 _config_template = """
420 """
421 _lua_dns_script_file = """
422 prefix = "2001:DB8:64::"
423
424 function nodata (dq)
425 if dq.qtype ~= pdns.AAAA then
426 return false
427 end -- only AAAA records
428
429 -- don't fake AAAA records if DNSSEC validation failed
430 if dq.validationState == pdns.validationstates.Bogus then
431 return false
432 end
433
434 dq.followupFunction = "getFakeAAAARecords"
435 dq.followupPrefix = prefix
436 dq.followupName = dq.qname
437 return true
438 end
439 """
440
441 def testAtoAAAA(self):
442 expected = [
443 dns.rrset.from_text('ns.secure.example.', 15, dns.rdataclass.IN, 'AAAA', '2001:db8:64::7f00:9')
444 ]
445 query = dns.message.make_query('ns.secure.example', 'AAAA')
446
447 for method in ("sendUDPQuery", "sendTCPQuery"):
448 sender = getattr(self, method)
449 res = sender(query)
450
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)
455
456 def testAtoCNAMEtoAAAA(self):
457 expected = [
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')
460 ]
461 query = dns.message.make_query('cname-to-insecure.secure.example.', 'AAAA')
462
463 for method in ("sendUDPQuery", "sendTCPQuery"):
464 sender = getattr(self, method)
465 res = sender(query)
466
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)
471
472
473 class PDNSRandomTest(RecursorTest):
474 """Tests if pdnsrandom works"""
475
476 _confdir = 'pdnsrandom'
477 _config_template = """
478 """
479 _lua_dns_script_file = """
480 function preresolve (dq)
481 dq.rcode = pdns.NOERROR
482 dq:addAnswer(pdns.TXT, pdnsrandom())
483 return true
484 end
485 """
486
487 def testRandom(self):
488 query = dns.message.make_query('whatever.example.', 'TXT')
489
490 ans = set()
491
492 ret = self.sendUDPQuery(query)
493 ans.add(ret.answer[0])
494 ret = self.sendUDPQuery(query)
495 ans.add(ret.answer[0])
496
497 self.assertEqual(len(ans), 2)
498
499
500 class PDNSFeaturesTest(RecursorTest):
501 """Tests if pdns_features works"""
502
503 _confdir = 'pdnsfeatures'
504 _config_template = """
505 """
506 _lua_dns_script_file = """
507 function preresolve (dq)
508 dq.rcode = pdns.NOERROR
509 -- test pdns_features
510 if pdns_features['nonexistent'] ~= nil then
511 print('PDNSFeaturesTest: case 1')
512 dq.rcode = pdns.SERVFAIL
513 end
514 if not pdns_features['PR8001_devicename'] then
515 print('PDNSFeaturesTest: case 2')
516 dq.rcode = pdns.SERVFAIL
517 end
518 return true
519 end
520 """
521
522 def testFeatures(self):
523 query = dns.message.make_query('whatever.example.', 'TXT')
524 res = self.sendUDPQuery(query)
525
526 self.assertRcodeEqual(res, dns.rcode.NOERROR)
527