]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.recursor-dnssec/test_Protobuf.py
Merge pull request #13509 from rgacogne/ddist-teeaction-proxyprotocol
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / test_Protobuf.py
CommitLineData
f1c7929a
RG
1import dns
2import dnsmessage_pb2
3import os
4import socket
5import struct
6import sys
7import threading
8import time
94770151 9import clientsubnetoption
f1c7929a
RG
10
11# Python2/3 compatibility hacks
7a0ea291 12try:
13 from queue import Queue
14except ImportError:
f1c7929a 15 from Queue import Queue
7a0ea291 16
17try:
f1c7929a 18 range = xrange
7a0ea291 19except NameError:
20 pass
f1c7929a
RG
21
22from recursortests import RecursorTest
23
f1c7929a
RG
24def ProtobufConnectionHandler(queue, conn):
25 data = None
26 while True:
27 data = conn.recv(2)
28 if not data:
29 break
30 (datalen,) = struct.unpack("!H", data)
31 data = conn.recv(datalen)
32 if not data:
33 break
34
35 queue.put(data, True, timeout=2.0)
36
37 conn.close()
38
b773359c 39def ProtobufListener(queue, port):
f1c7929a
RG
40 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
41 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
42 try:
43 sock.bind(("127.0.0.1", port))
44 except socket.error as e:
45 print("Error binding in the protobuf listener: %s" % str(e))
46 sys.exit(1)
47
48 sock.listen(100)
49 while True:
50 try:
51 (conn, _) = sock.accept()
52 thread = threading.Thread(name='Connection Handler',
53 target=ProtobufConnectionHandler,
b773359c 54 args=[queue, conn])
f1c7929a
RG
55 thread.setDaemon(True)
56 thread.start()
57
58 except socket.error as e:
59 print('Error in protobuf socket: %s' % str(e))
60
61 sock.close()
62
63
b773359c
RG
64class ProtobufServerParams:
65 def __init__(self, port):
66 self.queue = Queue()
67 self.port = port
68
69protobufServersParameters = [ProtobufServerParams(4243), ProtobufServerParams(4244)]
70protobufListeners = []
71for param in protobufServersParameters:
72 listener = threading.Thread(name='Protobuf Listener', target=ProtobufListener, args=[param.queue, param.port])
73 listener.setDaemon(True)
74 listener.start()
75 protobufListeners.append(listener)
f1c7929a
RG
76
77class TestRecursorProtobuf(RecursorTest):
78
f1c7929a 79 _lua_config_file = """
b773359c
RG
80 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
81 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
f1c7929a 82
5b4650e2
PL
83 _auth_zones = {
84 '8': {'threads': 1,
0d3ea020
PL
85 'zones': ['ROOT']},
86 '9': {'threads': 1,
87 'zones': ['secure.example', 'islandofsecurity.example']},
88 '10': {'threads': 1,
89 'zones': ['example']},
90 '18': {'threads': 1,
91 'zones': ['example']}
5b4650e2 92 }
f1c7929a
RG
93
94 def getFirstProtobufMessage(self, retries=1, waitTime=1):
b773359c
RG
95 msg = None
96
c837140e 97 #print("in getFirstProtobufMessage")
b773359c 98 for param in protobufServersParameters:
a4d0f523 99 #print(param.port)
b773359c
RG
100 failed = 0
101
a95b4e22 102 while param.queue.empty():
c837140e
OM
103 #print(failed)
104 #print(retries)
b773359c
RG
105 if failed >= retries:
106 break
107
108 failed = failed + 1
c837140e 109 #print("waiting")
b773359c
RG
110 time.sleep(waitTime)
111
112 self.assertFalse(param.queue.empty())
113 data = param.queue.get(False)
114 self.assertTrue(data)
115 oldmsg = msg
116 msg = dnsmessage_pb2.PBDNSMessage()
117 msg.ParseFromString(data)
118 if oldmsg is not None:
4bfebc93 119 self.assertEqual(msg, oldmsg)
f1c7929a 120
c837140e 121 #print(msg)
f1c7929a
RG
122 return msg
123
71db2766
OM
124 def emptyProtoBufQueue(self):
125 for param in protobufServersParameters:
126 while not param.queue.empty():
127 param.queue.get(False)
128
f1c7929a 129 def checkNoRemainingMessage(self):
b773359c
RG
130 for param in protobufServersParameters:
131 self.assertTrue(param.queue.empty())
f1c7929a 132
0bd2e252 133 def checkProtobufBase(self, msg, protocol, query, initiator, normalQueryResponse=True, expectedECS=None, receivedSize=None):
f1c7929a
RG
134 self.assertTrue(msg)
135 self.assertTrue(msg.HasField('timeSec'))
136 self.assertTrue(msg.HasField('socketFamily'))
4bfebc93 137 self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET)
f1c7929a
RG
138 self.assertTrue(msg.HasField('from'))
139 fromvalue = getattr(msg, 'from')
4bfebc93 140 self.assertEqual(socket.inet_ntop(socket.AF_INET, fromvalue), initiator)
f1c7929a 141 self.assertTrue(msg.HasField('socketProtocol'))
4bfebc93 142 self.assertEqual(msg.socketProtocol, protocol)
f1c7929a 143 self.assertTrue(msg.HasField('messageId'))
c165308b 144 self.assertTrue(msg.HasField('serverIdentity'))
f1c7929a 145 self.assertTrue(msg.HasField('id'))
4bfebc93 146 self.assertEqual(msg.id, query.id)
f1c7929a
RG
147 self.assertTrue(msg.HasField('inBytes'))
148 if normalQueryResponse:
bfd27bc9 149 # compare inBytes with length of query/response
0bd2e252
RG
150 # Note that for responses, the size we received might differ
151 # because dnspython might compress labels differently from
152 # the recursor
153 if receivedSize:
4bfebc93 154 self.assertEqual(msg.inBytes, receivedSize)
0bd2e252 155 else:
4bfebc93 156 self.assertEqual(msg.inBytes, len(query.to_wire()))
f1c7929a
RG
157 if expectedECS is not None:
158 self.assertTrue(msg.HasField('originalRequestorSubnet'))
159 # v4 only for now
4bfebc93
CH
160 self.assertEqual(len(msg.originalRequestorSubnet), 4)
161 self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), '127.0.0.1')
f1c7929a 162
94770151 163 def checkOutgoingProtobufBase(self, msg, protocol, query, initiator, length=None, expectedECS=None):
5dbc7fbe
CHB
164 self.assertTrue(msg)
165 self.assertTrue(msg.HasField('timeSec'))
166 self.assertTrue(msg.HasField('socketFamily'))
4bfebc93 167 self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET)
5dbc7fbe 168 self.assertTrue(msg.HasField('socketProtocol'))
4bfebc93 169 self.assertEqual(msg.socketProtocol, protocol)
5dbc7fbe 170 self.assertTrue(msg.HasField('messageId'))
c165308b 171 self.assertTrue(msg.HasField('serverIdentity'))
5dbc7fbe 172 self.assertTrue(msg.HasField('id'))
4bfebc93 173 self.assertNotEqual(msg.id, query.id)
5dbc7fbe 174 self.assertTrue(msg.HasField('inBytes'))
57f8413e 175 if length is not None:
4bfebc93 176 self.assertEqual(msg.inBytes, length)
57f8413e 177 else:
bfd27bc9 178 # compare inBytes with length of query/response
4bfebc93 179 self.assertEqual(msg.inBytes, len(query.to_wire()))
94770151
OM
180 if expectedECS is not None:
181 self.assertTrue(msg.HasField('originalRequestorSubnet'))
182 # v4 only for now
183 self.assertEqual(len(msg.originalRequestorSubnet), 4)
184 self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), expectedECS)
5dbc7fbe 185
49193d6f 186 def checkProtobufQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', to='127.0.0.1'):
4bfebc93 187 self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSQueryType)
f1c7929a
RG
188 self.checkProtobufBase(msg, protocol, query, initiator)
189 # dnsdist doesn't fill the responder field for responses
190 # because it doesn't keep the information around.
191 self.assertTrue(msg.HasField('to'))
49193d6f 192 self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.to), to)
f1c7929a
RG
193 self.assertTrue(msg.HasField('question'))
194 self.assertTrue(msg.question.HasField('qClass'))
4bfebc93 195 self.assertEqual(msg.question.qClass, qclass)
f1c7929a 196 self.assertTrue(msg.question.HasField('qType'))
4bfebc93 197 self.assertEqual(msg.question.qClass, qtype)
f1c7929a 198 self.assertTrue(msg.question.HasField('qName'))
4bfebc93 199 self.assertEqual(msg.question.qName, qname)
f1c7929a 200
2e627150 201 def checkProtobufResponse(self, msg, protocol, response, initiator='127.0.0.1', receivedSize=None, vstate=dnsmessage_pb2.PBDNSMessage.VState.Indeterminate):
4bfebc93 202 self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
0bd2e252 203 self.checkProtobufBase(msg, protocol, response, initiator, receivedSize=receivedSize)
f1c7929a
RG
204 self.assertTrue(msg.HasField('response'))
205 self.assertTrue(msg.response.HasField('queryTimeSec'))
2e627150 206 self.assertTrue(msg.response.HasField('validationState'))
4bfebc93 207 self.assertEqual(msg.response.validationState, vstate)
f1c7929a 208
0bd2e252 209 def checkProtobufResponseRecord(self, record, rclass, rtype, rname, rttl, checkTTL=True):
f1c7929a 210 self.assertTrue(record.HasField('class'))
4bfebc93 211 self.assertEqual(getattr(record, 'class'), rclass)
f1c7929a 212 self.assertTrue(record.HasField('type'))
4bfebc93 213 self.assertEqual(record.type, rtype)
f1c7929a 214 self.assertTrue(record.HasField('name'))
4bfebc93 215 self.assertEqual(record.name, rname)
f1c7929a 216 self.assertTrue(record.HasField('ttl'))
0bd2e252 217 if checkTTL:
4bfebc93 218 self.assertEqual(record.ttl, rttl)
f1c7929a
RG
219 self.assertTrue(record.HasField('rdata'))
220
fe1a9389 221 def checkProtobufPolicy(self, msg, policyType, reason, trigger, hit, kind):
4bfebc93 222 self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType)
f1c7929a
RG
223 self.assertTrue(msg.response.HasField('appliedPolicyType'))
224 self.assertTrue(msg.response.HasField('appliedPolicy'))
dd97a785 225 self.assertTrue(msg.response.HasField('appliedPolicyTrigger'))
5cd24bd5 226 self.assertTrue(msg.response.HasField('appliedPolicyHit'))
fe1a9389 227 self.assertTrue(msg.response.HasField('appliedPolicyKind'))
4bfebc93
CH
228 self.assertEqual(msg.response.appliedPolicy, reason)
229 self.assertEqual(msg.response.appliedPolicyType, policyType)
230 self.assertEqual(msg.response.appliedPolicyTrigger, trigger)
231 self.assertEqual(msg.response.appliedPolicyHit, hit)
232 self.assertEqual(msg.response.appliedPolicyKind, kind)
f1c7929a
RG
233
234 def checkProtobufTags(self, msg, tags):
c837140e
OM
235 #print(tags)
236 #print('---')
237 #print(msg.response.tags)
4bfebc93 238 self.assertEqual(len(msg.response.tags), len(tags))
f1c7929a
RG
239 for tag in msg.response.tags:
240 self.assertTrue(tag in tags)
241
3211bbaf 242 def checkProtobufMetas(self, msg, metas):
c837140e
OM
243 #print(metas)
244 #print('---')
245 #print(msg.meta)
3211bbaf
CHB
246 self.assertEqual(len(msg.meta), len(metas))
247 for m in msg.meta:
248 self.assertTrue(m.HasField('key'))
249 self.assertTrue(m.HasField('value'))
250 self.assertTrue(m.key in metas)
251 for i in m.value.intVal :
252 self.assertTrue(i in metas[m.key]['intVal'])
253 for s in m.value.stringVal :
254 self.assertTrue(s in metas[m.key]['stringVal'])
255
94770151 256 def checkProtobufOutgoingQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', length=None, expectedECS=None):
4bfebc93 257 self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSOutgoingQueryType)
94770151 258 self.checkOutgoingProtobufBase(msg, protocol, query, initiator, length=length, expectedECS=expectedECS)
5dbc7fbe
CHB
259 self.assertTrue(msg.HasField('to'))
260 self.assertTrue(msg.HasField('question'))
261 self.assertTrue(msg.question.HasField('qClass'))
4bfebc93 262 self.assertEqual(msg.question.qClass, qclass)
5dbc7fbe 263 self.assertTrue(msg.question.HasField('qType'))
4bfebc93 264 self.assertEqual(msg.question.qType, qtype)
5dbc7fbe 265 self.assertTrue(msg.question.HasField('qName'))
4bfebc93 266 self.assertEqual(msg.question.qName, qname)
5dbc7fbe 267
57f8413e 268 def checkProtobufIncomingResponse(self, msg, protocol, response, initiator='127.0.0.1', length=None):
4bfebc93 269 self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSIncomingResponseType)
57f8413e 270 self.checkOutgoingProtobufBase(msg, protocol, response, initiator, length=length)
5dbc7fbe 271 self.assertTrue(msg.HasField('response'))
57f8413e 272 self.assertTrue(msg.response.HasField('rcode'))
5dbc7fbe
CHB
273 self.assertTrue(msg.response.HasField('queryTimeSec'))
274
57f8413e
RG
275 def checkProtobufIncomingNetworkErrorResponse(self, msg, protocol, response, initiator='127.0.0.1'):
276 self.checkProtobufIncomingResponse(msg, protocol, response, initiator, length=0)
4bfebc93 277 self.assertEqual(msg.response.rcode, 65536)
57f8413e 278
0a6a45c8 279 def checkProtobufIdentity(self, msg, requestorId, deviceId, deviceName):
c837140e 280 #print(msg)
e596ec13 281 self.assertTrue((requestorId == '') == (not msg.HasField('requestorId')))
3d144e24 282 self.assertTrue((deviceId == b'') == (not msg.HasField('deviceId')))
e596ec13 283 self.assertTrue((deviceName == '') == (not msg.HasField('deviceName')))
4bfebc93
CH
284 self.assertEqual(msg.requestorId, requestorId)
285 self.assertEqual(msg.deviceId, deviceId)
286 self.assertEqual(msg.deviceName, deviceName)
0a6a45c8 287
f1c7929a 288 def setUp(self):
5b4650e2
PL
289 super(TestRecursorProtobuf, self).setUp()
290 # Make sure the queue is empty, in case
291 # a previous test failed
71db2766 292 self.emptyProtoBufQueue()
943b519e
RG
293 # wait long enough to be sure that the housekeeping has
294 # prime the root NS
295 time.sleep(1)
f1c7929a
RG
296
297 @classmethod
298 def generateRecursorConfig(cls, confdir):
299 authzonepath = os.path.join(confdir, 'example.zone')
300 with open(authzonepath, 'w') as authzone:
301 authzone.write("""$ORIGIN example.
302@ 3600 IN SOA {soa}
303a 3600 IN A 192.0.2.42
304tagged 3600 IN A 192.0.2.84
3211bbaf 305meta 3600 IN A 192.0.2.85
f1c7929a
RG
306query-selected 3600 IN A 192.0.2.84
307answer-selected 3600 IN A 192.0.2.84
0bd2e252
RG
308types 3600 IN A 192.0.2.84
309types 3600 IN AAAA 2001:DB8::1
310types 3600 IN TXT "Lorem ipsum dolor sit amet"
311types 3600 IN MX 10 a.example.
312types 3600 IN SPF "v=spf1 -all"
313types 3600 IN SRV 10 20 443 a.example.
314cname 3600 IN CNAME a.example.
315
f1c7929a
RG
316""".format(soa=cls._SOA))
317 super(TestRecursorProtobuf, cls).generateRecursorConfig(confdir)
318
f1c7929a
RG
319
320class ProtobufDefaultTest(TestRecursorProtobuf):
321 """
322 This test makes sure that we correctly export queries and response over protobuf.
323 """
324
325 _confdir = 'ProtobufDefault'
326 _config_template = """
327auth-zones=example=configs/%s/example.zone""" % _confdir
328
329 def testA(self):
330 name = 'a.example.'
331 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
332 query = dns.message.make_query(name, 'A', want_dnssec=True)
333 query.flags |= dns.flags.CD
334 res = self.sendUDPQuery(query)
0bd2e252 335
f1c7929a
RG
336 self.assertRRsetInAnswer(res, expected)
337
338 # check the protobuf messages corresponding to the UDP query and answer
339 msg = self.getFirstProtobufMessage()
340 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
341 # then the response
342 msg = self.getFirstProtobufMessage()
0bd2e252 343 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1')
4bfebc93 344 self.assertEqual(len(msg.response.rrs), 1)
f1c7929a
RG
345 rr = msg.response.rrs[0]
346 # we have max-cache-ttl set to 15
347 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 348 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
f1c7929a
RG
349 self.checkNoRemainingMessage()
350
0bd2e252
RG
351 def testCNAME(self):
352 name = 'cname.example.'
353 expectedCNAME = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'CNAME', 'a.example.')
354 expectedA = dns.rrset.from_text('a.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.42')
355 query = dns.message.make_query(name, 'A', want_dnssec=True)
356 query.flags |= dns.flags.CD
357 raw = self.sendUDPQuery(query, decode=False)
358 res = dns.message.from_wire(raw)
359 self.assertRRsetInAnswer(res, expectedCNAME)
360 self.assertRRsetInAnswer(res, expectedA)
361
362 # check the protobuf messages corresponding to the UDP query and answer
363 # but first let the protobuf messages the time to get there
364 msg = self.getFirstProtobufMessage()
365 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
366 # then the response
367 msg = self.getFirstProtobufMessage()
368 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1', receivedSize=len(raw))
4bfebc93 369 self.assertEqual(len(msg.response.rrs), 2)
0bd2e252
RG
370 rr = msg.response.rrs[0]
371 # we don't want to check the TTL for the A record, it has been cached by the previous test
372 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 15)
4bfebc93 373 self.assertEqual(rr.rdata, b'a.example.')
0bd2e252
RG
374 rr = msg.response.rrs[1]
375 # we have max-cache-ttl set to 15
376 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, 'a.example.', 15, checkTTL=False)
4bfebc93 377 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
0bd2e252
RG
378 self.checkNoRemainingMessage()
379
e81063e5
OM
380class ProtobufProxyMappingTest(TestRecursorProtobuf):
381 """
382 This test makes sure that we correctly export queries and response over protobuf with a proxyMapping
383 """
384
385 _confdir = 'ProtobufProxyMappingTest'
386 _config_template = """
387 auth-zones=example=configs/%s/example.zone
388 allow-from=3.4.5.0/24
389 """ % _confdir
390
391 _lua_config_file = """
392 addProxyMapping("127.0.0.1/24", "3.4.5.6:99")
393 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
394 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
395
396 def testA(self):
397 name = 'a.example.'
398 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
399 query = dns.message.make_query(name, 'A', want_dnssec=True)
400 query.flags |= dns.flags.CD
401 res = self.sendUDPQuery(query)
402
403 self.assertRRsetInAnswer(res, expected)
404
405 # check the protobuf messages corresponding to the UDP query and answer
406 msg = self.getFirstProtobufMessage()
407 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
408 # then the response
409 msg = self.getFirstProtobufMessage()
410 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1')
411 self.assertEqual(len(msg.response.rrs), 1)
412 rr = msg.response.rrs[0]
413 # we have max-cache-ttl set to 15
414 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
415 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
416 self.checkNoRemainingMessage()
417
418class ProtobufProxyMappingLogMappedTest(TestRecursorProtobuf):
419 """
420 This test makes sure that we correctly export queries and response over protobuf.
421 """
422
423 _confdir = 'ProtobufProxyMappingLogMappedTest'
424 _config_template = """
425 auth-zones=example=configs/%s/example.zone
426 allow-from=3.4.5.0/0"
427 """ % _confdir
428
429 _lua_config_file = """
430 addProxyMapping("127.0.0.1/24", "3.4.5.6:99")
431 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logMappedFrom = true })
432 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
433
434 def testA(self):
435 name = 'a.example.'
436 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
437 query = dns.message.make_query(name, 'A', want_dnssec=True)
438 query.flags |= dns.flags.CD
439 res = self.sendUDPQuery(query)
440
441 self.assertRRsetInAnswer(res, expected)
442
443 # check the protobuf messages corresponding to the UDP query and answer
444 msg = self.getFirstProtobufMessage()
445 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '3.4.5.6')
446 # then the response
447 msg = self.getFirstProtobufMessage()
448 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '3.4.5.6')
449 self.assertEqual(len(msg.response.rrs), 1)
450 rr = msg.response.rrs[0]
451 # we have max-cache-ttl set to 15
452 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
453 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
454 self.checkNoRemainingMessage()
455
49193d6f
OM
456class ProtobufProxyTest(TestRecursorProtobuf):
457 """
458 This test makes sure that we correctly export addresses over protobuf when the proxy protocol is used.
459 """
460
461 _confdir = 'ProtobufProxy'
462 _config_template = """
463auth-zones=example=configs/%s/example.zone
464proxy-protocol-from=127.0.0.1/32
465allow-from=127.0.0.1,6.6.6.6
466""" % _confdir
467
468 def testA(self):
469 name = 'a.example.'
470 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
471 query = dns.message.make_query(name, 'A', want_dnssec=True)
472 query.flags |= dns.flags.CD
473 res = self.sendUDPQueryWithProxyProtocol(query, False, '6.6.6.6', '7.7.7.7', 666, 777)
474
475 self.assertRRsetInAnswer(res, expected)
476
477 # check the protobuf messages corresponding to the UDP query and answer
478 msg = self.getFirstProtobufMessage()
479 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '6.6.6.6', '7.7.7.7')
480 # then the response
481 msg = self.getFirstProtobufMessage()
482 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '6.6.6.6')
483 self.assertEqual(len(msg.response.rrs), 1)
484 rr = msg.response.rrs[0]
485 # we have max-cache-ttl set to 15
486 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
487 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
488 self.checkNoRemainingMessage()
489
e81063e5
OM
490class ProtobufProxyWithProxyByTableTest(TestRecursorProtobuf):
491 """
492 This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
493 """
494
495 _confdir = 'ProtobufProxyWithProxyByTable'
496 _config_template = """
497auth-zones=example=configs/%s/example.zone
498proxy-protocol-from=127.0.0.1/32
499allow-from=3.4.5.6
500""" % _confdir
501
502 _lua_config_file = """
503 addProxyMapping("6.6.6.6/24", "3.4.5.6:99")
504 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
505 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
506
507 def testA(self):
508 name = 'a.example.'
509 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
510 query = dns.message.make_query(name, 'A', want_dnssec=True)
511 query.flags |= dns.flags.CD
512 res = self.sendUDPQueryWithProxyProtocol(query, False, '6.6.6.6', '7.7.7.7', 666, 777)
513
514 self.assertRRsetInAnswer(res, expected)
515
516 # check the protobuf messages corresponding to the UDP query and answer
517 msg = self.getFirstProtobufMessage()
518 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '6.6.6.6', '7.7.7.7')
519 # then the response
520 msg = self.getFirstProtobufMessage()
521 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '6.6.6.6')
522 self.assertEqual(len(msg.response.rrs), 1)
523 rr = msg.response.rrs[0]
524 # we have max-cache-ttl set to 15
525 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
526 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
527 self.checkNoRemainingMessage()
528
529class ProtobufProxyWithProxyByTableLogMappedTest(TestRecursorProtobuf):
530 """
531 This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
532 """
533
534 _confdir = 'ProtobufProxyWithProxyByTableLogMapped'
535 _config_template = """
536auth-zones=example=configs/%s/example.zone
537proxy-protocol-from=127.0.0.1/32
538allow-from=3.4.5.6
539""" % _confdir
540
541 _lua_config_file = """
542 addProxyMapping("6.6.6.6/24", "3.4.5.6:99")
543 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logMappedFrom = true })
544 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
545
546 def testA(self):
547 name = 'a.example.'
548 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
549 query = dns.message.make_query(name, 'A', want_dnssec=True)
550 query.flags |= dns.flags.CD
551 res = self.sendUDPQueryWithProxyProtocol(query, False, '6.6.6.6', '7.7.7.7', 666, 777)
552
553 self.assertRRsetInAnswer(res, expected)
554
555 # check the protobuf messages corresponding to the UDP query and answer
556 msg = self.getFirstProtobufMessage()
557 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '3.4.5.6', '7.7.7.7')
558 # then the response
559 msg = self.getFirstProtobufMessage()
560 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '3.4.5.6')
561 self.assertEqual(len(msg.response.rrs), 1)
562 rr = msg.response.rrs[0]
563 # we have max-cache-ttl set to 15
564 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
565 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
566 self.checkNoRemainingMessage()
567
568
5dbc7fbe
CHB
569class OutgoingProtobufDefaultTest(TestRecursorProtobuf):
570 """
571 This test makes sure that we correctly export outgoing queries over protobuf.
572 It must be improved and setup env so we can check for incoming responses, but makes sure for now
573 that the recursor at least connects to the protobuf server.
574 """
575
576 _confdir = 'OutgoingProtobufDefault'
577 _config_template = """
bfd27bc9 578 # Switch off QName Minimization, it generates much more protobuf messages
8949a3e0
OM
579 # (or make the test much more smart!)
580 qname-minimization=no
71db2766
OM
581 max-cache-ttl=600
582 loglevel=9
0d3ea020 583"""
5dbc7fbe 584 _lua_config_file = """
b773359c
RG
585 outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
586 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
5dbc7fbe
CHB
587
588 def testA(self):
71db2766
OM
589 # There is a race in priming (having the . DNSKEY in cache in particular) and this code.
590 # So make sure we have the . DNSKEY in cache
591 query = dns.message.make_query('.', 'A', want_dnssec=True)
592 query.flags |= dns.flags.RD
593 res = self.sendUDPQuery(query)
594 time.sleep(1)
595 self.emptyProtoBufQueue()
596
0d3ea020
PL
597 name = 'host1.secure.example.'
598 expected = list()
943b519e 599
d87ef9d3
RG
600 for qname, qtype, proto, responseSize in [
601 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248),
d87ef9d3 602 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221),
0d3ea020 603 ('example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219),
0d3ea020 604 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175),
d87ef9d3 605 ('secure.example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233),
0d3ea020
PL
606 ]:
607 if not qname:
608 expected.append((None, None, None, None, None, None))
609 continue
610 query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True)
611 resp = dns.message.make_response(query)
612 expected.append((
d87ef9d3 613 qname, qtype, query, resp, proto, responseSize
0d3ea020
PL
614 ))
615
5dbc7fbe
CHB
616 query = dns.message.make_query(name, 'A', want_dnssec=True)
617 query.flags |= dns.flags.RD
618 res = self.sendUDPQuery(query)
619
d87ef9d3 620 for qname, qtype, qry, ans, proto, responseSize in expected:
0d3ea020
PL
621 if not qname:
622 self.getFirstProtobufMessage()
623 self.getFirstProtobufMessage()
624 continue
625
626 msg = self.getFirstProtobufMessage()
627 self.checkProtobufOutgoingQuery(msg, proto, qry, dns.rdataclass.IN, qtype, qname)
628
629 # Check the answer
630 msg = self.getFirstProtobufMessage()
d87ef9d3 631 self.checkProtobufIncomingResponse(msg, proto, ans, length=responseSize)
0d3ea020 632
5dbc7fbe
CHB
633 self.checkNoRemainingMessage()
634
94770151
OM
635class OutgoingProtobufWithECSMappingTest(TestRecursorProtobuf):
636 """
637 This test makes sure that we correctly export outgoing queries over protobuf.
638 It must be improved and setup env so we can check for incoming responses, but makes sure for now
639 that the recursor at least connects to the protobuf server.
640 """
641
642 _confdir = 'OutgoingProtobuffWithECSMapping'
643 _config_template = """
644 # Switch off QName Minimization, it generates much more protobuf messages
645 # (or make the test much more smart!)
646 qname-minimization=no
647 edns-subnet-allow-list=example
648 allow-from=1.2.3.4/32
649 # this is to not let . queries interfere
71db2766
OM
650 max-cache-ttl=600
651 loglevel=9
94770151
OM
652"""
653 _lua_config_file = """
654 outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
655 addProxyMapping("127.0.0.0/8", "1.2.3.4", { "host1.secure.example." })
656 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
657
658 def testA(self):
71db2766
OM
659 # There is a race in priming (having the . DNSKEY in cache in particular) and this code.
660 # So make sure we have the . DNSKEY in cache
661 query = dns.message.make_query('.', 'A', want_dnssec=True)
662 query.flags |= dns.flags.RD
663 res = self.sendUDPQuery(query)
664 time.sleep(1)
665 self.emptyProtoBufQueue()
666
94770151
OM
667 name = 'host1.secure.example.'
668 expected = list()
669
94770151
OM
670 for qname, qtype, proto, responseSize, ecs in [
671 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248, "1.2.3.0"),
672 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221, "1.2.3.0"),
673 ('example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219, "1.2.3.0"),
674 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175, "1.2.3.0"),
675 ('secure.example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233, "1.2.3.0"),
676 ]:
677 if not qname:
678 expected.append((None, None, None, None, None, None, None))
679 continue
680 ecso = clientsubnetoption.ClientSubnetOption('9.10.11.12', 24)
681 query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True, options=[ecso], payload=512)
682 resp = dns.message.make_response(query)
683 expected.append((
684 qname, qtype, query, resp, proto, responseSize, ecs
685 ))
686
687 query = dns.message.make_query(name, 'A', want_dnssec=True)
688 query.flags |= dns.flags.RD
689 res = self.sendUDPQuery(query)
690
691 for qname, qtype, qry, ans, proto, responseSize, ecs in expected:
692 if not qname:
693 self.getFirstProtobufMessage()
694 self.getFirstProtobufMessage()
695 continue
696
697 msg = self.getFirstProtobufMessage()
94770151 698 self.checkProtobufOutgoingQuery(msg, proto, qry, dns.rdataclass.IN, qtype, qname, "127.0.0.1", None, ecs)
94770151
OM
699 # Check the answer
700 msg = self.getFirstProtobufMessage()
701 self.checkProtobufIncomingResponse(msg, proto, ans, length=responseSize)
702
703 self.checkNoRemainingMessage()
704
705 # this query should use the unmapped ECS
706 name = 'mx1.secure.example.'
707 expected = list()
708
94770151
OM
709 for qname, qtype, proto, responseSize, ecs in [
710 ('mx1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 173, "127.0.0.1"),
711 ]:
712 if not qname:
713 expected.append((None, None, None, None, None, None, None))
714 continue
715 ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 32)
716 query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True, options=[ecso], payload=512)
717 resp = dns.message.make_response(query)
718 expected.append((
719 qname, qtype, query, resp, proto, responseSize, ecs
720 ))
721
722 query = dns.message.make_query(name, 'A', want_dnssec=True)
723 query.flags |= dns.flags.RD
724 res = self.sendUDPQuery(query)
725
726 for qname, qtype, qry, ans, proto, responseSize, ecs in expected:
727 if not qname:
728 self.getFirstProtobufMessage()
729 self.getFirstProtobufMessage()
730 continue
731
732 msg = self.getFirstProtobufMessage()
94770151 733 self.checkProtobufOutgoingQuery(msg, proto, qry, dns.rdataclass.IN, qtype, qname, "127.0.0.1", None, ecs)
94770151
OM
734 # Check the answer
735 msg = self.getFirstProtobufMessage()
736 self.checkProtobufIncomingResponse(msg, proto, ans, length=responseSize)
737
738 self.checkNoRemainingMessage()
739
87cab7c0
RG
740class OutgoingProtobufNoQueriesTest(TestRecursorProtobuf):
741 """
742 This test makes sure that we correctly export incoming responses but not outgoing queries over protobuf.
743 It must be improved and setup env so we can check for incoming responses, but makes sure for now
744 that the recursor at least connects to the protobuf server.
745 """
746
747 _confdir = 'OutgoingProtobufNoQueries'
748 _config_template = """
bfd27bc9 749 # Switch off QName Minimization, it generates much more protobuf messages
8949a3e0 750 # (or make the test much more smart!)
71db2766
OM
751 qname-minimization=no
752 max-cache-ttl=600
753 loglevel=9
754"""
87cab7c0
RG
755 _lua_config_file = """
756 outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true })
757 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
758
759 def testA(self):
71db2766
OM
760 # There is a race in priming (having the . DNSKEY in cache in particular) and this code.
761 # So make sure we have the . DNSKEY in cache
762 query = dns.message.make_query('.', 'A', want_dnssec=True)
763 query.flags |= dns.flags.RD
764 res = self.sendUDPQuery(query)
765 time.sleep(1)
766 self.emptyProtoBufQueue()
767
fc2ad22e
PL
768 name = 'host1.secure.example.'
769 expected = list()
943b519e
RG
770 # the root DNSKEY has been learned with priming the root NS already
771 # ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
fc2ad22e 772 for qname, qtype, proto, size in [
d87ef9d3 773 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 248),
d87ef9d3 774 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 221),
fc2ad22e 775 ('example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 219),
fc2ad22e 776 ('host1.secure.example.', dns.rdatatype.A, dnsmessage_pb2.PBDNSMessage.UDP, 175),
d87ef9d3 777 ('secure.example.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 233),
fc2ad22e
PL
778 ]:
779 if not qname:
780 expected.append((None, None, None, None, None, None))
781 continue
782 query = dns.message.make_query(qname, qtype, use_edns=True, want_dnssec=True)
783 resp = dns.message.make_response(query)
784 expected.append((
785 qname, qtype, query, resp, proto, size
786 ))
787
87cab7c0
RG
788 query = dns.message.make_query(name, 'A', want_dnssec=True)
789 query.flags |= dns.flags.RD
790 res = self.sendUDPQuery(query)
791
fc2ad22e
PL
792 for qname, qtype, qry, ans, proto, size in expected:
793 if not qname:
794 self.getFirstProtobufMessage()
795 continue
796
797 # check the response
798 msg = self.getFirstProtobufMessage()
799 self.checkProtobufIncomingResponse(msg, proto, ans, length=size)
800
87cab7c0
RG
801 self.checkNoRemainingMessage()
802
f1c7929a
RG
803class ProtobufMasksTest(TestRecursorProtobuf):
804 """
805 This test makes sure that we correctly export queries and response over protobuf, respecting the configured initiator masking.
806 """
807
808 _confdir = 'ProtobufMasks'
809 _config_template = """
810auth-zones=example=configs/%s/example.zone""" % _confdir
f1c7929a
RG
811 _protobufMaskV4 = 4
812 _protobufMaskV6 = 128
813 _lua_config_file = """
b773359c 814 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
f1c7929a 815 setProtobufMasks(%d, %d)
b773359c 816 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _protobufMaskV4, _protobufMaskV6)
f1c7929a
RG
817
818 def testA(self):
819 name = 'a.example.'
820 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
821 query = dns.message.make_query(name, 'A', want_dnssec=True)
822 query.flags |= dns.flags.CD
823 res = self.sendUDPQuery(query)
824 self.assertRRsetInAnswer(res, expected)
825
826 # check the protobuf messages corresponding to the UDP query and answer
827 # but first let the protobuf messages the time to get there
828 msg = self.getFirstProtobufMessage()
829 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '112.0.0.0')
830 # then the response
831 msg = self.getFirstProtobufMessage()
832 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '112.0.0.0')
4bfebc93 833 self.assertEqual(len(msg.response.rrs), 1)
f1c7929a
RG
834 rr = msg.response.rrs[0]
835 # we have max-cache-ttl set to 15
836 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 837 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
f1c7929a
RG
838 self.checkNoRemainingMessage()
839
840class ProtobufQueriesOnlyTest(TestRecursorProtobuf):
841 """
842 This test makes sure that we correctly export queries but not responses over protobuf.
843 """
844
845 _confdir = 'ProtobufQueriesOnly'
846 _config_template = """
847auth-zones=example=configs/%s/example.zone""" % _confdir
f1c7929a 848 _lua_config_file = """
b773359c
RG
849 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=false } )
850 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
f1c7929a
RG
851
852 def testA(self):
853 name = 'a.example.'
854 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
855 query = dns.message.make_query(name, 'A', want_dnssec=True)
856 query.flags |= dns.flags.CD
857 res = self.sendUDPQuery(query)
858 self.assertRRsetInAnswer(res, expected)
859
860 # check the protobuf message corresponding to the UDP query
861 msg = self.getFirstProtobufMessage()
862 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
863 # no response
864 self.checkNoRemainingMessage()
865
866class ProtobufResponsesOnlyTest(TestRecursorProtobuf):
867 """
868 This test makes sure that we correctly export responses but not queries over protobuf.
869 """
870
871 _confdir = 'ProtobufResponsesOnly'
872 _config_template = """
873auth-zones=example=configs/%s/example.zone""" % _confdir
f1c7929a 874 _lua_config_file = """
b773359c
RG
875 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true } )
876 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
f1c7929a
RG
877
878 def testA(self):
879 name = 'a.example.'
880 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
881 query = dns.message.make_query(name, 'A', want_dnssec=True)
882 query.flags |= dns.flags.CD
883 res = self.sendUDPQuery(query)
884 self.assertRRsetInAnswer(res, expected)
885
886 # check the protobuf message corresponding to the UDP response
887 msg = self.getFirstProtobufMessage()
888 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
4bfebc93 889 self.assertEqual(len(msg.response.rrs), 1)
f1c7929a
RG
890 rr = msg.response.rrs[0]
891 # we have max-cache-ttl set to 15
892 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 893 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
f1c7929a
RG
894 # nothing else in the queue
895 self.checkNoRemainingMessage()
896
897class ProtobufTaggedOnlyTest(TestRecursorProtobuf):
898 """
899 This test makes sure that we correctly export queries and responses but only if they have been tagged.
900 """
901
902 _confdir = 'ProtobufTaggedOnly'
903 _config_template = """
904auth-zones=example=configs/%s/example.zone""" % _confdir
f1c7929a 905 _lua_config_file = """
b773359c
RG
906 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, taggedOnly=true } )
907 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
f1c7929a
RG
908 _tags = ['tag1', 'tag2']
909 _tag_from_gettag = 'tag-from-gettag'
910 _lua_dns_script_file = """
911 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
912 if qname:equal('tagged.example.') then
913 return 0, { '%s' }
914 end
915 return 0
916 end
917 function preresolve(dq)
918 if dq.qname:equal('tagged.example.') then
919 dq:addPolicyTag('%s')
920 dq:addPolicyTag('%s')
921 end
922 return false
923 end
924 """ % (_tag_from_gettag, _tags[0], _tags[1])
925
926 def testA(self):
927 name = 'a.example.'
928 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
929 query = dns.message.make_query(name, 'A', want_dnssec=True)
930 query.flags |= dns.flags.CD
931 res = self.sendUDPQuery(query)
932 self.assertRRsetInAnswer(res, expected)
933
934 # check the protobuf message corresponding to the UDP response
935 # the first query and answer are not tagged, so there is nothing in the queue
936 time.sleep(1)
937 self.checkNoRemainingMessage()
938
939 def testTagged(self):
940 name = 'tagged.example.'
941 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
942 query = dns.message.make_query(name, 'A', want_dnssec=True)
943 query.flags |= dns.flags.CD
944 res = self.sendUDPQuery(query)
945 self.assertRRsetInAnswer(res, expected)
946
a4d0f523
OM
947 # check the protobuf messages corresponding to the UDP query and answer
948 msg = self.getFirstProtobufMessage()
949 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
950 self.checkProtobufTags(msg, [ self._tag_from_gettag ])
951 # then the response
952 msg = self.getFirstProtobufMessage()
953 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
954 self.assertEqual(len(msg.response.rrs), 1)
955 rr = msg.response.rrs[0]
956 # we have max-cache-ttl set to 15
957 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
958 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
959 tags = [ self._tag_from_gettag ] + self._tags
960 #print(msg)
961 self.checkProtobufTags(msg, tags)
962 self.checkNoRemainingMessage()
963
964 # Again to check PC case
965 res = self.sendUDPQuery(query)
966 self.assertRRsetInAnswer(res, expected)
967
f1c7929a
RG
968 # check the protobuf messages corresponding to the UDP query and answer
969 msg = self.getFirstProtobufMessage()
970 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
b502d522 971 self.checkProtobufTags(msg, [ self._tag_from_gettag ])
f1c7929a
RG
972 # then the response
973 msg = self.getFirstProtobufMessage()
974 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
4bfebc93 975 self.assertEqual(len(msg.response.rrs), 1)
f1c7929a 976 rr = msg.response.rrs[0]
d2b8a997
OM
977 # time may have passed, so do not check TTL
978 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=False)
4bfebc93 979 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
b502d522 980 tags = [ self._tag_from_gettag ] + self._tags
f1c7929a
RG
981 self.checkProtobufTags(msg, tags)
982 self.checkNoRemainingMessage()
983
461f4b23
OM
984class ProtobufTagCacheTest(TestRecursorProtobuf):
985 """
986 This test makes sure that we correctly cache tags (actually not cache them)
987 """
988
989 _confdir = 'ProtobufTagCache'
990 _config_template = """
991auth-zones=example=configs/%s/example.zone""" % _confdir
992 _lua_config_file = """
993 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true } )
994 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
995 _lua_dns_script_file = """
80562e69
OM
996 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
997 if qname:equal('tagged.example.') then
998 return 0, { '' .. math.random() }
461f4b23 999 end
80562e69 1000 return 0
461f4b23
OM
1001 end
1002 """
1003
1004 def testTagged(self):
1005 name = 'tagged.example.'
1006 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
1007 query = dns.message.make_query(name, 'A', want_dnssec=True)
1008 query.flags |= dns.flags.CD
1009 res = self.sendUDPQuery(query)
1010 self.assertRRsetInAnswer(res, expected)
1011
1012 msg = self.getFirstProtobufMessage()
1013 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
1014 self.assertEqual(len(msg.response.rrs), 1)
1015 rr = msg.response.rrs[0]
1016 # we have max-cache-ttl set to 15
1017 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
1018 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
1019 self.checkNoRemainingMessage()
1020 self.assertEqual(len(msg.response.tags), 1)
1021 ts1 = msg.response.tags[0]
a4d0f523
OM
1022
1023 # Again to check PC case
461f4b23
OM
1024 res = self.sendUDPQuery(query)
1025 self.assertRRsetInAnswer(res, expected)
1026
1027 msg = self.getFirstProtobufMessage()
1028 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
1029 self.assertEqual(len(msg.response.rrs), 1)
1030 rr = msg.response.rrs[0]
d2b8a997
OM
1031 # time may have passed, so do not check TTL
1032 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15, checkTTL=False)
461f4b23
OM
1033 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
1034 self.checkNoRemainingMessage()
1035 self.assertEqual(len(msg.response.tags), 1)
1036 ts2 = msg.response.tags[0]
461f4b23
OM
1037 self.assertNotEqual(ts1, ts2)
1038
f1c7929a
RG
1039class ProtobufSelectedFromLuaTest(TestRecursorProtobuf):
1040 """
1041 This test makes sure that we correctly export queries and responses but only if they have been selected from Lua.
1042 """
1043
1044 _confdir = 'ProtobufSelectedFromLua'
1045 _config_template = """
1046auth-zones=example=configs/%s/example.zone""" % _confdir
f1c7929a 1047 _lua_config_file = """
b773359c
RG
1048 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=false } )
1049 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
f1c7929a
RG
1050 _lua_dns_script_file = """
1051 local ffi = require("ffi")
1052
1053 ffi.cdef[[
1054 typedef struct pdns_ffi_param pdns_ffi_param_t;
1055
1056 const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
1057 void pdns_ffi_param_set_log_query(pdns_ffi_param_t* ref, bool logQuery);
1058 ]]
1059
1060 function gettag_ffi(obj)
1061 qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
1062 if qname == 'query-selected.example' then
1063 ffi.C.pdns_ffi_param_set_log_query(obj, true)
1064 end
1065 return 0
1066 end
1067
1068 function preresolve(dq)
1069 if dq.qname:equal('answer-selected.example.') then
1070 dq.logResponse = true
1071 end
1072 return false
1073 end
1074 """
1075
1076 def testA(self):
1077 name = 'a.example.'
1078 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
1079 query = dns.message.make_query(name, 'A', want_dnssec=True)
1080 query.flags |= dns.flags.CD
1081 res = self.sendUDPQuery(query)
1082 self.assertRRsetInAnswer(res, expected)
1083
1084 # check the protobuf message corresponding to the UDP response
1085 # the first query and answer are not selected, so there is nothing in the queue
1086 self.checkNoRemainingMessage()
1087
1088 def testQuerySelected(self):
1089 name = 'query-selected.example.'
1090 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
1091 query = dns.message.make_query(name, 'A', want_dnssec=True)
1092 query.flags |= dns.flags.CD
1093 res = self.sendUDPQuery(query)
1094 self.assertRRsetInAnswer(res, expected)
1095
1096 # check the protobuf messages corresponding to the UDP query
1097 msg = self.getFirstProtobufMessage()
1098 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
1099 # there should be no response
1100 self.checkNoRemainingMessage()
1101
1102 def testResponseSelected(self):
1103 name = 'answer-selected.example.'
1104 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
1105 query = dns.message.make_query(name, 'A', want_dnssec=True)
1106 query.flags |= dns.flags.CD
1107 res = self.sendUDPQuery(query)
1108 self.assertRRsetInAnswer(res, expected)
1109
1110 # check the protobuf messages corresponding to the UDP response
1111 msg = self.getFirstProtobufMessage()
1112 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
4bfebc93 1113 self.assertEqual(len(msg.response.rrs), 1)
f1c7929a
RG
1114 rr = msg.response.rrs[0]
1115 # we have max-cache-ttl set to 15
1116 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 1117 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
f1c7929a 1118 self.checkNoRemainingMessage()
0bd2e252
RG
1119
1120class ProtobufExportTypesTest(TestRecursorProtobuf):
1121 """
1122 This test makes sure that we correctly export other types than A, AAAA and CNAME over protobuf.
1123 """
1124
1125 _confdir = 'ProtobufExportTypes'
1126 _config_template = """
1127auth-zones=example=configs/%s/example.zone""" % _confdir
0bd2e252 1128 _lua_config_file = """
b773359c
RG
1129 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { exportTypes={"AAAA", "MX", "SPF", "SRV", "TXT"} } )
1130 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
0bd2e252
RG
1131
1132 def testA(self):
1133 name = 'types.example.'
1134 expected = [dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84'),
1135 dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'AAAA', '2001:DB8::1'),
1136 dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'MX', '10 a.example.'),
1137 dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'SPF', '"v=spf1 -all"'),
1138 dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'SRV', '10 20 443 a.example.'),
1139 dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'TXT', '"Lorem ipsum dolor sit amet"'),
1140 ]
1141 query = dns.message.make_query(name, 'ANY', want_dnssec=True)
1142 query.flags |= dns.flags.CD
1143 raw = self.sendUDPQuery(query, decode=False)
1144 res = dns.message.from_wire(raw)
1145
1146 for rrset in expected:
1147 self.assertRRsetInAnswer(res, rrset)
1148
1149 # check the protobuf messages corresponding to the UDP query and answer
1150 msg = self.getFirstProtobufMessage()
1151 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
1152 # then the response
1153 msg = self.getFirstProtobufMessage()
1154 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1', receivedSize=len(raw))
4bfebc93 1155 self.assertEqual(len(msg.response.rrs), 5)
0bd2e252
RG
1156 for rr in msg.response.rrs:
1157 self.assertTrue(rr.type in [dns.rdatatype.AAAA, dns.rdatatype.TXT, dns.rdatatype.MX, dns.rdatatype.SPF, dns.rdatatype.SRV])
1158
1159 if rr.type == dns.rdatatype.AAAA:
1160 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.AAAA, name, 15)
4bfebc93 1161 self.assertEqual(socket.inet_ntop(socket.AF_INET6, rr.rdata), '2001:db8::1')
0bd2e252
RG
1162 elif rr.type == dns.rdatatype.TXT:
1163 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.TXT, name, 15)
4bfebc93 1164 self.assertEqual(rr.rdata, b'"Lorem ipsum dolor sit amet"')
0bd2e252
RG
1165 elif rr.type == dns.rdatatype.MX:
1166 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.MX, name, 15)
4bfebc93 1167 self.assertEqual(rr.rdata, b'a.example.')
0bd2e252
RG
1168 elif rr.type == dns.rdatatype.SPF:
1169 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.SPF, name, 15)
4bfebc93 1170 self.assertEqual(rr.rdata, b'"v=spf1 -all"')
0bd2e252
RG
1171 elif rr.type == dns.rdatatype.SRV:
1172 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.SRV, name, 15)
4bfebc93 1173 self.assertEqual(rr.rdata, b'a.example.')
0bd2e252
RG
1174
1175 self.checkNoRemainingMessage()
0a6a45c8
CHB
1176
1177class ProtobufTaggedExtraFieldsTest(TestRecursorProtobuf):
1178 """
1179 This test makes sure that we correctly export extra fields that may have been set while being tagged.
1180 """
1181
1182 _confdir = 'ProtobufTaggedExtraFields'
1183 _config_template = """
1184auth-zones=example=configs/%s/example.zone""" % _confdir
1185 _lua_config_file = """
1186 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
1187 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
1188 _requestorId = 'S-000001727'
1189 _deviceId = 'd1:0a:91:dc:cc:82'
1190 _deviceName = 'Joe'
1191 _lua_dns_script_file = """
1192 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
1193 if qname:equal('tagged.example.') then
1194 -- tag number, policy tags, data, requestorId, deviceId, deviceName
1195 return 0, {}, {}, '%s', '%s', '%s'
1196 end
1197 return 0
1198 end
1199 """ % (_requestorId, _deviceId, _deviceName)
1200
1201 def testA(self):
1202 name = 'a.example.'
1203 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
1204 query = dns.message.make_query(name, 'A', want_dnssec=True)
1205 query.flags |= dns.flags.CD
1206 res = self.sendUDPQuery(query)
1207 self.assertRRsetInAnswer(res, expected)
1208
1209 # check the protobuf message corresponding to the UDP response
1210 # the first query and answer are not tagged, so there is nothing in the queue
1211 # check the protobuf messages corresponding to the UDP query and answer
1212 msg = self.getFirstProtobufMessage()
1213 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
3d144e24 1214 self.checkProtobufIdentity(msg, '', b'', '')
0a6a45c8
CHB
1215
1216 # then the response
1217 msg = self.getFirstProtobufMessage()
1218 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res, '127.0.0.1')
4bfebc93 1219 self.assertEqual(len(msg.response.rrs), 1)
0a6a45c8
CHB
1220 rr = msg.response.rrs[0]
1221 # we have max-cache-ttl set to 15
1222 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 1223 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
3d144e24 1224 self.checkProtobufIdentity(msg, '', b'', '')
0a6a45c8
CHB
1225 self.checkNoRemainingMessage()
1226
1227 def testTagged(self):
1228 name = 'tagged.example.'
1229 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.84')
1230 query = dns.message.make_query(name, 'A', want_dnssec=True)
1231 query.flags |= dns.flags.CD
1232 res = self.sendUDPQuery(query)
1233 self.assertRRsetInAnswer(res, expected)
1234
1235 # check the protobuf messages corresponding to the UDP query and answer
1236 msg = self.getFirstProtobufMessage()
1237 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
3d144e24 1238 self.checkProtobufIdentity(msg, self._requestorId, self._deviceId.encode('ascii'), self._deviceName)
0a6a45c8
CHB
1239
1240 # then the response
1241 msg = self.getFirstProtobufMessage()
1242 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
4bfebc93 1243 self.assertEqual(len(msg.response.rrs), 1)
0a6a45c8
CHB
1244 rr = msg.response.rrs[0]
1245 # we have max-cache-ttl set to 15
1246 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 1247 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.84')
3d144e24 1248 self.checkProtobufIdentity(msg, self._requestorId, self._deviceId.encode('ascii'), self._deviceName)
0a6a45c8
CHB
1249 self.checkNoRemainingMessage()
1250
1251class ProtobufTaggedExtraFieldsFFITest(ProtobufTaggedExtraFieldsTest):
1252 """
1253 This test makes sure that we correctly export extra fields that may have been set while being tagged (FFI version).
1254 """
1255 _confdir = 'ProtobufTaggedExtraFieldsFFI'
1256 _config_template = """
1257auth-zones=example=configs/%s/example.zone""" % _confdir
1258 _lua_config_file = """
1259 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
1260 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
1261 _lua_dns_script_file = """
1262 local ffi = require("ffi")
1263
1264 ffi.cdef[[
1265 typedef struct pdns_ffi_param pdns_ffi_param_t;
1266
1267 const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
1268 void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag);
1269 void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name);
1270 void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name);
1271 void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name);
1272 ]]
1273
1274 function gettag_ffi(obj)
1275 qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
1276 if qname == 'tagged.example' then
1277 ffi.C.pdns_ffi_param_set_requestorid(obj, "%s")
1278 deviceid = "%s"
1279 ffi.C.pdns_ffi_param_set_deviceid(obj, string.len(deviceid), deviceid)
1280 ffi.C.pdns_ffi_param_set_devicename(obj, "%s")
1281 end
1282 return 0
1283 end
1284 """ % (ProtobufTaggedExtraFieldsTest._requestorId, ProtobufTaggedExtraFieldsTest._deviceId, ProtobufTaggedExtraFieldsTest._deviceName)
f89ae456
RG
1285
1286class ProtobufRPZTest(TestRecursorProtobuf):
1287 """
1288 This test makes sure that we correctly export the RPZ applied policy in our protobuf messages
1289 """
1290
1291 _confdir = 'ProtobufRPZ'
1292 _config_template = """
1293auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
1294 _lua_config_file = """
1295 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
1296 rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
1297 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir)
1298
1299 @classmethod
1300 def generateRecursorConfig(cls, confdir):
1301 authzonepath = os.path.join(confdir, 'example.rpz.zone')
1302 with open(authzonepath, 'w') as authzone:
1303 authzone.write("""$ORIGIN example.
1304@ 3600 IN SOA {soa}
1305sub.test 3600 IN A 192.0.2.42
9524d9c1 1306ip 3600 IN A 33.22.11.99
f89ae456
RG
1307""".format(soa=cls._SOA))
1308
1309 rpzFilePath = os.path.join(confdir, 'zone.rpz')
1310 with open(rpzFilePath, 'w') as rpzZone:
1311 rpzZone.write("""$ORIGIN zone.rpz.
1312@ 3600 IN SOA {soa}
1313*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
9524d9c1 131424.0.11.22.33.rpz-ip 60 IN A 1.2.3.4
f89ae456
RG
1315""".format(soa=cls._SOA))
1316
1317 super(ProtobufRPZTest, cls).generateRecursorConfig(confdir)
1318
1319 def testA(self):
1320 name = 'sub.test.example.'
1321 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
1322 query = dns.message.make_query(name, 'A', want_dnssec=True)
1323 query.flags |= dns.flags.CD
1324 res = self.sendUDPQuery(query)
1325 self.assertRRsetInAnswer(res, expected)
1326
1327 # check the protobuf messages corresponding to the UDP query and answer
1328 msg = self.getFirstProtobufMessage()
1329 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
1330
1331 # then the response
1332 msg = self.getFirstProtobufMessage()
1333 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
fe1a9389 1334 self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2.PBDNSMessage.PolicyKind.NoAction)
4bfebc93 1335 self.assertEqual(len(msg.response.rrs), 1)
f89ae456
RG
1336 rr = msg.response.rrs[0]
1337 # we have max-cache-ttl set to 15
1338 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 1339 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
f89ae456 1340 self.checkNoRemainingMessage()
b502d522 1341
9524d9c1
O
1342 def testB(self):
1343 name = 'ip.example.'
1344 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '1.2.3.4')
1345 query = dns.message.make_query(name, 'A', want_dnssec=True)
1346 query.flags |= dns.flags.CD
1347 res = self.sendUDPQuery(query)
1348 self.assertRRsetInAnswer(res, expected)
1349
1350 # check the protobuf messages corresponding to the UDP query and answer
1351 msg = self.getFirstProtobufMessage()
1352 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
1353
1354 # then the response
1355 msg = self.getFirstProtobufMessage()
1356 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
1357 self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.RESPONSEIP, 'zone.rpz.', '24.0.11.22.33.rpz-ip.', '33.22.11.99', dnsmessage_pb2.PBDNSMessage.PolicyKind.Custom)
1358 self.assertEqual(len(msg.response.rrs), 1)
1359 self.checkNoRemainingMessage()
1360
b502d522
RG
1361class ProtobufRPZTagsTest(TestRecursorProtobuf):
1362 """
1363 This test makes sure that we correctly export the RPZ tags in our protobuf messages
1364 """
1365
1366 _confdir = 'ProtobufRPZTags'
1367 _config_template = """
1368auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
1369 _tags = ['tag1', 'tag2']
1370 _tags_from_gettag = ['tag1-from-gettag', 'tag2-from-gettag']
1371 _tags_from_rpz = ['tag1-from-rpz', 'tag2-from-rpz' ]
1372 _lua_config_file = """
1373 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, tags={'tag1', 'tag2'} } )
1374 rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", tags={ '%s', '%s'} })
1375 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port, _confdir, _tags_from_rpz[0], _tags_from_rpz[1])
1376 _lua_dns_script_file = """
1377 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
1378 return 0, { '%s', '%s' }
1379 end
1380 function preresolve(dq)
1381 dq:addPolicyTag('%s')
1382 dq:addPolicyTag('%s')
1383 return false
1384 end
1385 """ % (_tags_from_gettag[0], _tags_from_gettag[1], _tags[0], _tags[1])
1386
1387 @classmethod
1388 def generateRecursorConfig(cls, confdir):
1389 authzonepath = os.path.join(confdir, 'example.rpz.zone')
1390 with open(authzonepath, 'w') as authzone:
1391 authzone.write("""$ORIGIN example.
1392@ 3600 IN SOA {soa}
1393sub.test 3600 IN A 192.0.2.42
1394""".format(soa=cls._SOA))
1395
1396 rpzFilePath = os.path.join(confdir, 'zone.rpz')
1397 with open(rpzFilePath, 'w') as rpzZone:
1398 rpzZone.write("""$ORIGIN zone.rpz.
1399@ 3600 IN SOA {soa}
1400*.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
1401""".format(soa=cls._SOA))
1402
1403 super(ProtobufRPZTagsTest, cls).generateRecursorConfig(confdir)
1404
1405 def testA(self):
1406 name = 'sub.test.example.'
1407 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.42')
1408 query = dns.message.make_query(name, 'A', want_dnssec=True)
1409 query.flags |= dns.flags.CD
1410 res = self.sendUDPQuery(query)
1411 self.assertRRsetInAnswer(res, expected)
1412
1413 # check the protobuf messages corresponding to the UDP query and answer
1414 msg = self.getFirstProtobufMessage()
1415 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
1416
1417 # then the response
1418 msg = self.getFirstProtobufMessage()
1419 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
fe1a9389 1420 self.checkProtobufPolicy(msg, dnsmessage_pb2.PBDNSMessage.PolicyType.QNAME, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2.PBDNSMessage.PolicyKind.NoAction)
b502d522 1421 self.checkProtobufTags(msg, self._tags + self._tags_from_gettag + self._tags_from_rpz)
4bfebc93 1422 self.assertEqual(len(msg.response.rrs), 1)
b502d522
RG
1423 rr = msg.response.rrs[0]
1424 # we have max-cache-ttl set to 15
1425 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
4bfebc93 1426 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.42')
b502d522 1427 self.checkNoRemainingMessage()
3211bbaf
CHB
1428
1429
1430class ProtobufMetaFFITest(TestRecursorProtobuf):
1431 """
1432 This test makes sure that we can correctly add extra meta fields (FFI version).
1433 """
1434 _confdir = 'ProtobufMetaFFITest'
1435 _config_template = """
1436auth-zones=example=configs/%s/example.zone""" % _confdir
1437 _lua_config_file = """
1438 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
1439 """ % (protobufServersParameters[0].port, protobufServersParameters[1].port)
1440 _lua_dns_script_file = """
1441 local ffi = require("ffi")
1442
1443 ffi.cdef[[
1444 typedef struct pdns_ffi_param pdns_ffi_param_t;
1445
1446 const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
1447 void pdns_ffi_param_add_meta_single_string_kv(pdns_ffi_param_t *ref, const char* key, const char* val);
1448 void pdns_ffi_param_add_meta_single_int64_kv(pdns_ffi_param_t *ref, const char* key, int64_t val);
1449 ]]
1450
1451 function gettag_ffi(obj)
1452 qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
1453 if qname == 'meta.example' then
1454 ffi.C.pdns_ffi_param_add_meta_single_string_kv(obj, "meta-str", "keyword")
1455 ffi.C.pdns_ffi_param_add_meta_single_int64_kv(obj, "meta-int", 42)
1456 ffi.C.pdns_ffi_param_add_meta_single_string_kv(obj, "meta-str", "content")
1457 ffi.C.pdns_ffi_param_add_meta_single_int64_kv(obj, "meta-int", 21)
1458 end
1459 return 0
1460 end
1461 """
1462 def testMeta(self):
1463 name = 'meta.example.'
1464 expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.85')
1465 query = dns.message.make_query(name, 'A', want_dnssec=True)
1466 query.flags |= dns.flags.CD
1467 res = self.sendUDPQuery(query)
1468 self.assertRRsetInAnswer(res, expected)
1469
1470 # check the protobuf messages corresponding to the UDP query and answer
1471 msg = self.getFirstProtobufMessage()
1472 self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name)
1473 self.checkProtobufMetas(msg, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}})
1474
1475 # then the response
1476 msg = self.getFirstProtobufMessage()
1477 self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, res)
1478 self.assertEqual(len(msg.response.rrs), 1)
1479 rr = msg.response.rrs[0]
1480 # we have max-cache-ttl set to 15
1481 self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 15)
1482 self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '192.0.2.85')
1483 self.checkProtobufMetas(msg, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}})
1484
1485 self.checkNoRemainingMessage()