8 from dnsdisttests
import DNSDistTest
, pickAvailablePort
, Queue
9 from proxyprotocol
import ProxyProtocol
14 class DNSDistProtobufTest(DNSDistTest
):
15 _protobufServerPort
= pickAvailablePort()
16 _protobufQueue
= Queue()
17 _protobufServerID
= 'dnsdist-server-1'
21 def ProtobufListener(cls
, port
):
22 cls
._backgroundThreads
[threading
.get_native_id()] = True
23 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
24 sock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEPORT
, 1)
26 sock
.bind(("127.0.0.1", port
))
27 except socket
.error
as e
:
28 print("Error binding in the protbuf listener: %s" % str(e
))
35 (conn
, _
) = sock
.accept()
36 except socket
.timeout
:
37 if cls
._backgroundThreads
.get(threading
.get_native_id(), False) == False:
38 del cls
._backgroundThreads
[threading
.get_native_id()]
48 (datalen
,) = struct
.unpack("!H", data
)
49 data
= conn
.recv(datalen
)
53 cls
._protobufQueue
.put(data
, True, timeout
=2.0)
59 def startResponders(cls
):
60 cls
._UDPResponder
= threading
.Thread(name
='UDP Responder', target
=cls
.UDPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
61 cls
._UDPResponder
.daemon
= True
62 cls
._UDPResponder
.start()
64 cls
._TCPResponder
= threading
.Thread(name
='TCP Responder', target
=cls
.TCPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
65 cls
._TCPResponder
.daemon
= True
66 cls
._TCPResponder
.start()
68 cls
._protobufListener
= threading
.Thread(name
='Protobuf Listener', target
=cls
.ProtobufListener
, args
=[cls
._protobufServerPort
])
69 cls
._protobufListener
.daemon
= True
70 cls
._protobufListener
.start()
72 def getFirstProtobufMessage(self
):
73 self
.assertFalse(self
._protobufQueue
.empty())
74 data
= self
._protobufQueue
.get(False)
76 msg
= dnsmessage_pb2
.PBDNSMessage()
77 msg
.ParseFromString(data
)
80 def checkProtobufBase(self
, msg
, protocol
, query
, initiator
, normalQueryResponse
=True, v6
=False):
82 self
.assertTrue(msg
.HasField('timeSec'))
83 self
.assertTrue(msg
.HasField('socketFamily'))
85 self
.assertEqual(msg
.socketFamily
, dnsmessage_pb2
.PBDNSMessage
.INET6
)
87 self
.assertEqual(msg
.socketFamily
, dnsmessage_pb2
.PBDNSMessage
.INET
)
88 self
.assertTrue(msg
.HasField('from'))
89 fromvalue
= getattr(msg
, 'from')
91 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET6
, fromvalue
), initiator
)
93 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, fromvalue
), initiator
)
94 self
.assertTrue(msg
.HasField('socketProtocol'))
95 self
.assertEqual(msg
.socketProtocol
, protocol
)
96 self
.assertTrue(msg
.HasField('messageId'))
97 self
.assertTrue(msg
.HasField('id'))
98 self
.assertEqual(msg
.id, query
.id)
99 self
.assertTrue(msg
.HasField('inBytes'))
100 self
.assertTrue(msg
.HasField('serverIdentity'))
101 self
.assertEqual(msg
.serverIdentity
, self
._protobufServerID
.encode('utf-8'))
103 if normalQueryResponse
and (protocol
== dnsmessage_pb2
.PBDNSMessage
.UDP
or protocol
== dnsmessage_pb2
.PBDNSMessage
.TCP
):
104 # compare inBytes with length of query/response
105 self
.assertEqual(msg
.inBytes
, len(query
.to_wire()))
106 # dnsdist doesn't set the existing EDNS Subnet for now,
107 # although it might be set from Lua
108 # self.assertTrue(msg.HasField('originalRequestorSubnet'))
109 # self.assertEqual(len(msg.originalRequestorSubnet), 4)
110 # self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), '127.0.0.1')
112 def checkProtobufQuery(self
, msg
, protocol
, query
, qclass
, qtype
, qname
, initiator
='127.0.0.1', v6
=False):
113 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSQueryType
)
114 self
.checkProtobufBase(msg
, protocol
, query
, initiator
, v6
=v6
)
115 # dnsdist doesn't fill the responder field for responses
116 # because it doesn't keep the information around.
117 self
.assertTrue(msg
.HasField('to'))
119 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, msg
.to
), '127.0.0.1')
120 self
.assertTrue(msg
.HasField('question'))
121 self
.assertTrue(msg
.question
.HasField('qClass'))
122 self
.assertEqual(msg
.question
.qClass
, qclass
)
123 self
.assertTrue(msg
.question
.HasField('qType'))
124 self
.assertEqual(msg
.question
.qClass
, qtype
)
125 self
.assertTrue(msg
.question
.HasField('qName'))
126 self
.assertEqual(msg
.question
.qName
, qname
)
128 def checkProtobufTags(self
, tags
, expectedTags
):
129 # only differences will be in new list
130 listx
= set(tags
) ^
set(expectedTags
)
131 # exclusive or of lists should be empty
132 self
.assertEqual(len(listx
), 0, "Protobuf tags don't match")
134 def checkProtobufQueryConvertedToResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.0'):
135 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
136 # skip comparing inBytes (size of the query) with the length of the generated response
137 self
.checkProtobufBase(msg
, protocol
, response
, initiator
, False)
138 self
.assertTrue(msg
.HasField('response'))
139 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
141 def checkProtobufResponse(self
, msg
, protocol
, response
, initiator
='127.0.0.1', v6
=False):
142 self
.assertEqual(msg
.type, dnsmessage_pb2
.PBDNSMessage
.DNSResponseType
)
143 self
.checkProtobufBase(msg
, protocol
, response
, initiator
, v6
=v6
)
144 self
.assertTrue(msg
.HasField('response'))
145 self
.assertTrue(msg
.response
.HasField('queryTimeSec'))
147 def checkProtobufResponseRecord(self
, record
, rclass
, rtype
, rname
, rttl
):
148 self
.assertTrue(record
.HasField('class'))
149 self
.assertEqual(getattr(record
, 'class'), rclass
)
150 self
.assertTrue(record
.HasField('type'))
151 self
.assertEqual(record
.type, rtype
)
152 self
.assertTrue(record
.HasField('name'))
153 self
.assertEqual(record
.name
, rname
)
154 self
.assertTrue(record
.HasField('ttl'))
155 self
.assertEqual(record
.ttl
, rttl
)
156 self
.assertTrue(record
.HasField('rdata'))
158 class TestProtobuf(DNSDistProtobufTest
):
159 _config_params
= ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
160 _config_template
= """
161 luasmn = newSuffixMatchNode()
162 luasmn:add(newDNSName('lua.protobuf.tests.powerdns.com.'))
164 function alterProtobufResponse(dq, protobuf)
165 if luasmn:check(dq.qname) then
166 requestor = newCA(tostring(dq.remoteaddr)) -- called by testLuaProtobuf()
167 if requestor:isIPv4() then
168 requestor:truncate(24)
170 requestor:truncate(56)
172 protobuf:setRequestor(requestor)
175 table.insert(tableTags, "TestLabel1,TestData1")
176 table.insert(tableTags, "TestLabel2,TestData2")
178 protobuf:setTagArray(tableTags)
180 protobuf:setTag('TestLabel3,TestData3')
182 protobuf:setTag("Response,456")
186 local tableTags = {} -- called by testProtobuf()
187 table.insert(tableTags, "TestLabel1,TestData1")
188 table.insert(tableTags, "TestLabel2,TestData2")
189 protobuf:setTagArray(tableTags)
191 protobuf:setTag('TestLabel3,TestData3')
193 protobuf:setTag("Response,456")
198 function alterProtobufQuery(dq, protobuf)
200 if luasmn:check(dq.qname) then
201 requestor = newCA(tostring(dq.remoteaddr)) -- called by testLuaProtobuf()
202 if requestor:isIPv4() then
203 requestor:truncate(24)
205 requestor:truncate(56)
207 protobuf:setRequestor(requestor)
210 tableTags = dq:getTagArray() -- get table from DNSQuery
213 for k, v in pairs( tableTags) do
214 table.insert(tablePB, k .. "," .. v)
217 protobuf:setTagArray(tablePB) -- store table in protobuf
218 protobuf:setTag("Query,123") -- add another tag entry in protobuf
220 protobuf:setResponseCode(DNSRCode.NXDOMAIN) -- set protobuf response code to be NXDOMAIN
222 local strReqName = tostring(dq.qname) -- get request dns name
224 protobuf:setProtobufResponseType() -- set protobuf to look like a response and not a query, with 0 default time
226 blobData = '\127' .. '\000' .. '\000' .. '\002' -- 127.0.0.2, note: lua 5.1 can only embed decimal not hex
228 protobuf:addResponseRR(strReqName, 1, 1, 123, blobData) -- add a RR to the protobuf
230 protobuf:setBytes(65) -- set the size of the query to confirm in checkProtobufBase
234 local tableTags = {} -- called by testProtobuf()
235 table.insert(tableTags, "TestLabel1,TestData1")
236 table.insert(tableTags, "TestLabel2,TestData2")
238 protobuf:setTagArray(tableTags)
239 protobuf:setTag('TestLabel3,TestData3')
240 protobuf:setTag("Query,123")
245 function alterLuaFirst(dq) -- called when dnsdist receives new request
247 tt["TestLabel1"] = "TestData1"
248 tt["TestLabel2"] = "TestData2"
252 dq:setTag("TestLabel3","TestData3")
253 return DNSAction.None, "" -- continue to the next rule
256 newServer{address="127.0.0.1:%s", useClientSubnet=true}
257 rl = newRemoteLogger('127.0.0.1:%s')
259 addAction(AllRule(), LuaAction(alterLuaFirst)) -- Add tags to DNSQuery first
261 addAction(AllRule(), RemoteLogAction(rl, alterProtobufQuery, {serverID='%s'})) -- Send protobuf message before lookup
263 addResponseAction(AllRule(), RemoteLogResponseAction(rl, alterProtobufResponse, true, {serverID='%s'})) -- Send protobuf message after lookup
267 def testProtobuf(self
):
269 Protobuf: Send data to a protobuf server
271 name
= 'query.protobuf.tests.powerdns.com.'
273 target
= 'target.protobuf.tests.powerdns.com.'
274 query
= dns
.message
.make_query(name
, 'A', 'IN')
275 response
= dns
.message
.make_response(query
)
277 rrset
= dns
.rrset
.from_text(name
,
282 response
.answer
.append(rrset
)
284 rrset
= dns
.rrset
.from_text(target
,
289 response
.answer
.append(rrset
)
291 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
292 self
.assertTrue(receivedQuery
)
293 self
.assertTrue(receivedResponse
)
294 receivedQuery
.id = query
.id
295 self
.assertEqual(query
, receivedQuery
)
296 self
.assertEqual(response
, receivedResponse
)
298 # let the protobuf messages the time to get there
301 # check the protobuf message corresponding to the UDP query
302 msg
= self
.getFirstProtobufMessage()
304 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
305 self
.checkProtobufTags(msg
.response
.tags
, [u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
307 # check the protobuf message corresponding to the UDP response
308 msg
= self
.getFirstProtobufMessage()
309 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
)
310 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
311 self
.assertEqual(len(msg
.response
.rrs
), 2)
312 rr
= msg
.response
.rrs
[0]
313 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
314 self
.assertEqual(rr
.rdata
.decode('utf-8'), target
)
315 rr
= msg
.response
.rrs
[1]
316 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
317 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
319 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
320 self
.assertTrue(receivedQuery
)
321 self
.assertTrue(receivedResponse
)
322 receivedQuery
.id = query
.id
323 self
.assertEqual(query
, receivedQuery
)
324 self
.assertEqual(response
, receivedResponse
)
326 # let the protobuf messages the time to get there
329 # check the protobuf message corresponding to the TCP query
330 msg
= self
.getFirstProtobufMessage()
332 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
333 self
.checkProtobufTags(msg
.response
.tags
, [u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
335 # check the protobuf message corresponding to the TCP response
336 msg
= self
.getFirstProtobufMessage()
337 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
)
338 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
339 self
.assertEqual(len(msg
.response
.rrs
), 2)
340 rr
= msg
.response
.rrs
[0]
341 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
342 self
.assertEqual(rr
.rdata
.decode('utf-8'), target
)
343 rr
= msg
.response
.rrs
[1]
344 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
345 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
347 def testLuaProtobuf(self
):
350 Protobuf: Check that the Lua callback rewrote the initiator
352 name
= 'lua.protobuf.tests.powerdns.com.'
353 query
= dns
.message
.make_query(name
, 'A', 'IN')
354 response
= dns
.message
.make_response(query
)
355 rrset
= dns
.rrset
.from_text(name
,
360 response
.answer
.append(rrset
)
363 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
365 self
.assertTrue(receivedQuery
)
366 self
.assertTrue(receivedResponse
)
367 receivedQuery
.id = query
.id
368 self
.assertEqual(query
, receivedQuery
)
369 self
.assertEqual(response
, receivedResponse
)
372 # let the protobuf messages the time to get there
375 # check the protobuf message corresponding to the UDP query
376 msg
= self
.getFirstProtobufMessage()
378 self
.checkProtobufQueryConvertedToResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
, '127.0.0.0')
379 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
381 # check the protobuf message corresponding to the UDP response
382 msg
= self
.getFirstProtobufMessage()
383 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
, '127.0.0.0')
384 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
385 self
.assertEqual(len(msg
.response
.rrs
), 1)
386 for rr
in msg
.response
.rrs
:
387 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 3600)
388 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
390 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
391 self
.assertTrue(receivedQuery
)
392 self
.assertTrue(receivedResponse
)
393 receivedQuery
.id = query
.id
394 self
.assertEqual(query
, receivedQuery
)
395 self
.assertEqual(response
, receivedResponse
)
397 # let the protobuf messages the time to get there
400 # check the protobuf message corresponding to the TCP query
401 msg
= self
.getFirstProtobufMessage()
402 self
.checkProtobufQueryConvertedToResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
, '127.0.0.0')
403 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Query,123"])
405 # check the protobuf message corresponding to the TCP response
406 msg
= self
.getFirstProtobufMessage()
407 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
, '127.0.0.0')
408 self
.checkProtobufTags(msg
.response
.tags
, [ u
"TestLabel1,TestData1", u
"TestLabel2,TestData2", u
"TestLabel3,TestData3", u
"Response,456"])
409 self
.assertEqual(len(msg
.response
.rrs
), 1)
410 for rr
in msg
.response
.rrs
:
411 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, 3600)
412 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
414 class TestProtobufMetaTags(DNSDistProtobufTest
):
415 _config_params
= ['_testServerPort', '_protobufServerPort']
416 _config_template
= """
417 newServer{address="127.0.0.1:%s"}
418 rl = newRemoteLogger('127.0.0.1:%d')
420 addAction(AllRule(), SetTagAction('my-tag-key', 'my-tag-value'))
421 addAction(AllRule(), SetTagAction('my-empty-key', ''))
422 addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1', exportTags='*'}, {b64='b64-content', ['my-tag-export-name']='tag:my-tag-key'}))
423 addResponseAction(AllRule(), SetTagResponseAction('my-tag-key2', 'my-tag-value2'))
424 addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {serverID='dnsdist-server-1', exportTags='my-empty-key,my-tag-key2'}, {['my-tag-export-name']='tags'}))
427 def testProtobufMeta(self
):
429 Protobuf: Meta values
431 name
= 'meta.protobuf.tests.powerdns.com.'
432 query
= dns
.message
.make_query(name
, 'A', 'IN')
433 response
= dns
.message
.make_response(query
)
434 rrset
= dns
.rrset
.from_text(name
,
439 response
.answer
.append(rrset
)
441 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
442 self
.assertTrue(receivedQuery
)
443 self
.assertTrue(receivedResponse
)
444 receivedQuery
.id = query
.id
445 self
.assertEqual(query
, receivedQuery
)
446 self
.assertEqual(response
, receivedResponse
)
448 # let the protobuf messages the time to get there
451 # check the protobuf message corresponding to the UDP query
452 msg
= self
.getFirstProtobufMessage()
454 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
456 self
.assertEqual(len(msg
.response
.tags
), 2)
457 self
.assertIn('my-tag-key:my-tag-value', msg
.response
.tags
)
458 self
.assertIn('my-empty-key', msg
.response
.tags
)
460 self
.assertEqual(len(msg
.meta
), 2)
462 for entry
in msg
.meta
:
463 tags
[entry
.key
] = entry
.value
.stringVal
465 self
.assertIn('b64', tags
)
466 self
.assertIn('my-tag-export-name', tags
)
468 b64EncodedQuery
= base64
.b64encode(query
.to_wire()).decode('ascii')
469 self
.assertEqual(tags
['b64'], [b64EncodedQuery
])
470 self
.assertEqual(tags
['my-tag-export-name'], ['my-tag-value'])
472 # check the protobuf message corresponding to the UDP response
473 msg
= self
.getFirstProtobufMessage()
474 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
)
476 self
.assertEqual(len(msg
.response
.tags
), 2)
477 self
.assertIn('my-tag-key2:my-tag-value2', msg
.response
.tags
)
478 self
.assertIn('my-empty-key', msg
.response
.tags
)
480 self
.assertEqual(len(msg
.meta
), 1)
481 self
.assertEqual(msg
.meta
[0].key
, 'my-tag-export-name')
482 self
.assertEqual(len(msg
.meta
[0].value
.stringVal
), 3)
483 self
.assertIn('my-tag-key:my-tag-value', msg
.meta
[0].value
.stringVal
)
484 self
.assertIn('my-tag-key2:my-tag-value2', msg
.meta
[0].value
.stringVal
)
485 # no ':' when the value is empty
486 self
.assertIn('my-empty-key', msg
.meta
[0].value
.stringVal
)
488 class TestProtobufMetaDOH(DNSDistProtobufTest
):
490 _serverKey
= 'server.key'
491 _serverCert
= 'server.chain'
492 _serverName
= 'tls.tests.dnsdist.org'
494 _tlsServerPort
= pickAvailablePort()
495 _dohWithNGHTTP2ServerPort
= pickAvailablePort()
496 _dohWithNGHTTP2BaseURL
= ("https://%s:%d/dns-query" % (_serverName
, _dohWithNGHTTP2ServerPort
))
497 _dohWithH2OServerPort
= pickAvailablePort()
498 _dohWithH2OBaseURL
= ("https://%s:%d/dns-query" % (_serverName
, _dohWithH2OServerPort
))
499 _config_template
= """
500 newServer{address="127.0.0.1:%d"}
501 rl = newRemoteLogger('127.0.0.1:%d')
503 addTLSLocal("127.0.0.1:%s", "%s", "%s", { provider="openssl" })
504 addDOHLocal("127.0.0.1:%d", "%s", "%s", { '/dns-query' }, { keepIncomingHeaders=true, library='nghttp2' })
505 addDOHLocal("127.0.0.1:%d", "%s", "%s", { '/dns-query' }, { keepIncomingHeaders=true, library='h2o' })
507 local mytags = {path='doh-path', host='doh-host', ['query-string']='doh-query-string', scheme='doh-scheme', agent='doh-header:user-agent'}
508 addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'}, mytags))
509 addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {serverID='dnsdist-server-1'}, mytags))
511 _config_params
= ['_testServerPort', '_protobufServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey', '_dohWithH2OServerPort', '_serverCert', '_serverKey']
513 def testProtobufMetaDoH(self
):
515 Protobuf: Meta values - DoH
517 name
= 'meta-doh.protobuf.tests.powerdns.com.'
518 query
= dns
.message
.make_query(name
, 'A', 'IN')
519 response
= dns
.message
.make_response(query
)
520 rrset
= dns
.rrset
.from_text(name
,
525 response
.answer
.append(rrset
)
527 for method
in ("sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper", "sendDOHWithH2OQueryWrapper"):
528 sender
= getattr(self
, method
)
529 (receivedQuery
, receivedResponse
) = sender(query
, response
)
531 self
.assertTrue(receivedQuery
)
532 self
.assertTrue(receivedResponse
)
533 receivedQuery
.id = query
.id
534 self
.assertEqual(query
, receivedQuery
)
535 self
.assertEqual(response
, receivedResponse
)
537 # let the protobuf messages the time to get there
540 # check the protobuf message corresponding to the query
541 msg
= self
.getFirstProtobufMessage()
543 if method
== "sendUDPQuery":
544 pbMessageType
= dnsmessage_pb2
.PBDNSMessage
.UDP
545 elif method
== "sendTCPQuery":
546 pbMessageType
= dnsmessage_pb2
.PBDNSMessage
.TCP
547 elif method
== "sendDOTQueryWrapper":
548 pbMessageType
= dnsmessage_pb2
.PBDNSMessage
.DOT
549 elif method
== "sendDOHWithNGHTTP2QueryWrapper" or method
== "sendDOHWithH2OQueryWrapper":
550 pbMessageType
= dnsmessage_pb2
.PBDNSMessage
.DOH
552 self
.checkProtobufQuery(msg
, pbMessageType
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
)
553 self
.assertEqual(len(msg
.meta
), 5)
555 for entry
in msg
.meta
:
556 self
.assertEqual(len(entry
.value
.stringVal
), 1)
557 tags
[entry
.key
] = entry
.value
.stringVal
[0]
559 self
.assertIn('agent', tags
)
560 if method
== "sendDOHWithNGHTTP2QueryWrapper" or method
== "sendDOHWithH2OQueryWrapper":
561 self
.assertIn('PycURL', tags
['agent'])
562 self
.assertIn('host', tags
)
563 if method
== "sendDOHWithNGHTTP2QueryWrapper":
564 self
.assertEqual(tags
['host'], self
._serverName
+ ':' + str(self
._dohWithNGHTTP
2ServerPort
))
565 elif method
== "sendDOHWithH2OQueryWrapper":
566 self
.assertEqual(tags
['host'], self
._serverName
+ ':' + str(self
._dohWithH
2OServerPort
))
567 self
.assertIn('path', tags
)
568 self
.assertEqual(tags
['path'], '/dns-query')
569 self
.assertIn('query-string', tags
)
570 self
.assertIn('?dns=', tags
['query-string'])
571 self
.assertIn('scheme', tags
)
572 self
.assertEqual(tags
['scheme'], 'https')
574 # check the protobuf message corresponding to the response
575 msg
= self
.getFirstProtobufMessage()
576 self
.checkProtobufResponse(msg
, pbMessageType
, response
)
577 self
.assertEqual(len(msg
.meta
), 5)
579 for entry
in msg
.meta
:
580 self
.assertEqual(len(entry
.value
.stringVal
), 1)
581 tags
[entry
.key
] = entry
.value
.stringVal
[0]
583 self
.assertIn('agent', tags
)
584 if method
== "sendDOHWithNGHTTP2QueryWrapper" or method
== "sendDOHWithH2OQueryWrapper":
585 self
.assertIn('PycURL', tags
['agent'])
586 self
.assertIn('host', tags
)
587 if method
== "sendDOHWithNGHTTP2QueryWrapper":
588 self
.assertEqual(tags
['host'], self
._serverName
+ ':' + str(self
._dohWithNGHTTP
2ServerPort
))
589 elif method
== "sendDOHWithH2OQueryWrapper":
590 self
.assertEqual(tags
['host'], self
._serverName
+ ':' + str(self
._dohWithH
2OServerPort
))
591 self
.assertIn('path', tags
)
592 self
.assertEqual(tags
['path'], '/dns-query')
593 self
.assertIn('query-string', tags
)
594 self
.assertIn('?dns=', tags
['query-string'])
595 self
.assertIn('scheme', tags
)
596 self
.assertEqual(tags
['scheme'], 'https')
598 class TestProtobufMetaProxy(DNSDistProtobufTest
):
600 _config_params
= ['_testServerPort', '_protobufServerPort']
601 _config_template
= """
602 setProxyProtocolACL( { "127.0.0.1/32" } )
604 newServer{address="127.0.0.1:%d"}
605 rl = newRemoteLogger('127.0.0.1:%d')
607 local mytags = {pp='proxy-protocol-values', pp42='proxy-protocol-value:42'}
608 addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'}, mytags))
610 -- proxy protocol values are NOT passed to the response
613 def testProtobufMetaProxy(self
):
615 Protobuf: Meta values - Proxy
617 name
= 'meta-proxy.protobuf.tests.powerdns.com.'
618 query
= dns
.message
.make_query(name
, 'A', 'IN')
619 response
= dns
.message
.make_response(query
)
620 rrset
= dns
.rrset
.from_text(name
,
625 response
.answer
.append(rrset
)
627 destAddr
= "2001:db8::9"
629 srcAddr
= "2001:db8::8"
631 udpPayload
= ProxyProtocol
.getPayload(False, False, True, srcAddr
, destAddr
, srcPort
, destPort
, [ [ 2, b
'foo'], [ 42, b
'proxy'] ])
632 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(udpPayload
+ query
.to_wire(), response
, rawQuery
=True)
634 self
.assertTrue(receivedQuery
)
635 self
.assertTrue(receivedResponse
)
636 receivedQuery
.id = query
.id
637 self
.assertEqual(query
, receivedQuery
)
638 self
.assertEqual(response
, receivedResponse
)
640 # let the protobuf messages the time to get there
643 # check the protobuf message corresponding to the UDP query
644 msg
= self
.getFirstProtobufMessage()
646 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, initiator
='2001:db8::8', v6
=True)
647 self
.assertEqual(len(msg
.meta
), 2)
649 for entry
in msg
.meta
:
650 tags
[entry
.key
] = entry
.value
.stringVal
652 self
.assertIn('pp42', tags
)
653 self
.assertEqual(tags
['pp42'], ['proxy'])
654 self
.assertIn('pp', tags
)
655 self
.assertEqual(len(tags
['pp']), 2)
656 self
.assertIn('2:foo', tags
['pp'])
657 self
.assertIn('42:proxy', tags
['pp'])
659 class TestProtobufIPCipher(DNSDistProtobufTest
):
660 _config_params
= ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID']
661 _config_template
= """
662 newServer{address="127.0.0.1:%s", useClientSubnet=true}
663 key = makeIPCipherKey("some 16-byte key")
664 rl = newRemoteLogger('127.0.0.1:%s')
665 addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='%s', ipEncryptKey=key})) -- Send protobuf message before lookup
666 addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, true, {serverID='%s', ipEncryptKey=key})) -- Send protobuf message after lookup
670 def testProtobuf(self
):
672 Protobuf: Send data to a protobuf server, with pseudonymization
674 name
= 'query.protobuf-ipcipher.tests.powerdns.com.'
676 target
= 'target.protobuf-ipcipher.tests.powerdns.com.'
677 query
= dns
.message
.make_query(name
, 'A', 'IN')
678 response
= dns
.message
.make_response(query
)
680 rrset
= dns
.rrset
.from_text(name
,
685 response
.answer
.append(rrset
)
687 rrset
= dns
.rrset
.from_text(target
,
692 response
.answer
.append(rrset
)
694 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
695 self
.assertTrue(receivedQuery
)
696 self
.assertTrue(receivedResponse
)
697 receivedQuery
.id = query
.id
698 self
.assertEqual(query
, receivedQuery
)
699 self
.assertEqual(response
, receivedResponse
)
701 # let the protobuf messages the time to get there
704 # check the protobuf message corresponding to the UDP query
705 msg
= self
.getFirstProtobufMessage()
707 # 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key
708 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '108.41.239.98')
710 # check the protobuf message corresponding to the UDP response
711 msg
= self
.getFirstProtobufMessage()
712 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.UDP
, response
, '108.41.239.98')
714 self
.assertEqual(len(msg
.response
.rrs
), 2)
715 rr
= msg
.response
.rrs
[0]
716 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
717 self
.assertEqual(rr
.rdata
.decode('ascii'), target
)
718 rr
= msg
.response
.rrs
[1]
719 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
720 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')
722 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
723 self
.assertTrue(receivedQuery
)
724 self
.assertTrue(receivedResponse
)
725 receivedQuery
.id = query
.id
726 self
.assertEqual(query
, receivedQuery
)
727 self
.assertEqual(response
, receivedResponse
)
729 # let the protobuf messages the time to get there
732 # check the protobuf message corresponding to the TCP query
733 msg
= self
.getFirstProtobufMessage()
734 # 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key
735 self
.checkProtobufQuery(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, query
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, name
, '108.41.239.98')
737 # check the protobuf message corresponding to the TCP response
738 msg
= self
.getFirstProtobufMessage()
739 self
.checkProtobufResponse(msg
, dnsmessage_pb2
.PBDNSMessage
.TCP
, response
, '108.41.239.98')
740 self
.assertEqual(len(msg
.response
.rrs
), 2)
741 rr
= msg
.response
.rrs
[0]
742 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.CNAME
, name
, 3600)
743 self
.assertEqual(rr
.rdata
.decode('ascii'), target
)
744 rr
= msg
.response
.rrs
[1]
745 self
.checkProtobufResponseRecord(rr
, dns
.rdataclass
.IN
, dns
.rdatatype
.A
, target
, 3600)
746 self
.assertEqual(socket
.inet_ntop(socket
.AF_INET
, rr
.rdata
), '127.0.0.1')