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