]>
git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.dnsdist/test_Protobuf.py
7 from dnsdisttests
import DNSDistTest
, Queue
12 class DNSDistProtobufTest(DNSDistTest
):
13 _protobufServerPort
= 4242
14 _protobufQueue
= Queue()
15 _protobufServerID
= 'dnsdist-server-1'
19 def ProtobufListener(cls
, port
):
20 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
21 sock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEPORT
, 1)
23 sock
.bind(("127.0.0.1", port
))
24 except socket
.error
as e
:
25 print("Error binding in the protbuf listener: %s" % str(e
))
30 (conn
, _
) = sock
.accept()
36 (datalen
,) = struct
.unpack("!H", data
)
37 data
= conn
.recv(datalen
)
41 cls
._protobufQueue
.put(data
, True, timeout
=2.0)
47 def startResponders(cls
):
48 cls
._UDPResponder
= threading
.Thread(name
='UDP Responder', target
=cls
.UDPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
49 cls
._UDPResponder
.setDaemon(True)
50 cls
._UDPResponder
.start()
52 cls
._TCPResponder
= threading
.Thread(name
='TCP Responder', target
=cls
.TCPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
53 cls
._TCPResponder
.setDaemon(True)
54 cls
._TCPResponder
.start()
56 cls
._protobufListener
= threading
.Thread(name
='Protobuf Listener', target
=cls
.ProtobufListener
, args
=[cls
._protobufServerPort
])
57 cls
._protobufListener
.setDaemon(True)
58 cls
._protobufListener
.start()
60 def getFirstProtobufMessage(self
):
61 self
.assertFalse(self
._protobufQueue
.empty())
62 data
= self
._protobufQueue
.get(False)
64 msg
= dnsmessage_pb2
.PBDNSMessage()
65 msg
.ParseFromString(data
)
68 def checkProtobufBase(self
, msg
, protocol
, query
, initiator
, normalQueryResponse
=True):
70 self
.assertTrue(msg
.HasField('timeSec'))
71 self
.assertTrue(msg
.HasField('socketFamily'))
72 self
.assertEquals(msg
.socketFamily
, dnsmessage_pb2
.PBDNSMessage
.INET
)
73 self
.assertTrue(msg
.HasField('from'))
74 fromvalue
= getattr(msg
, 'from')
75 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, fromvalue
), initiator
)
76 self
.assertTrue(msg
.HasField('socketProtocol'))
77 self
.assertEquals(msg
.socketProtocol
, protocol
)
78 self
.assertTrue(msg
.HasField('messageId'))
79 self
.assertTrue(msg
.HasField('id'))
80 self
.assertEquals(msg
.id, query
.id)
81 self
.assertTrue(msg
.HasField('inBytes'))
82 self
.assertTrue(msg
.HasField('serverIdentity'))
83 self
.assertEquals(msg
.serverIdentity
, self
._protobufServerID
.encode('utf-8'))
85 if normalQueryResponse
:
86 # compare inBytes with length of query/response
87 self
.assertEquals(msg
.inBytes
, len(query
.to_wire()))
88 # dnsdist doesn't set the existing EDNS Subnet for now,
89 # although it might be set from Lua
90 # self.assertTrue(msg.HasField('originalRequestorSubnet'))
91 # self.assertEquals(len(msg.originalRequestorSubnet), 4)
92 # self.assertEquals(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), '127.0.0.1')
94 def checkProtobufQuery(self
, msg
, protocol
, query
, qclass
, qtype
, qname
, initiator
='127.0.0.1'):
95 self
.assertEquals(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSQueryType
)
96 self
.checkProtobufBase(msg
, protocol
, query
, initiator
)
97 # dnsdist doesn't fill the responder field for responses
98 # because it doesn't keep the information around.
99 self
.assertTrue(msg
.HasField('to'))
100 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, msg
.to
), '127.0.0.1')
101 self
.assertTrue(msg
.HasField('question'))
102 self
.assertTrue(msg
.question
.HasField('qClass'))
103 self
.assertEquals(msg
.question
.qClass
, qclass
)
104 self
.assertTrue(msg
.question
.HasField('qType'))
105 self
.assertEquals(msg
.question
.qClass
, qtype
)
106 self
.assertTrue(msg
.question
.HasField('qName'))
107 self
.assertEquals(msg
.question
.qName
, qname
)
109 def checkProtobufTags(self
, tags
, expectedTags
):
110 # only differences will be in new list
111 listx
= set(tags
) ^
set(expectedTags
)
112 # exclusive or of lists should be empty
113 self
.assertEqual(len(listx
), 0, "Protobuf tags don't match")
115 def checkProtobufQueryConvertedToResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.0'):
116 self
.assertEquals(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
117 # skip comparing inBytes (size of the query) with the length of the generated response
118 self
.checkProtobufBase(msg
, protocol
, response
, initiator
, False)
119 self
.assertTrue(msg
.HasField('response'))
120 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
122 def checkProtobufResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1'):
123 self
.assertEquals(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
124 self
.checkProtobufBase(msg
, protocol
, response
, initiator
)
125 self
.assertTrue(msg
.HasField('response'))
126 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
128 def checkProtobufResponseRecord(self
, record
, rclass
, rtype
, rname
, rttl
):
129 self
.assertTrue(record
.HasField('class'))
130 self
.assertEquals(getattr(record
, 'class'), rclass
)
131 self
.assertTrue(record
.HasField('type'))
132 self
.assertEquals(record
.type, rtype
)
133 self
.assertTrue(record
.HasField('name'))
134 self
.assertEquals(record
.name
, rname
)
135 self
.assertTrue(record
.HasField('ttl'))
136 self
.assertEquals(record
.ttl
, rttl
)
137 self
.assertTrue(record
.HasField('rdata'))
139 class TestProtobuf(DNSDistProtobufTest
):
140 _config_params
= ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
141 _config_template
= """
142 luasmn = newSuffixMatchNode()
143 luasmn:add(newDNSName('lua.protobuf.tests.powerdns.com.'))
145 function alterProtobufResponse(dq, protobuf)
146 if luasmn:check(dq.qname) then
147 requestor = newCA(dq.remoteaddr:toString()) -- called by testLuaProtobuf()
148 if requestor:isIPv4() then
149 requestor:truncate(24)
151 requestor:truncate(56)
153 protobuf:setRequestor(requestor)
156 table.insert(tableTags, "TestLabel1,TestData1")
157 table.insert(tableTags, "TestLabel2,TestData2")
159 protobuf:setTagArray(tableTags)
161 protobuf:setTag('TestLabel3,TestData3')
163 protobuf:setTag("Response,456")
167 local tableTags = {} -- called by testProtobuf()
168 table.insert(tableTags, "TestLabel1,TestData1")
169 table.insert(tableTags, "TestLabel2,TestData2")
170 protobuf:setTagArray(tableTags)
172 protobuf:setTag('TestLabel3,TestData3')
174 protobuf:setTag("Response,456")
179 function alterProtobufQuery(dq, protobuf)
181 if luasmn:check(dq.qname) then
182 requestor = newCA(dq.remoteaddr:toString()) -- called by testLuaProtobuf()
183 if requestor:isIPv4() then
184 requestor:truncate(24)
186 requestor:truncate(56)
188 protobuf:setRequestor(requestor)
191 tableTags = dq:getTagArray() -- get table from DNSQuery
194 for k, v in pairs( tableTags) do
195 table.insert(tablePB, k .. "," .. v)
198 protobuf:setTagArray(tablePB) -- store table in protobuf
199 protobuf:setTag("Query,123") -- add another tag entry in protobuf
201 protobuf:setResponseCode(DNSRCode.NXDOMAIN) -- set protobuf response code to be NXDOMAIN
203 local strReqName = dq.qname:toString() -- get request dns name
205 protobuf:setProtobufResponseType() -- set protobuf to look like a response and not a query, with 0 default time
207 blobData = '\127' .. '\000' .. '\000' .. '\001' -- 127.0.0.1, note: lua 5.1 can only embed decimal not hex
209 protobuf:addResponseRR(strReqName, 1, 1, 123, blobData) -- add a RR to the protobuf
211 protobuf:setBytes(65) -- set the size of the query to confirm in checkProtobufBase
215 local tableTags = {} -- called by testProtobuf()
216 table.insert(tableTags, "TestLabel1,TestData1")
217 table.insert(tableTags, "TestLabel2,TestData2")
219 protobuf:setTagArray(tableTags)
220 protobuf:setTag('TestLabel3,TestData3')
221 protobuf:setTag("Query,123")
226 function alterLuaFirst(dq) -- called when dnsdist receives new request
228 tt["TestLabel1"] = "TestData1"
229 tt["TestLabel2"] = "TestData2"
233 dq:setTag("TestLabel3","TestData3")
234 return DNSAction.None, "" -- continue to the next rule
237 newServer{address="127.0.0.1:%s", useClientSubnet=true}
238 rl = newRemoteLogger('127.0.0.1:%s')
240 addAction(AllRule(), LuaAction(alterLuaFirst)) -- Add tags to DNSQuery first
242 addAction(AllRule(), RemoteLogAction(rl, alterProtobufQuery, {serverID='%s'})) -- Send protobuf message before lookup
244 addResponseAction(AllRule(), RemoteLogResponseAction(rl, alterProtobufResponse, true, {serverID='%s'})) -- Send protobuf message after lookup
248 def testProtobuf(self
):
250 Protobuf: Send data to a protobuf server
252 name
= 'query.protobuf.tests.powerdns.com.'
254 target
= 'target.protobuf.tests.powerdns.com.'
255 query
= dns
.message
.make_query(name
, 'A', 'IN')
256 response
= dns
.message
.make_response(query
)
258 rrset
= dns
.rrset
.from_text(name
,
263 response
.answer
.append(rrset
)
265 rrset
= dns
.rrset
.from_text(target
,
270 response
.answer
.append(rrset
)
272 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
273 self
.assertTrue(receivedQuery
)
274 self
.assertTrue(receivedResponse
)
275 receivedQuery
.id = query
.id
276 self
.assertEquals(query
, receivedQuery
)
277 self
.assertEquals(response
, receivedResponse
)
279 # let the protobuf messages the time to get there
282 # check the protobuf message corresponding to the UDP query
283 msg
= self
.getFirstProtobufMessage()
285 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
286 self
.checkProtobufTags(msg
.response
.tags
, [u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
288 # check the protobuf message corresponding to the UDP response
289 msg
= self
.getFirstProtobufMessage()
290 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
)
291 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
292 self
.assertEquals(len(msg
.response
.rrs
), 2)
293 rr
= msg
.response
.rrs
[0]
294 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
295 self
.assertEquals(rr
.rdata
.decode('utf-8'), target
)
296 rr
= msg
.response
.rrs
[1]
297 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
298 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
300 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
301 self
.assertTrue(receivedQuery
)
302 self
.assertTrue(receivedResponse
)
303 receivedQuery
.id = query
.id
304 self
.assertEquals(query
, receivedQuery
)
305 self
.assertEquals(response
, receivedResponse
)
307 # let the protobuf messages the time to get there
310 # check the protobuf message corresponding to the TCP query
311 msg
= self
.getFirstProtobufMessage()
313 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
314 self
.checkProtobufTags(msg
.response
.tags
, [u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
316 # check the protobuf message corresponding to the TCP response
317 msg
= self
.getFirstProtobufMessage()
318 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
)
319 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
320 self
.assertEquals(len(msg
.response
.rrs
), 2)
321 rr
= msg
.response
.rrs
[0]
322 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
323 self
.assertEquals(rr
.rdata
.decode('utf-8'), target
)
324 rr
= msg
.response
.rrs
[1]
325 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
326 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
328 def testLuaProtobuf(self
):
331 Protobuf: Check that the Lua callback rewrote the initiator
333 name
= 'lua.protobuf.tests.powerdns.com.'
334 query
= dns
.message
.make_query(name
, 'A', 'IN')
335 response
= dns
.message
.make_response(query
)
336 rrset
= dns
.rrset
.from_text(name
,
341 response
.answer
.append(rrset
)
344 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
346 self
.assertTrue(receivedQuery
)
347 self
.assertTrue(receivedResponse
)
348 receivedQuery
.id = query
.id
349 self
.assertEquals(query
, receivedQuery
)
350 self
.assertEquals(response
, receivedResponse
)
353 # let the protobuf messages the time to get there
356 # check the protobuf message corresponding to the UDP query
357 msg
= self
.getFirstProtobufMessage()
359 self
.checkProtobufQueryConvertedToResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
, '127.0.0.0')
360 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
362 # check the protobuf message corresponding to the UDP response
363 msg
= self
.getFirstProtobufMessage()
364 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
, '127.0.0.0')
365 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
366 self
.assertEquals(len(msg
.response
.rrs
), 1)
367 for rr
in msg
.response
.rrs
:
368 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 3600)
369 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
371 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
372 self
.assertTrue(receivedQuery
)
373 self
.assertTrue(receivedResponse
)
374 receivedQuery
.id = query
.id
375 self
.assertEquals(query
, receivedQuery
)
376 self
.assertEquals(response
, receivedResponse
)
378 # let the protobuf messages the time to get there
381 # check the protobuf message corresponding to the TCP query
382 msg
= self
.getFirstProtobufMessage()
383 self
.checkProtobufQueryConvertedToResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
, '127.0.0.0')
384 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
386 # check the protobuf message corresponding to the TCP response
387 msg
= self
.getFirstProtobufMessage()
388 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
, '127.0.0.0')
389 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
390 self
.assertEquals(len(msg
.response
.rrs
), 1)
391 for rr
in msg
.response
.rrs
:
392 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 3600)
393 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
395 class TestProtobufIPCipher(DNSDistProtobufTest
):
396 _config_params
= ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
397 _config_template
= """
398 newServer{address="127.0.0.1:%s", useClientSubnet=true}
399 key = makeIPCipherKey("some 16-byte key")
400 rl = newRemoteLogger('127.0.0.1:%s')
401 addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='%s', ipEncryptKey=key})) -- Send protobuf message before lookup
402 addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, true, {serverID='%s', ipEncryptKey=key})) -- Send protobuf message after lookup
406 def testProtobuf(self
):
408 Protobuf: Send data to a protobuf server
410 name
= 'query.protobuf-ipcipher.tests.powerdns.com.'
412 target
= 'target.protobuf-ipcipher.tests.powerdns.com.'
413 query
= dns
.message
.make_query(name
, 'A', 'IN')
414 response
= dns
.message
.make_response(query
)
416 rrset
= dns
.rrset
.from_text(name
,
421 response
.answer
.append(rrset
)
423 rrset
= dns
.rrset
.from_text(target
,
428 response
.answer
.append(rrset
)
430 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
431 self
.assertTrue(receivedQuery
)
432 self
.assertTrue(receivedResponse
)
433 receivedQuery
.id = query
.id
434 self
.assertEquals(query
, receivedQuery
)
435 self
.assertEquals(response
, receivedResponse
)
437 # let the protobuf messages the time to get there
440 # check the protobuf message corresponding to the UDP query
441 msg
= self
.getFirstProtobufMessage()
443 # 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key
444 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '108.41.239.98')
446 # check the protobuf message corresponding to the UDP response
447 msg
= self
.getFirstProtobufMessage()
448 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
, '108.41.239.98')
450 self
.assertEquals(len(msg
.response
.rrs
), 2)
451 rr
= msg
.response
.rrs
[0]
452 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
453 self
.assertEquals(rr
.rdata
.decode('ascii'), target
)
454 rr
= msg
.response
.rrs
[1]
455 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
456 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
458 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
459 self
.assertTrue(receivedQuery
)
460 self
.assertTrue(receivedResponse
)
461 receivedQuery
.id = query
.id
462 self
.assertEquals(query
, receivedQuery
)
463 self
.assertEquals(response
, receivedResponse
)
465 # let the protobuf messages the time to get there
468 # check the protobuf message corresponding to the TCP query
469 msg
= self
.getFirstProtobufMessage()
470 # 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key
471 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '108.41.239.98')
473 # check the protobuf message corresponding to the TCP response
474 msg
= self
.getFirstProtobufMessage()
475 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
, '108.41.239.98')
476 self
.assertEquals(len(msg
.response
.rrs
), 2)
477 rr
= msg
.response
.rrs
[0]
478 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
479 self
.assertEquals(rr
.rdata
.decode('ascii'), target
)
480 rr
= msg
.response
.rrs
[1]
481 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
482 self
.assertEquals(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')