]> git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.auth-py/test_LuaRecords.py
dnsdist: Apply suggestions from code review, delint
[thirdparty/pdns.git] / regression-tests.auth-py / test_LuaRecords.py
1 #!/usr/bin/env python
2 import unittest
3 import requests
4 import threading
5 import dns
6 import time
7 import clientsubnetoption
8
9 from authtests import AuthTest
10
11 from http.server import BaseHTTPRequestHandler, HTTPServer
12
13 webserver = None
14
15 class FakeHTTPServer(BaseHTTPRequestHandler):
16 def _set_headers(self, response_code=200):
17 self.send_response(response_code)
18 self.send_header('Content-type', 'text/html')
19 self.end_headers()
20
21 def do_GET(self):
22 if self.path == '/404':
23 self._set_headers(404)
24 self.wfile.write(bytes('this page does not exist', 'utf-8'))
25 return
26
27 self._set_headers()
28 if self.path == '/ping.json':
29 self.wfile.write(bytes('{"ping":"pong"}', 'utf-8'))
30 else:
31 self.wfile.write(bytes("<html><body><h1>hi!</h1><h2>Programming in Lua !</h2></body></html>", "utf-8"))
32
33 def log_message(self, format, *args):
34 return
35
36 def do_HEAD(self):
37 self._set_headers()
38
39 class TestLuaRecords(AuthTest):
40 _config_template = """
41 geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
42 edns-subnet-processing=yes
43 launch=bind geoip
44 any-to-tcp=no
45 enable-lua-records
46 lua-health-checks-interval=1
47 """
48
49 _zones = {
50 'example.org': """
51 example.org. 3600 IN SOA {soa}
52 example.org. 3600 IN NS ns1.example.org.
53 example.org. 3600 IN NS ns2.example.org.
54 ns1.example.org. 3600 IN A {prefix}.10
55 ns2.example.org. 3600 IN A {prefix}.11
56
57 web1.example.org. 3600 IN A {prefix}.101
58 web2.example.org. 3600 IN A {prefix}.102
59 web3.example.org. 3600 IN A {prefix}.103
60
61 all.ifportup 3600 IN LUA A "ifportup(8080, {{'{prefix}.101', '{prefix}.102'}})"
62 some.ifportup 3600 IN LUA A "ifportup(8080, {{'192.168.42.21', '{prefix}.102'}})"
63 multi.ifportup 3600 IN LUA A "ifportup(8080, {{ {{'192.168.42.23'}}, {{'192.168.42.21', '{prefix}.102'}}, {{'{prefix}.101'}} }})"
64 none.ifportup 3600 IN LUA A "ifportup(8080, {{'192.168.42.21', '192.168.21.42'}})"
65 all.noneup.ifportup 3600 IN LUA A "ifportup(8080, {{'192.168.42.21', '192.168.21.42'}}, {{ backupSelector='all' }})"
66
67 hashed.example.org. 3600 IN LUA A "pickhashed({{ '1.2.3.4', '4.3.2.1' }})"
68 hashed-v6.example.org. 3600 IN LUA AAAA "pickhashed({{ '2001:db8:a0b:12f0::1', 'fe80::2a1:9bff:fe9b:f268' }})"
69 hashed-txt.example.org. 3600 IN LUA TXT "pickhashed({{ 'bob', 'alice' }})"
70 whashed.example.org. 3600 IN LUA A "pickwhashed({{ {{15, '1.2.3.4'}}, {{42, '4.3.2.1'}} }})"
71 whashed-txt.example.org. 3600 IN LUA TXT "pickwhashed({{ {{15, 'bob'}}, {{42, 'alice'}} }})"
72 rand.example.org. 3600 IN LUA A "pickrandom({{'{prefix}.101', '{prefix}.102'}})"
73 rand-txt.example.org. 3600 IN LUA TXT "pickrandom({{ 'bob', 'alice' }})"
74 randn-txt.example.org. 3600 IN LUA TXT "pickrandomsample( 2, {{ 'bob', 'alice', 'john' }} )"
75 v6-bogus.rand.example.org. 3600 IN LUA AAAA "pickrandom({{'{prefix}.101', '{prefix}.102'}})"
76 v6.rand.example.org. 3600 IN LUA AAAA "pickrandom({{ '2001:db8:a0b:12f0::1', 'fe80::2a1:9bff:fe9b:f268' }})"
77 closest.geo 3600 IN LUA A "pickclosest({{ '1.1.1.2', '1.2.3.4' }})"
78 empty.rand.example.org. 3600 IN LUA A "pickrandom()"
79 timeout.example.org. 3600 IN LUA A "; local i = 0 ; while i < 1000 do pickrandom() ; i = i + 1 end return '1.2.3.4'"
80 wrand.example.org. 3600 IN LUA A "pickwrandom({{ {{30, '{prefix}.102'}}, {{15, '{prefix}.103'}} }})"
81 wrand-txt.example.org. 3600 IN LUA TXT "pickwrandom({{ {{30, 'bob'}}, {{15, 'alice'}} }})"
82 all.example.org. 3600 IN LUA A "all({{'1.2.3.4','4.3.2.1'}})"
83
84 config IN LUA LUA ("settings={{stringmatch='Programming in Lua'}} "
85 "EUWips={{'{prefix}.101','{prefix}.102'}} "
86 "EUEips={{'192.168.42.101','192.168.42.102'}} "
87 "NLips={{'{prefix}.111', '{prefix}.112'}} "
88 "USAips={{'{prefix}.103', '192.168.42.105'}} ")
89
90 usa IN LUA A ( ";include('config') "
91 "return ifurlup('http://www.lua.org:8080/', "
92 "USAips, settings) ")
93
94 usa-ext IN LUA A ( ";include('config') "
95 "return ifurlup('http://www.lua.org:8080/', "
96 "{{EUEips, USAips}}, settings) ")
97
98 mix.ifurlup IN LUA A ("ifurlup('http://www.other.org:8080/ping.json', "
99 "{{ '192.168.42.101', '{prefix}.101' }}, "
100 "{{ stringmatch='pong' }}) ")
101
102 ifurlextup IN LUA A "ifurlextup({{{{['192.168.0.1']='http://{prefix}.101:8080/404',['192.168.0.2']='http://{prefix}.102:8080/404'}}, {{['192.168.0.3']='http://{prefix}.101:8080/'}}}})"
103
104 nl IN LUA A ( ";include('config') "
105 "return ifportup(8081, NLips) ")
106 latlon.geo IN LUA TXT "latlon()"
107 continent.geo IN LUA TXT ";if(continent('NA')) then return 'true' else return 'false' end"
108 continent-code.geo IN LUA TXT ";return continentCode()"
109 asnum.geo IN LUA TXT ";if(asnum('4242')) then return 'true' else return 'false' end"
110 country.geo IN LUA TXT ";if(country('US')) then return 'true' else return 'false' end"
111 country-code.geo IN LUA TXT ";return countryCode()"
112 region.geo IN LUA TXT ";if(region('CA')) then return 'true' else return 'false' end"
113 region-code.geo IN LUA TXT ";return regionCode()"
114 latlonloc.geo IN LUA TXT "latlonloc()"
115
116 true.netmask IN LUA TXT ( ";if(netmask({{ '{prefix}.0/24' }})) "
117 "then return 'true' "
118 "else return 'false' end " )
119 false.netmask IN LUA TXT ( ";if(netmask({{ '1.2.3.4/8' }})) "
120 "then return 'true' "
121 "else return 'false' end " )
122
123 view IN LUA A ("view({{ "
124 "{{ {{'192.168.0.0/16'}}, {{'192.168.1.54'}}}},"
125 "{{ {{'{prefix}.0/16'}}, {{'{prefix}.54'}}}}, "
126 "{{ {{'0.0.0.0/0'}}, {{'192.0.2.1'}}}} "
127 " }}) " )
128 txt.view IN LUA TXT ("view({{ "
129 "{{ {{'192.168.0.0/16'}}, {{'txt'}}}}, "
130 "{{ {{'0.0.0.0/0'}}, {{'else'}}}} "
131 " }}) " )
132 none.view IN LUA A ("view({{ "
133 "{{ {{'192.168.0.0/16'}}, {{'192.168.1.54'}}}},"
134 "{{ {{'1.2.0.0/16'}}, {{'1.2.3.4'}}}}, "
135 " }}) " )
136 *.magic IN LUA A "closestMagic()"
137 www-balanced IN CNAME 1-1-1-3.17-1-2-4.1-2-3-5.magic.example.org.
138
139 any IN LUA A "'192.0.2.1'"
140 any IN TXT "hello there"
141
142 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"
143
144 *.createforward IN LUA A "filterForward(createForward(), newNMG{{'1.0.0.0/8', '64.0.0.0/8'}})"
145 *.createreverse IN LUA PTR "createReverse('%5%.example.com', {{['10.10.10.10'] = 'quad10.example.com.'}})"
146 *.createreverse6 IN LUA PTR "createReverse6('%33%.example.com', {{['2001:db8::1'] = 'example.example.com.'}})"
147
148 newcafromraw IN LUA A "newCAFromRaw('ABCD'):toString()"
149 newcafromraw IN LUA AAAA "newCAFromRaw('ABCD020340506070'):toString()"
150
151 counter IN LUA TXT ";counter = counter or 0 counter=counter+1 return tostring(counter)"
152 """,
153 'createforward6.example.org': """
154 createforward6.example.org. 3600 IN SOA {soa}
155 createforward6.example.org. 3600 IN NS ns1.example.org.
156 createforward6.example.org. 3600 IN NS ns2.example.org.
157 * IN LUA AAAA "filterForward(createForward6(), newNMG{{'2000::/3'}}, 'fe80::1')"
158 """
159 # the separate createforward6 zone is because some of the code in lua-record.cc insists on working relatively to the zone apex
160 }
161 _web_rrsets = []
162
163 @classmethod
164 def startResponders(cls):
165 global webserver
166 if webserver: return # it is already running
167
168 webserver = threading.Thread(name='HTTP Listener',
169 target=cls.HTTPResponder,
170 args=[8080]
171 )
172 webserver.setDaemon(True)
173 webserver.start()
174
175 @classmethod
176 def HTTPResponder(cls, port):
177 server_address = ('', port)
178 httpd = HTTPServer(server_address, FakeHTTPServer)
179 httpd.serve_forever()
180
181 @classmethod
182 def setUpClass(cls):
183
184 super(TestLuaRecords, cls).setUpClass()
185
186 cls._web_rrsets = [dns.rrset.from_text('web1.example.org.', 0, dns.rdataclass.IN, 'A',
187 '{prefix}.101'.format(prefix=cls._PREFIX)),
188 dns.rrset.from_text('web2.example.org.', 0, dns.rdataclass.IN, 'A',
189 '{prefix}.102'.format(prefix=cls._PREFIX)),
190 dns.rrset.from_text('web3.example.org.', 0, dns.rdataclass.IN, 'A',
191 '{prefix}.103'.format(prefix=cls._PREFIX))
192 ]
193
194 def testPickRandom(self):
195 """
196 Basic pickrandom() test with a set of A records
197 """
198 expected = [dns.rrset.from_text('rand.example.org.', 0, dns.rdataclass.IN, 'A',
199 '{prefix}.101'.format(prefix=self._PREFIX)),
200 dns.rrset.from_text('rand.example.org.', 0, dns.rdataclass.IN, 'A',
201 '{prefix}.102'.format(prefix=self._PREFIX))]
202 query = dns.message.make_query('rand.example.org', 'A')
203
204 res = self.sendUDPQuery(query)
205 self.assertRcodeEqual(res, dns.rcode.NOERROR)
206 self.assertAnyRRsetInAnswer(res, expected)
207
208 def testPickRandomTxt(self):
209 """
210 Basic pickrandom() test with a set of TXT records
211 """
212 expected = [dns.rrset.from_text('rand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob'),
213 dns.rrset.from_text('rand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice')]
214 query = dns.message.make_query('rand-txt.example.org', 'TXT')
215
216 res = self.sendUDPQuery(query)
217 self.assertRcodeEqual(res, dns.rcode.NOERROR)
218 self.assertAnyRRsetInAnswer(res, expected)
219
220 def testBogusV6PickRandom(self):
221 """
222 Test a bogus AAAA pickrandom() record with a set of v4 addr
223 """
224 query = dns.message.make_query('v6-bogus.rand.example.org', 'AAAA')
225
226 res = self.sendUDPQuery(query)
227 self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
228
229 def testV6PickRandom(self):
230 """
231 Test pickrandom() AAAA record
232 """
233 expected = [dns.rrset.from_text('v6.rand.example.org.', 0, dns.rdataclass.IN, 'AAAA',
234 '2001:db8:a0b:12f0::1'),
235 dns.rrset.from_text('v6.rand.example.org.', 0, dns.rdataclass.IN, 'AAAA',
236 'fe80::2a1:9bff:fe9b:f268')]
237 query = dns.message.make_query('v6.rand.example.org', 'AAAA')
238
239 res = self.sendUDPQuery(query)
240 self.assertRcodeEqual(res, dns.rcode.NOERROR)
241 self.assertAnyRRsetInAnswer(res, expected)
242
243 def testEmptyRandom(self):
244 """
245 Basic pickrandom() test with an empty set
246 """
247 query = dns.message.make_query('empty.rand.example.org', 'A')
248
249 res = self.sendUDPQuery(query)
250 self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
251
252 def testPickRandomSampleTxt(self):
253 """
254 Basic pickrandomsample() test with a set of TXT records
255 """
256 expected = [dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob', 'alice'),
257 dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob', 'john'),
258 dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice', 'bob'),
259 dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice', 'john'),
260 dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'john', 'bob'),
261 dns.rrset.from_text('randn-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'john', 'alice')]
262 query = dns.message.make_query('randn-txt.example.org', 'TXT')
263
264 res = self.sendUDPQuery(query)
265 self.assertRcodeEqual(res, dns.rcode.NOERROR)
266 self.assertIn(res.answer[0], expected)
267
268 def testWRandom(self):
269 """
270 Basic pickwrandom() test with a set of A records
271 """
272 expected = [dns.rrset.from_text('wrand.example.org.', 0, dns.rdataclass.IN, 'A',
273 '{prefix}.103'.format(prefix=self._PREFIX)),
274 dns.rrset.from_text('wrand.example.org.', 0, dns.rdataclass.IN, 'A',
275 '{prefix}.102'.format(prefix=self._PREFIX))]
276 query = dns.message.make_query('wrand.example.org', 'A')
277
278 res = self.sendUDPQuery(query)
279 self.assertRcodeEqual(res, dns.rcode.NOERROR)
280 self.assertAnyRRsetInAnswer(res, expected)
281
282 def testWRandomTxt(self):
283 """
284 Basic pickwrandom() test with a set of TXT records
285 """
286 expected = [dns.rrset.from_text('wrand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob'),
287 dns.rrset.from_text('wrand-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice')]
288 query = dns.message.make_query('wrand-txt.example.org', 'TXT')
289
290 res = self.sendUDPQuery(query)
291 self.assertRcodeEqual(res, dns.rcode.NOERROR)
292 self.assertAnyRRsetInAnswer(res, expected)
293
294 def testIfportup(self):
295 """
296 Basic ifportup() test
297 """
298 query = dns.message.make_query('all.ifportup.example.org', 'A')
299 expected = [
300 dns.rrset.from_text('all.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
301 '{prefix}.101'.format(prefix=self._PREFIX)),
302 dns.rrset.from_text('all.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
303 '{prefix}.102'.format(prefix=self._PREFIX))]
304
305 res = self.sendUDPQuery(query)
306 self.assertRcodeEqual(res, dns.rcode.NOERROR)
307 self.assertAnyRRsetInAnswer(res, expected)
308
309 def testIfportupWithSomeDown(self):
310 """
311 Basic ifportup() test with some ports DOWN
312 """
313 query = dns.message.make_query('some.ifportup.example.org', 'A')
314 expected = [
315 dns.rrset.from_text('some.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
316 '192.168.42.21'),
317 dns.rrset.from_text('some.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
318 '{prefix}.102'.format(prefix=self._PREFIX))]
319
320 # we first expect any of the IPs as no check has been performed yet
321 res = self.sendUDPQuery(query)
322 self.assertRcodeEqual(res, dns.rcode.NOERROR)
323 self.assertAnyRRsetInAnswer(res, expected)
324
325 # the first IP should not be up so only second should be returned
326 expected = [expected[1]]
327 res = self.sendUDPQuery(query)
328 self.assertRcodeEqual(res, dns.rcode.NOERROR)
329 self.assertAnyRRsetInAnswer(res, expected)
330
331 def testIfportupWithSomeDownMultiset(self):
332 """
333 Basic ifportup() test with some ports DOWN from multiple sets
334 """
335 query = dns.message.make_query('multi.ifportup.example.org', 'A')
336 expected = [
337 dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
338 '192.168.42.21'),
339 dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
340 '192.168.42.23'),
341 dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
342 '{prefix}.102'.format(prefix=self._PREFIX)),
343 dns.rrset.from_text('multi.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
344 '{prefix}.101'.format(prefix=self._PREFIX))
345 ]
346
347 # we first expect any of the IPs as no check has been performed yet
348 res = self.sendUDPQuery(query)
349 self.assertRcodeEqual(res, dns.rcode.NOERROR)
350 self.assertAnyRRsetInAnswer(res, expected)
351
352 # An ip is up in 2 sets, but we expect only the one from the middle set
353 expected = [expected[2]]
354 res = self.sendUDPQuery(query)
355 self.assertRcodeEqual(res, dns.rcode.NOERROR)
356 self.assertAnyRRsetInAnswer(res, expected)
357
358 def testIfportupWithAllDown(self):
359 """
360 Basic ifportup() test with all ports DOWN
361 """
362 query = dns.message.make_query('none.ifportup.example.org', 'A')
363 expected = [
364 dns.rrset.from_text('none.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
365 '192.168.42.21'),
366 dns.rrset.from_text('none.ifportup.example.org.', 0, dns.rdataclass.IN, 'A',
367 '192.168.21.42'.format(prefix=self._PREFIX))]
368
369 # we first expect any of the IPs as no check has been performed yet
370 res = self.sendUDPQuery(query)
371 self.assertRcodeEqual(res, dns.rcode.NOERROR)
372 self.assertAnyRRsetInAnswer(res, expected)
373
374 # no port should be up so we expect any
375 res = self.sendUDPQuery(query)
376 self.assertRcodeEqual(res, dns.rcode.NOERROR)
377 self.assertAnyRRsetInAnswer(res, expected)
378
379 def testIfportupWithAllDownAndAllBackupSelector(self):
380 """
381 Basic ifportup() test with all ports DOWN, fallback to 'all' backup selector
382 """
383 name = 'all.noneup.ifportup.example.org.'
384 query = dns.message.make_query(name, dns.rdatatype.A)
385 expected = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '192.168.42.21', '192.168.21.42')]
386
387 res = self.sendUDPQuery(query)
388 self.assertRcodeEqual(res, dns.rcode.NOERROR)
389
390 time.sleep(3)
391 res = self.sendUDPQuery(query)
392 self.assertRcodeEqual(res, dns.rcode.NOERROR)
393 self.assertEqual(res.answer, expected)
394
395 def testIfurlup(self):
396 """
397 Basic ifurlup() test
398 """
399 reachable = [
400 '{prefix}.103'.format(prefix=self._PREFIX)
401 ]
402 unreachable = ['192.168.42.105']
403 ips = reachable + unreachable
404 all_rrs = []
405 reachable_rrs = []
406 for ip in ips:
407 rr = dns.rrset.from_text('usa.example.org.', 0, dns.rdataclass.IN, 'A', ip)
408 all_rrs.append(rr)
409 if ip in reachable:
410 reachable_rrs.append(rr)
411
412 query = dns.message.make_query('usa.example.org', 'A')
413 res = self.sendUDPQuery(query)
414 self.assertRcodeEqual(res, dns.rcode.NOERROR)
415 self.assertAnyRRsetInAnswer(res, all_rrs)
416
417 # the timeout in the LUA health checker is 2 second, so we make sure to wait slightly longer here
418 time.sleep(3)
419 res = self.sendUDPQuery(query)
420 self.assertRcodeEqual(res, dns.rcode.NOERROR)
421 self.assertAnyRRsetInAnswer(res, reachable_rrs)
422
423 def testIfurlupMultiSet(self):
424 """
425 Basic ifurlup() test with mutiple sets
426 """
427 reachable = [
428 '{prefix}.103'.format(prefix=self._PREFIX)
429 ]
430 unreachable = ['192.168.42.101', '192.168.42.102', '192.168.42.105']
431 ips = reachable + unreachable
432 all_rrs = []
433 reachable_rrs = []
434 for ip in ips:
435 rr = dns.rrset.from_text('usa-ext.example.org.', 0, dns.rdataclass.IN, 'A', ip)
436 all_rrs.append(rr)
437 if ip in reachable:
438 reachable_rrs.append(rr)
439
440 query = dns.message.make_query('usa-ext.example.org', 'A')
441 res = self.sendUDPQuery(query)
442 self.assertRcodeEqual(res, dns.rcode.NOERROR)
443 self.assertAnyRRsetInAnswer(res, all_rrs)
444
445 # the timeout in the LUA health checker is 2 second, so we make sure to wait slightly longer here
446 time.sleep(3)
447 res = self.sendUDPQuery(query)
448 self.assertRcodeEqual(res, dns.rcode.NOERROR)
449 self.assertAnyRRsetInAnswer(res, reachable_rrs)
450
451 def testIfurlextup(self):
452 expected = [dns.rrset.from_text('ifurlextup.example.org.', 0, dns.rdataclass.IN, dns.rdatatype.A, '192.168.0.3')]
453
454 query = dns.message.make_query('ifurlextup.example.org', 'A')
455 res = self.sendUDPQuery(query)
456
457 # wait for health checks to happen
458 time.sleep(5)
459
460 res = self.sendUDPQuery(query)
461
462 self.assertRcodeEqual(res, dns.rcode.NOERROR)
463 self.assertEqual(res.answer, expected)
464
465 def testIfurlupSimplified(self):
466 """
467 Basic ifurlup() test with the simplified list of ips
468 Also ensures the correct path is queried
469 """
470 reachable = [
471 '{prefix}.101'.format(prefix=self._PREFIX)
472 ]
473 unreachable = ['192.168.42.101']
474 ips = reachable + unreachable
475 all_rrs = []
476 reachable_rrs = []
477 for ip in ips:
478 rr = dns.rrset.from_text('mix.ifurlup.example.org.', 0, dns.rdataclass.IN, 'A', ip)
479 all_rrs.append(rr)
480 if ip in reachable:
481 reachable_rrs.append(rr)
482
483 query = dns.message.make_query('mix.ifurlup.example.org', 'A')
484 res = self.sendUDPQuery(query)
485 self.assertRcodeEqual(res, dns.rcode.NOERROR)
486 self.assertAnyRRsetInAnswer(res, all_rrs)
487
488 time.sleep(3)
489 res = self.sendUDPQuery(query)
490 self.assertRcodeEqual(res, dns.rcode.NOERROR)
491 self.assertAnyRRsetInAnswer(res, reachable_rrs)
492
493 def testLatlon(self):
494 """
495 Basic latlon() test
496 """
497 name = 'latlon.geo.example.org.'
498 ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24)
499 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
500 expected = dns.rrset.from_text(name, 0,
501 dns.rdataclass.IN, 'TXT',
502 '"47.913000 -122.304200"')
503
504 res = self.sendUDPQuery(query)
505 self.assertRcodeEqual(res, dns.rcode.NOERROR)
506 self.assertRRsetInAnswer(res, expected)
507
508 def testLatlonloc(self):
509 """
510 Basic latlonloc() test
511 """
512 name = 'latlonloc.geo.example.org.'
513 expected = dns.rrset.from_text(name, 0,dns.rdataclass.IN, 'TXT',
514 '"47 54 46.8 N 122 18 15.12 W 0.00m 1.00m 10000.00m 10.00m"')
515 ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24)
516 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
517
518 res = self.sendUDPQuery(query)
519 self.assertRcodeEqual(res, dns.rcode.NOERROR)
520 self.assertRRsetInAnswer(res, expected)
521
522 def testWildcardError(self):
523 """
524 Ensure errors coming from LUA wildcards are reported
525 """
526 query = dns.message.make_query('failure.magic.example.org', 'A')
527
528 res = self.sendUDPQuery(query)
529 self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
530 self.assertAnswerEmpty(res)
531
532 def testClosestMagic(self):
533 """
534 Basic closestMagic() test
535 """
536 name = 'www-balanced.example.org.'
537 cname = '1-1-1-3.17-1-2-4.1-2-3-5.magic.example.org.'
538 queries = [
539 ('1.1.1.0', 24, '1.1.1.3'),
540 ('1.2.3.0', 24, '1.2.3.5'),
541 ('17.1.0.0', 16, '17.1.2.4')
542 ]
543
544 for (subnet, mask, ip) in queries:
545 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
546 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
547
548 response = dns.message.make_response(query)
549
550 response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.CNAME, cname))
551 response.answer.append(dns.rrset.from_text(cname, 0, dns.rdataclass.IN, 'A', ip))
552
553 res = self.sendUDPQuery(query)
554 self.assertRcodeEqual(res, dns.rcode.NOERROR)
555 self.assertEqual(res.answer, response.answer)
556
557 def testAsnum(self):
558 """
559 Basic asnum() test
560 """
561 queries = [
562 ('1.1.1.0', 24, '"true"'),
563 ('1.2.3.0', 24, '"false"'),
564 ('17.1.0.0', 16, '"false"')
565 ]
566 name = 'asnum.geo.example.org.'
567 for (subnet, mask, txt) in queries:
568 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
569 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
570 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
571
572 res = self.sendUDPQuery(query)
573 self.assertRcodeEqual(res, dns.rcode.NOERROR)
574 self.assertRRsetInAnswer(res, expected)
575
576 def testCountry(self):
577 """
578 Basic country() test
579 """
580 queries = [
581 ('1.1.1.0', 24, '"false"'),
582 ('1.2.3.0', 24, '"true"'),
583 ('17.1.0.0', 16, '"false"')
584 ]
585 name = 'country.geo.example.org.'
586 for (subnet, mask, txt) in queries:
587 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
588 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
589 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
590
591 res = self.sendUDPQuery(query)
592 self.assertRcodeEqual(res, dns.rcode.NOERROR)
593 self.assertRRsetInAnswer(res, expected)
594
595 def testCountryCode(self):
596 """
597 Basic countryCode() test
598 """
599 queries = [
600 ('1.1.1.0', 24, '"au"'),
601 ('1.2.3.0', 24, '"us"'),
602 ('17.1.0.0', 16, '"--"')
603 ]
604 name = 'country-code.geo.example.org.'
605 for (subnet, mask, txt) in queries:
606 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
607 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
608 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
609
610 res = self.sendUDPQuery(query)
611 self.assertRcodeEqual(res, dns.rcode.NOERROR)
612 self.assertRRsetInAnswer(res, expected)
613
614 def testRegion(self):
615 """
616 Basic region() test
617 """
618 queries = [
619 ('1.1.1.0', 24, '"false"'),
620 ('1.2.3.0', 24, '"true"'),
621 ('17.1.0.0', 16, '"false"')
622 ]
623 name = 'region.geo.example.org.'
624 for (subnet, mask, txt) in queries:
625 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
626 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
627 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
628
629 res = self.sendUDPQuery(query)
630 self.assertRcodeEqual(res, dns.rcode.NOERROR)
631 self.assertRRsetInAnswer(res, expected)
632
633 def testRegionCode(self):
634 """
635 Basic regionCode() test
636 """
637 queries = [
638 ('1.1.1.0', 24, '"--"'),
639 ('1.2.3.0', 24, '"ca"'),
640 ('17.1.0.0', 16, '"--"')
641 ]
642 name = 'region-code.geo.example.org.'
643 for (subnet, mask, txt) in queries:
644 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
645 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
646 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
647
648 res = self.sendUDPQuery(query)
649 self.assertRcodeEqual(res, dns.rcode.NOERROR)
650 self.assertRRsetInAnswer(res, expected)
651
652 def testContinent(self):
653 """
654 Basic continent() test
655 """
656 queries = [
657 ('1.1.1.0', 24, '"false"'),
658 ('1.2.3.0', 24, '"true"'),
659 ('17.1.0.0', 16, '"false"')
660 ]
661 name = 'continent.geo.example.org.'
662 for (subnet, mask, txt) in queries:
663 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
664 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
665 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
666
667 res = self.sendUDPQuery(query)
668 self.assertRcodeEqual(res, dns.rcode.NOERROR)
669 self.assertRRsetInAnswer(res, expected)
670
671 def testContinentCode(self):
672 """
673 Basic continentCode() test
674 """
675 queries = [
676 ('1.1.1.0', 24, '"oc"'),
677 ('1.2.3.0', 24, '"na"'),
678 ('17.1.0.0', 16, '"--"')
679 ]
680 name = 'continent-code.geo.example.org.'
681 for (subnet, mask, txt) in queries:
682 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
683 query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso])
684 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', txt)
685
686 res = self.sendUDPQuery(query)
687 self.assertRcodeEqual(res, dns.rcode.NOERROR)
688 self.assertRRsetInAnswer(res, expected)
689
690 def testClosest(self):
691 """
692 Basic pickclosest() test
693 """
694 queries = [
695 ('1.1.1.0', 24, '1.1.1.2'),
696 ('1.2.3.0', 24, '1.2.3.4'),
697 ('17.1.0.0', 16, '1.1.1.2')
698 ]
699 name = 'closest.geo.example.org.'
700 for (subnet, mask, ip) in queries:
701 ecso = clientsubnetoption.ClientSubnetOption(subnet, mask)
702 query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
703 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', ip)
704
705 res = self.sendUDPQuery(query)
706 self.assertRcodeEqual(res, dns.rcode.NOERROR)
707 self.assertRRsetInAnswer(res, expected)
708
709 def testAll(self):
710 """
711 Basic all() test
712 """
713 expected = [dns.rrset.from_text('all.example.org.', 0, dns.rdataclass.IN, dns.rdatatype.A, '1.2.3.4', '4.3.2.1')]
714 query = dns.message.make_query('all.example.org.', 'A')
715
716 res = self.sendUDPQuery(query)
717 self.assertRcodeEqual(res, dns.rcode.NOERROR)
718 self.assertEqual(res.answer, expected)
719
720 def testNetmask(self):
721 """
722 Basic netmask() test
723 """
724 queries = [
725 {
726 'expected': dns.rrset.from_text('true.netmask.example.org.', 0,
727 dns.rdataclass.IN, 'TXT',
728 '"true"'),
729 'query': dns.message.make_query('true.netmask.example.org', 'TXT')
730 },
731 {
732 'expected': dns.rrset.from_text('false.netmask.example.org.', 0,
733 dns.rdataclass.IN, 'TXT',
734 '"false"'),
735 'query': dns.message.make_query('false.netmask.example.org', 'TXT')
736 }
737 ]
738 for query in queries :
739 res = self.sendUDPQuery(query['query'])
740 self.assertRcodeEqual(res, dns.rcode.NOERROR)
741 self.assertRRsetInAnswer(res, query['expected'])
742
743 def testView(self):
744 """
745 Basic view() test
746 """
747 queries = [
748 {
749 'expected': dns.rrset.from_text('view.example.org.', 0,
750 dns.rdataclass.IN, 'A',
751 '{prefix}.54'.format(prefix=self._PREFIX)),
752 'query': dns.message.make_query('view.example.org', 'A')
753 },
754 {
755 'expected': dns.rrset.from_text('txt.view.example.org.', 0,
756 dns.rdataclass.IN, 'TXT',
757 '"else"'),
758 'query': dns.message.make_query('txt.view.example.org', 'TXT')
759 }
760 ]
761 for query in queries :
762 res = self.sendUDPQuery(query['query'])
763 self.assertRcodeEqual(res, dns.rcode.NOERROR)
764 self.assertRRsetInAnswer(res, query['expected'])
765
766 def testViewNoMatch(self):
767 """
768 view() test where no netmask match
769 """
770 query = dns.message.make_query('none.view.example.org', 'A')
771
772 res = self.sendUDPQuery(query)
773 self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
774 self.assertAnswerEmpty(res)
775
776 def testWHashed(self):
777 """
778 Basic pickwhashed() test with a set of A records
779 As the `bestwho` is hashed, we should always get the same answer
780 """
781 expected = [dns.rrset.from_text('whashed.example.org.', 0, dns.rdataclass.IN, 'A', '1.2.3.4'),
782 dns.rrset.from_text('whashed.example.org.', 0, dns.rdataclass.IN, 'A', '4.3.2.1')]
783 query = dns.message.make_query('whashed.example.org', 'A')
784
785 first = self.sendUDPQuery(query)
786 self.assertRcodeEqual(first, dns.rcode.NOERROR)
787 self.assertAnyRRsetInAnswer(first, expected)
788 for _ in range(5):
789 res = self.sendUDPQuery(query)
790 self.assertRcodeEqual(res, dns.rcode.NOERROR)
791 self.assertRRsetInAnswer(res, first.answer[0])
792
793 def testWHashedTxt(self):
794 """
795 Basic pickwhashed() test with a set of TXT records
796 As the `bestwho` is hashed, we should always get the same answer
797 """
798 expected = [dns.rrset.from_text('whashed-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob'),
799 dns.rrset.from_text('whashed-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice')]
800 query = dns.message.make_query('whashed-txt.example.org', 'TXT')
801
802 first = self.sendUDPQuery(query)
803 self.assertRcodeEqual(first, dns.rcode.NOERROR)
804 self.assertAnyRRsetInAnswer(first, expected)
805 for _ in range(5):
806 res = self.sendUDPQuery(query)
807 self.assertRcodeEqual(res, dns.rcode.NOERROR)
808 self.assertRRsetInAnswer(res, first.answer[0])
809
810 def testHashed(self):
811 """
812 Basic pickhashed() test with a set of A records
813 As the `bestwho` is hashed, we should always get the same answer
814 """
815 expected = [dns.rrset.from_text('hashed.example.org.', 0, dns.rdataclass.IN, 'A', '1.2.3.4'),
816 dns.rrset.from_text('hashed.example.org.', 0, dns.rdataclass.IN, 'A', '4.3.2.1')]
817 query = dns.message.make_query('hashed.example.org', 'A')
818
819 first = self.sendUDPQuery(query)
820 self.assertRcodeEqual(first, dns.rcode.NOERROR)
821 self.assertAnyRRsetInAnswer(first, expected)
822 for _ in range(5):
823 res = self.sendUDPQuery(query)
824 self.assertRcodeEqual(res, dns.rcode.NOERROR)
825 self.assertRRsetInAnswer(res, first.answer[0])
826
827 def testHashedV6(self):
828 """
829 Basic pickhashed() test with a set of AAAA records
830 As the `bestwho` is hashed, we should always get the same answer
831 """
832 expected = [dns.rrset.from_text('hashed-v6.example.org.', 0, dns.rdataclass.IN, 'AAAA', '2001:db8:a0b:12f0::1'),
833 dns.rrset.from_text('hashed-v6.example.org.', 0, dns.rdataclass.IN, 'AAAA', 'fe80::2a1:9bff:fe9b:f268')]
834 query = dns.message.make_query('hashed-v6.example.org', 'AAAA')
835
836 first = self.sendUDPQuery(query)
837 self.assertRcodeEqual(first, dns.rcode.NOERROR)
838 self.assertAnyRRsetInAnswer(first, expected)
839 for _ in range(5):
840 res = self.sendUDPQuery(query)
841 self.assertRcodeEqual(res, dns.rcode.NOERROR)
842 self.assertRRsetInAnswer(res, first.answer[0])
843
844 def testHashedTXT(self):
845 """
846 Basic pickhashed() test with a set of TXT records
847 As the `bestwho` is hashed, we should always get the same answer
848 """
849 expected = [dns.rrset.from_text('hashed-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'bob'),
850 dns.rrset.from_text('hashed-txt.example.org.', 0, dns.rdataclass.IN, 'TXT', 'alice')]
851 query = dns.message.make_query('hashed-txt.example.org', 'TXT')
852
853 first = self.sendUDPQuery(query)
854 self.assertRcodeEqual(first, dns.rcode.NOERROR)
855 self.assertAnyRRsetInAnswer(first, expected)
856 for _ in range(5):
857 res = self.sendUDPQuery(query)
858 self.assertRcodeEqual(res, dns.rcode.NOERROR)
859 self.assertRRsetInAnswer(res, first.answer[0])
860
861 def testTimeout(self):
862 """
863 Test if LUA scripts are aborted if script execution takes too long
864 """
865 query = dns.message.make_query('timeout.example.org', 'A')
866
867 first = self.sendUDPQuery(query)
868 self.assertRcodeEqual(first, dns.rcode.SERVFAIL)
869
870
871 def testA(self):
872 """
873 Test A query against `any`
874 """
875 name = 'any.example.org.'
876
877 query = dns.message.make_query(name, 'A')
878
879 response = dns.message.make_response(query)
880
881 response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
882
883 res = self.sendUDPQuery(query)
884 self.assertRcodeEqual(res, dns.rcode.NOERROR)
885 self.assertEqual(res.answer, response.answer)
886
887 def testANY(self):
888 """
889 Test ANY query against `any`
890 """
891
892 name = 'any.example.org.'
893
894 query = dns.message.make_query(name, 'ANY')
895
896 response = dns.message.make_response(query)
897
898 response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'))
899 response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', '"hello there"'))
900
901 res = self.sendUDPQuery(query)
902 self.assertRcodeEqual(res, dns.rcode.NOERROR)
903 self.assertEqual(self.sortRRsets(res.answer), self.sortRRsets(response.answer))
904
905 def testCAFromRaw(self):
906 """
907 Test newCAFromRaw() function
908 """
909 name = 'newcafromraw.example.org.'
910
911 query = dns.message.make_query(name, 'A')
912
913 response = dns.message.make_response(query)
914
915 response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '65.66.67.68'))
916
917 res = self.sendUDPQuery(query)
918 self.assertRcodeEqual(res, dns.rcode.NOERROR)
919 self.assertEqual(res.answer, response.answer)
920
921 query = dns.message.make_query(name, 'AAAA')
922
923 response = dns.message.make_response(query)
924
925 response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.AAAA, '4142:4344:3032:3033:3430:3530:3630:3730'))
926
927 res = self.sendUDPQuery(query)
928 self.assertRcodeEqual(res, dns.rcode.NOERROR)
929 self.assertEqual(res.answer, response.answer)
930
931 def testResolve(self):
932 """
933 Test resolve() function
934 """
935 name = 'resolve.example.org.'
936
937 query = dns.message.make_query(name, 'A')
938
939 response = dns.message.make_response(query)
940
941 response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1'))
942
943 res = self.sendUDPQuery(query)
944 self.assertRcodeEqual(res, dns.rcode.NOERROR)
945 self.assertEqual(res.answer, response.answer)
946
947 def testCreateForwardAndReverse(self):
948 expected = {
949 ".createforward.example.org." : (dns.rdatatype.A, {
950 "1.2.3.4": "1.2.3.4",
951 "1.2.3.4.static": "1.2.3.4",
952 "1.2.3.4.5.6": "1.2.3.4",
953 "invalid.1.2.3.4": "0.0.0.0",
954 "invalid": "0.0.0.0",
955 "1-2-3-4": "1.2.3.4",
956 "1-2-3-4.foo": "1.2.3.4",
957 "1-2-3-4.foo.bar": "0.0.0.0",
958 "1-2-3-4.foo.bar.baz": "0.0.0.0",
959 "1-2-3-4.foo.bar.baz.quux": "0.0.0.0",
960 "ip-1-2-3-4": "1.2.3.4",
961 "ip-is-here-for-you-1-2-3-4": "1.2.3.4",
962 "40414243": "64.65.66.67",
963 "p40414243": "64.65.66.67",
964 "ip40414243": "64.65.66.67",
965 "ipp40414243": "64.65.66.67",
966 "ip4041424": "0.0.0.0",
967 "host64-22-33-44": "64.22.33.44",
968 "2.2.2.2": "0.0.0.0" # filtered
969 }),
970 ".createreverse.example.org." : (dns.rdatatype.PTR, {
971 "4.3.2.1": "1-2-3-4.example.com.",
972 "10.10.10.10": "quad10.example.com." # exception
973 }),
974 ".createforward6.example.org." : (dns.rdatatype.AAAA, {
975 "2001--db8" : "2001::db8",
976 "20010002000300040005000600070db8" : "2001:2:3:4:5:6:7:db8",
977 "blabla20010002000300040005000600070db8" : "2001:2:3:4:5:6:7:db8",
978 "4000-db8--1" : "fe80::1" # filtered, with fallback address override
979 }),
980 ".createreverse6.example.org." : (dns.rdatatype.PTR, {
981 "8.b.d.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.2" : "2001--db8.example.com.",
982 "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" : "example.example.com." # exception
983 })
984 }
985
986 for suffix, v in expected.items():
987 qtype, pairs = v
988 for prefix, target in pairs.items():
989 name = prefix + suffix
990
991 query = dns.message.make_query(name, qtype)
992 response = dns.message.make_response(query)
993 response.answer.append(dns.rrset.from_text(
994 name, 0, dns.rdataclass.IN, qtype, target))
995
996 res = self.sendUDPQuery(query)
997 print(res)
998 self.assertRcodeEqual(res, dns.rcode.NOERROR)
999 self.assertEqual(res.answer, response.answer)
1000
1001 def _getCounter(self, tcp=False):
1002 """
1003 Helper function for shared/non-shared testing
1004 """
1005 name = 'counter.example.org.'
1006
1007 query = dns.message.make_query(name, 'TXT')
1008 responses = []
1009
1010 sender = self.sendTCPQuery if tcp else self.sendUDPQuery
1011
1012 for i in range(50):
1013 res = sender(query)
1014 responses.append(res.answer[0][0])
1015
1016 return(responses)
1017
1018 def testCounter(self):
1019 """
1020 Test non-shared behaviour
1021 """
1022
1023 resUDP = set(self._getCounter(tcp=False))
1024 resTCP = set(self._getCounter(tcp=True))
1025
1026 self.assertEqual(len(resUDP), 1)
1027 self.assertEqual(len(resTCP), 1)
1028
1029 class TestLuaRecordsShared(TestLuaRecords):
1030 _config_template = """
1031 geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
1032 edns-subnet-processing=yes
1033 launch=bind geoip
1034 any-to-tcp=no
1035 enable-lua-records=shared
1036 lua-health-checks-interval=1
1037 """
1038
1039 def testCounter(self):
1040 """
1041 Test shared behaviour
1042 """
1043
1044 resUDP = set(self._getCounter(tcp=False))
1045 resTCP = set(self._getCounter(tcp=True))
1046
1047 self.assertEqual(len(resUDP), 50)
1048 self.assertEqual(len(resTCP), 50)
1049
1050 if __name__ == '__main__':
1051 unittest.main()
1052 exit(0)