]> git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.recursor-dnssec/recursortests.py
Merge pull request #13062 from Habbie/auth-loglevel-prefix
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / recursortests.py
1 #!/usr/bin/env python2
2
3 from __future__ import print_function
4 import errno
5 import shutil
6 import os
7 import socket
8 import struct
9 import subprocess
10 import sys
11 import time
12 import unittest
13 import dns
14 import dns.message
15
16 from proxyprotocol import ProxyProtocol
17
18 from eqdnsmessage import AssertEqualDNSMessageMixin
19
20
21 def have_ipv6():
22 """
23 Try to make an IPv6 socket and bind it, if it fails, no ipv6...
24 """
25 try:
26 sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
27 sock.bind(('::1', 56581))
28 sock.close()
29 return True
30 except:
31 return False
32 return False
33
34
35 class RecursorTest(AssertEqualDNSMessageMixin, unittest.TestCase):
36 """
37 Setup all recursors and auths required for the tests
38 """
39
40 _confdir = 'recursor'
41
42 _recursorPort = 5300
43
44 _recursor = None
45
46 _PREFIX = os.environ['PREFIX']
47
48 _config_template_default = """
49 daemon=no
50 trace=yes
51 dont-query=
52 local-address=127.0.0.1
53 packetcache-ttl=15
54 packetcache-servfail-ttl=15
55 max-cache-ttl=15
56 threads=2
57 loglevel=9
58 disable-syslog=yes
59 log-common-errors=yes
60 statistics-interval=0
61 """
62 _config_template_yaml_default = """
63 recursor:
64 daemon: false
65 threads: 2
66 include_dir: %s
67 recordcache:
68 max_ttl: 15
69 incoming:
70 listen:
71 - 127.0.0.1
72 packetcache:
73 ttl: 15
74 servfail_ttl: 15
75 outgoing:
76 dont_query: []
77 logging:
78 trace: true
79 disable_syslog: true
80 common_errors: true
81 loglevel: 9
82 statistics_interval: 0
83 """
84 _config_template = """
85 """
86 _config_params = []
87 _lua_config_file = None
88 _lua_dns_script_file = None
89 _roothints = """
90 . 3600 IN NS ns.root.
91 ns.root. 3600 IN A %s.8
92 ns.root. 3600 IN AAAA ::1
93 """ % _PREFIX
94 _root_DS = "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
95
96 # The default SOA for zones in the authoritative servers
97 _SOA = "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
98
99 # The definitions of the zones on the authoritative servers, the key is the
100 # zonename and the value is the zonefile content. several strings are replaced:
101 # - {soa} => value of _SOA
102 # - {prefix} value of _PREFIX
103 _zones = {
104 'ROOT': """
105 . 3600 IN SOA {soa}
106 . 3600 IN NS ns.root.
107 ns.root. 3600 IN A {prefix}.8
108
109 example. 3600 IN NS ns1.example.
110 example. 3600 IN NS ns2.example.
111 example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
112
113 ns1.example. 3600 IN A {prefix}.10
114 ns2.example. 3600 IN A {prefix}.18
115 """,
116 'example': """
117 example. 3600 IN SOA {soa}
118 example. 3600 IN NS ns1.example.
119 example. 3600 IN NS ns2.example.
120 ns1.example. 3600 IN A {prefix}.10
121 ns2.example. 3600 IN A {prefix}.18
122
123 secure.example. 3600 IN NS ns.secure.example.
124 secure.example. 3600 IN DS 64723 13 1 53eb985040d3a89bacf29dbddb55a65834706f33
125 ns.secure.example. 3600 IN A {prefix}.9
126
127 cname-secure.example. 3600 IN NS ns.cname-secure.example.
128 cname-secure.example. 3600 IN DS 49148 13 1 a10314452d5ec4d97fcc6d7e275d217261fe790f
129 ns.cname-secure.example. 3600 IN A {prefix}.15
130
131 dname-secure.example. 3600 IN NS ns.dname-secure.example.
132 dname-secure.example. 3600 IN DS 42043 13 2 11c67f46b7c4d5968bc5f6cc944d58377b762bda53ddb4f3a6dbe6faf7a9940f
133 ns.dname-secure.example. 3600 IN A {prefix}.13
134
135 bogus.example. 3600 IN NS ns.bogus.example.
136 bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee
137 ns.bogus.example. 3600 IN A {prefix}.12
138
139 insecure.example. 3600 IN NS ns.insecure.example.
140 ns.insecure.example. 3600 IN A {prefix}.13
141
142 optout.example. 3600 IN NS ns1.optout.example.
143 optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
144 ns1.optout.example. 3600 IN A {prefix}.14
145
146 postresolve_ffi.example. 3600 IN A 1.2.3.4
147 postresolve_ffi.example. 3600 IN A 1.2.3.5
148 postresolve_ffi.example. 3600 IN AAAA ::1
149 postresolve_ffi.example. 3600 IN AAAA ::2
150
151 insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
152 ns1.insecure-formerr.example. 3600 IN A {prefix}.2
153
154 ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
155 ns1.ecs-echo.example. 3600 IN A {prefix}.21
156
157 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
158 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
159
160 sortcname.example. 3600 IN CNAME sort
161 sort.example. 3600 IN A 17.38.42.80
162 sort.example. 3600 IN A 192.168.0.1
163 sort.example. 3600 IN A 17.238.240.5
164 sort.example. 3600 IN MX 25 mx
165
166 delay1.example. 3600 IN NS ns1.delay1.example.
167 ns1.delay1.example. 3600 IN A {prefix}.16
168 delay1.example. 3600 IN DS 42043 13 2 7319fa605cf117f36e3de070157577ebb9a05a1d1f963d80eda55b5d6e793eb2
169
170 delay2.example. 3600 IN NS ns1.delay2.example.
171 ns1.delay2.example. 3600 IN A {prefix}.17
172 delay2.example. 3600 IN DS 42043 13 2 60a047b87740c8564c21d5fd34626c10a77a6c41e3b34564230119c2f13937b8
173
174 cname-nxd.example. 3600 IN CNAME cname-nxd-target.example.
175 cname-nxd-target.example. 3600 IN A 192.0.2.100
176 cname-nodata.example. 3600 IN CNAME cname-nodata-target.example.
177 cname-nodata-target.example. 3600 IN A 192.0.2.101
178 cname-custom-a.example. 3600 IN CNAME cname-custom-a-target.example.
179 cname-custom-a-target.example. 3600 IN A 192.0.2.102
180 """,
181 'secure.example': """
182 secure.example. 3600 IN SOA {soa}
183 secure.example. 3600 IN NS ns.secure.example.
184 ns.secure.example. 3600 IN A {prefix}.9
185 secure.example. 3600 IN MX 10 mx1.secure.example.
186 secure.example. 3600 IN MX 20 mx2.secure.example.
187 sub.secure.example. 3600 IN MX 10 mx1.secure.example.
188 sub.secure.example. 3600 IN MX 20 mx2.secure.example.
189
190 naptr.secure.example. 60 IN NAPTR 10 10 "a" "X" "A" s1.secure.example.
191 naptr.secure.example. 60 IN NAPTR 10 10 "s" "Y" "B" service1.secure.example.
192 naptr.secure.example. 60 IN NAPTR 10 10 "s" "Z" "C" service2.secure.example.
193 service1.secure.example. 60 IN SRV 20 100 8080 a.secure.example.
194 service2.secure.example. 60 IN SRV 20 100 8080 b.secure.example.
195
196
197 secure.example. 3600 IN A 192.0.2.17
198 mx1.secure.example. 3600 IN A 192.0.2.18
199 mx2.secure.example. 3600 IN AAAA 1::2
200 s1.secure.example. 3600 IN A 192.0.2.19
201 a.secure.example. 3600 IN A 192.0.2.20
202 a.secure.example. 3600 IN A 192.0.2.22
203 b.secure.example. 3600 IN A 192.0.2.21
204 b.secure.example. 3600 IN AAAA 1::3
205
206 host1.secure.example. 3600 IN A 192.0.2.2
207 cname.secure.example. 3600 IN CNAME host1.secure.example.
208 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
209 cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
210 cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
211
212 host1.sub.secure.example. 3600 IN A 192.0.2.11
213
214 ;; See #4158
215 sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
216 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
217
218 *.wildcard.secure.example. 3600 IN A 192.0.2.10
219
220 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
221
222 *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
223
224 cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
225
226 dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
227 dname-insecure.secure.example. 3600 IN DNAME insecure.example.
228 dname-bogus.secure.example. 3600 IN DNAME bogus.example.
229
230 non-apex-dnskey.secure.example. 3600 IN DNSKEY 257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
231 non-apex-dnskey2.secure.example. 3600 IN DNSKEY 256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
232 non-apex-dnskey3.secure.example. 3600 IN DNSKEY 256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
233 """,
234 'dname-secure.example': """
235 dname-secure.example. 3600 IN SOA {soa}
236 dname-secure.example. 3600 IN NS ns.dname-secure.example.
237 ns.dname-secure.example. 3600 IN A {prefix}.13
238
239 host1.dname-secure.example. IN A 192.0.2.21
240
241 cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
242 cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
243 cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
244 """,
245 'cname-secure.example': """
246 cname-secure.example. 3600 IN SOA {soa}
247 cname-secure.example. 3600 IN NS ns.cname-secure.example.
248 ns.cname-secure.example. 3600 IN A {prefix}.15
249 cname-secure.example. 3600 IN CNAME secure.example.
250 """,
251 'bogus.example': """
252 bogus.example. 3600 IN SOA {soa}
253 bogus.example. 3600 IN NS ns1.bogus.example.
254 ns1.bogus.example. 3600 IN A {prefix}.12
255 ted.bogus.example. 3600 IN A 192.0.2.1
256 bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
257 """,
258 'insecure.sub2.secure.example': """
259 insecure.sub2.secure.example. 3600 IN SOA {soa}
260 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
261
262 node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
263 """,
264 'insecure.example': """
265 insecure.example. 3600 IN SOA {soa}
266 insecure.example. 3600 IN NS ns1.insecure.example.
267 ns1.insecure.example. 3600 IN A {prefix}.13
268
269 node1.insecure.example. 3600 IN A 192.0.2.6
270
271 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
272
273 dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
274 """,
275 'optout.example': """
276 optout.example. 3600 IN SOA {soa}
277 optout.example. 3600 IN NS ns1.optout.example.
278 ns1.optout.example. 3600 IN A {prefix}.14
279
280 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
281 ns1.insecure.optout.example. 3600 IN A {prefix}.15
282
283 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
284 secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
285 ns1.secure.optout.example. 3600 IN A {prefix}.15
286 """,
287 'insecure.optout.example': """
288 insecure.optout.example. 3600 IN SOA {soa}
289 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
290 ns1.insecure.optout.example. 3600 IN A {prefix}.15
291
292 node1.insecure.optout.example. 3600 IN A 192.0.2.7
293 """,
294 'secure.optout.example': """
295 secure.optout.example. 3600 IN SOA {soa}
296 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
297 ns1.secure.optout.example. 3600 IN A {prefix}.15
298
299 node1.secure.optout.example. 3600 IN A 192.0.2.8
300 """,
301 'islandofsecurity.example': """
302 islandofsecurity.example. 3600 IN SOA {soa}
303 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
304 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
305
306 node1.islandofsecurity.example. 3600 IN A 192.0.2.20
307 """,
308 'undelegated.secure.example': """
309 undelegated.secure.example. 3600 IN SOA {soa}
310 undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
311
312 node1.undelegated.secure.example. 3600 IN A 192.0.2.21
313 """,
314 'undelegated.insecure.example': """
315 undelegated.insecure.example. 3600 IN SOA {soa}
316 undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
317
318 node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
319 """,
320
321 'delay1.example': """
322 delay1.example. 3600 IN SOA {soa}
323 delay1.example. 3600 IN NS n1.delay1.example.
324 ns1.delay1.example. 3600 IN A {prefix}.16
325 8.delay1.example. 3600 IN A 192.0.2.100
326 *.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
327 *.delay1.example. 0 LUA AAAA ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return '1::2'"
328 """,
329
330 'delay2.example': """
331 delay2.example. 3600 IN SOA {soa}
332 delay2.example. 3600 IN NS n1.delay2.example.
333 ns1.delay2.example. 3600 IN A {prefix}.17
334 *.delay2.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
335 """
336 }
337
338 # The private keys for the zones (note that DS records should go into
339 # the zonecontent in _zones
340 _zone_keys = {
341 'ROOT': """
342 Private-key-format: v1.2
343 Algorithm: 13 (ECDSAP256SHA256)
344 PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
345 """,
346
347 'example': """
348 Private-key-format: v1.2
349 Algorithm: 13 (ECDSAP256SHA256)
350 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
351 """,
352
353 'secure.example': """
354 Private-key-format: v1.2
355 Algorithm: 13 (ECDSAP256SHA256)
356 PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
357 """,
358
359 'bogus.example': """
360 Private-key-format: v1.2
361 Algorithm: 13 (ECDSAP256SHA256)
362 PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
363 """,
364
365 'optout.example': """
366 Private-key-format: v1.2
367 Algorithm: 13 (ECDSAP256SHA256)
368 PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
369 """,
370
371 'secure.optout.example': """
372 Private-key-format: v1.2
373 Algorithm: 13 (ECDSAP256SHA256)
374 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
375 """,
376
377 'islandofsecurity.example': """
378 Private-key-format: v1.2
379 Algorithm: 13 (ECDSAP256SHA256)
380 PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
381 """,
382
383 'cname-secure.example': """
384 Private-key-format: v1.2
385 Algorithm: 13 (ECDSAP256SHA256)
386 PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
387 """,
388
389 'dname-secure.example': """
390 Private-key-format: v1.2
391 Algorithm: 13 (ECDSAP256SHA256)
392 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
393 """,
394
395 'delay1.example': """
396 Private-key-format: v1.2
397 Algorithm: 13 (ECDSAP256SHA256)
398 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
399 """,
400
401 'delay2.example': """
402 Private-key-format: v1.2
403 Algorithm: 13 (ECDSAP256SHA256)
404 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
405 """
406 }
407
408 # This dict is keyed with the suffix of the IP address and its value
409 # is a list of zones hosted on that IP. Note that delegations should
410 # go into the _zones's zonecontent
411 _auth_zones = {
412 '8': {'threads': 1,
413 'zones': ['ROOT']},
414 '9': {'threads': 1,
415 'zones': ['secure.example', 'islandofsecurity.example']},
416 '10': {'threads': 1,
417 'zones': ['example']},
418
419 # 11 is used by CircleCI provided resolver
420
421 '12': {'threads': 1,
422 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
423 '13': {'threads': 1,
424 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
425 '14': {'threads': 1,
426 'zones': ['optout.example']},
427 '15': {'threads': 1,
428 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
429 '16': {'threads': 2,
430 'zones': ['delay1.example']},
431 '17': {'threads': 2,
432 'zones': ['delay2.example']},
433 '18': {'threads': 1,
434 'zones': ['example']}
435 }
436 # Other IPs used:
437 # 2: test_Interop.py
438 # 3-7: free?
439 # 19: free?
440 # 20: free?
441 # 21: test_ECS.py
442 # 22: test_EDNSBuffer.py
443 # 23: test_Lua.py
444 # 24: test_RoutingTag.py
445
446 _auth_cmd = ['authbind',
447 os.environ['PDNS']]
448 _auth_env = {}
449 _auths = {}
450
451 @classmethod
452 def createConfigDir(cls, confdir):
453 try:
454 shutil.rmtree(confdir)
455 except OSError as e:
456 if e.errno != errno.ENOENT:
457 raise
458 os.mkdir(confdir, 0o755)
459
460 @classmethod
461 def generateAuthZone(cls, confdir, zonename, zonecontent):
462 with open(os.path.join(confdir, '%s.zone' % zonename), 'w') as zonefile:
463 zonefile.write(zonecontent.format(prefix=cls._PREFIX, soa=cls._SOA))
464
465 @classmethod
466 def generateAuthNamedConf(cls, confdir, zones):
467 with open(os.path.join(confdir, 'named.conf'), 'w') as namedconf:
468 namedconf.write("""
469 options {
470 directory "%s";
471 };""" % confdir)
472 for zonename in zones:
473 zone = '.' if zonename == 'ROOT' else zonename
474
475 namedconf.write("""
476 zone "%s" {
477 type master;
478 file "%s.zone";
479 };""" % (zone, zonename))
480
481 @classmethod
482 def generateAuthConfig(cls, confdir, threads):
483 bind_dnssec_db = os.path.join(confdir, 'bind-dnssec.sqlite3')
484
485 with open(os.path.join(confdir, 'pdns.conf'), 'w') as pdnsconf:
486 pdnsconf.write("""
487 module-dir=../regression-tests/modules
488 launch=bind
489 daemon=no
490 bind-config={confdir}/named.conf
491 bind-dnssec-db={bind_dnssec_db}
492 socket-dir={confdir}
493 cache-ttl=0
494 negquery-cache-ttl=0
495 query-cache-ttl=0
496 log-dns-queries=yes
497 log-dns-details=yes
498 loglevel=9
499 enable-lua-records
500 dname-processing=yes
501 distributor-threads={threads}""".format(confdir=confdir,
502 bind_dnssec_db=bind_dnssec_db,
503 threads=threads))
504
505 pdnsutilCmd = [os.environ['PDNSUTIL'],
506 '--config-dir=%s' % confdir,
507 'create-bind-db',
508 bind_dnssec_db]
509
510 print(' '.join(pdnsutilCmd))
511 try:
512 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
513 except subprocess.CalledProcessError as e:
514 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
515
516 @classmethod
517 def secureZone(cls, confdir, zonename, key=None):
518 zone = '.' if zonename == 'ROOT' else zonename
519 if not key:
520 pdnsutilCmd = [os.environ['PDNSUTIL'],
521 '--config-dir=%s' % confdir,
522 'secure-zone',
523 zone]
524 else:
525 keyfile = os.path.join(confdir, 'dnssec.key')
526 with open(keyfile, 'w') as fdKeyfile:
527 fdKeyfile.write(key)
528
529 pdnsutilCmd = [os.environ['PDNSUTIL'],
530 '--config-dir=%s' % confdir,
531 'import-zone-key',
532 zone,
533 keyfile,
534 'active',
535 'ksk']
536
537 print(' '.join(pdnsutilCmd))
538 try:
539 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
540 except subprocess.CalledProcessError as e:
541 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
542
543 @classmethod
544 def generateAllAuthConfig(cls, confdir):
545 if cls._auth_zones:
546 for auth_suffix, zoneinfo in cls._auth_zones.items():
547 threads = zoneinfo['threads']
548 zones = zoneinfo['zones']
549 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
550
551 os.mkdir(authconfdir)
552
553 cls.generateAuthConfig(authconfdir, threads)
554 cls.generateAuthNamedConf(authconfdir, zones)
555
556 for zone in zones:
557 cls.generateAuthZone(authconfdir,
558 zone,
559 cls._zones[zone])
560 if cls._zone_keys.get(zone, None):
561 cls.secureZone(authconfdir, zone, cls._zone_keys.get(zone))
562
563 @classmethod
564 def startAllAuth(cls, confdir):
565 if cls._auth_zones:
566 for auth_suffix, _ in cls._auth_zones.items():
567 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
568 ipaddress = cls._PREFIX + '.' + auth_suffix
569 cls.startAuth(authconfdir, ipaddress)
570
571 @classmethod
572 def waitForTCPSocket(cls, ipaddress, port):
573 for try_number in range(0, 100):
574 try:
575 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
576 sock.settimeout(1.0)
577 sock.connect((ipaddress, port))
578 sock.close()
579 return
580 except Exception as err:
581 if err.errno != errno.ECONNREFUSED:
582 print(f'Error occurred: {try_number} {err}', file=sys.stderr)
583 time.sleep(0.1)
584
585 @classmethod
586 def startAuth(cls, confdir, ipaddress):
587 print("Launching pdns_server..")
588 authcmd = list(cls._auth_cmd)
589 authcmd.append('--config-dir=%s' % confdir)
590 ipconfig = ipaddress
591 # auth-8 is the auth serving the root, it gets an ipv6 address
592 if (confdir[-6:] == "auth-8") and have_ipv6():
593 ipconfig += ',::1'
594 authcmd.append('--local-address=%s' % ipconfig)
595 print(' '.join(authcmd))
596
597 logFile = os.path.join(confdir, 'pdns.log')
598 with open(logFile, 'w') as fdLog:
599 cls._auths[ipaddress] = subprocess.Popen(authcmd, close_fds=True,
600 stdout=fdLog, stderr=fdLog,
601 env=cls._auth_env)
602
603 cls.waitForTCPSocket(ipaddress, 53)
604
605 if cls._auths[ipaddress].poll() is not None:
606 print(f"\n*** startAuth log for {logFile} ***")
607 with open(logFile, 'r') as fdLog:
608 print(fdLog.read())
609 print(f"*** End startAuth log for {logFile} ***")
610 raise AssertionError('%s failed (%d)' % (authcmd, cls._auths[ipaddress].returncode))
611
612 @classmethod
613 def generateRecursorConfig(cls, confdir):
614 params = tuple([getattr(cls, param) for param in cls._config_params])
615 if len(params):
616 print(params)
617
618 recursorconf = os.path.join(confdir, 'recursor.conf')
619
620 with open(recursorconf, 'w') as conf:
621 conf.write("# Autogenerated by recursortests.py\n")
622 conf.write(cls._config_template_default)
623 conf.write(cls._config_template % params)
624 conf.write("\n")
625 conf.write("socket-dir=%s\n" % confdir)
626 if cls._lua_config_file or cls._root_DS:
627 luaconfpath = os.path.join(confdir, 'conffile.lua')
628 with open(luaconfpath, 'w') as luaconf:
629 if cls._root_DS:
630 luaconf.write("addTA('.', '%s')\n" % cls._root_DS)
631 if cls._lua_config_file:
632 luaconf.write(cls._lua_config_file)
633 conf.write("lua-config-file=%s\n" % luaconfpath)
634 if cls._lua_dns_script_file:
635 luascriptpath = os.path.join(confdir, 'dnsscript.lua')
636 with open(luascriptpath, 'w') as luascript:
637 luascript.write(cls._lua_dns_script_file)
638 conf.write("lua-dns-script=%s\n" % luascriptpath)
639 if cls._roothints:
640 roothintspath = os.path.join(confdir, 'root.hints')
641 with open(roothintspath, 'w') as roothints:
642 roothints.write(cls._roothints)
643 conf.write("hint-file=%s\n" % roothintspath)
644
645 @classmethod
646 def generateRecursorYamlConfig(cls, confdir):
647 params = tuple([getattr(cls, param) for param in cls._config_params])
648 if len(params):
649 print(params)
650
651 recursorconf = os.path.join(confdir, 'recursor.yml')
652
653 with open(recursorconf, 'w') as conf:
654 conf.write("# Autogenerated by recursortests.py\n")
655 conf.write(cls._config_template_yaml_default % confdir)
656 recursorconf = os.path.join(confdir, 'recursor01.yml')
657 with open(recursorconf, 'w') as conf:
658 conf.write(cls._config_template % params)
659 conf.write("\n")
660 recursorconf = os.path.join(confdir, 'recursor02.yml')
661 with open(recursorconf, 'w') as conf:
662 conf.write("recursor:\n")
663 conf.write(" socket_dir: %s\n" % confdir)
664 if cls._lua_config_file or cls._root_DS:
665 luaconfpath = os.path.join(confdir, 'conffile.lua')
666 with open(luaconfpath, 'w') as luaconf:
667 if cls._root_DS:
668 luaconf.write("addTA('.', '%s')\n" % cls._root_DS)
669 if cls._lua_config_file:
670 luaconf.write(cls._lua_config_file)
671 conf.write(" lua_config_file: %s\n" % luaconfpath)
672 if cls._lua_dns_script_file:
673 luascriptpath = os.path.join(confdir, 'dnsscript.lua')
674 with open(luascriptpath, 'w') as luascript:
675 luascript.write(cls._lua_dns_script_file)
676 conf.write(" lua_dns_script: %s\n" % luascriptpath)
677 if cls._roothints:
678 roothintspath = os.path.join(confdir, 'root.hints')
679 with open(roothintspath, 'w') as roothints:
680 roothints.write(cls._roothints)
681 conf.write(" hint_file: %s\n" % roothintspath)
682
683 @classmethod
684 def startResponders(cls):
685 pass
686
687 @classmethod
688 def startRecursor(cls, confdir, port):
689 print("Launching pdns_recursor..")
690 recursorcmd = [os.environ['PDNSRECURSOR'],
691 '--config-dir=%s' % confdir,
692 '--local-port=%s' % port,
693 '--security-poll-suffix=']
694 print(' '.join(recursorcmd))
695
696 logFile = os.path.join(confdir, 'recursor.log')
697 with open(logFile, 'w') as fdLog:
698 cls._recursor = subprocess.Popen(recursorcmd, close_fds=True,
699 stdout=fdLog, stderr=fdLog)
700
701 cls.waitForTCPSocket("127.0.0.1", port)
702
703 if cls._recursor.poll() is not None:
704 print(f"\n*** startRecursor log for {logFile} ***")
705 with open(logFile, 'r') as fdLog:
706 print(fdLog.read())
707 print(f"*** End startRecursor log for {logFile} ***")
708 raise AssertionError('%s failed (%d)' % (recursorcmd, cls._recursor.returncode))
709
710 @classmethod
711 def wipeRecursorCache(cls, confdir, name='.$'):
712 rec_controlCmd = [os.environ['RECCONTROL'],
713 '--config-dir=%s' % confdir,
714 'wipe-cache',
715 name]
716 try:
717 subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
718 except subprocess.CalledProcessError as e:
719 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd, e.returncode, e.output))
720
721 @classmethod
722 def setUpSockets(cls):
723 print("Setting up UDP socket..")
724 cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
725 cls._sock.settimeout(2.0)
726 cls._sock.connect(("127.0.0.1", cls._recursorPort))
727
728 @classmethod
729 def setUpClass(cls):
730 cls.setUpSockets()
731
732 cls.startResponders()
733
734 confdir = os.path.join('configs', cls._confdir)
735 cls.createConfigDir(confdir)
736 cls.generateAllAuthConfig(confdir)
737 cls.startAllAuth(confdir)
738
739 cls.generateRecursorConfig(confdir)
740 cls.startRecursor(confdir, cls._recursorPort)
741
742 print("Launching tests..")
743
744 @classmethod
745 def tearDownClass(cls):
746 cls.tearDownRecursor()
747 cls.tearDownAuth()
748 cls.tearDownResponders()
749
750 @classmethod
751 def tearDownResponders(cls):
752 pass
753
754 @classmethod
755 def killProcess(cls, p):
756 # Don't try to kill it if it's already dead
757 if p.poll() is not None:
758 return
759 try:
760 p.terminate()
761 for count in range(10):
762 x = p.poll()
763 if x is not None:
764 break
765 time.sleep(0.1)
766 if x is None:
767 print("kill...", p, file=sys.stderr)
768 p.kill()
769 p.wait()
770 except OSError as e:
771 # There is a race-condition with the poll() and
772 # kill() statements, when the process is dead on the
773 # kill(), this is fine
774 if e.errno != errno.ESRCH:
775 raise
776
777 @classmethod
778 def tearDownAuth(cls):
779 for _, auth in cls._auths.items():
780 cls.killProcess(auth);
781
782 @classmethod
783 def tearDownRecursor(cls):
784 cls.killProcess(cls._recursor)
785
786 @classmethod
787 def sendUDPQuery(cls, query, timeout=2.0, decode=True, fwparams=dict()):
788 if timeout:
789 cls._sock.settimeout(timeout)
790
791 try:
792 cls._sock.send(query.to_wire())
793 data = cls._sock.recv(4096)
794 except socket.timeout:
795 data = None
796 finally:
797 if timeout:
798 cls._sock.settimeout(None)
799
800 message = None
801 if data:
802 if not decode:
803 return data
804 message = dns.message.from_wire(data, **fwparams)
805 return message
806
807 @classmethod
808 def sendTCPQuery(cls, query, timeout=2.0):
809 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
810 if timeout:
811 sock.settimeout(timeout)
812
813 sock.connect(("127.0.0.1", cls._recursorPort))
814
815 try:
816 wire = query.to_wire()
817 sock.send(struct.pack("!H", len(wire)))
818 sock.send(wire)
819 data = sock.recv(2)
820 if data:
821 (datalen,) = struct.unpack("!H", data)
822 data = sock.recv(datalen)
823 except socket.timeout as e:
824 print("Timeout: %s" % (str(e)))
825 data = None
826 except socket.error as e:
827 print("Network error: %s" % (str(e)))
828 data = None
829 finally:
830 sock.close()
831
832 message = None
833 if data:
834 message = dns.message.from_wire(data)
835 return message
836
837 @classmethod
838 def sendTCPQueries(cls, queries, timeout=2.0):
839 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
840 if timeout:
841 sock.settimeout(timeout)
842
843 sock.connect(("127.0.0.1", cls._recursorPort))
844 data = []
845 try:
846 for query in queries:
847 wire = query.to_wire()
848 sock.send(struct.pack("!H", len(wire)))
849 sock.send(wire)
850 for i in range(len(queries)):
851 try:
852 datalen = sock.recv(2)
853 if datalen:
854 (datalen,) = struct.unpack("!H", datalen)
855 data.append(sock.recv(datalen))
856 except socket.timeout as e:
857 continue
858 except socket.error as e:
859 print("Network error: %s" % (str(e)))
860 data = None
861 finally:
862 sock.close()
863
864 messages = []
865 for d in data:
866 messages.append(dns.message.from_wire(d))
867 return messages
868
869 def setUp(self):
870 # This function is called before every tests
871 super(RecursorTest, self).setUp()
872
873 ## Functions for comparisons
874 def assertMessageHasFlags(self, msg, flags, ednsflags=[]):
875 """Asserts that msg has all the flags from flags set
876
877 @param msg: the dns.message.Message to check
878 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
879 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
880
881 if not isinstance(msg, dns.message.Message):
882 raise TypeError("msg is not a dns.message.Message")
883
884 if isinstance(flags, list):
885 for elem in flags:
886 if not isinstance(elem, str):
887 raise TypeError("flags is not a list of strings")
888 else:
889 raise TypeError("flags is not a list of strings")
890
891 if isinstance(ednsflags, list):
892 for elem in ednsflags:
893 if not isinstance(elem, str):
894 raise TypeError("ednsflags is not a list of strings")
895 else:
896 raise TypeError("ednsflags is not a list of strings")
897
898 msgFlags = dns.flags.to_text(msg.flags).split()
899 missingFlags = [flag for flag in flags if flag not in msgFlags]
900
901 msgEdnsFlags = dns.flags.edns_to_text(msg.ednsflags).split()
902 missingEdnsFlags = [ednsflag for ednsflag in ednsflags if ednsflag not in msgEdnsFlags]
903
904 if len(missingFlags) or len(missingEdnsFlags) or len(msgFlags) > len(flags):
905 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
906 (' '.join(flags), ' '.join(ednsflags),
907 ' '.join(msgFlags), ' '.join(msgEdnsFlags),
908 msg.question[0]))
909
910 def assertMessageIsAuthenticated(self, msg):
911 """Asserts that the message has the AD bit set
912
913 @param msg: the dns.message.Message to check"""
914
915 if not isinstance(msg, dns.message.Message):
916 raise TypeError("msg is not a dns.message.Message")
917
918 msgFlags = dns.flags.to_text(msg.flags)
919 self.assertTrue('AD' in msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
920
921 def assertRRsetInAnswer(self, msg, rrset):
922 """Asserts the rrset (without comparing TTL) exists in the
923 answer section of msg
924
925 @param msg: the dns.message.Message to check
926 @param rrset: a dns.rrset.RRset object"""
927
928 ret = ''
929 if not isinstance(msg, dns.message.Message):
930 raise TypeError("msg is not a dns.message.Message")
931
932 if not isinstance(rrset, dns.rrset.RRset):
933 raise TypeError("rrset is not a dns.rrset.RRset")
934
935 found = False
936 for ans in msg.answer:
937 ret += "%s\n" % ans.to_text()
938 if ans.match(rrset.name, rrset.rdclass, rrset.rdtype, 0, None):
939 self.assertEqual(ans, rrset, "'%s' != '%s'" % (ans.to_text(), rrset.to_text()))
940 found = True
941
942 if not found:
943 raise AssertionError("RRset not found in answer\n\n%s" % ret)
944
945 def assertRRsetInAdditional(self, msg, rrset):
946 """Asserts the rrset (without comparing TTL) exists in the
947 additional section of msg
948
949 @param msg: the dns.message.Message to check
950 @param rrset: a dns.rrset.RRset object"""
951
952 ret = ''
953 if not isinstance(msg, dns.message.Message):
954 raise TypeError("msg is not a dns.message.Message")
955
956 if not isinstance(rrset, dns.rrset.RRset):
957 raise TypeError("rrset is not a dns.rrset.RRset")
958
959 found = False
960 for ans in msg.additional:
961 ret += "%s\n" % ans.to_text()
962 if ans.match(rrset.name, rrset.rdclass, rrset.rdtype, 0, None):
963 self.assertEqual(ans, rrset, "'%s' != '%s'" % (ans.to_text(), rrset.to_text()))
964 found = True
965
966 if not found:
967 raise AssertionError("RRset not found in additional section\n\n%s" % ret)
968
969 def assertMatchingRRSIGInAnswer(self, msg, coveredRRset, keys=None):
970 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
971 that covers that RRset. If keys is not None, this function will also try to
972 validate the RRset against the RRSIG
973
974 @param msg: The dns.message.Message to check
975 @param coveredRRset: The RRSet to check for
976 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
977
978 if not isinstance(msg, dns.message.Message):
979 raise TypeError("msg is not a dns.message.Message")
980
981 if not isinstance(coveredRRset, dns.rrset.RRset):
982 raise TypeError("coveredRRset is not a dns.rrset.RRset")
983
984 msgRRsigRRSet = None
985 msgRRSet = None
986
987 ret = ''
988 for ans in msg.answer:
989 ret += ans.to_text() + "\n"
990
991 if ans.match(coveredRRset.name, coveredRRset.rdclass, coveredRRset.rdtype, 0, None):
992 msgRRSet = ans
993 if ans.match(coveredRRset.name, dns.rdataclass.IN, dns.rdatatype.RRSIG, coveredRRset.rdtype, None):
994 msgRRsigRRSet = ans
995 if msgRRSet and msgRRsigRRSet:
996 break
997
998 if not msgRRSet:
999 raise AssertionError("RRset for '%s' not found in answer" % msg.question[0].to_text())
1000
1001 if not msgRRsigRRSet:
1002 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
1003
1004 if keys:
1005 try:
1006 dns.dnssec.validate(msgRRSet, msgRRsigRRSet.to_rdataset(), keys)
1007 except dns.dnssec.ValidationFailure as e:
1008 raise AssertionError("Signature validation failed for %s:\n%s" % (msg.question[0].to_text(), e))
1009
1010 def assertMatchingRRSIGInAdditional(self, msg, coveredRRset, keys=None):
1011 """Looks for coveredRRset in the additional section and if there is an RRSIG RRset
1012 that covers that RRset. If keys is not None, this function will also try to
1013 validate the RRset against the RRSIG
1014
1015 @param msg: The dns.message.Message to check
1016 @param coveredRRset: The RRSet to check for
1017 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
1018
1019 if not isinstance(msg, dns.message.Message):
1020 raise TypeError("msg is not a dns.message.Message")
1021
1022 if not isinstance(coveredRRset, dns.rrset.RRset):
1023 raise TypeError("coveredRRset is not a dns.rrset.RRset")
1024
1025 msgRRsigRRSet = None
1026 msgRRSet = None
1027
1028 ret = ''
1029 for ans in msg.additional:
1030 ret += ans.to_text() + "\n"
1031
1032 if ans.match(coveredRRset.name, coveredRRset.rdclass, coveredRRset.rdtype, 0, None):
1033 msgRRSet = ans
1034 if ans.match(coveredRRset.name, dns.rdataclass.IN, dns.rdatatype.RRSIG, coveredRRset.rdtype, None):
1035 msgRRsigRRSet = ans
1036 if msgRRSet and msgRRsigRRSet:
1037 break
1038
1039 if not msgRRSet:
1040 raise AssertionError("RRset for '%s' not found in additional" % msg.question[0].to_text())
1041
1042 if not msgRRsigRRSet:
1043 raise AssertionError("No RRSIGs found in additional for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
1044
1045 if keys:
1046 try:
1047 dns.dnssec.validate(msgRRSet, msgRRsigRRSet.to_rdataset(), keys)
1048 except dns.dnssec.ValidationFailure as e:
1049 raise AssertionError("Signature validation failed for %s:\n%s" % (msg.question[0].to_text(), e))
1050
1051 def assertNoRRSIGsInAnswer(self, msg):
1052 """Checks if there are _no_ RRSIGs in the answer section of msg"""
1053
1054 if not isinstance(msg, dns.message.Message):
1055 raise TypeError("msg is not a dns.message.Message")
1056
1057 ret = ""
1058 for ans in msg.answer:
1059 if ans.rdtype == dns.rdatatype.RRSIG:
1060 ret += ans.name.to_text() + "\n"
1061
1062 if len(ret):
1063 raise AssertionError("RRSIG found in answers for:\n%s" % ret)
1064
1065 def assertAnswerEmpty(self, msg):
1066 self.assertTrue(len(msg.answer) == 0, "Data found in the the answer section for %s:\n%s" % (msg.question[0].to_text(), '\n'.join([i.to_text() for i in msg.answer])))
1067
1068 def assertAdditionalEmpty(self, msg):
1069 self.assertTrue(len(msg.additional) == 0, "Data found in the the additional section for %s:\n%s" % (msg.question[0].to_text(), '\n'.join([i.to_text() for i in msg.additional])))
1070
1071 def assertRcodeEqual(self, msg, rcode):
1072 if not isinstance(msg, dns.message.Message):
1073 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
1074
1075 if not isinstance(rcode, int):
1076 if isinstance(rcode, str):
1077 rcode = dns.rcode.from_text(rcode)
1078 else:
1079 raise TypeError("rcode is neither a str nor int")
1080
1081 if msg.rcode() != rcode:
1082 msgRcode = dns.rcode.to_text(msg.rcode())
1083 wantedRcode = dns.rcode.to_text(rcode)
1084
1085 raise AssertionError("Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode))
1086
1087 def assertAuthorityHasSOA(self, msg):
1088 if not isinstance(msg, dns.message.Message):
1089 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
1090
1091 found = False
1092 for rrset in msg.authority:
1093 if rrset.rdtype == dns.rdatatype.SOA:
1094 found = True
1095 break
1096
1097 if not found:
1098 raise AssertionError("No SOA record found in the authority section:\n%s" % msg.to_text())
1099
1100 def assertResponseMatches(self, query, expectedRRs, response):
1101 expectedResponse = dns.message.make_response(query)
1102
1103 if query.flags & dns.flags.RD:
1104 expectedResponse.flags |= dns.flags.RA
1105 if query.flags & dns.flags.CD:
1106 expectedResponse.flags |= dns.flags.CD
1107
1108 expectedResponse.answer = expectedRRs
1109 print(expectedResponse)
1110 print(response)
1111 self.assertEqual(response, expectedResponse)
1112
1113 @classmethod
1114 def sendQuery(cls, name, rdtype, useTCP=False):
1115 """Helper function that creates the query"""
1116 msg = dns.message.make_query(name, rdtype, want_dnssec=True)
1117 msg.flags |= dns.flags.AD
1118
1119 if useTCP:
1120 return cls.sendTCPQuery(msg)
1121 return cls.sendUDPQuery(msg)
1122
1123 def createQuery(self, name, rdtype, flags, ednsflags):
1124 """Helper function that creates the query with the specified flags.
1125 The flags need to be strings (no checking is performed atm)"""
1126 msg = dns.message.make_query(name, rdtype)
1127 msg.flags = dns.flags.from_text(flags)
1128 msg.flags += dns.flags.from_text('RD')
1129 msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text(ednsflags))
1130 return msg
1131
1132 @classmethod
1133 def sendUDPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
1134 queryPayload = query.to_wire()
1135 ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
1136 payload = ppPayload + queryPayload
1137
1138 if timeout:
1139 cls._sock.settimeout(timeout)
1140
1141 try:
1142 cls._sock.send(payload)
1143 data = cls._sock.recv(4096)
1144 except socket.timeout:
1145 data = None
1146 finally:
1147 if timeout:
1148 cls._sock.settimeout(None)
1149
1150 message = None
1151 if data:
1152 message = dns.message.from_wire(data)
1153 return message
1154
1155 @classmethod
1156 def sendTCPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
1157 queryPayload = query.to_wire()
1158 ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
1159
1160 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1161 if timeout:
1162 sock.settimeout(timeout)
1163
1164 sock.connect(("127.0.0.1", cls._recursorPort))
1165
1166 try:
1167 sock.send(ppPayload)
1168 sock.send(struct.pack("!H", len(queryPayload)))
1169 sock.send(queryPayload)
1170 data = sock.recv(2)
1171 if data:
1172 (datalen,) = struct.unpack("!H", data)
1173 data = sock.recv(datalen)
1174 except socket.timeout as e:
1175 print("Timeout: %s" % (str(e)))
1176 data = None
1177 except socket.error as e:
1178 print("Network error: %s" % (str(e)))
1179 data = None
1180 finally:
1181 sock.close()
1182
1183 message = None
1184 if data:
1185 message = dns.message.from_wire(data)
1186 return message