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