9 import clientsubnetoption
11 # Python2/3 compatibility hacks
13 from queue
import Queue
15 from Queue
import Queue
22 from recursortests
import RecursorTest
24 def ProtobufConnectionHandler(queue
, conn
):
30 (datalen
,) = struct
.unpack("!H", data
)
31 data
= conn
.recv(datalen
)
35 queue
.put(data
, True, timeout
=2.0)
39 def ProtobufListener(queue
, port
):
40 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
41 sock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEPORT
, 1)
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
))
51 (conn
, _
) = sock
.accept()
52 thread
= threading
.Thread(name
='Connection Handler',
53 target
=ProtobufConnectionHandler
,
55 thread
.setDaemon(True)
58 except socket
.error
as e
:
59 print('Error in protobuf socket: %s' % str(e
))
64 class ProtobufServerParams
:
65 def __init__(self
, port
):
69 protobufServersParameters
= [ProtobufServerParams(4243), ProtobufServerParams(4244)]
70 protobufListeners
= []
71 for param
in protobufServersParameters
:
72 listener
= threading
.Thread(name
='Protobuf Listener', target
=ProtobufListener
, args
=[param
.queue
, param
.port
])
73 listener
.setDaemon(True)
75 protobufListeners
.append(listener
)
77 class TestRecursorProtobuf(RecursorTest
):
79 _lua_config_file
= """
80 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
81 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
87 'zones': ['secure.example', 'islandofsecurity.example']},
89 'zones': ['example']},
94 def getFirstProtobufMessage(self
, retries
=1, waitTime
=1):
97 #print("in getFirstProtobufMessage")
98 for param
in protobufServersParameters
:
102 while param
.queue
.empty():
105 if failed
>= retries
:
112 self
.assertFalse(param
.queue
.empty())
113 data
= param
.queue
.get(False)
114 self
.assertTrue(data
)
116 msg
= dnsmessage_pb2
.PBDNSMessage()
117 msg
.ParseFromString(data
)
118 if oldmsg
is not None:
119 self
.assertEqual(msg
, oldmsg
)
124 def emptyProtoBufQueue(self
):
125 for param
in protobufServersParameters
:
126 while not param
.queue
.empty():
127 param
.queue
.get(False)
129 def checkNoRemainingMessage(self
):
130 for param
in protobufServersParameters
:
131 self
.assertTrue(param
.queue
.empty())
133 def checkProtobufBase(self
, msg
, protocol
, query
, initiator
, normalQueryResponse
=True, expectedECS
=None, receivedSize
=None):
135 self
.assertTrue(msg
.HasField('timeSec'))
136 self
.assertTrue(msg
.HasField('socketFamily'))
137 self
.assertEqual(msg
.socketFamily
, dnsmessage_pb2
.PBDNSMessage
.INET
)
138 self
.assertTrue(msg
.HasField('from'))
139 fromvalue
= getattr(msg
, 'from')
140 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, fromvalue
), initiator
)
141 self
.assertTrue(msg
.HasField('socketProtocol'))
142 self
.assertEqual(msg
.socketProtocol
, protocol
)
143 self
.assertTrue(msg
.HasField('messageId'))
144 self
.assertTrue(msg
.HasField('serverIdentity'))
145 self
.assertTrue(msg
.HasField('id'))
146 self
.assertEqual(msg
.id, query
.id)
147 self
.assertTrue(msg
.HasField('inBytes'))
148 if normalQueryResponse
:
149 # compare inBytes with length of query/response
150 # Note that for responses, the size we received might differ
151 # because dnspython might compress labels differently from
154 self
.assertEqual(msg
.inBytes
, receivedSize
)
156 self
.assertEqual(msg
.inBytes
, len(query
.to_wire()))
157 if expectedECS
is not None:
158 self
.assertTrue(msg
.HasField('originalRequestorSubnet'))
160 self
.assertEqual(len(msg
.originalRequestorSubnet
), 4)
161 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, msg
.originalRequestorSubnet
), '127.0.0.1')
163 def checkOutgoingProtobufBase(self
, msg
, protocol
, query
, initiator
, length
=None, expectedECS
=None):
165 self
.assertTrue(msg
.HasField('timeSec'))
166 self
.assertTrue(msg
.HasField('socketFamily'))
167 self
.assertEqual(msg
.socketFamily
, dnsmessage_pb2
.PBDNSMessage
.INET
)
168 self
.assertTrue(msg
.HasField('socketProtocol'))
169 self
.assertEqual(msg
.socketProtocol
, protocol
)
170 self
.assertTrue(msg
.HasField('messageId'))
171 self
.assertTrue(msg
.HasField('serverIdentity'))
172 self
.assertTrue(msg
.HasField('id'))
173 self
.assertNotEqual(msg
.id, query
.id)
174 self
.assertTrue(msg
.HasField('inBytes'))
175 if length
is not None:
176 self
.assertEqual(msg
.inBytes
, length
)
178 # compare inBytes with length of query/response
179 self
.assertEqual(msg
.inBytes
, len(query
.to_wire()))
180 if expectedECS
is not None:
181 self
.assertTrue(msg
.HasField('originalRequestorSubnet'))
183 self
.assertEqual(len(msg
.originalRequestorSubnet
), 4)
184 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, msg
.originalRequestorSubnet
), expectedECS
)
186 def checkProtobufQuery(self
, msg
, protocol
, query
, qclass
, qtype
, qname
, initiator
='127.0.0.1', to
='127.0.0.1'):
187 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSQueryType
)
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'))
192 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, msg
.to
), to
)
193 self
.assertTrue(msg
.HasField('question'))
194 self
.assertTrue(msg
.question
.HasField('qClass'))
195 self
.assertEqual(msg
.question
.qClass
, qclass
)
196 self
.assertTrue(msg
.question
.HasField('qType'))
197 self
.assertEqual(msg
.question
.qClass
, qtype
)
198 self
.assertTrue(msg
.question
.HasField('qName'))
199 self
.assertEqual(msg
.question
.qName
, qname
)
201 def checkProtobufResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1', receivedSize
=None, vstate
=dnsmessage_pb2
.PBDNSMessage
.VState
.Indeterminate
):
202 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
203 self
.checkProtobufBase(msg
, protocol
, response
, initiator
, receivedSize
=receivedSize
)
204 self
.assertTrue(msg
.HasField('response'))
205 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
206 self
.assertTrue(msg
.response
.HasField('validationState'))
207 self
.assertEqual(msg
.response
.validationState
, vstate
)
209 def checkProtobufResponseRecord(self
, record
, rclass
, rtype
, rname
, rttl
, checkTTL
=True):
210 self
.assertTrue(record
.HasField('class'))
211 self
.assertEqual(getattr(record
, 'class'), rclass
)
212 self
.assertTrue(record
.HasField('type'))
213 self
.assertEqual(record
.type, rtype
)
214 self
.assertTrue(record
.HasField('name'))
215 self
.assertEqual(record
.name
, rname
)
216 self
.assertTrue(record
.HasField('ttl'))
218 self
.assertEqual(record
.ttl
, rttl
)
219 self
.assertTrue(record
.HasField('rdata'))
221 def checkProtobufPolicy(self
, msg
, policyType
, reason
, trigger
, hit
, kind
):
222 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
223 self
.assertTrue(msg
.response
.HasField('appliedPolicyType'))
224 self
.assertTrue(msg
.response
.HasField('appliedPolicy'))
225 self
.assertTrue(msg
.response
.HasField('appliedPolicyTrigger'))
226 self
.assertTrue(msg
.response
.HasField('appliedPolicyHit'))
227 self
.assertTrue(msg
.response
.HasField('appliedPolicyKind'))
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
)
234 def checkProtobufTags(self
, msg
, tags
):
237 #print(msg.response.tags)
238 self
.assertEqual(len(msg
.response
.tags
), len(tags
))
239 for tag
in msg
.response
.tags
:
240 self
.assertTrue(tag
in tags
)
242 def checkProtobufMetas(self
, msg
, metas
):
246 self
.assertEqual(len(msg
.meta
), len(metas
))
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'])
256 def checkProtobufOutgoingQuery(self
, msg
, protocol
, query
, qclass
, qtype
, qname
, initiator
='127.0.0.1', length
=None, expectedECS
=None):
257 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSOutgoingQueryType
)
258 self
.checkOutgoingProtobufBase(msg
, protocol
, query
, initiator
, length
=length
, expectedECS
=expectedECS
)
259 self
.assertTrue(msg
.HasField('to'))
260 self
.assertTrue(msg
.HasField('question'))
261 self
.assertTrue(msg
.question
.HasField('qClass'))
262 self
.assertEqual(msg
.question
.qClass
, qclass
)
263 self
.assertTrue(msg
.question
.HasField('qType'))
264 self
.assertEqual(msg
.question
.qType
, qtype
)
265 self
.assertTrue(msg
.question
.HasField('qName'))
266 self
.assertEqual(msg
.question
.qName
, qname
)
268 def checkProtobufIncomingResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1', length
=None):
269 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSIncomingResponseType
)
270 self
.checkOutgoingProtobufBase(msg
, protocol
, response
, initiator
, length
=length
)
271 self
.assertTrue(msg
.HasField('response'))
272 self
.assertTrue(msg
.response
.HasField('rcode'))
273 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
275 def checkProtobufIncomingNetworkErrorResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1'):
276 self
.checkProtobufIncomingResponse(msg
, protocol
, response
, initiator
, length
=0)
277 self
.assertEqual(msg
.response
.rcode
, 65536)
279 def checkProtobufIdentity(self
, msg
, requestorId
, deviceId
, deviceName
):
281 self
.assertTrue((requestorId
== '') == (not msg
.HasField('requestorId')))
282 self
.assertTrue((deviceId
== b
'') == (not msg
.HasField('deviceId')))
283 self
.assertTrue((deviceName
== '') == (not msg
.HasField('deviceName')))
284 self
.assertEqual(msg
.requestorId
, requestorId
)
285 self
.assertEqual(msg
.deviceId
, deviceId
)
286 self
.assertEqual(msg
.deviceName
, deviceName
)
289 super(TestRecursorProtobuf
, self
).setUp()
290 # Make sure the queue is empty, in case
291 # a previous test failed
292 self
.emptyProtoBufQueue()
293 # wait long enough to be sure that the housekeeping has
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.
303 a 3600 IN A 192.0.2.42
304 tagged 3600 IN A 192.0.2.84
305 meta 3600 IN A 192.0.2.85
306 query-selected 3600 IN A 192.0.2.84
307 answer-selected 3600 IN A 192.0.2.84
308 types 3600 IN A 192.0.2.84
309 types 3600 IN AAAA 2001:DB8::1
310 types 3600 IN TXT "Lorem ipsum dolor sit amet"
311 types 3600 IN MX 10 a.example.
312 types 3600 IN SPF "v=spf1 -all"
313 types 3600 IN SRV 10 20 443 a.example.
314 cname 3600 IN CNAME a.example.
316 """.format(soa
=cls
._SOA
))
317 super(TestRecursorProtobuf
, cls
).generateRecursorConfig(confdir
)
320 class ProtobufDefaultTest(TestRecursorProtobuf
):
322 This test makes sure that we correctly export queries and response over protobuf.
325 _confdir
= 'ProtobufDefault'
326 _config_template
= """
327 auth-zones=example=configs/%s/example.zone""" % _confdir
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
)
336 self
.assertRRsetInAnswer(res
, expected
)
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
)
342 msg
= self
.getFirstProtobufMessage()
343 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1')
344 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
348 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
349 self
.checkNoRemainingMessage()
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
)
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
)
367 msg
= self
.getFirstProtobufMessage()
368 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1', receivedSize
=len(raw
))
369 self
.assertEqual(len(msg
.response
.rrs
), 2)
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)
373 self
.assertEqual(rr
.rdata
, b
'a.example.')
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)
377 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
378 self
.checkNoRemainingMessage()
380 class ProtobufProxyMappingTest(TestRecursorProtobuf
):
382 This test makes sure that we correctly export queries and response over protobuf with a proxyMapping
385 _confdir
= 'ProtobufProxyMappingTest'
386 _config_template
= """
387 auth-zones=example=configs/%s/example.zone
388 allow-from=3.4.5.0/24
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
)
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
)
403 self
.assertRRsetInAnswer(res
, expected
)
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
)
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()
418 class ProtobufProxyMappingLogMappedTest(TestRecursorProtobuf
):
420 This test makes sure that we correctly export queries and response over protobuf.
423 _confdir
= 'ProtobufProxyMappingLogMappedTest'
424 _config_template
= """
425 auth-zones=example=configs/%s/example.zone
426 allow-from=3.4.5.0/0"
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
)
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
)
441 self
.assertRRsetInAnswer(res
, expected
)
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')
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()
456 class ProtobufProxyTest(TestRecursorProtobuf
):
458 This test makes sure that we correctly export addresses over protobuf when the proxy protocol is used.
461 _confdir
= 'ProtobufProxy'
462 _config_template
= """
463 auth-zones=example=configs/%s/example.zone
464 proxy-protocol-from=127.0.0.1/32
465 allow-from=127.0.0.1,6.6.6.6
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)
475 self
.assertRRsetInAnswer(res
, expected
)
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')
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()
490 class ProtobufProxyWithProxyByTableTest(TestRecursorProtobuf
):
492 This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
495 _confdir
= 'ProtobufProxyWithProxyByTable'
496 _config_template
= """
497 auth-zones=example=configs/%s/example.zone
498 proxy-protocol-from=127.0.0.1/32
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
)
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)
514 self
.assertRRsetInAnswer(res
, expected
)
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')
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()
529 class ProtobufProxyWithProxyByTableLogMappedTest(TestRecursorProtobuf
):
531 This test makes sure that we correctly export addresses over protobuf when the proxy protocol and a proxy table mapping is used
534 _confdir
= 'ProtobufProxyWithProxyByTableLogMapped'
535 _config_template
= """
536 auth-zones=example=configs/%s/example.zone
537 proxy-protocol-from=127.0.0.1/32
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
)
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)
553 self
.assertRRsetInAnswer(res
, expected
)
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')
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()
569 class OutgoingProtobufDefaultTest(TestRecursorProtobuf
):
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.
576 _confdir
= 'OutgoingProtobufDefault'
577 _config_template
= """
578 # Switch off QName Minimization, it generates much more protobuf messages
579 # (or make the test much more smart!)
580 qname-minimization=no
584 _lua_config_file
= """
585 outgoingProtobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
586 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
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
)
595 self
.emptyProtoBufQueue()
597 name
= 'host1.secure.example.'
600 for qname
, qtype
, proto
, responseSize
in [
601 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 248),
602 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 221),
603 ('example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 219),
604 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 175),
605 ('secure.example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 233),
608 expected
.append((None, None, None, None, None, None))
610 query
= dns
.message
.make_query(qname
, qtype
, use_edns
=True, want_dnssec
=True)
611 resp
= dns
.message
.make_response(query
)
613 qname
, qtype
, query
, resp
, proto
, responseSize
616 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
617 query
.flags |
= dns
.flags
.RD
618 res
= self
.sendUDPQuery(query
)
620 for qname
, qtype
, qry
, ans
, proto
, responseSize
in expected
:
622 self
.getFirstProtobufMessage()
623 self
.getFirstProtobufMessage()
626 msg
= self
.getFirstProtobufMessage()
627 self
.checkProtobufOutgoingQuery(msg
, proto
, qry
, dns
.rdataclass
.IN
, qtype
, qname
)
630 msg
= self
.getFirstProtobufMessage()
631 self
.checkProtobufIncomingResponse(msg
, proto
, ans
, length
=responseSize
)
633 self
.checkNoRemainingMessage()
635 class OutgoingProtobufWithECSMappingTest(TestRecursorProtobuf
):
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.
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
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
)
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
)
665 self
.emptyProtoBufQueue()
667 name
= 'host1.secure.example.'
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"),
678 expected
.append((None, None, None, None, None, None, None))
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
)
684 qname
, qtype
, query
, resp
, proto
, responseSize
, ecs
687 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
688 query
.flags |
= dns
.flags
.RD
689 res
= self
.sendUDPQuery(query
)
691 for qname
, qtype
, qry
, ans
, proto
, responseSize
, ecs
in expected
:
693 self
.getFirstProtobufMessage()
694 self
.getFirstProtobufMessage()
697 msg
= self
.getFirstProtobufMessage()
698 self
.checkProtobufOutgoingQuery(msg
, proto
, qry
, dns
.rdataclass
.IN
, qtype
, qname
, "127.0.0.1", None, ecs
)
700 msg
= self
.getFirstProtobufMessage()
701 self
.checkProtobufIncomingResponse(msg
, proto
, ans
, length
=responseSize
)
703 self
.checkNoRemainingMessage()
705 # this query should use the unmapped ECS
706 name
= 'mx1.secure.example.'
709 for qname
, qtype
, proto
, responseSize
, ecs
in [
710 ('mx1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 173, "127.0.0.1"),
713 expected
.append((None, None, None, None, None, None, None))
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
)
719 qname
, qtype
, query
, resp
, proto
, responseSize
, ecs
722 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
723 query
.flags |
= dns
.flags
.RD
724 res
= self
.sendUDPQuery(query
)
726 for qname
, qtype
, qry
, ans
, proto
, responseSize
, ecs
in expected
:
728 self
.getFirstProtobufMessage()
729 self
.getFirstProtobufMessage()
732 msg
= self
.getFirstProtobufMessage()
733 self
.checkProtobufOutgoingQuery(msg
, proto
, qry
, dns
.rdataclass
.IN
, qtype
, qname
, "127.0.0.1", None, ecs
)
735 msg
= self
.getFirstProtobufMessage()
736 self
.checkProtobufIncomingResponse(msg
, proto
, ans
, length
=responseSize
)
738 self
.checkNoRemainingMessage()
740 class OutgoingProtobufNoQueriesTest(TestRecursorProtobuf
):
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.
747 _confdir
= 'OutgoingProtobufNoQueries'
748 _config_template
= """
749 # Switch off QName Minimization, it generates much more protobuf messages
750 # (or make the test much more smart!)
751 qname-minimization=no
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
)
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
)
766 self
.emptyProtoBufQueue()
768 name
= 'host1.secure.example.'
770 # the root DNSKEY has been learned with priming the root NS already
771 # ('.', dns.rdatatype.DNSKEY, dnsmessage_pb2.PBDNSMessage.UDP, 201),
772 for qname
, qtype
, proto
, size
in [
773 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 248),
774 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 221),
775 ('example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 219),
776 ('host1.secure.example.', dns
.rdatatype
.A
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 175),
777 ('secure.example.', dns
.rdatatype
.DNSKEY
, dnsmessage_pb2
.PBDNSMessage
.UDP
, 233),
780 expected
.append((None, None, None, None, None, None))
782 query
= dns
.message
.make_query(qname
, qtype
, use_edns
=True, want_dnssec
=True)
783 resp
= dns
.message
.make_response(query
)
785 qname
, qtype
, query
, resp
, proto
, size
788 query
= dns
.message
.make_query(name
, 'A', want_dnssec
=True)
789 query
.flags |
= dns
.flags
.RD
790 res
= self
.sendUDPQuery(query
)
792 for qname
, qtype
, qry
, ans
, proto
, size
in expected
:
794 self
.getFirstProtobufMessage()
798 msg
= self
.getFirstProtobufMessage()
799 self
.checkProtobufIncomingResponse(msg
, proto
, ans
, length
=size
)
801 self
.checkNoRemainingMessage()
803 class ProtobufMasksTest(TestRecursorProtobuf
):
805 This test makes sure that we correctly export queries and response over protobuf, respecting the configured initiator masking.
808 _confdir
= 'ProtobufMasks'
809 _config_template
= """
810 auth-zones=example=configs/%s/example.zone""" % _confdir
812 _protobufMaskV6
= 128
813 _lua_config_file
= """
814 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"})
815 setProtobufMasks(%d, %d)
816 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
, _protobufMaskV4
, _protobufMaskV6
)
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
)
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')
831 msg
= self
.getFirstProtobufMessage()
832 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '112.0.0.0')
833 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
837 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
838 self
.checkNoRemainingMessage()
840 class ProtobufQueriesOnlyTest(TestRecursorProtobuf
):
842 This test makes sure that we correctly export queries but not responses over protobuf.
845 _confdir
= 'ProtobufQueriesOnly'
846 _config_template
= """
847 auth-zones=example=configs/%s/example.zone""" % _confdir
848 _lua_config_file
= """
849 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=true, logResponses=false } )
850 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
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
)
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
)
864 self
.checkNoRemainingMessage()
866 class ProtobufResponsesOnlyTest(TestRecursorProtobuf
):
868 This test makes sure that we correctly export responses but not queries over protobuf.
871 _confdir
= 'ProtobufResponsesOnly'
872 _config_template
= """
873 auth-zones=example=configs/%s/example.zone""" % _confdir
874 _lua_config_file
= """
875 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=true } )
876 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
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
)
886 # check the protobuf message corresponding to the UDP response
887 msg
= self
.getFirstProtobufMessage()
888 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
889 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
893 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
894 # nothing else in the queue
895 self
.checkNoRemainingMessage()
897 class ProtobufTaggedOnlyTest(TestRecursorProtobuf
):
899 This test makes sure that we correctly export queries and responses but only if they have been tagged.
902 _confdir
= 'ProtobufTaggedOnly'
903 _config_template
= """
904 auth-zones=example=configs/%s/example.zone""" % _confdir
905 _lua_config_file
= """
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
)
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
917 function preresolve(dq)
918 if dq.qname:equal('tagged.example.') then
919 dq:addPolicyTag('%s')
920 dq:addPolicyTag('%s')
924 """ % (_tag_from_gettag
, _tags
[0], _tags
[1])
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
)
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
937 self
.checkNoRemainingMessage()
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
)
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
])
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
961 self
.checkProtobufTags(msg
, tags
)
962 self
.checkNoRemainingMessage()
964 # Again to check PC case
965 res
= self
.sendUDPQuery(query
)
966 self
.assertRRsetInAnswer(res
, expected
)
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
)
971 self
.checkProtobufTags(msg
, [ self
._tag
_from
_gettag
])
973 msg
= self
.getFirstProtobufMessage()
974 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
975 self
.assertEqual(len(msg
.response
.rrs
), 1)
976 rr
= msg
.response
.rrs
[0]
977 # time may have passed, so do not check TTL
978 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15, checkTTL
=False)
979 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.84')
980 tags
= [ self
._tag
_from
_gettag
] + self
._tags
981 self
.checkProtobufTags(msg
, tags
)
982 self
.checkNoRemainingMessage()
984 class ProtobufTagCacheTest(TestRecursorProtobuf
):
986 This test makes sure that we correctly cache tags (actually not cache them)
989 _confdir
= 'ProtobufTagCache'
990 _config_template
= """
991 auth-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
= """
996 function gettag(remote, ednssubnet, localip, qname, qtype, ednsoptions, tcp)
997 if qname:equal('tagged.example.') then
998 return 0, { '' .. math.random() }
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
)
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]
1023 # Again to check PC case
1024 res
= self
.sendUDPQuery(query
)
1025 self
.assertRRsetInAnswer(res
, expected
)
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]
1031 # time may have passed, so do not check TTL
1032 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 15, checkTTL
=False)
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]
1037 self
.assertNotEqual(ts1
, ts2
)
1039 class ProtobufSelectedFromLuaTest(TestRecursorProtobuf
):
1041 This test makes sure that we correctly export queries and responses but only if they have been selected from Lua.
1044 _confdir
= 'ProtobufSelectedFromLua'
1045 _config_template
= """
1046 auth-zones=example=configs/%s/example.zone""" % _confdir
1047 _lua_config_file
= """
1048 protobufServer({"127.0.0.1:%d", "127.0.0.1:%d"}, { logQueries=false, logResponses=false } )
1049 """ % (protobufServersParameters
[0].port
, protobufServersParameters
[1].port
)
1050 _lua_dns_script_file
= """
1051 local ffi = require("ffi")
1054 typedef struct pdns_ffi_param pdns_ffi_param_t;
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);
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)
1068 function preresolve(dq)
1069 if dq.qname:equal('answer-selected.example.') then
1070 dq.logResponse = true
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
)
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()
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
)
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()
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
)
1110 # check the protobuf messages corresponding to the UDP response
1111 msg
= self
.getFirstProtobufMessage()
1112 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1113 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
1117 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.84')
1118 self
.checkNoRemainingMessage()
1120 class ProtobufExportTypesTest(TestRecursorProtobuf
):
1122 This test makes sure that we correctly export other types than A, AAAA and CNAME over protobuf.
1125 _confdir
= 'ProtobufExportTypes'
1126 _config_template
= """
1127 auth-zones=example=configs/%s/example.zone""" % _confdir
1128 _lua_config_file
= """
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
)
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"'),
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
)
1146 for rrset
in expected
:
1147 self
.assertRRsetInAnswer(res
, rrset
)
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
)
1153 msg
= self
.getFirstProtobufMessage()
1154 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1', receivedSize
=len(raw
))
1155 self
.assertEqual(len(msg
.response
.rrs
), 5)
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
])
1159 if rr
.type == dns
.rdatatype
.AAAA
:
1160 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.AAAA
, name
, 15)
1161 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET6
, rr
.rdata
), '2001:db8::1')
1162 elif rr
.type == dns
.rdatatype
.TXT
:
1163 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.TXT
, name
, 15)
1164 self
.assertEqual(rr
.rdata
, b
'"Lorem ipsum dolor sit amet"')
1165 elif rr
.type == dns
.rdatatype
.MX
:
1166 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.MX
, name
, 15)
1167 self
.assertEqual(rr
.rdata
, b
'a.example.')
1168 elif rr
.type == dns
.rdatatype
.SPF
:
1169 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.SPF
, name
, 15)
1170 self
.assertEqual(rr
.rdata
, b
'"v=spf1 -all"')
1171 elif rr
.type == dns
.rdatatype
.SRV
:
1172 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.SRV
, name
, 15)
1173 self
.assertEqual(rr
.rdata
, b
'a.example.')
1175 self
.checkNoRemainingMessage()
1177 class ProtobufTaggedExtraFieldsTest(TestRecursorProtobuf
):
1179 This test makes sure that we correctly export extra fields that may have been set while being tagged.
1182 _confdir
= 'ProtobufTaggedExtraFields'
1183 _config_template
= """
1184 auth-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'
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'
1199 """ % (_requestorId
, _deviceId
, _deviceName
)
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
)
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
)
1214 self
.checkProtobufIdentity(msg
, '', b
'', '')
1217 msg
= self
.getFirstProtobufMessage()
1218 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
, '127.0.0.1')
1219 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
1223 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
1224 self
.checkProtobufIdentity(msg
, '', b
'', '')
1225 self
.checkNoRemainingMessage()
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
)
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
)
1238 self
.checkProtobufIdentity(msg
, self
._requestorId
, self
._deviceId
.encode('ascii'), self
._deviceName
)
1241 msg
= self
.getFirstProtobufMessage()
1242 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1243 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
1247 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.84')
1248 self
.checkProtobufIdentity(msg
, self
._requestorId
, self
._deviceId
.encode('ascii'), self
._deviceName
)
1249 self
.checkNoRemainingMessage()
1251 class ProtobufTaggedExtraFieldsFFITest(ProtobufTaggedExtraFieldsTest
):
1253 This test makes sure that we correctly export extra fields that may have been set while being tagged (FFI version).
1255 _confdir
= 'ProtobufTaggedExtraFieldsFFI'
1256 _config_template
= """
1257 auth-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")
1265 typedef struct pdns_ffi_param pdns_ffi_param_t;
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);
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")
1279 ffi.C.pdns_ffi_param_set_deviceid(obj, string.len(deviceid), deviceid)
1280 ffi.C.pdns_ffi_param_set_devicename(obj, "%s")
1284 """ % (ProtobufTaggedExtraFieldsTest
._requestorId
, ProtobufTaggedExtraFieldsTest
._deviceId
, ProtobufTaggedExtraFieldsTest
._deviceName
)
1286 class ProtobufRPZTest(TestRecursorProtobuf
):
1288 This test makes sure that we correctly export the RPZ applied policy in our protobuf messages
1291 _confdir
= 'ProtobufRPZ'
1292 _config_template
= """
1293 auth-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
)
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.
1305 sub.test 3600 IN A 192.0.2.42
1306 ip 3600 IN A 33.22.11.99
1307 """.format(soa
=cls
._SOA
))
1309 rpzFilePath
= os
.path
.join(confdir
, 'zone.rpz')
1310 with
open(rpzFilePath
, 'w') as rpzZone
:
1311 rpzZone
.write("""$ORIGIN zone.rpz.
1313 *.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
1314 24.0.11.22.33.rpz-ip 60 IN A 1.2.3.4
1315 """.format(soa
=cls
._SOA
))
1317 super(ProtobufRPZTest
, cls
).generateRecursorConfig(confdir
)
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
)
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
)
1332 msg
= self
.getFirstProtobufMessage()
1333 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1334 self
.checkProtobufPolicy(msg
, dnsmessage_pb2
.PBDNSMessage
.PolicyType
.QNAME
, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2
.PBDNSMessage
.PolicyKind
.NoAction
)
1335 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
1339 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
1340 self
.checkNoRemainingMessage()
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
)
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
)
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()
1361 class ProtobufRPZTagsTest(TestRecursorProtobuf
):
1363 This test makes sure that we correctly export the RPZ tags in our protobuf messages
1366 _confdir
= 'ProtobufRPZTags'
1367 _config_template
= """
1368 auth-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' }
1380 function preresolve(dq)
1381 dq:addPolicyTag('%s')
1382 dq:addPolicyTag('%s')
1385 """ % (_tags_from_gettag
[0], _tags_from_gettag
[1], _tags
[0], _tags
[1])
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.
1393 sub.test 3600 IN A 192.0.2.42
1394 """.format(soa
=cls
._SOA
))
1396 rpzFilePath
= os
.path
.join(confdir
, 'zone.rpz')
1397 with
open(rpzFilePath
, 'w') as rpzZone
:
1398 rpzZone
.write("""$ORIGIN zone.rpz.
1400 *.test.example.zone.rpz. 60 IN CNAME rpz-passthru.
1401 """.format(soa
=cls
._SOA
))
1403 super(ProtobufRPZTagsTest
, cls
).generateRecursorConfig(confdir
)
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
)
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
)
1418 msg
= self
.getFirstProtobufMessage()
1419 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, res
)
1420 self
.checkProtobufPolicy(msg
, dnsmessage_pb2
.PBDNSMessage
.PolicyType
.QNAME
, 'zone.rpz.', '*.test.example.', 'sub.test.example', dnsmessage_pb2
.PBDNSMessage
.PolicyKind
.NoAction
)
1421 self
.checkProtobufTags(msg
, self
._tags
+ self
._tags
_from
_gettag
+ self
._tags
_from
_rpz
)
1422 self
.assertEqual(len(msg
.response
.rrs
), 1)
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)
1426 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '192.0.2.42')
1427 self
.checkNoRemainingMessage()
1430 class ProtobufMetaFFITest(TestRecursorProtobuf
):
1432 This test makes sure that we can correctly add extra meta fields (FFI version).
1434 _confdir
= 'ProtobufMetaFFITest'
1435 _config_template
= """
1436 auth-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")
1444 typedef struct pdns_ffi_param pdns_ffi_param_t;
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);
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)
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
)
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]}})
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]}})
1485 self
.checkNoRemainingMessage()