]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.recursor-dnssec/test_ECS.py
Merge pull request #7728 from neilcook/nod_docs
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / test_ECS.py
CommitLineData
9a0b88e8
RG
1import dns
2import os
3import socket
4import struct
5import threading
a5849a16 6import time
9a0b88e8
RG
7import clientsubnetoption
8from recursortests import RecursorTest
9from twisted.internet.protocol import DatagramProtocol
10from twisted.internet import reactor
11
12emptyECSText = 'No ECS received'
13nameECS = 'ecs-echo.example.'
635a6765 14nameECSInvalidScope = 'invalid-scope.ecs-echo.example.'
a5849a16 15ttlECS = 60
8a3a3822 16ecsReactorRunning = False
9a0b88e8
RG
17
18class ECSTest(RecursorTest):
a5849a16
RG
19 _config_template_default = """
20daemon=no
21trace=yes
22dont-query=
2fe3354d 23ecs-add-for=0.0.0.0/0
a5849a16
RG
24local-address=127.0.0.1
25packetcache-ttl=0
26packetcache-servfail-ttl=0
27max-cache-ttl=600
28threads=1
29loglevel=9
30disable-syslog=yes
31"""
32
33 def sendECSQuery(self, query, expected, expectedFirstTTL=None):
34 res = self.sendUDPQuery(query)
35
36 self.assertRcodeEqual(res, dns.rcode.NOERROR)
37 self.assertRRsetInAnswer(res, expected)
38 # this will break if you are not looking for the first RR, sorry!
39 if expectedFirstTTL is not None:
40 self.assertEqual(res.answer[0].ttl, expectedFirstTTL)
41 else:
42 expectedFirstTTL = res.answer[0].ttl
43
44 # wait one second, check that the TTL has been
45 # decreased indicating a cache hit
46 time.sleep(1)
47
48 res = self.sendUDPQuery(query)
49
50 self.assertRcodeEqual(res, dns.rcode.NOERROR)
51 self.assertRRsetInAnswer(res, expected)
52 self.assertLess(res.answer[0].ttl, expectedFirstTTL)
53
54 def checkECSQueryHit(self, query, expected):
55 res = self.sendUDPQuery(query)
56
57 self.assertRcodeEqual(res, dns.rcode.NOERROR)
58 self.assertRRsetInAnswer(res, expected)
59 # this will break if you are not looking for the first RR, sorry!
60 self.assertLess(res.answer[0].ttl, ttlECS)
9a0b88e8
RG
61
62 @classmethod
63 def startResponders(cls):
8a3a3822 64 global ecsReactorRunning
9a0b88e8
RG
65 print("Launching responders..")
66
67 address = cls._PREFIX + '.21'
68 port = 53
69
8a3a3822 70 if not ecsReactorRunning:
9a0b88e8 71 reactor.listenUDP(port, UDPECSResponder(), interface=address)
8a3a3822 72 ecsReactorRunning = True
9a0b88e8 73
8a3a3822 74 if not reactor.running:
fbfaa4a7 75 cls._UDPResponder = threading.Thread(name='UDP Responder', target=reactor.run, args=(False,))
9a0b88e8
RG
76 cls._UDPResponder.setDaemon(True)
77 cls._UDPResponder.start()
78
9a0b88e8
RG
79 @classmethod
80 def setUpClass(cls):
81 cls.setUpSockets()
82
83 cls.startResponders()
84
85 confdir = os.path.join('configs', cls._confdir)
86 cls.createConfigDir(confdir)
87
88 cls.generateRecursorConfig(confdir)
89 cls.startRecursor(confdir, cls._recursorPort)
90
91 print("Launching tests..")
92
93 @classmethod
94 def tearDownClass(cls):
95 cls.tearDownRecursor()
96
97class testNoECS(ECSTest):
98 _confdir = 'NoECS'
99
100 _config_template = """edns-subnet-whitelist=
101forward-zones=ecs-echo.example=%s.21
102 """ % (os.environ['PREFIX'])
103
104 def testSendECS(self):
a5849a16 105 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8
RG
106 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
107 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 108 self.sendECSQuery(query, expected)
9a0b88e8
RG
109
110 def testNoECS(self):
a5849a16 111 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8 112 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 113 self.sendECSQuery(query, expected)
9a0b88e8 114
8a3a3822
RG
115 def testRequireNoECS(self):
116 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
117
118 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
119 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
120 self.sendECSQuery(query, expected)
121
9a0b88e8
RG
122class testIncomingNoECS(ECSTest):
123 _confdir = 'IncomingNoECS'
124
125 _config_template = """edns-subnet-whitelist=
126use-incoming-edns-subnet=yes
127forward-zones=ecs-echo.example=%s.21
128 """ % (os.environ['PREFIX'])
129
130 def testSendECS(self):
a5849a16 131 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8
RG
132
133 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
134 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 135 self.sendECSQuery(query, expected)
9a0b88e8
RG
136
137 def testNoECS(self):
a5849a16 138 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8
RG
139
140 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 141 self.sendECSQuery(query, expected)
9a0b88e8 142
8a3a3822
RG
143 def testRequireNoECS(self):
144 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
145
146 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
147 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
148 self.sendECSQuery(query, expected)
149
9a0b88e8
RG
150class testECSByName(ECSTest):
151 _confdir = 'ECSByName'
152
153 _config_template = """edns-subnet-whitelist=ecs-echo.example.
154forward-zones=ecs-echo.example=%s.21
155 """ % (os.environ['PREFIX'])
156
157 def testSendECS(self):
a5849a16 158 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
9a0b88e8
RG
159 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
160 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 161 self.sendECSQuery(query, expected)
9a0b88e8 162
a5849a16
RG
163 # check that a query in a different ECS range is a hit, because we don't use the incoming ECS
164 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
165 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
166 self.checkECSQueryHit(query, expected)
9a0b88e8
RG
167
168 def testNoECS(self):
a5849a16 169 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
9a0b88e8 170 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 171 self.sendECSQuery(query, expected)
9a0b88e8 172
8a3a3822
RG
173 def testRequireNoECS(self):
174 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
175
176 # the request for no ECS is ignored because use-incoming-edns-subnet is not set
177 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
178 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
179 self.sendECSQuery(query, expected)
180
9a0b88e8
RG
181class testECSByNameLarger(ECSTest):
182 _confdir = 'ECSByNameLarger'
183
184 _config_template = """edns-subnet-whitelist=ecs-echo.example.
185ecs-ipv4-bits=32
186forward-zones=ecs-echo.example=%s.21
30974ecc
RG
187ecs-ipv4-cache-bits=32
188ecs-ipv6-cache-bits=128
9a0b88e8
RG
189 """ % (os.environ['PREFIX'])
190
191 def testSendECS(self):
a5849a16 192 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
9a0b88e8
RG
193 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
194 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 195 self.sendECSQuery(query, expected)
9a0b88e8 196
a5849a16
RG
197 # check that a query in a different range is a miss
198 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
199 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
200 self.sendECSQuery(query, expected)
9a0b88e8
RG
201
202 def testNoECS(self):
a5849a16 203 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
9a0b88e8 204 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 205 self.sendECSQuery(query, expected)
9a0b88e8 206
8a3a3822
RG
207 def testRequireNoECS(self):
208 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
209
210 # the request for no ECS is ignored because use-incoming-edns-subnet is not set
211 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
212 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
213 self.sendECSQuery(query, expected)
214
9a0b88e8
RG
215class testECSByNameSmaller(ECSTest):
216 _confdir = 'ECSByNameLarger'
217
218 _config_template = """edns-subnet-whitelist=ecs-echo.example.
219ecs-ipv4-bits=16
220forward-zones=ecs-echo.example=%s.21
221 """ % (os.environ['PREFIX'])
222
223 def testSendECS(self):
a5849a16 224 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
9a0b88e8
RG
225 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
226 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 227 self.sendECSQuery(query, expected)
9a0b88e8
RG
228
229 def testNoECS(self):
a5849a16 230 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
9a0b88e8 231 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 232 self.sendECSQuery(query, expected)
9a0b88e8 233
8a3a3822
RG
234 def testRequireNoECS(self):
235 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
236
237 # the request for no ECS is ignored because use-incoming-edns-subnet is not set
238 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
239 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
240 self.sendECSQuery(query, expected)
241
9a0b88e8
RG
242class testIncomingECSByName(ECSTest):
243 _confdir = 'ECSIncomingByName'
244
245 _config_template = """edns-subnet-whitelist=ecs-echo.example.
246use-incoming-edns-subnet=yes
247forward-zones=ecs-echo.example=%s.21
8a3a3822 248ecs-scope-zero-address=2001:db8::42
30974ecc
RG
249ecs-ipv4-cache-bits=32
250ecs-ipv6-cache-bits=128
9a0b88e8
RG
251 """ % (os.environ['PREFIX'])
252
253 def testSendECS(self):
a5849a16 254 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
9a0b88e8
RG
255 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
256 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 257 self.sendECSQuery(query, expected, ttlECS)
9a0b88e8 258
a5849a16
RG
259 # check that a query in the same ECS range is a hit
260 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32)
261 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
262 self.checkECSQueryHit(query, expected)
9a0b88e8 263
a5849a16
RG
264 # check that a query in a different ECS range is a miss
265 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.1.2.0/24')
266 ecso = clientsubnetoption.ClientSubnetOption('192.1.2.2', 32)
267 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
268 self.sendECSQuery(query, expected)
9a0b88e8 269
a5849a16
RG
270 def testNoECS(self):
271 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
9a0b88e8 272 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 273 self.sendECSQuery(query, expected, ttlECS)
9a0b88e8 274
8a3a3822
RG
275 def testRequireNoECS(self):
276 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "2001:db8::42/128")
277
278 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
279 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
280 self.sendECSQuery(query, expected, ttlECS)
281
9a0b88e8
RG
282class testIncomingECSByNameLarger(ECSTest):
283 _confdir = 'ECSIncomingByNameLarger'
284
285 _config_template = """edns-subnet-whitelist=ecs-echo.example.
286use-incoming-edns-subnet=yes
287ecs-ipv4-bits=32
288forward-zones=ecs-echo.example=%s.21
8a3a3822 289ecs-scope-zero-address=192.168.0.1
30974ecc
RG
290ecs-ipv4-cache-bits=32
291ecs-ipv6-cache-bits=128
9a0b88e8
RG
292 """ % (os.environ['PREFIX'])
293
294 def testSendECS(self):
a5849a16 295 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.1/32')
9a0b88e8
RG
296
297 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
298 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 299 self.sendECSQuery(query, expected, ttlECS)
9a0b88e8
RG
300
301 def testNoECS(self):
a5849a16 302 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32')
9a0b88e8
RG
303
304 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 305 self.sendECSQuery(query, expected, ttlECS)
9a0b88e8 306
8a3a3822
RG
307 def testRequireNoECS(self):
308 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.168.0.1/32')
309
310 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
311 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
312 self.sendECSQuery(query, expected, ttlECS)
313
9a0b88e8
RG
314class testIncomingECSByNameSmaller(ECSTest):
315 _confdir = 'ECSIncomingByNameSmaller'
316
317 _config_template = """edns-subnet-whitelist=ecs-echo.example.
318use-incoming-edns-subnet=yes
319ecs-ipv4-bits=16
320forward-zones=ecs-echo.example=%s.21
8a3a3822 321ecs-scope-zero-address=192.168.0.1
30974ecc
RG
322ecs-ipv4-cache-bits=32
323ecs-ipv6-cache-bits=128
9a0b88e8
RG
324 """ % (os.environ['PREFIX'])
325
326 def testSendECS(self):
a5849a16 327 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.0.0/16')
9a0b88e8
RG
328 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
329 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 330 self.sendECSQuery(query, expected)
9a0b88e8
RG
331
332 def testNoECS(self):
a5849a16 333 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/16')
9a0b88e8 334 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 335 self.sendECSQuery(query, expected)
9a0b88e8 336
8a3a3822
RG
337 def testRequireNoECS(self):
338 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.168.0.1/32')
339
340 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
341 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
342 self.sendECSQuery(query, expected, ttlECS)
343
9a0b88e8
RG
344class testIncomingECSByNameV6(ECSTest):
345 _confdir = 'ECSIncomingByNameV6'
346
347 _config_template = """edns-subnet-whitelist=ecs-echo.example.
348use-incoming-edns-subnet=yes
349ecs-ipv6-bits=128
30974ecc
RG
350ecs-ipv4-cache-bits=32
351ecs-ipv6-cache-bits=128
9a0b88e8 352forward-zones=ecs-echo.example=%s.21
8a3a3822 353query-local-address6=::1
9a0b88e8
RG
354 """ % (os.environ['PREFIX'])
355
356 def testSendECS(self):
a5849a16 357 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '2001:db8::1/128')
9a0b88e8
RG
358 ecso = clientsubnetoption.ClientSubnetOption('2001:db8::1', 128)
359 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 360 self.sendECSQuery(query, expected, ttlECS)
9a0b88e8
RG
361
362 def testNoECS(self):
a5849a16 363 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
9a0b88e8
RG
364
365 query = dns.message.make_query(nameECS, 'TXT')
366 res = self.sendUDPQuery(query)
a5849a16 367 self.sendECSQuery(query, expected, ttlECS)
9a0b88e8 368
8a3a3822
RG
369 def testRequireNoECS(self):
370 # we should get ::1/128 because neither ecs-scope-zero-addr nor query-local-address are set,
371 # but query-local-address6 is set to ::1
372 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "::1/128")
373
374 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
375 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
376 self.sendECSQuery(query, expected, ttlECS)
377
9a0b88e8
RG
378class testECSNameMismatch(ECSTest):
379 _confdir = 'ECSNameMismatch'
380
381 _config_template = """edns-subnet-whitelist=not-the-right-name.example.
382forward-zones=ecs-echo.example=%s.21
383 """ % (os.environ['PREFIX'])
384
385 def testSendECS(self):
a5849a16 386 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8
RG
387 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
388 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 389 self.sendECSQuery(query, expected)
9a0b88e8
RG
390
391 def testNoECS(self):
a5849a16 392 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8 393 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 394 self.sendECSQuery(query, expected)
9a0b88e8 395
8a3a3822
RG
396 def testRequireNoECS(self):
397 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
398
399 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
400 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
401 self.sendECSQuery(query, expected)
402
9a0b88e8
RG
403class testECSByIP(ECSTest):
404 _confdir = 'ECSByIP'
405
406 _config_template = """edns-subnet-whitelist=%s.21
407forward-zones=ecs-echo.example=%s.21
408 """ % (os.environ['PREFIX'], os.environ['PREFIX'])
409
410 def testSendECS(self):
a5849a16 411 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
9a0b88e8
RG
412 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
413 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 414 self.sendECSQuery(query, expected)
9a0b88e8
RG
415
416 def testNoECS(self):
a5849a16 417 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
9a0b88e8 418 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 419 self.sendECSQuery(query, expected)
9a0b88e8 420
8a3a3822
RG
421 def testRequireNoECS(self):
422 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
423
424 # the request for no ECS is ignored because use-incoming-edns-subnet is not set
425 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
426 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
427 self.sendECSQuery(query, expected)
428
9a0b88e8
RG
429class testIncomingECSByIP(ECSTest):
430 _confdir = 'ECSIncomingByIP'
431
432 _config_template = """edns-subnet-whitelist=%s.21
433use-incoming-edns-subnet=yes
434forward-zones=ecs-echo.example=%s.21
8a3a3822 435ecs-scope-zero-address=::1
30974ecc
RG
436ecs-ipv4-cache-bits=32
437ecs-ipv6-cache-bits=128
9a0b88e8
RG
438 """ % (os.environ['PREFIX'], os.environ['PREFIX'])
439
440 def testSendECS(self):
a5849a16 441 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
9a0b88e8
RG
442
443 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
444 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 445 self.sendECSQuery(query, expected)
9a0b88e8
RG
446
447 def testNoECS(self):
a5849a16 448 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24')
9a0b88e8 449 query = dns.message.make_query(nameECS, 'TXT')
a5849a16 450 self.sendECSQuery(query, expected)
9a0b88e8 451
8a3a3822
RG
452 def testRequireNoECS(self):
453 # we will get ::1 because ecs-scope-zero-addr is set to ::1
454 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '::1/128')
455
456 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
457 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
458 self.sendECSQuery(query, expected, ttlECS)
459
635a6765
RG
460 def testSendECSInvalidScope(self):
461 # test that the recursor does not cache with a more specific scope than the source it sent
462 expected = dns.rrset.from_text(nameECSInvalidScope, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24')
463
464 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
465 query = dns.message.make_query(nameECSInvalidScope, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
466
467 self.sendECSQuery(query, expected)
468
9a0b88e8
RG
469class testECSIPMismatch(ECSTest):
470 _confdir = 'ECSIPMismatch'
471
472 _config_template = """edns-subnet-whitelist=192.0.2.1
473forward-zones=ecs-echo.example=%s.21
474 """ % (os.environ['PREFIX'])
475
476 def testSendECS(self):
a5849a16 477 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8
RG
478 ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32)
479 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
a5849a16 480 self.sendECSQuery(query, expected)
9a0b88e8
RG
481
482 def testNoECS(self):
a5849a16 483 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
9a0b88e8
RG
484 query = dns.message.make_query(nameECS, 'TXT')
485 res = self.sendUDPQuery(query)
a5849a16 486 self.sendECSQuery(query, expected)
9a0b88e8 487
8a3a3822
RG
488 def testRequireNoECS(self):
489 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText)
490
491 ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
492 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
493 self.sendECSQuery(query, expected)
494
9a0b88e8
RG
495class UDPECSResponder(DatagramProtocol):
496 @staticmethod
497 def ipToStr(option):
498 if option.family == clientsubnetoption.FAMILY_IPV4:
499 ip = socket.inet_ntop(socket.AF_INET, struct.pack('!L', option.ip))
500 elif option.family == clientsubnetoption.FAMILY_IPV6:
501 ip = socket.inet_ntop(socket.AF_INET6,
502 struct.pack('!QQ',
503 option.ip >> 64,
504 option.ip & (2 ** 64 - 1)))
505 return ip
506
507 def datagramReceived(self, datagram, address):
508 request = dns.message.from_wire(datagram)
509
510 response = dns.message.make_response(request)
a5849a16
RG
511 response.flags |= dns.flags.AA
512 ecso = None
9a0b88e8 513
635a6765
RG
514 if (request.question[0].name == dns.name.from_text(nameECS) or request.question[0].name == dns.name.from_text(nameECSInvalidScope)) and request.question[0].rdtype == dns.rdatatype.TXT:
515
9a0b88e8
RG
516 text = emptyECSText
517 for option in request.options:
518 if option.otype == clientsubnetoption.ASSIGNED_OPTION_CODE and isinstance(option, clientsubnetoption.ClientSubnetOption):
519 text = self.ipToStr(option) + '/' + str(option.mask)
520
635a6765
RG
521 # Send a scope more specific than the received source for nameECSInvalidScope
522 if request.question[0].name == dns.name.from_text(nameECSInvalidScope):
523 ecso = clientsubnetoption.ClientSubnetOption("192.0.42.42", 32, 32)
524 else:
525 ecso = clientsubnetoption.ClientSubnetOption(self.ipToStr(option), option.mask, option.mask)
526
527 answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, 'TXT', text)
9a0b88e8 528 response.answer.append(answer)
635a6765 529
9a0b88e8 530 elif request.question[0].name == dns.name.from_text(nameECS) and request.question[0].rdtype == dns.rdatatype.NS:
a5849a16 531 answer = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'NS', 'ns1.ecs-echo.example.')
9a0b88e8 532 response.answer.append(answer)
efd26793 533 additional = dns.rrset.from_text('ns1.ecs-echo.example.', 15, dns.rdataclass.IN, 'A', os.environ['PREFIX'] + '.21')
9a0b88e8
RG
534 response.additional.append(additional)
535
a5849a16
RG
536 if ecso:
537 response.options = [ecso]
538
9a0b88e8 539 self.transport.write(response.to_wire(), address)