10 # Python2/3 compatibility hacks
12 from queue
import Queue
14 from Queue
import Queue
21 from recursortests
import RecursorTest
23 def ProtobufConnectionHandler(queue
, conn
):
29 (datalen
,) = struct
.unpack("!H", data
)
30 data
= conn
.recv(datalen
)
34 queue
.put(data
, True, timeout
=2.0)
38 def ProtobufListener(queue
, port
):
39 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
40 sock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEPORT
, 1)
42 sock
.bind(("127.0.0.1", port
))
43 except socket
.error
as e
:
44 print("Error binding in the protobuf listener: %s" % str(e
))
50 (conn
, _
) = sock
.accept()
51 thread
= threading
.Thread(name
='Connection Handler',
52 target
=ProtobufConnectionHandler
,
54 thread
.setDaemon(True)
57 except socket
.error
as e
:
58 print('Error in protobuf socket: %s' % str(e
))
63 class ProtobufServerParams
:
64 def __init__(self
, port
):
68 protobufServersParameters
= [ProtobufServerParams(4243), ProtobufServerParams(4244)]
69 protobufListeners
= []
70 for param
in protobufServersParameters
:
71 listener
= threading
.Thread(name
='Protobuf Listener', target
=ProtobufListener
, args
=[param
.queue
, param
.port
])
72 listener
.setDaemon(True)
74 protobufListeners
.append(listener
)
76 class TestRecursorProtobuf(RecursorTest
):
78 _lua_config_file
= """
79 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
80 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
86 'zones': ['secure.example', 'islandofsecurity.example']},
88 'zones': ['example']},
93 def getFirstProtobufMessage(self
, retries
=1, waitTime
=1):
96 print("in getFirstProtobufMessage")
97 for param
in protobufServersParameters
:
101 while param
.queue
.empty
:
104 if failed
>= retries
:
111 self
.assertFalse(param
.queue
.empty())
112 data
= param
.queue
.get(False)
113 self
.assertTrue(data
)
115 msg
= dnsmessage_pb2
.PBDNSMessage()
116 msg
.ParseFromString(data
)
117 if oldmsg
is not None:
118 self
.assertEqual(msg
, oldmsg
)
123 def checkNoRemainingMessage(self
):
124 for param
in protobufServersParameters
:
125 self
.assertTrue(param
.queue
.empty())
127 def checkProtobufBase(self
, msg
, protocol
, query
, initiator
, normalQueryResponse
=True, expectedECS
=None, receivedSize
=None):
129 self
.assertTrue(msg
.HasField('timeSec'))
130 self
.assertTrue(msg
.HasField('socketFamily'))
131 self
.assertEqual(msg
.socketFamily
, dnsmessage_pb2
.PBDNSMessage
.INET
)
132 self
.assertTrue(msg
.HasField('from'))
133 fromvalue
= getattr(msg
, 'from')
134 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, fromvalue
), initiator
)
135 self
.assertTrue(msg
.HasField('socketProtocol'))
136 self
.assertEqual(msg
.socketProtocol
, protocol
)
137 self
.assertTrue(msg
.HasField('messageId'))
138 self
.assertTrue(msg
.HasField('serverIdentity'))
139 self
.assertTrue(msg
.HasField('id'))
140 self
.assertEqual(msg
.id, query
.id)
141 self
.assertTrue(msg
.HasField('inBytes'))
142 if normalQueryResponse
:
143 # compare inBytes with length of query/response
144 # Note that for responses, the size we received might differ
145 # because dnspython might compress labels differently from
148 self
.assertEqual(msg
.inBytes
, receivedSize
)
150 self
.assertEqual(msg
.inBytes
, len(query
.to_wire()))
151 if expectedECS
is not None:
152 self
.assertTrue(msg
.HasField('originalRequestorSubnet'))
154 self
.assertEqual(len(msg
.originalRequestorSubnet
), 4)
155 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, msg
.originalRequestorSubnet
), '127.0.0.1')
157 def checkOutgoingProtobufBase(self
, msg
, protocol
, query
, initiator
, length
=None):
159 self
.assertTrue(msg
.HasField('timeSec'))
160 self
.assertTrue(msg
.HasField('socketFamily'))
161 self
.assertEqual(msg
.socketFamily
, dnsmessage_pb2
.PBDNSMessage
.INET
)
162 self
.assertTrue(msg
.HasField('socketProtocol'))
163 self
.assertEqual(msg
.socketProtocol
, protocol
)
164 self
.assertTrue(msg
.HasField('messageId'))
165 self
.assertTrue(msg
.HasField('serverIdentity'))
166 self
.assertTrue(msg
.HasField('id'))
167 self
.assertNotEqual(msg
.id, query
.id)
168 self
.assertTrue(msg
.HasField('inBytes'))
169 if length
is not None:
170 self
.assertEqual(msg
.inBytes
, length
)
172 # compare inBytes with length of query/response
173 self
.assertEqual(msg
.inBytes
, len(query
.to_wire()))
175 def checkProtobufQuery(self
, msg
, protocol
, query
, qclass
, qtype
, qname
, initiator
='127.0.0.1', to
='127.0.0.1'):
176 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSQueryType
)
177 self
.checkProtobufBase(msg
, protocol
, query
, initiator
)
178 # dnsdist doesn't fill the responder field for responses
179 # because it doesn't keep the information around.
180 self
.assertTrue(msg
.HasField('to'))
181 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, msg
.to
), to
)
182 self
.assertTrue(msg
.HasField('question'))
183 self
.assertTrue(msg
.question
.HasField('qClass'))
184 self
.assertEqual(msg
.question
.qClass
, qclass
)
185 self
.assertTrue(msg
.question
.HasField('qType'))
186 self
.assertEqual(msg
.question
.qClass
, qtype
)
187 self
.assertTrue(msg
.question
.HasField('qName'))
188 self
.assertEqual(msg
.question
.qName
, qname
)
190 def checkProtobufResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1', receivedSize
=None, vstate
=dnsmessage_pb2
.PBDNSMessage
.VState
.Indeterminate
):
191 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
192 self
.checkProtobufBase(msg
, protocol
, response
, initiator
, receivedSize
=receivedSize
)
193 self
.assertTrue(msg
.HasField('response'))
194 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
195 self
.assertTrue(msg
.response
.HasField('validationState'))
196 self
.assertEqual(msg
.response
.validationState
, vstate
)
198 def checkProtobufResponseRecord(self
, record
, rclass
, rtype
, rname
, rttl
, checkTTL
=True):
199 self
.assertTrue(record
.HasField('class'))
200 self
.assertEqual(getattr(record
, 'class'), rclass
)
201 self
.assertTrue(record
.HasField('type'))
202 self
.assertEqual(record
.type, rtype
)
203 self
.assertTrue(record
.HasField('name'))
204 self
.assertEqual(record
.name
, rname
)
205 self
.assertTrue(record
.HasField('ttl'))
207 self
.assertEqual(record
.ttl
, rttl
)
208 self
.assertTrue(record
.HasField('rdata'))
210 def checkProtobufPolicy(self
, msg
, policyType
, reason
, trigger
, hit
, kind
):
211 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
212 self
.assertTrue(msg
.response
.HasField('appliedPolicyType'))
213 self
.assertTrue(msg
.response
.HasField('appliedPolicy'))
214 self
.assertTrue(msg
.response
.HasField('appliedPolicyTrigger'))
215 self
.assertTrue(msg
.response
.HasField('appliedPolicyHit'))
216 self
.assertTrue(msg
.response
.HasField('appliedPolicyKind'))
217 self
.assertEqual(msg
.response
.appliedPolicy
, reason
)
218 self
.assertEqual(msg
.response
.appliedPolicyType
, policyType
)
219 self
.assertEqual(msg
.response
.appliedPolicyTrigger
, trigger
)
220 self
.assertEqual(msg
.response
.appliedPolicyHit
, hit
)
221 self
.assertEqual(msg
.response
.appliedPolicyKind
, kind
)
223 def checkProtobufTags(self
, msg
, tags
):
226 print(msg
.response
.tags
)
227 self
.assertEqual(len(msg
.response
.tags
), len(tags
))
228 for tag
in msg
.response
.tags
:
229 self
.assertTrue(tag
in tags
)
231 def checkProtobufMetas(self
, msg
, metas
):
235 self
.assertEqual(len(msg
.meta
), len(metas
))
237 self
.assertTrue(m
.HasField('key'))
238 self
.assertTrue(m
.HasField('value'))
239 self
.assertTrue(m
.key
in metas
)
240 for i
in m
.value
.intVal
:
241 self
.assertTrue(i
in metas
[m
.key
]['intVal'])
242 for s
in m
.value
.stringVal
:
243 self
.assertTrue(s
in metas
[m
.key
]['stringVal'])
245 def checkProtobufOutgoingQuery(self
, msg
, protocol
, query
, qclass
, qtype
, qname
, initiator
='127.0.0.1', length
=None):
246 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSOutgoingQueryType
)
247 self
.checkOutgoingProtobufBase(msg
, protocol
, query
, initiator
, length
=length
)
248 self
.assertTrue(msg
.HasField('to'))
249 self
.assertTrue(msg
.HasField('question'))
250 self
.assertTrue(msg
.question
.HasField('qClass'))
251 self
.assertEqual(msg
.question
.qClass
, qclass
)
252 self
.assertTrue(msg
.question
.HasField('qType'))
253 self
.assertEqual(msg
.question
.qType
, qtype
)
254 self
.assertTrue(msg
.question
.HasField('qName'))
255 self
.assertEqual(msg
.question
.qName
, qname
)
257 def checkProtobufIncomingResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1', length
=None):
258 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSIncomingResponseType
)
259 self
.checkOutgoingProtobufBase(msg
, protocol
, response
, initiator
, length
=length
)
260 self
.assertTrue(msg
.HasField('response'))
261 self
.assertTrue(msg
.response
.HasField('rcode'))
262 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
264 def checkProtobufIncomingNetworkErrorResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1'):
265 self
.checkProtobufIncomingResponse(msg
, protocol
, response
, initiator
, length
=0)
266 self
.assertEqual(msg
.response
.rcode
, 65536)
268 def checkProtobufIdentity(self
, msg
, requestorId
, deviceId
, deviceName
):
270 self
.assertTrue((requestorId
== '') == (not msg
.HasField('requestorId')))
271 self
.assertTrue((deviceId
== b
'') == (not msg
.HasField('deviceId')))
272 self
.assertTrue((deviceName
== '') == (not msg
.HasField('deviceName')))
273 self
.assertEqual(msg
.requestorId
, requestorId
)
274 self
.assertEqual(msg
.deviceId
, deviceId
)
275 self
.assertEqual(msg
.deviceName
, deviceName
)
278 super(TestRecursorProtobuf
, self
).setUp()
279 # Make sure the queue is empty, in case
280 # a previous test failed
281 for param
in protobufServersParameters
:
282 while not param
.queue
.empty():
283 param
.queue
.get(False)
284 # wait long enough to be sure that the housekeeping has
289 def generateRecursorConfig(cls
, confdir
):
290 authzonepath
= os
.path
.join(confdir
, 'example.zone')
291 with
open(authzonepath
, 'w') as authzone
:
292 authzone
.write("""$ORIGIN example.
294 a 3600 IN A 192.0.2.42
295 tagged 3600 IN A 192.0.2.84
296 meta 3600 IN A 192.0.2.85
297 query-selected 3600 IN A 192.0.2.84
298 answer-selected 3600 IN A 192.0.2.84
299 types 3600 IN A 192.0.2.84
300 types 3600 IN AAAA 2001:DB8::1
301 types 3600 IN TXT "Lorem ipsum dolor sit amet"
302 types 3600 IN MX 10 a.example.
303 types 3600 IN SPF "v=spf1 -all"
304 types 3600 IN SRV 10 20 443 a.example.
305 cname 3600 IN CNAME a.example.
307 """.format(soa
=cls
._SOA
))
308 super(TestRecursorProtobuf
, cls
).generateRecursorConfig(confdir
)
311 class ProtobufDefaultTest(TestRecursorProtobuf
):
313 This test makes sure that we correctly export queries and response over protobuf.
316 _confdir
= 'ProtobufDefault'
317 _config_template
= """
318 auth-zones=example=configs/%s/example.zone""" % _confdir
322 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
323 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
324 query
.flags |
= dns
.flags
.CD
325 res
= self
.sendUDPQuery(query
)
327 self
.assertRRsetInAnswer(res
, expected
)
329 # check the protobuf messages corresponding to the UDP query and answer
330 msg
= self
.getFirstProtobufMessage()
331 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
333 msg
= self
.getFirstProtobufMessage()
334 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1')
335 self
.assertEqual(len(msg
.response
.rrs
), 1)
336 rr
= msg
.response
.rrs
[0]
337 # we have max-cache-ttl set to 15
338 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
339 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
340 self
.checkNoRemainingMessage()
343 name
= 'cname.example.'
344 expectedCNAME
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'CNAME', 'a.example.')
345 expectedA
= dns
.rrset
.from_text('a.example.', 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
346 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
347 query
.flags |
= dns
.flags
.CD
348 raw
= self
.sendUDPQuery(query
, decode
=False)
349 res
= dns
.message
.from_wire(raw
)
350 self
.assertRRsetInAnswer(res
, expectedCNAME
)
351 self
.assertRRsetInAnswer(res
, expectedA
)
353 # check the protobuf messages corresponding to the UDP query and answer
354 # but first let the protobuf messages the time to get there
355 msg
= self
.getFirstProtobufMessage()
356 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
358 msg
= self
.getFirstProtobufMessage()
359 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1', receivedSize
=len(raw
))
360 self
.assertEqual(len(msg
.response
.rrs
), 2)
361 rr
= msg
.response
.rrs
[0]
362 # we don't want to check the TTL for the A record, it has been cached by the previous test
363 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 15)
364 self
.assertEqual(rr
.rdata
, b
'a.example.')
365 rr
= msg
.response
.rrs
[1]
366 # we have max-cache-ttl set to 15
367 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, 'a.example.', 15, checkTTL
=False)
368 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
369 self
.checkNoRemainingMessage()
371 class ProtobufProxyMappingTest(TestRecursorProtobuf
):
373 This test makes sure that we correctly export queries and response over protobuf with a proxyMapping
376 _confdir
= 'ProtobufProxyMappingTest'
377 _config_template
= """
378 auth-zones=example=configs/%s/example.zone
379 allow-from=3.4.5.0/24
382 _lua_config_file
= """
383 addProxyMapping("127.0.0.1/24", "3.4.5.6:99")
384 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
385 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
389 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
390 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
391 query
.flags |
= dns
.flags
.CD
392 res
= self
.sendUDPQuery(query
)
394 self
.assertRRsetInAnswer(res
, expected
)
396 # check the protobuf messages corresponding to the UDP query and answer
397 msg
= self
.getFirstProtobufMessage()
398 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
400 msg
= self
.getFirstProtobufMessage()
401 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1')
402 self
.assertEqual(len(msg
.response
.rrs
), 1)
403 rr
= msg
.response
.rrs
[0]
404 # we have max-cache-ttl set to 15
405 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
406 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
407 self
.checkNoRemainingMessage()
409 class ProtobufProxyMappingLogMappedTest(TestRecursorProtobuf
):
411 This test makes sure that we correctly export queries and response over protobuf.
414 _confdir
= 'ProtobufProxyMappingLogMappedTest'
415 _config_template
= """
416 auth-zones=example=configs/%s/example.zone
417 allow-from=3.4.5.0/0"
420 _lua_config_file
= """
421 addProxyMapping("127.0.0.1/24", "3.4.5.6:99")
422 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logMappedFrom = true })
423 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
427 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
428 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
429 query
.flags |
= dns
.flags
.CD
430 res
= self
.sendUDPQuery(query
)
432 self
.assertRRsetInAnswer(res
, expected
)
434 # check the protobuf messages corresponding to the UDP query and answer
435 msg
= self
.getFirstProtobufMessage()
436 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '3.4.5.6')
438 msg
= self
.getFirstProtobufMessage()
439 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '3.4.5.6')
440 self
.assertEqual(len(msg
.response
.rrs
), 1)
441 rr
= msg
.response
.rrs
[0]
442 # we have max-cache-ttl set to 15
443 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
444 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
445 self
.checkNoRemainingMessage()
447 class ProtobufProxyTest(TestRecursorProtobuf
):
449 This test makes sure that we correctly export addresses over protobuf when the proxy protocol is used.
452 _confdir
= 'ProtobufProxy'
453 _config_template
= """
454 auth-zones=example=configs/%s/example.zone
455 proxy-protocol-from=127.0.0.1/32
456 allow-from=127.0.0.1,6.6.6.6
461 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
462 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
463 query
.flags |
= dns
.flags
.CD
464 res
= self
.sendUDPQueryWithProxyProtocol(query
, False, '6.6.6.6', '7.7.7.7', 666, 777)
466 self
.assertRRsetInAnswer(res
, expected
)
468 # check the protobuf messages corresponding to the UDP query and answer
469 msg
= self
.getFirstProtobufMessage()
470 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '6.6.6.6', '7.7.7.7')
472 msg
= self
.getFirstProtobufMessage()
473 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '6.6.6.6')
474 self
.assertEqual(len(msg
.response
.rrs
), 1)
475 rr
= msg
.response
.rrs
[0]
476 # we have max-cache-ttl set to 15
477 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
478 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
479 self
.checkNoRemainingMessage()
481 class ProtobufProxyWithProxyByTableTest(TestRecursorProtobuf
):
483 This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
486 _confdir
= 'ProtobufProxyWithProxyByTable'
487 _config_template
= """
488 auth-zones=example=configs/%s/example.zone
489 proxy-protocol-from=127.0.0.1/32
493 _lua_config_file
= """
494 addProxyMapping("6.6.6.6/24", "3.4.5.6:99")
495 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
496 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
500 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
501 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
502 query
.flags |
= dns
.flags
.CD
503 res
= self
.sendUDPQueryWithProxyProtocol(query
, False, '6.6.6.6', '7.7.7.7', 666, 777)
505 self
.assertRRsetInAnswer(res
, expected
)
507 # check the protobuf messages corresponding to the UDP query and answer
508 msg
= self
.getFirstProtobufMessage()
509 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '6.6.6.6', '7.7.7.7')
511 msg
= self
.getFirstProtobufMessage()
512 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '6.6.6.6')
513 self
.assertEqual(len(msg
.response
.rrs
), 1)
514 rr
= msg
.response
.rrs
[0]
515 # we have max-cache-ttl set to 15
516 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
517 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
518 self
.checkNoRemainingMessage()
520 class ProtobufProxyWithProxyByTableLogMappedTest(TestRecursorProtobuf
):
522 This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
525 _confdir
= 'ProtobufProxyWithProxyByTableLogMapped'
526 _config_template
= """
527 auth-zones=example=configs/%s/example.zone
528 proxy-protocol-from=127.0.0.1/32
532 _lua_config_file
= """
533 addProxyMapping("6.6.6.6/24", "3.4.5.6:99")
534 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logMappedFrom = true })
535 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
539 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
540 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
541 query
.flags |
= dns
.flags
.CD
542 res
= self
.sendUDPQueryWithProxyProtocol(query
, False, '6.6.6.6', '7.7.7.7', 666, 777)
544 self
.assertRRsetInAnswer(res
, expected
)
546 # check the protobuf messages corresponding to the UDP query and answer
547 msg
= self
.getFirstProtobufMessage()
548 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '3.4.5.6', '7.7.7.7')
550 msg
= self
.getFirstProtobufMessage()
551 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '3.4.5.6')
552 self
.assertEqual(len(msg
.response
.rrs
), 1)
553 rr
= msg
.response
.rrs
[0]
554 # we have max-cache-ttl set to 15
555 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
556 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
557 self
.checkNoRemainingMessage()
560 class OutgoingProtobufDefaultTest(TestRecursorProtobuf
):
562 This test makes sure that we correctly export outgoing queries over protobuf.
563 It must be improved and setup env so we can check for incoming responses, but makes sure for now
564 that the recursor at least connects to the protobuf server.
567 _confdir
= 'OutgoingProtobufDefault'
568 _config_template
= """
569 # Switch off QName Minimization, it generates much more protobuf messages
570 # (or make the test much more smart!)
571 qname-minimization=no
573 _lua_config_file
= """
574 outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
575 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
578 name
= 'host1.secure.example.'
581 # the root DNSKEY has been learned with priming the root NS already
582 # ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
583 for qname
, qtype
, proto
, responseSize
in [
584 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 248),
585 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 221),
586 ('example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 219),
587 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 175),
588 ('secure.example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 233),
591 expected
.append((None, None, None, None, None, None))
593 query
= dns
.message
.make_query(qname
, qtype
, use_edns
=True, want_dnssec
=True)
594 resp
= dns
.message
.make_response(query
)
596 qname
, qtype
, query
, resp
, proto
, responseSize
599 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
600 query
.flags |
= dns
.flags
.RD
601 res
= self
.sendUDPQuery(query
)
603 for qname
, qtype
, qry
, ans
, proto
, responseSize
in expected
:
605 self
.getFirstProtobufMessage()
606 self
.getFirstProtobufMessage()
609 msg
= self
.getFirstProtobufMessage()
610 self
.checkProtobufOutgoingQuery(msg
, proto
, qry
, dns
.rdataclass
.IN
, qtype
, qname
)
613 msg
= self
.getFirstProtobufMessage()
614 self
.checkProtobufIncomingResponse(msg
, proto
, ans
, length
=responseSize
)
616 self
.checkNoRemainingMessage()
618 class OutgoingProtobufNoQueriesTest(TestRecursorProtobuf
):
620 This test makes sure that we correctly export incoming responses but not outgoing queries over protobuf.
621 It must be improved and setup env so we can check for incoming responses, but makes sure for now
622 that the recursor at least connects to the protobuf server.
625 _confdir
= 'OutgoingProtobufNoQueries'
626 _config_template
= """
627 # Switch off QName Minimization, it generates much more protobuf messages
628 # (or make the test much more smart!)
629 qname-minimization=no"""
630 _lua_config_file
= """
631 outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true })
632 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
635 name
= 'host1.secure.example.'
637 # the root DNSKEY has been learned with priming the root NS already
638 # ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
639 for qname
, qtype
, proto
, size
in [
640 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 248),
641 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 221),
642 ('example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 219),
643 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 175),
644 ('secure.example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 233),
647 expected
.append((None, None, None, None, None, None))
649 query
= dns
.message
.make_query(qname
, qtype
, use_edns
=True, want_dnssec
=True)
650 resp
= dns
.message
.make_response(query
)
652 qname
, qtype
, query
, resp
, proto
, size
655 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
656 query
.flags |
= dns
.flags
.RD
657 res
= self
.sendUDPQuery(query
)
659 for qname
, qtype
, qry
, ans
, proto
, size
in expected
:
661 self
.getFirstProtobufMessage()
665 msg
= self
.getFirstProtobufMessage()
666 self
.checkProtobufIncomingResponse(msg
, proto
, ans
, length
=size
)
668 self
.checkNoRemainingMessage()
670 class ProtobufMasksTest(TestRecursorProtobuf
):
672 This test makes sure that we correctly export queries and response over protobuf, respecting the configured initiator masking.
675 _confdir
= 'ProtobufMasks'
676 _config_template
= """
677 auth-zones=example=configs/%s/example.zone""" % _confdir
679 _protobufMaskV6
= 128
680 _lua_config_file
= """
681 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
682 setProtobufMasks(%d, %d)
683 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
, _protobufMaskV4
, _protobufMaskV6
)
687 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
688 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
689 query
.flags |
= dns
.flags
.CD
690 res
= self
.sendUDPQuery(query
)
691 self
.assertRRsetInAnswer(res
, expected
)
693 # check the protobuf messages corresponding to the UDP query and answer
694 # but first let the protobuf messages the time to get there
695 msg
= self
.getFirstProtobufMessage()
696 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '112.0.0.0')
698 msg
= self
.getFirstProtobufMessage()
699 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '112.0.0.0')
700 self
.assertEqual(len(msg
.response
.rrs
), 1)
701 rr
= msg
.response
.rrs
[0]
702 # we have max-cache-ttl set to 15
703 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
704 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
705 self
.checkNoRemainingMessage()
707 class ProtobufQueriesOnlyTest(TestRecursorProtobuf
):
709 This test makes sure that we correctly export queries but not responses over protobuf.
712 _confdir
= 'ProtobufQueriesOnly'
713 _config_template
= """
714 auth-zones=example=configs/%s/example.zone""" % _confdir
715 _lua_config_file
= """
716 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=false } )
717 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
721 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
722 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
723 query
.flags |
= dns
.flags
.CD
724 res
= self
.sendUDPQuery(query
)
725 self
.assertRRsetInAnswer(res
, expected
)
727 # check the protobuf message corresponding to the UDP query
728 msg
= self
.getFirstProtobufMessage()
729 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
731 self
.checkNoRemainingMessage()
733 class ProtobufResponsesOnlyTest(TestRecursorProtobuf
):
735 This test makes sure that we correctly export responses but not queries over protobuf.
738 _confdir
= 'ProtobufResponsesOnly'
739 _config_template
= """
740 auth-zones=example=configs/%s/example.zone""" % _confdir
741 _lua_config_file
= """
742 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true } )
743 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
747 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
748 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
749 query
.flags |
= dns
.flags
.CD
750 res
= self
.sendUDPQuery(query
)
751 self
.assertRRsetInAnswer(res
, expected
)
753 # check the protobuf message corresponding to the UDP response
754 msg
= self
.getFirstProtobufMessage()
755 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
756 self
.assertEqual(len(msg
.response
.rrs
), 1)
757 rr
= msg
.response
.rrs
[0]
758 # we have max-cache-ttl set to 15
759 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
760 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
761 # nothing else in the queue
762 self
.checkNoRemainingMessage()
764 class ProtobufTaggedOnlyTest(TestRecursorProtobuf
):
766 This test makes sure that we correctly export queries and responses but only if they have been tagged.
769 _confdir
= 'ProtobufTaggedOnly'
770 _config_template
= """
771 auth-zones=example=configs/%s/example.zone""" % _confdir
772 _lua_config_file
= """
773 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, taggedOnly=true } )
774 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
775 _tags
= ['tag1', 'tag2']
776 _tag_from_gettag
= 'tag-from-gettag'
777 _lua_dns_script_file
= """
778 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
779 if qname:equal('tagged.example.') then
784 function preresolve(dq)
785 if dq.qname:equal('tagged.example.') then
786 dq:addPolicyTag('%s')
787 dq:addPolicyTag('%s')
791 """ % (_tag_from_gettag
, _tags
[0], _tags
[1])
795 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
796 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
797 query
.flags |
= dns
.flags
.CD
798 res
= self
.sendUDPQuery(query
)
799 self
.assertRRsetInAnswer(res
, expected
)
801 # check the protobuf message corresponding to the UDP response
802 # the first query and answer are not tagged, so there is nothing in the queue
804 self
.checkNoRemainingMessage()
806 def testTagged(self
):
807 name
= 'tagged.example.'
808 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.84')
809 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
810 query
.flags |
= dns
.flags
.CD
811 res
= self
.sendUDPQuery(query
)
812 self
.assertRRsetInAnswer(res
, expected
)
814 # check the protobuf messages corresponding to the UDP query and answer
815 msg
= self
.getFirstProtobufMessage()
816 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
817 self
.checkProtobufTags(msg
, [ self
._tag
_from
_gettag
])
819 msg
= self
.getFirstProtobufMessage()
820 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
821 self
.assertEqual(len(msg
.response
.rrs
), 1)
822 rr
= msg
.response
.rrs
[0]
823 # we have max-cache-ttl set to 15
824 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
825 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.84')
826 tags
= [ self
._tag
_from
_gettag
] + self
._tags
827 self
.checkProtobufTags(msg
, tags
)
828 self
.checkNoRemainingMessage()
830 class ProtobufSelectedFromLuaTest(TestRecursorProtobuf
):
832 This test makes sure that we correctly export queries and responses but only if they have been selected from Lua.
835 _confdir
= 'ProtobufSelectedFromLua'
836 _config_template
= """
837 auth-zones=example=configs/%s/example.zone""" % _confdir
838 _lua_config_file
= """
839 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=false } )
840 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
841 _lua_dns_script_file
= """
842 local ffi = require("ffi")
845 typedef struct pdns_ffi_param pdns_ffi_param_t;
847 const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
848 void pdns_ffi_param_set_log_query(pdns_ffi_param_t* ref, bool logQuery);
851 function gettag_ffi(obj)
852 qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
853 if qname == 'query-selected.example' then
854 ffi.C.pdns_ffi_param_set_log_query(obj, true)
859 function preresolve(dq)
860 if dq.qname:equal('answer-selected.example.') then
861 dq.logResponse = true
869 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
870 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
871 query
.flags |
= dns
.flags
.CD
872 res
= self
.sendUDPQuery(query
)
873 self
.assertRRsetInAnswer(res
, expected
)
875 # check the protobuf message corresponding to the UDP response
876 # the first query and answer are not selected, so there is nothing in the queue
877 self
.checkNoRemainingMessage()
879 def testQuerySelected(self
):
880 name
= 'query-selected.example.'
881 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.84')
882 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
883 query
.flags |
= dns
.flags
.CD
884 res
= self
.sendUDPQuery(query
)
885 self
.assertRRsetInAnswer(res
, expected
)
887 # check the protobuf messages corresponding to the UDP query
888 msg
= self
.getFirstProtobufMessage()
889 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
890 # there should be no response
891 self
.checkNoRemainingMessage()
893 def testResponseSelected(self
):
894 name
= 'answer-selected.example.'
895 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.84')
896 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
897 query
.flags |
= dns
.flags
.CD
898 res
= self
.sendUDPQuery(query
)
899 self
.assertRRsetInAnswer(res
, expected
)
901 # check the protobuf messages corresponding to the UDP response
902 msg
= self
.getFirstProtobufMessage()
903 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
904 self
.assertEqual(len(msg
.response
.rrs
), 1)
905 rr
= msg
.response
.rrs
[0]
906 # we have max-cache-ttl set to 15
907 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
908 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.84')
909 self
.checkNoRemainingMessage()
911 class ProtobufExportTypesTest(TestRecursorProtobuf
):
913 This test makes sure that we correctly export other types than A, AAAA and CNAME over protobuf.
916 _confdir
= 'ProtobufExportTypes'
917 _config_template
= """
918 auth-zones=example=configs/%s/example.zone""" % _confdir
919 _lua_config_file
= """
920 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { exportTypes={"AAAA", "MX", "SPF", "SRV", "TXT"} } )
921 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
924 name
= 'types.example.'
925 expected
= [dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.84'),
926 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'AAAA', '2001:DB8::1'),
927 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'MX', '10 a.example.'),
928 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'SPF', '"v=spf1 -all"'),
929 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'SRV', '10 20 443 a.example.'),
930 dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'TXT', '"Lorem ipsum dolor sit amet"'),
932 query
= dns
.message
.make_query(name
, 'ANY', want_dnssec
=True)
933 query
.flags |
= dns
.flags
.CD
934 raw
= self
.sendUDPQuery(query
, decode
=False)
935 res
= dns
.message
.from_wire(raw
)
937 for rrset
in expected
:
938 self
.assertRRsetInAnswer(res
, rrset
)
940 # check the protobuf messages corresponding to the UDP query and answer
941 msg
= self
.getFirstProtobufMessage()
942 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
944 msg
= self
.getFirstProtobufMessage()
945 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1', receivedSize
=len(raw
))
946 self
.assertEqual(len(msg
.response
.rrs
), 5)
947 for rr
in msg
.response
.rrs
:
948 self
.assertTrue(rr
.type in [dns
.rdatatype
.AAAA
, dns
.rdatatype
.TXT
, dns
.rdatatype
.MX
, dns
.rdatatype
.SPF
, dns
.rdatatype
.SRV
])
950 if rr
.type == dns
.rdatatype
.AAAA
:
951 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.AAAA
, name
, 15)
952 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET6
, rr
.rdata
), '2001:db8::1')
953 elif rr
.type == dns
.rdatatype
.TXT
:
954 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.TXT
, name
, 15)
955 self
.assertEqual(rr
.rdata
, b
'"Lorem ipsum dolor sit amet"')
956 elif rr
.type == dns
.rdatatype
.MX
:
957 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.MX
, name
, 15)
958 self
.assertEqual(rr
.rdata
, b
'a.example.')
959 elif rr
.type == dns
.rdatatype
.SPF
:
960 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.SPF
, name
, 15)
961 self
.assertEqual(rr
.rdata
, b
'"v=spf1 -all"')
962 elif rr
.type == dns
.rdatatype
.SRV
:
963 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.SRV
, name
, 15)
964 self
.assertEqual(rr
.rdata
, b
'a.example.')
966 self
.checkNoRemainingMessage()
968 class ProtobufTaggedExtraFieldsTest(TestRecursorProtobuf
):
970 This test makes sure that we correctly export extra fields that may have been set while being tagged.
973 _confdir
= 'ProtobufTaggedExtraFields'
974 _config_template
= """
975 auth-zones=example=configs/%s/example.zone""" % _confdir
976 _lua_config_file
= """
977 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
978 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
979 _requestorId
= 'S-000001727'
980 _deviceId
= 'd1:0a:91:dc:cc:82'
982 _lua_dns_script_file
= """
983 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
984 if qname:equal('tagged.example.') then
985 -- tag number, policy tags, data, requestorId, deviceId, deviceName
986 return 0, {}, {}, '%s', '%s', '%s'
990 """ % (_requestorId
, _deviceId
, _deviceName
)
994 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
995 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
996 query
.flags |
= dns
.flags
.CD
997 res
= self
.sendUDPQuery(query
)
998 self
.assertRRsetInAnswer(res
, expected
)
1000 # check the protobuf message corresponding to the UDP response
1001 # the first query and answer are not tagged, so there is nothing in the queue
1002 # check the protobuf messages corresponding to the UDP query and answer
1003 msg
= self
.getFirstProtobufMessage()
1004 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
1005 self
.checkProtobufIdentity(msg
, '', b
'', '')
1008 msg
= self
.getFirstProtobufMessage()
1009 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1')
1010 self
.assertEqual(len(msg
.response
.rrs
), 1)
1011 rr
= msg
.response
.rrs
[0]
1012 # we have max-cache-ttl set to 15
1013 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
1014 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
1015 self
.checkProtobufIdentity(msg
, '', b
'', '')
1016 self
.checkNoRemainingMessage()
1018 def testTagged(self
):
1019 name
= 'tagged.example.'
1020 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.84')
1021 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
1022 query
.flags |
= dns
.flags
.CD
1023 res
= self
.sendUDPQuery(query
)
1024 self
.assertRRsetInAnswer(res
, expected
)
1026 # check the protobuf messages corresponding to the UDP query and answer
1027 msg
= self
.getFirstProtobufMessage()
1028 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
1029 self
.checkProtobufIdentity(msg
, self
._requestorId
, self
._deviceId
.encode('ascii'), self
._deviceName
)
1032 msg
= self
.getFirstProtobufMessage()
1033 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1034 self
.assertEqual(len(msg
.response
.rrs
), 1)
1035 rr
= msg
.response
.rrs
[0]
1036 # we have max-cache-ttl set to 15
1037 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
1038 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.84')
1039 self
.checkProtobufIdentity(msg
, self
._requestorId
, self
._deviceId
.encode('ascii'), self
._deviceName
)
1040 self
.checkNoRemainingMessage()
1042 class ProtobufTaggedExtraFieldsFFITest(ProtobufTaggedExtraFieldsTest
):
1044 This test makes sure that we correctly export extra fields that may have been set while being tagged (FFI version).
1046 _confdir
= 'ProtobufTaggedExtraFieldsFFI'
1047 _config_template
= """
1048 auth-zones=example=configs/%s/example.zone""" % _confdir
1049 _lua_config_file
= """
1050 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
1051 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
1052 _lua_dns_script_file
= """
1053 local ffi = require("ffi")
1056 typedef struct pdns_ffi_param pdns_ffi_param_t;
1058 const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
1059 void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag);
1060 void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name);
1061 void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name);
1062 void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name);
1065 function gettag_ffi(obj)
1066 qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
1067 if qname == 'tagged.example' then
1068 ffi.C.pdns_ffi_param_set_requestorid(obj, "%s")
1070 ffi.C.pdns_ffi_param_set_deviceid(obj, string.len(deviceid), deviceid)
1071 ffi.C.pdns_ffi_param_set_devicename(obj, "%s")
1075 """ % (ProtobufTaggedExtraFieldsTest
._requestorId
, ProtobufTaggedExtraFieldsTest
._deviceId
, ProtobufTaggedExtraFieldsTest
._deviceName
)
1077 class ProtobufRPZTest(TestRecursorProtobuf
):
1079 This test makes sure that we correctly export the RPZ applied policy in our protobuf messages
1082 _confdir
= 'ProtobufRPZ'
1083 _config_template
= """
1084 auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
1085 _lua_config_file
= """
1086 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
1087 rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz."})
1088 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
, _confdir
)
1091 def generateRecursorConfig(cls
, confdir
):
1092 authzonepath
= os
.path
.join(confdir
, 'example.rpz.zone')
1093 with
open(authzonepath
, 'w') as authzone
:
1094 authzone
.write("""$ORIGIN example.
1096 sub.test 3600 IN A 192.0.2.42
1097 ip 3600 IN A 33.22.11.99
1098 """.format(soa
=cls
._SOA
))
1100 rpzFilePath
= os
.path
.join(confdir
, 'zone.rpz')
1101 with
open(rpzFilePath
, 'w') as rpzZone
:
1102 rpzZone
.write("""$ORIGIN zone.rpz.
1104 *.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
1105 24.0.11.22.33.rpz-ip 60 IN A 1.2.3.4
1106 """.format(soa
=cls
._SOA
))
1108 super(ProtobufRPZTest
, cls
).generateRecursorConfig(confdir
)
1111 name
= 'sub.test.example.'
1112 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
1113 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
1114 query
.flags |
= dns
.flags
.CD
1115 res
= self
.sendUDPQuery(query
)
1116 self
.assertRRsetInAnswer(res
, expected
)
1118 # check the protobuf messages corresponding to the UDP query and answer
1119 msg
= self
.getFirstProtobufMessage()
1120 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
1123 msg
= self
.getFirstProtobufMessage()
1124 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1125 self
.checkProtobufPolicy(msg
, dnsmessage_pb2
.PBDNSMessage
.PolicyType
.QNAME
, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2
.PBDNSMessage
.PolicyKind
.NoAction
)
1126 self
.assertEqual(len(msg
.response
.rrs
), 1)
1127 rr
= msg
.response
.rrs
[0]
1128 # we have max-cache-ttl set to 15
1129 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
1130 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
1131 self
.checkNoRemainingMessage()
1134 name
= 'ip.example.'
1135 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '1.2.3.4')
1136 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
1137 query
.flags |
= dns
.flags
.CD
1138 res
= self
.sendUDPQuery(query
)
1139 self
.assertRRsetInAnswer(res
, expected
)
1141 # check the protobuf messages corresponding to the UDP query and answer
1142 msg
= self
.getFirstProtobufMessage()
1143 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
1146 msg
= self
.getFirstProtobufMessage()
1147 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1148 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
)
1149 self
.assertEqual(len(msg
.response
.rrs
), 1)
1150 self
.checkNoRemainingMessage()
1152 class ProtobufRPZTagsTest(TestRecursorProtobuf
):
1154 This test makes sure that we correctly export the RPZ tags in our protobuf messages
1157 _confdir
= 'ProtobufRPZTags'
1158 _config_template
= """
1159 auth-zones=example=configs/%s/example.rpz.zone""" % _confdir
1160 _tags
= ['tag1', 'tag2']
1161 _tags_from_gettag
= ['tag1-from-gettag', 'tag2-from-gettag']
1162 _tags_from_rpz
= ['tag1-from-rpz', 'tag2-from-rpz' ]
1163 _lua_config_file
= """
1164 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true, tags={'tag1', 'tag2'} } )
1165 rpzFile('configs/%s/zone.rpz', { policyName="zone.rpz.", tags={ '%s', '%s'} })
1166 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
, _confdir
, _tags_from_rpz
[0], _tags_from_rpz
[1])
1167 _lua_dns_script_file
= """
1168 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
1169 return 0, { '%s', '%s' }
1171 function preresolve(dq)
1172 dq:addPolicyTag('%s')
1173 dq:addPolicyTag('%s')
1176 """ % (_tags_from_gettag
[0], _tags_from_gettag
[1], _tags
[0], _tags
[1])
1179 def generateRecursorConfig(cls
, confdir
):
1180 authzonepath
= os
.path
.join(confdir
, 'example.rpz.zone')
1181 with
open(authzonepath
, 'w') as authzone
:
1182 authzone
.write("""$ORIGIN example.
1184 sub.test 3600 IN A 192.0.2.42
1185 """.format(soa
=cls
._SOA
))
1187 rpzFilePath
= os
.path
.join(confdir
, 'zone.rpz')
1188 with
open(rpzFilePath
, 'w') as rpzZone
:
1189 rpzZone
.write("""$ORIGIN zone.rpz.
1191 *.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
1192 """.format(soa
=cls
._SOA
))
1194 super(ProtobufRPZTagsTest
, cls
).generateRecursorConfig(confdir
)
1197 name
= 'sub.test.example.'
1198 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.42')
1199 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
1200 query
.flags |
= dns
.flags
.CD
1201 res
= self
.sendUDPQuery(query
)
1202 self
.assertRRsetInAnswer(res
, expected
)
1204 # check the protobuf messages corresponding to the UDP query and answer
1205 msg
= self
.getFirstProtobufMessage()
1206 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
1209 msg
= self
.getFirstProtobufMessage()
1210 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1211 self
.checkProtobufPolicy(msg
, dnsmessage_pb2
.PBDNSMessage
.PolicyType
.QNAME
, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2
.PBDNSMessage
.PolicyKind
.NoAction
)
1212 self
.checkProtobufTags(msg
, self
._tags
+ self
._tags
_from
_gettag
+ self
._tags
_from
_rpz
)
1213 self
.assertEqual(len(msg
.response
.rrs
), 1)
1214 rr
= msg
.response
.rrs
[0]
1215 # we have max-cache-ttl set to 15
1216 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
1217 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
1218 self
.checkNoRemainingMessage()
1221 class ProtobufMetaFFITest(TestRecursorProtobuf
):
1223 This test makes sure that we can correctly add extra meta fields (FFI version).
1225 _confdir
= 'ProtobufMetaFFITest'
1226 _config_template
= """
1227 auth-zones=example=configs/%s/example.zone""" % _confdir
1228 _lua_config_file
= """
1229 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=true } )
1230 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
1231 _lua_dns_script_file
= """
1232 local ffi = require("ffi")
1235 typedef struct pdns_ffi_param pdns_ffi_param_t;
1237 const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref);
1238 void pdns_ffi_param_add_meta_single_string_kv(pdns_ffi_param_t *ref, const char* key, const char* val);
1239 void pdns_ffi_param_add_meta_single_int64_kv(pdns_ffi_param_t *ref, const char* key, int64_t val);
1242 function gettag_ffi(obj)
1243 qname = ffi.string(ffi.C.pdns_ffi_param_get_qname(obj))
1244 if qname == 'meta.example' then
1245 ffi.C.pdns_ffi_param_add_meta_single_string_kv(obj, "meta-str", "keyword")
1246 ffi.C.pdns_ffi_param_add_meta_single_int64_kv(obj, "meta-int", 42)
1247 ffi.C.pdns_ffi_param_add_meta_single_string_kv(obj, "meta-str", "content")
1248 ffi.C.pdns_ffi_param_add_meta_single_int64_kv(obj, "meta-int", 21)
1254 name
= 'meta.example.'
1255 expected
= dns
.rrset
.from_text(name
, 0, dns
.rdataclass
.IN
, 'A', '192.0.2.85')
1256 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
1257 query
.flags |
= dns
.flags
.CD
1258 res
= self
.sendUDPQuery(query
)
1259 self
.assertRRsetInAnswer(res
, expected
)
1261 # check the protobuf messages corresponding to the UDP query and answer
1262 msg
= self
.getFirstProtobufMessage()
1263 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
1264 self
.checkProtobufMetas(msg
, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}})
1267 msg
= self
.getFirstProtobufMessage()
1268 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1269 self
.assertEqual(len(msg
.response
.rrs
), 1)
1270 rr
= msg
.response
.rrs
[0]
1271 # we have max-cache-ttl set to 15
1272 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15)
1273 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.85')
1274 self
.checkProtobufMetas(msg
, {'meta-str': { "stringVal" : ["content", "keyword"]}, 'meta-int': {"intVal" : [21, 42]}})
1276 self
.checkNoRemainingMessage()