]>
Commit | Line | Data |
---|---|---|
1d0bd88a | 1 | #!/usr/bin/env python |
c02b6723 | 2 | import base64 |
1d0bd88a RG |
3 | import threading |
4 | import socket | |
5 | import struct | |
6 | import sys | |
7 | import time | |
630eb526 | 8 | from dnsdisttests import DNSDistTest, pickAvailablePort, Queue |
c02b6723 | 9 | from proxyprotocol import ProxyProtocol |
1d0bd88a RG |
10 | |
11 | import dns | |
12 | import dnsmessage_pb2 | |
e3762c2e | 13 | import extendederrors |
1d0bd88a | 14 | |
f29fabd9 | 15 | class DNSDistProtobufTest(DNSDistTest): |
630eb526 | 16 | _protobufServerPort = pickAvailablePort() |
b4f23783 | 17 | _protobufQueue = Queue() |
312a09a6 | 18 | _protobufServerID = 'dnsdist-server-1' |
1d0bd88a | 19 | _protobufCounter = 0 |
1d0bd88a RG |
20 | |
21 | @classmethod | |
22 | def ProtobufListener(cls, port): | |
7373e3a6 | 23 | cls._backgroundThreads[threading.get_native_id()] = True |
1d0bd88a RG |
24 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
25 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | |
26 | try: | |
27 | sock.bind(("127.0.0.1", port)) | |
28 | except socket.error as e: | |
29 | print("Error binding in the protbuf listener: %s" % str(e)) | |
30 | sys.exit(1) | |
31 | ||
32 | sock.listen(100) | |
7373e3a6 | 33 | sock.settimeout(1.0) |
1d0bd88a | 34 | while True: |
7373e3a6 RG |
35 | try: |
36 | (conn, _) = sock.accept() | |
37 | except socket.timeout: | |
38 | if cls._backgroundThreads.get(threading.get_native_id(), False) == False: | |
39 | del cls._backgroundThreads[threading.get_native_id()] | |
40 | break | |
41 | else: | |
42 | continue | |
43 | ||
1d0bd88a RG |
44 | data = None |
45 | while True: | |
46 | data = conn.recv(2) | |
47 | if not data: | |
48 | break | |
49 | (datalen,) = struct.unpack("!H", data) | |
50 | data = conn.recv(datalen) | |
51 | if not data: | |
52 | break | |
53 | ||
54 | cls._protobufQueue.put(data, True, timeout=2.0) | |
55 | ||
56 | conn.close() | |
57 | sock.close() | |
58 | ||
59 | @classmethod | |
60 | def startResponders(cls): | |
5df86a8a | 61 | cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue]) |
630eb526 | 62 | cls._UDPResponder.daemon = True |
1d0bd88a | 63 | cls._UDPResponder.start() |
741ebe08 | 64 | |
5df86a8a | 65 | cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue]) |
630eb526 | 66 | cls._TCPResponder.daemon = True |
1d0bd88a RG |
67 | cls._TCPResponder.start() |
68 | ||
69 | cls._protobufListener = threading.Thread(name='Protobuf Listener', target=cls.ProtobufListener, args=[cls._protobufServerPort]) | |
630eb526 | 70 | cls._protobufListener.daemon = True |
1d0bd88a RG |
71 | cls._protobufListener.start() |
72 | ||
73 | def getFirstProtobufMessage(self): | |
74 | self.assertFalse(self._protobufQueue.empty()) | |
75 | data = self._protobufQueue.get(False) | |
76 | self.assertTrue(data) | |
77 | msg = dnsmessage_pb2.PBDNSMessage() | |
78 | msg.ParseFromString(data) | |
79 | return msg | |
80 | ||
c02b6723 | 81 | def checkProtobufBase(self, msg, protocol, query, initiator, normalQueryResponse=True, v6=False): |
1d0bd88a RG |
82 | self.assertTrue(msg) |
83 | self.assertTrue(msg.HasField('timeSec')) | |
84 | self.assertTrue(msg.HasField('socketFamily')) | |
c02b6723 RG |
85 | if v6: |
86 | self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET6) | |
87 | else: | |
88 | self.assertEqual(msg.socketFamily, dnsmessage_pb2.PBDNSMessage.INET) | |
1d0bd88a RG |
89 | self.assertTrue(msg.HasField('from')) |
90 | fromvalue = getattr(msg, 'from') | |
c02b6723 RG |
91 | if v6: |
92 | self.assertEqual(socket.inet_ntop(socket.AF_INET6, fromvalue), initiator) | |
93 | else: | |
94 | self.assertEqual(socket.inet_ntop(socket.AF_INET, fromvalue), initiator) | |
1d0bd88a | 95 | self.assertTrue(msg.HasField('socketProtocol')) |
4bfebc93 | 96 | self.assertEqual(msg.socketProtocol, protocol) |
1d0bd88a RG |
97 | self.assertTrue(msg.HasField('messageId')) |
98 | self.assertTrue(msg.HasField('id')) | |
4bfebc93 | 99 | self.assertEqual(msg.id, query.id) |
1d0bd88a | 100 | self.assertTrue(msg.HasField('inBytes')) |
312a09a6 | 101 | self.assertTrue(msg.HasField('serverIdentity')) |
4bfebc93 | 102 | self.assertEqual(msg.serverIdentity, self._protobufServerID.encode('utf-8')) |
312a09a6 | 103 | |
c02b6723 | 104 | if normalQueryResponse and (protocol == dnsmessage_pb2.PBDNSMessage.UDP or protocol == dnsmessage_pb2.PBDNSMessage.TCP): |
0096ada6 | 105 | # compare inBytes with length of query/response |
4bfebc93 | 106 | self.assertEqual(msg.inBytes, len(query.to_wire())) |
1d0bd88a RG |
107 | # dnsdist doesn't set the existing EDNS Subnet for now, |
108 | # although it might be set from Lua | |
109 | # self.assertTrue(msg.HasField('originalRequestorSubnet')) | |
4bfebc93 CH |
110 | # self.assertEqual(len(msg.originalRequestorSubnet), 4) |
111 | # self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.originalRequestorSubnet), '127.0.0.1') | |
1d0bd88a | 112 | |
c02b6723 | 113 | def checkProtobufQuery(self, msg, protocol, query, qclass, qtype, qname, initiator='127.0.0.1', v6=False): |
4bfebc93 | 114 | self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSQueryType) |
c02b6723 | 115 | self.checkProtobufBase(msg, protocol, query, initiator, v6=v6) |
1d0bd88a RG |
116 | # dnsdist doesn't fill the responder field for responses |
117 | # because it doesn't keep the information around. | |
118 | self.assertTrue(msg.HasField('to')) | |
c02b6723 RG |
119 | if not v6: |
120 | self.assertEqual(socket.inet_ntop(socket.AF_INET, msg.to), '127.0.0.1') | |
1d0bd88a RG |
121 | self.assertTrue(msg.HasField('question')) |
122 | self.assertTrue(msg.question.HasField('qClass')) | |
4bfebc93 | 123 | self.assertEqual(msg.question.qClass, qclass) |
1d0bd88a | 124 | self.assertTrue(msg.question.HasField('qType')) |
4bfebc93 | 125 | self.assertEqual(msg.question.qClass, qtype) |
1d0bd88a | 126 | self.assertTrue(msg.question.HasField('qName')) |
4bfebc93 | 127 | self.assertEqual(msg.question.qName, qname) |
1d0bd88a | 128 | |
0096ada6 RG |
129 | def checkProtobufTags(self, tags, expectedTags): |
130 | # only differences will be in new list | |
131 | listx = set(tags) ^ set(expectedTags) | |
132 | # exclusive or of lists should be empty | |
133 | self.assertEqual(len(listx), 0, "Protobuf tags don't match") | |
741ebe08 | 134 | |
29cd61cc | 135 | def checkProtobufQueryConvertedToResponse(self, msg, protocol, response, initiator='127.0.0.0'): |
4bfebc93 | 136 | self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType) |
0096ada6 RG |
137 | # skip comparing inBytes (size of the query) with the length of the generated response |
138 | self.checkProtobufBase(msg, protocol, response, initiator, False) | |
29cd61cc SO |
139 | self.assertTrue(msg.HasField('response')) |
140 | self.assertTrue(msg.response.HasField('queryTimeSec')) | |
141 | ||
c02b6723 | 142 | def checkProtobufResponse(self, msg, protocol, response, initiator='127.0.0.1', v6=False): |
4bfebc93 | 143 | self.assertEqual(msg.type, dnsmessage_pb2.PBDNSMessage.DNSResponseType) |
c02b6723 | 144 | self.checkProtobufBase(msg, protocol, response, initiator, v6=v6) |
29cd61cc SO |
145 | self.assertTrue(msg.HasField('response')) |
146 | self.assertTrue(msg.response.HasField('queryTimeSec')) | |
741ebe08 | 147 | |
8667904b RG |
148 | def checkProtobufResponseRecord(self, record, rclass, rtype, rname, rttl): |
149 | self.assertTrue(record.HasField('class')) | |
4bfebc93 | 150 | self.assertEqual(getattr(record, 'class'), rclass) |
8667904b | 151 | self.assertTrue(record.HasField('type')) |
4bfebc93 | 152 | self.assertEqual(record.type, rtype) |
8667904b | 153 | self.assertTrue(record.HasField('name')) |
4bfebc93 | 154 | self.assertEqual(record.name, rname) |
8667904b | 155 | self.assertTrue(record.HasField('ttl')) |
4bfebc93 | 156 | self.assertEqual(record.ttl, rttl) |
8667904b RG |
157 | self.assertTrue(record.HasField('rdata')) |
158 | ||
f29fabd9 RG |
159 | class TestProtobuf(DNSDistProtobufTest): |
160 | _config_params = ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID'] | |
161 | _config_template = """ | |
162 | luasmn = newSuffixMatchNode() | |
163 | luasmn:add(newDNSName('lua.protobuf.tests.powerdns.com.')) | |
164 | ||
165 | function alterProtobufResponse(dq, protobuf) | |
166 | if luasmn:check(dq.qname) then | |
207e77dd | 167 | requestor = newCA(tostring(dq.remoteaddr)) -- called by testLuaProtobuf() |
f29fabd9 RG |
168 | if requestor:isIPv4() then |
169 | requestor:truncate(24) | |
170 | else | |
171 | requestor:truncate(56) | |
172 | end | |
173 | protobuf:setRequestor(requestor) | |
174 | ||
175 | local tableTags = {} | |
176 | table.insert(tableTags, "TestLabel1,TestData1") | |
177 | table.insert(tableTags, "TestLabel2,TestData2") | |
178 | ||
179 | protobuf:setTagArray(tableTags) | |
180 | ||
181 | protobuf:setTag('TestLabel3,TestData3') | |
182 | ||
183 | protobuf:setTag("Response,456") | |
184 | ||
185 | else | |
186 | ||
187 | local tableTags = {} -- called by testProtobuf() | |
188 | table.insert(tableTags, "TestLabel1,TestData1") | |
189 | table.insert(tableTags, "TestLabel2,TestData2") | |
190 | protobuf:setTagArray(tableTags) | |
191 | ||
192 | protobuf:setTag('TestLabel3,TestData3') | |
193 | ||
194 | protobuf:setTag("Response,456") | |
195 | ||
196 | end | |
197 | end | |
198 | ||
199 | function alterProtobufQuery(dq, protobuf) | |
200 | ||
201 | if luasmn:check(dq.qname) then | |
207e77dd | 202 | requestor = newCA(tostring(dq.remoteaddr)) -- called by testLuaProtobuf() |
f29fabd9 RG |
203 | if requestor:isIPv4() then |
204 | requestor:truncate(24) | |
205 | else | |
206 | requestor:truncate(56) | |
207 | end | |
208 | protobuf:setRequestor(requestor) | |
209 | ||
210 | local tableTags = {} | |
211 | tableTags = dq:getTagArray() -- get table from DNSQuery | |
212 | ||
213 | local tablePB = {} | |
214 | for k, v in pairs( tableTags) do | |
215 | table.insert(tablePB, k .. "," .. v) | |
216 | end | |
217 | ||
218 | protobuf:setTagArray(tablePB) -- store table in protobuf | |
219 | protobuf:setTag("Query,123") -- add another tag entry in protobuf | |
220 | ||
d3ec24f9 | 221 | protobuf:setResponseCode(DNSRCode.NXDOMAIN) -- set protobuf response code to be NXDOMAIN |
f29fabd9 | 222 | |
207e77dd | 223 | local strReqName = tostring(dq.qname) -- get request dns name |
f29fabd9 RG |
224 | |
225 | protobuf:setProtobufResponseType() -- set protobuf to look like a response and not a query, with 0 default time | |
226 | ||
87f46425 | 227 | blobData = '\127' .. '\000' .. '\000' .. '\002' -- 127.0.0.2, note: lua 5.1 can only embed decimal not hex |
f29fabd9 RG |
228 | |
229 | protobuf:addResponseRR(strReqName, 1, 1, 123, blobData) -- add a RR to the protobuf | |
230 | ||
231 | protobuf:setBytes(65) -- set the size of the query to confirm in checkProtobufBase | |
232 | ||
233 | else | |
234 | ||
235 | local tableTags = {} -- called by testProtobuf() | |
236 | table.insert(tableTags, "TestLabel1,TestData1") | |
237 | table.insert(tableTags, "TestLabel2,TestData2") | |
238 | ||
239 | protobuf:setTagArray(tableTags) | |
240 | protobuf:setTag('TestLabel3,TestData3') | |
241 | protobuf:setTag("Query,123") | |
242 | ||
243 | end | |
244 | end | |
245 | ||
246 | function alterLuaFirst(dq) -- called when dnsdist receives new request | |
247 | local tt = {} | |
248 | tt["TestLabel1"] = "TestData1" | |
249 | tt["TestLabel2"] = "TestData2" | |
250 | ||
251 | dq:setTagArray(tt) | |
252 | ||
253 | dq:setTag("TestLabel3","TestData3") | |
254 | return DNSAction.None, "" -- continue to the next rule | |
255 | end | |
256 | ||
257 | newServer{address="127.0.0.1:%s", useClientSubnet=true} | |
258 | rl = newRemoteLogger('127.0.0.1:%s') | |
259 | ||
260 | addAction(AllRule(), LuaAction(alterLuaFirst)) -- Add tags to DNSQuery first | |
261 | ||
262 | addAction(AllRule(), RemoteLogAction(rl, alterProtobufQuery, {serverID='%s'})) -- Send protobuf message before lookup | |
263 | ||
264 | addResponseAction(AllRule(), RemoteLogResponseAction(rl, alterProtobufResponse, true, {serverID='%s'})) -- Send protobuf message after lookup | |
265 | ||
266 | """ | |
267 | ||
1d0bd88a RG |
268 | def testProtobuf(self): |
269 | """ | |
270 | Protobuf: Send data to a protobuf server | |
271 | """ | |
29cd61cc | 272 | name = 'query.protobuf.tests.powerdns.com.' |
741ebe08 | 273 | |
165c9030 | 274 | target = 'target.protobuf.tests.powerdns.com.' |
1d0bd88a RG |
275 | query = dns.message.make_query(name, 'A', 'IN') |
276 | response = dns.message.make_response(query) | |
741ebe08 | 277 | |
1d0bd88a | 278 | rrset = dns.rrset.from_text(name, |
165c9030 RG |
279 | 3600, |
280 | dns.rdataclass.IN, | |
281 | dns.rdatatype.CNAME, | |
282 | target) | |
283 | response.answer.append(rrset) | |
741ebe08 | 284 | |
165c9030 | 285 | rrset = dns.rrset.from_text(target, |
1d0bd88a RG |
286 | 3600, |
287 | dns.rdataclass.IN, | |
288 | dns.rdatatype.A, | |
289 | '127.0.0.1') | |
290 | response.answer.append(rrset) | |
291 | ||
292 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
293 | self.assertTrue(receivedQuery) | |
294 | self.assertTrue(receivedResponse) | |
295 | receivedQuery.id = query.id | |
4bfebc93 CH |
296 | self.assertEqual(query, receivedQuery) |
297 | self.assertEqual(response, receivedResponse) | |
1d0bd88a | 298 | |
e3762c2e RG |
299 | if self._protobufQueue.empty(): |
300 | # let the protobuf messages the time to get there | |
301 | time.sleep(1) | |
1d0bd88a RG |
302 | |
303 | # check the protobuf message corresponding to the UDP query | |
304 | msg = self.getFirstProtobufMessage() | |
741ebe08 | 305 | |
29cd61cc | 306 | self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name) |
0096ada6 | 307 | self.checkProtobufTags(msg.response.tags, [u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"]) |
1d0bd88a RG |
308 | |
309 | # check the protobuf message corresponding to the UDP response | |
310 | msg = self.getFirstProtobufMessage() | |
0096ada6 RG |
311 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response) |
312 | self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"]) | |
4bfebc93 | 313 | self.assertEqual(len(msg.response.rrs), 2) |
165c9030 RG |
314 | rr = msg.response.rrs[0] |
315 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600) | |
4bfebc93 | 316 | self.assertEqual(rr.rdata.decode('utf-8'), target) |
165c9030 RG |
317 | rr = msg.response.rrs[1] |
318 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600) | |
4bfebc93 | 319 | self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1') |
1d0bd88a RG |
320 | |
321 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
322 | self.assertTrue(receivedQuery) | |
323 | self.assertTrue(receivedResponse) | |
324 | receivedQuery.id = query.id | |
4bfebc93 CH |
325 | self.assertEqual(query, receivedQuery) |
326 | self.assertEqual(response, receivedResponse) | |
1d0bd88a | 327 | |
e3762c2e RG |
328 | if self._protobufQueue.empty(): |
329 | # let the protobuf messages the time to get there | |
330 | time.sleep(1) | |
1d0bd88a RG |
331 | |
332 | # check the protobuf message corresponding to the TCP query | |
333 | msg = self.getFirstProtobufMessage() | |
29cd61cc | 334 | |
1d0bd88a | 335 | self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.A, name) |
0096ada6 | 336 | self.checkProtobufTags(msg.response.tags, [u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"]) |
1d0bd88a RG |
337 | |
338 | # check the protobuf message corresponding to the TCP response | |
339 | msg = self.getFirstProtobufMessage() | |
0096ada6 RG |
340 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response) |
341 | self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"]) | |
4bfebc93 | 342 | self.assertEqual(len(msg.response.rrs), 2) |
165c9030 RG |
343 | rr = msg.response.rrs[0] |
344 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600) | |
4bfebc93 | 345 | self.assertEqual(rr.rdata.decode('utf-8'), target) |
165c9030 RG |
346 | rr = msg.response.rrs[1] |
347 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600) | |
4bfebc93 | 348 | self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1') |
8667904b RG |
349 | |
350 | def testLuaProtobuf(self): | |
741ebe08 | 351 | |
8667904b RG |
352 | """ |
353 | Protobuf: Check that the Lua callback rewrote the initiator | |
354 | """ | |
355 | name = 'lua.protobuf.tests.powerdns.com.' | |
356 | query = dns.message.make_query(name, 'A', 'IN') | |
357 | response = dns.message.make_response(query) | |
358 | rrset = dns.rrset.from_text(name, | |
359 | 3600, | |
360 | dns.rdataclass.IN, | |
361 | dns.rdatatype.A, | |
362 | '127.0.0.1') | |
363 | response.answer.append(rrset) | |
364 | ||
741ebe08 | 365 | |
8667904b | 366 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) |
741ebe08 | 367 | |
29cd61cc SO |
368 | self.assertTrue(receivedQuery) |
369 | self.assertTrue(receivedResponse) | |
8667904b | 370 | receivedQuery.id = query.id |
4bfebc93 CH |
371 | self.assertEqual(query, receivedQuery) |
372 | self.assertEqual(response, receivedResponse) | |
8667904b | 373 | |
e3762c2e RG |
374 | if self._protobufQueue.empty(): |
375 | # let the protobuf messages the time to get there | |
376 | time.sleep(1) | |
8667904b RG |
377 | |
378 | # check the protobuf message corresponding to the UDP query | |
379 | msg = self.getFirstProtobufMessage() | |
29cd61cc | 380 | |
0096ada6 RG |
381 | self.checkProtobufQueryConvertedToResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, '127.0.0.0') |
382 | self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"]) | |
8667904b RG |
383 | |
384 | # check the protobuf message corresponding to the UDP response | |
385 | msg = self.getFirstProtobufMessage() | |
0096ada6 RG |
386 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, '127.0.0.0') |
387 | self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"]) | |
4bfebc93 | 388 | self.assertEqual(len(msg.response.rrs), 1) |
8667904b RG |
389 | for rr in msg.response.rrs: |
390 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 3600) | |
4bfebc93 | 391 | self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1') |
8667904b RG |
392 | |
393 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
394 | self.assertTrue(receivedQuery) | |
395 | self.assertTrue(receivedResponse) | |
396 | receivedQuery.id = query.id | |
4bfebc93 CH |
397 | self.assertEqual(query, receivedQuery) |
398 | self.assertEqual(response, receivedResponse) | |
8667904b | 399 | |
e3762c2e RG |
400 | if self._protobufQueue.empty(): |
401 | # let the protobuf messages the time to get there | |
402 | time.sleep(1) | |
8667904b RG |
403 | |
404 | # check the protobuf message corresponding to the TCP query | |
405 | msg = self.getFirstProtobufMessage() | |
0096ada6 RG |
406 | self.checkProtobufQueryConvertedToResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, '127.0.0.0') |
407 | self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Query,123"]) | |
8667904b RG |
408 | |
409 | # check the protobuf message corresponding to the TCP response | |
410 | msg = self.getFirstProtobufMessage() | |
0096ada6 RG |
411 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, '127.0.0.0') |
412 | self.checkProtobufTags(msg.response.tags, [ u"TestLabel1,TestData1", u"TestLabel2,TestData2", u"TestLabel3,TestData3", u"Response,456"]) | |
4bfebc93 | 413 | self.assertEqual(len(msg.response.rrs), 1) |
8667904b RG |
414 | for rr in msg.response.rrs: |
415 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, name, 3600) | |
4bfebc93 | 416 | self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1') |
f29fabd9 | 417 | |
c02b6723 RG |
418 | class TestProtobufMetaTags(DNSDistProtobufTest): |
419 | _config_params = ['_testServerPort', '_protobufServerPort'] | |
420 | _config_template = """ | |
421 | newServer{address="127.0.0.1:%s"} | |
422 | rl = newRemoteLogger('127.0.0.1:%d') | |
423 | ||
424 | addAction(AllRule(), SetTagAction('my-tag-key', 'my-tag-value')) | |
6b9302bd | 425 | addAction(AllRule(), SetTagAction('my-empty-key', '')) |
fb63c25e | 426 | addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1', exportTags='*'}, {b64='b64-content', ['my-tag-export-name']='tag:my-tag-key'})) |
c02b6723 | 427 | addResponseAction(AllRule(), SetTagResponseAction('my-tag-key2', 'my-tag-value2')) |
fb63c25e | 428 | addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {serverID='dnsdist-server-1', exportTags='my-empty-key,my-tag-key2'}, {['my-tag-export-name']='tags'})) |
c02b6723 RG |
429 | """ |
430 | ||
431 | def testProtobufMeta(self): | |
432 | """ | |
433 | Protobuf: Meta values | |
434 | """ | |
435 | name = 'meta.protobuf.tests.powerdns.com.' | |
436 | query = dns.message.make_query(name, 'A', 'IN') | |
437 | response = dns.message.make_response(query) | |
438 | rrset = dns.rrset.from_text(name, | |
439 | 3600, | |
440 | dns.rdataclass.IN, | |
441 | dns.rdatatype.A, | |
442 | '127.0.0.1') | |
443 | response.answer.append(rrset) | |
444 | ||
445 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
446 | self.assertTrue(receivedQuery) | |
447 | self.assertTrue(receivedResponse) | |
448 | receivedQuery.id = query.id | |
449 | self.assertEqual(query, receivedQuery) | |
450 | self.assertEqual(response, receivedResponse) | |
451 | ||
e3762c2e RG |
452 | if self._protobufQueue.empty(): |
453 | # let the protobuf messages the time to get there | |
454 | time.sleep(1) | |
c02b6723 RG |
455 | |
456 | # check the protobuf message corresponding to the UDP query | |
457 | msg = self.getFirstProtobufMessage() | |
458 | ||
459 | self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name) | |
fb63c25e RG |
460 | # regular tags |
461 | self.assertEqual(len(msg.response.tags), 2) | |
462 | self.assertIn('my-tag-key:my-tag-value', msg.response.tags) | |
463 | self.assertIn('my-empty-key', msg.response.tags) | |
464 | # meta tags | |
c02b6723 | 465 | self.assertEqual(len(msg.meta), 2) |
30b91a66 RG |
466 | tags = {} |
467 | for entry in msg.meta: | |
468 | tags[entry.key] = entry.value.stringVal | |
469 | ||
470 | self.assertIn('b64', tags) | |
471 | self.assertIn('my-tag-export-name', tags) | |
472 | ||
c02b6723 | 473 | b64EncodedQuery = base64.b64encode(query.to_wire()).decode('ascii') |
30b91a66 RG |
474 | self.assertEqual(tags['b64'], [b64EncodedQuery]) |
475 | self.assertEqual(tags['my-tag-export-name'], ['my-tag-value']) | |
c02b6723 RG |
476 | |
477 | # check the protobuf message corresponding to the UDP response | |
478 | msg = self.getFirstProtobufMessage() | |
479 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response) | |
fb63c25e RG |
480 | # regular tags |
481 | self.assertEqual(len(msg.response.tags), 2) | |
482 | self.assertIn('my-tag-key2:my-tag-value2', msg.response.tags) | |
483 | self.assertIn('my-empty-key', msg.response.tags) | |
484 | # meta tags | |
c02b6723 RG |
485 | self.assertEqual(len(msg.meta), 1) |
486 | self.assertEqual(msg.meta[0].key, 'my-tag-export-name') | |
6b9302bd | 487 | self.assertEqual(len(msg.meta[0].value.stringVal), 3) |
30b91a66 RG |
488 | self.assertIn('my-tag-key:my-tag-value', msg.meta[0].value.stringVal) |
489 | self.assertIn('my-tag-key2:my-tag-value2', msg.meta[0].value.stringVal) | |
6b9302bd RG |
490 | # no ':' when the value is empty |
491 | self.assertIn('my-empty-key', msg.meta[0].value.stringVal) | |
c02b6723 | 492 | |
e3762c2e RG |
493 | class TestProtobufExtendedDNSErrorTags(DNSDistProtobufTest): |
494 | _config_params = ['_testServerPort', '_protobufServerPort'] | |
495 | _config_template = """ | |
496 | newServer{address="127.0.0.1:%s"} | |
497 | rl = newRemoteLogger('127.0.0.1:%d') | |
498 | ||
499 | addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'})) | |
500 | addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {serverID='dnsdist-server-1', exportExtendedErrorsToMeta='extended-error'})) | |
501 | """ | |
502 | ||
503 | def testProtobufExtendedError(self): | |
504 | """ | |
505 | Protobuf: Extended Error | |
506 | """ | |
507 | name = 'extended-error.protobuf.tests.powerdns.com.' | |
508 | query = dns.message.make_query(name, 'A', 'IN') | |
509 | response = dns.message.make_response(query) | |
510 | rrset = dns.rrset.from_text(name, | |
511 | 3600, | |
512 | dns.rdataclass.IN, | |
513 | dns.rdatatype.A, | |
514 | '127.0.0.1') | |
515 | response.answer.append(rrset) | |
516 | ede = extendederrors.ExtendedErrorOption(15, b'Blocked by RPZ!') | |
517 | response.use_edns(edns=True, payload=4096, options=[ede]) | |
518 | ||
519 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
520 | self.assertTrue(receivedQuery) | |
521 | self.assertTrue(receivedResponse) | |
522 | receivedQuery.id = query.id | |
523 | self.assertEqual(query, receivedQuery) | |
524 | self.assertEqual(response, receivedResponse) | |
525 | ||
526 | if self._protobufQueue.empty(): | |
527 | # let the protobuf messages the time to get there | |
528 | time.sleep(1) | |
529 | ||
530 | # check the protobuf message corresponding to the UDP query | |
531 | msg = self.getFirstProtobufMessage() | |
532 | ||
533 | self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name) | |
534 | ||
535 | # meta tags | |
536 | self.assertEqual(len(msg.meta), 0) | |
537 | ||
538 | # check the protobuf message corresponding to the UDP response | |
539 | msg = self.getFirstProtobufMessage() | |
540 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response) | |
541 | ||
542 | # meta tags | |
543 | self.assertEqual(len(msg.meta), 1) | |
544 | ||
545 | self.assertEqual(msg.meta[0].key, 'extended-error') | |
546 | self.assertEqual(len(msg.meta[0].value.intVal), 1) | |
547 | self.assertEqual(len(msg.meta[0].value.stringVal), 1) | |
548 | self.assertIn(15, msg.meta[0].value.intVal) | |
549 | self.assertIn('Blocked by RPZ!', msg.meta[0].value.stringVal) | |
550 | ||
c02b6723 RG |
551 | class TestProtobufMetaDOH(DNSDistProtobufTest): |
552 | ||
553 | _serverKey = 'server.key' | |
554 | _serverCert = 'server.chain' | |
555 | _serverName = 'tls.tests.dnsdist.org' | |
556 | _caCert = 'ca.pem' | |
630eb526 | 557 | _tlsServerPort = pickAvailablePort() |
b030f5bb RG |
558 | _dohWithNGHTTP2ServerPort = pickAvailablePort() |
559 | _dohWithNGHTTP2BaseURL = ("https://%s:%d/dns-query" % (_serverName, _dohWithNGHTTP2ServerPort)) | |
560 | _dohWithH2OServerPort = pickAvailablePort() | |
561 | _dohWithH2OBaseURL = ("https://%s:%d/dns-query" % (_serverName, _dohWithH2OServerPort)) | |
c02b6723 RG |
562 | _config_template = """ |
563 | newServer{address="127.0.0.1:%d"} | |
564 | rl = newRemoteLogger('127.0.0.1:%d') | |
334e3549 RG |
565 | |
566 | addTLSLocal("127.0.0.1:%s", "%s", "%s", { provider="openssl" }) | |
b030f5bb RG |
567 | addDOHLocal("127.0.0.1:%d", "%s", "%s", { '/dns-query' }, { keepIncomingHeaders=true, library='nghttp2' }) |
568 | addDOHLocal("127.0.0.1:%d", "%s", "%s", { '/dns-query' }, { keepIncomingHeaders=true, library='h2o' }) | |
c02b6723 RG |
569 | |
570 | local mytags = {path='doh-path', host='doh-host', ['query-string']='doh-query-string', scheme='doh-scheme', agent='doh-header:user-agent'} | |
571 | addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'}, mytags)) | |
572 | addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, false, {serverID='dnsdist-server-1'}, mytags)) | |
573 | """ | |
b030f5bb RG |
574 | _config_params = ['_testServerPort', '_protobufServerPort', '_tlsServerPort', '_serverCert', '_serverKey', '_dohWithNGHTTP2ServerPort', '_serverCert', '_serverKey', '_dohWithH2OServerPort', '_serverCert', '_serverKey'] |
575 | ||
c02b6723 RG |
576 | def testProtobufMetaDoH(self): |
577 | """ | |
578 | Protobuf: Meta values - DoH | |
579 | """ | |
580 | name = 'meta-doh.protobuf.tests.powerdns.com.' | |
581 | query = dns.message.make_query(name, 'A', 'IN') | |
582 | response = dns.message.make_response(query) | |
583 | rrset = dns.rrset.from_text(name, | |
584 | 3600, | |
585 | dns.rdataclass.IN, | |
586 | dns.rdatatype.A, | |
587 | '127.0.0.1') | |
588 | response.answer.append(rrset) | |
589 | ||
b030f5bb | 590 | for method in ("sendUDPQuery", "sendTCPQuery", "sendDOTQueryWrapper", "sendDOHWithNGHTTP2QueryWrapper", "sendDOHWithH2OQueryWrapper"): |
334e3549 RG |
591 | sender = getattr(self, method) |
592 | (receivedQuery, receivedResponse) = sender(query, response) | |
593 | ||
594 | self.assertTrue(receivedQuery) | |
595 | self.assertTrue(receivedResponse) | |
596 | receivedQuery.id = query.id | |
597 | self.assertEqual(query, receivedQuery) | |
598 | self.assertEqual(response, receivedResponse) | |
599 | ||
e3762c2e RG |
600 | if self._protobufQueue.empty(): |
601 | # let the protobuf messages the time to get there | |
602 | time.sleep(1) | |
334e3549 RG |
603 | |
604 | # check the protobuf message corresponding to the query | |
605 | msg = self.getFirstProtobufMessage() | |
606 | ||
607 | if method == "sendUDPQuery": | |
608 | pbMessageType = dnsmessage_pb2.PBDNSMessage.UDP | |
609 | elif method == "sendTCPQuery": | |
610 | pbMessageType = dnsmessage_pb2.PBDNSMessage.TCP | |
611 | elif method == "sendDOTQueryWrapper": | |
612 | pbMessageType = dnsmessage_pb2.PBDNSMessage.DOT | |
b030f5bb | 613 | elif method == "sendDOHWithNGHTTP2QueryWrapper" or method == "sendDOHWithH2OQueryWrapper": |
334e3549 RG |
614 | pbMessageType = dnsmessage_pb2.PBDNSMessage.DOH |
615 | ||
334e3549 RG |
616 | self.checkProtobufQuery(msg, pbMessageType, query, dns.rdataclass.IN, dns.rdatatype.A, name) |
617 | self.assertEqual(len(msg.meta), 5) | |
618 | tags = {} | |
619 | for entry in msg.meta: | |
04ba56ed RG |
620 | self.assertEqual(len(entry.value.stringVal), 1) |
621 | tags[entry.key] = entry.value.stringVal[0] | |
334e3549 RG |
622 | |
623 | self.assertIn('agent', tags) | |
b030f5bb | 624 | if method == "sendDOHWithNGHTTP2QueryWrapper" or method == "sendDOHWithH2OQueryWrapper": |
334e3549 RG |
625 | self.assertIn('PycURL', tags['agent']) |
626 | self.assertIn('host', tags) | |
b030f5bb RG |
627 | if method == "sendDOHWithNGHTTP2QueryWrapper": |
628 | self.assertEqual(tags['host'], self._serverName + ':' + str(self._dohWithNGHTTP2ServerPort)) | |
629 | elif method == "sendDOHWithH2OQueryWrapper": | |
630 | self.assertEqual(tags['host'], self._serverName + ':' + str(self._dohWithH2OServerPort)) | |
334e3549 RG |
631 | self.assertIn('path', tags) |
632 | self.assertEqual(tags['path'], '/dns-query') | |
633 | self.assertIn('query-string', tags) | |
634 | self.assertIn('?dns=', tags['query-string']) | |
635 | self.assertIn('scheme', tags) | |
636 | self.assertEqual(tags['scheme'], 'https') | |
94fe1b32 | 637 | self.assertEqual(msg.httpVersion, dnsmessage_pb2.PBDNSMessage.HTTPVersion.HTTP2) |
334e3549 RG |
638 | |
639 | # check the protobuf message corresponding to the response | |
640 | msg = self.getFirstProtobufMessage() | |
641 | self.checkProtobufResponse(msg, pbMessageType, response) | |
642 | self.assertEqual(len(msg.meta), 5) | |
643 | tags = {} | |
644 | for entry in msg.meta: | |
04ba56ed RG |
645 | self.assertEqual(len(entry.value.stringVal), 1) |
646 | tags[entry.key] = entry.value.stringVal[0] | |
334e3549 RG |
647 | |
648 | self.assertIn('agent', tags) | |
b030f5bb | 649 | if method == "sendDOHWithNGHTTP2QueryWrapper" or method == "sendDOHWithH2OQueryWrapper": |
334e3549 RG |
650 | self.assertIn('PycURL', tags['agent']) |
651 | self.assertIn('host', tags) | |
b030f5bb RG |
652 | if method == "sendDOHWithNGHTTP2QueryWrapper": |
653 | self.assertEqual(tags['host'], self._serverName + ':' + str(self._dohWithNGHTTP2ServerPort)) | |
654 | elif method == "sendDOHWithH2OQueryWrapper": | |
655 | self.assertEqual(tags['host'], self._serverName + ':' + str(self._dohWithH2OServerPort)) | |
334e3549 RG |
656 | self.assertIn('path', tags) |
657 | self.assertEqual(tags['path'], '/dns-query') | |
658 | self.assertIn('query-string', tags) | |
659 | self.assertIn('?dns=', tags['query-string']) | |
660 | self.assertIn('scheme', tags) | |
661 | self.assertEqual(tags['scheme'], 'https') | |
c02b6723 RG |
662 | |
663 | class TestProtobufMetaProxy(DNSDistProtobufTest): | |
664 | ||
665 | _config_params = ['_testServerPort', '_protobufServerPort'] | |
666 | _config_template = """ | |
667 | setProxyProtocolACL( { "127.0.0.1/32" } ) | |
668 | ||
669 | newServer{address="127.0.0.1:%d"} | |
670 | rl = newRemoteLogger('127.0.0.1:%d') | |
671 | ||
672 | local mytags = {pp='proxy-protocol-values', pp42='proxy-protocol-value:42'} | |
673 | addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'}, mytags)) | |
674 | ||
675 | -- proxy protocol values are NOT passed to the response | |
676 | """ | |
677 | ||
678 | def testProtobufMetaProxy(self): | |
679 | """ | |
680 | Protobuf: Meta values - Proxy | |
681 | """ | |
682 | name = 'meta-proxy.protobuf.tests.powerdns.com.' | |
683 | query = dns.message.make_query(name, 'A', 'IN') | |
684 | response = dns.message.make_response(query) | |
685 | rrset = dns.rrset.from_text(name, | |
686 | 3600, | |
687 | dns.rdataclass.IN, | |
688 | dns.rdatatype.A, | |
689 | '127.0.0.1') | |
690 | response.answer.append(rrset) | |
691 | ||
692 | destAddr = "2001:db8::9" | |
693 | destPort = 9999 | |
694 | srcAddr = "2001:db8::8" | |
695 | srcPort = 8888 | |
696 | udpPayload = ProxyProtocol.getPayload(False, False, True, srcAddr, destAddr, srcPort, destPort, [ [ 2, b'foo'], [ 42, b'proxy'] ]) | |
697 | (receivedQuery, receivedResponse) = self.sendUDPQuery(udpPayload + query.to_wire(), response, rawQuery=True) | |
698 | ||
699 | self.assertTrue(receivedQuery) | |
700 | self.assertTrue(receivedResponse) | |
701 | receivedQuery.id = query.id | |
702 | self.assertEqual(query, receivedQuery) | |
703 | self.assertEqual(response, receivedResponse) | |
704 | ||
e3762c2e RG |
705 | if self._protobufQueue.empty(): |
706 | # let the protobuf messages the time to get there | |
707 | time.sleep(1) | |
c02b6723 RG |
708 | |
709 | # check the protobuf message corresponding to the UDP query | |
710 | msg = self.getFirstProtobufMessage() | |
711 | ||
712 | self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, initiator='2001:db8::8', v6=True) | |
713 | self.assertEqual(len(msg.meta), 2) | |
714 | tags = {} | |
715 | for entry in msg.meta: | |
30b91a66 | 716 | tags[entry.key] = entry.value.stringVal |
c02b6723 RG |
717 | |
718 | self.assertIn('pp42', tags) | |
30b91a66 | 719 | self.assertEqual(tags['pp42'], ['proxy']) |
c02b6723 | 720 | self.assertIn('pp', tags) |
30b91a66 RG |
721 | self.assertEqual(len(tags['pp']), 2) |
722 | self.assertIn('2:foo', tags['pp']) | |
723 | self.assertIn('42:proxy', tags['pp']) | |
c02b6723 | 724 | |
f29fabd9 RG |
725 | class TestProtobufIPCipher(DNSDistProtobufTest): |
726 | _config_params = ['_testServerPort', '_protobufServerPort', '_protobufServerID', '_protobufServerID'] | |
727 | _config_template = """ | |
728 | newServer{address="127.0.0.1:%s", useClientSubnet=true} | |
729 | key = makeIPCipherKey("some 16-byte key") | |
730 | rl = newRemoteLogger('127.0.0.1:%s') | |
731 | addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='%s', ipEncryptKey=key})) -- Send protobuf message before lookup | |
732 | addResponseAction(AllRule(), RemoteLogResponseAction(rl, nil, true, {serverID='%s', ipEncryptKey=key})) -- Send protobuf message after lookup | |
733 | ||
734 | """ | |
735 | ||
736 | def testProtobuf(self): | |
737 | """ | |
87f46425 | 738 | Protobuf: Send data to a protobuf server, with pseudonymization |
f29fabd9 RG |
739 | """ |
740 | name = 'query.protobuf-ipcipher.tests.powerdns.com.' | |
741 | ||
78603024 | 742 | target = 'target.protobuf-ipcipher.tests.powerdns.com.' |
f29fabd9 RG |
743 | query = dns.message.make_query(name, 'A', 'IN') |
744 | response = dns.message.make_response(query) | |
745 | ||
746 | rrset = dns.rrset.from_text(name, | |
747 | 3600, | |
748 | dns.rdataclass.IN, | |
749 | dns.rdatatype.CNAME, | |
750 | target) | |
751 | response.answer.append(rrset) | |
752 | ||
753 | rrset = dns.rrset.from_text(target, | |
754 | 3600, | |
755 | dns.rdataclass.IN, | |
756 | dns.rdatatype.A, | |
757 | '127.0.0.1') | |
758 | response.answer.append(rrset) | |
759 | ||
760 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
761 | self.assertTrue(receivedQuery) | |
762 | self.assertTrue(receivedResponse) | |
763 | receivedQuery.id = query.id | |
4bfebc93 CH |
764 | self.assertEqual(query, receivedQuery) |
765 | self.assertEqual(response, receivedResponse) | |
f29fabd9 | 766 | |
e3762c2e RG |
767 | if self._protobufQueue.empty(): |
768 | # let the protobuf messages the time to get there | |
769 | time.sleep(1) | |
f29fabd9 RG |
770 | |
771 | # check the protobuf message corresponding to the UDP query | |
772 | msg = self.getFirstProtobufMessage() | |
773 | ||
774 | # 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key | |
775 | self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.UDP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '108.41.239.98') | |
776 | ||
777 | # check the protobuf message corresponding to the UDP response | |
778 | msg = self.getFirstProtobufMessage() | |
779 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.UDP, response, '108.41.239.98') | |
780 | ||
4bfebc93 | 781 | self.assertEqual(len(msg.response.rrs), 2) |
f29fabd9 RG |
782 | rr = msg.response.rrs[0] |
783 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600) | |
4bfebc93 | 784 | self.assertEqual(rr.rdata.decode('ascii'), target) |
f29fabd9 RG |
785 | rr = msg.response.rrs[1] |
786 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600) | |
4bfebc93 | 787 | self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1') |
f29fabd9 RG |
788 | |
789 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
790 | self.assertTrue(receivedQuery) | |
791 | self.assertTrue(receivedResponse) | |
792 | receivedQuery.id = query.id | |
4bfebc93 CH |
793 | self.assertEqual(query, receivedQuery) |
794 | self.assertEqual(response, receivedResponse) | |
f29fabd9 | 795 | |
e3762c2e RG |
796 | if self._protobufQueue.empty(): |
797 | # let the protobuf messages the time to get there | |
798 | time.sleep(1) | |
f29fabd9 RG |
799 | |
800 | # check the protobuf message corresponding to the TCP query | |
801 | msg = self.getFirstProtobufMessage() | |
802 | # 108.41.239.98 is 127.0.0.1 pseudonymized with ipcipher and the current key | |
803 | self.checkProtobufQuery(msg, dnsmessage_pb2.PBDNSMessage.TCP, query, dns.rdataclass.IN, dns.rdatatype.A, name, '108.41.239.98') | |
804 | ||
805 | # check the protobuf message corresponding to the TCP response | |
806 | msg = self.getFirstProtobufMessage() | |
807 | self.checkProtobufResponse(msg, dnsmessage_pb2.PBDNSMessage.TCP, response, '108.41.239.98') | |
4bfebc93 | 808 | self.assertEqual(len(msg.response.rrs), 2) |
f29fabd9 RG |
809 | rr = msg.response.rrs[0] |
810 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.CNAME, name, 3600) | |
4bfebc93 | 811 | self.assertEqual(rr.rdata.decode('ascii'), target) |
f29fabd9 RG |
812 | rr = msg.response.rrs[1] |
813 | self.checkProtobufResponseRecord(rr, dns.rdataclass.IN, dns.rdatatype.A, target, 3600) | |
4bfebc93 | 814 | self.assertEqual(socket.inet_ntop(socket.AF_INET, rr.rdata), '127.0.0.1') |
655fe34d CHB |
815 | |
816 | class TestProtobufQUIC(DNSDistProtobufTest): | |
817 | ||
818 | _serverKey = 'server.key' | |
819 | _serverCert = 'server.chain' | |
820 | _serverName = 'tls.tests.dnsdist.org' | |
821 | _caCert = 'ca.pem' | |
822 | _doqServerPort = pickAvailablePort() | |
823 | _doh3ServerPort = pickAvailablePort() | |
824 | _dohBaseURL = ("https://%s:%d/" % (_serverName, _doh3ServerPort)) | |
825 | _config_template = """ | |
826 | newServer{address="127.0.0.1:%d"} | |
827 | rl = newRemoteLogger('127.0.0.1:%d') | |
828 | ||
829 | addDOQLocal("127.0.0.1:%d", "%s", "%s") | |
830 | addDOH3Local("127.0.0.1:%d", "%s", "%s") | |
831 | ||
832 | addAction(AllRule(), RemoteLogAction(rl, nil, {serverID='dnsdist-server-1'})) | |
833 | """ | |
834 | _config_params = ['_testServerPort', '_protobufServerPort', '_doqServerPort', '_serverCert', '_serverKey', '_doh3ServerPort', '_serverCert', '_serverKey'] | |
835 | ||
836 | def testProtobufMetaDoH(self): | |
837 | """ | |
838 | Protobuf: Test logged protocol for QUIC and DOH3 | |
839 | """ | |
840 | name = 'quic.protobuf.tests.powerdns.com.' | |
841 | query = dns.message.make_query(name, 'A', 'IN') | |
842 | response = dns.message.make_response(query) | |
843 | rrset = dns.rrset.from_text(name, | |
844 | 3600, | |
845 | dns.rdataclass.IN, | |
846 | dns.rdatatype.A, | |
847 | '127.0.0.1') | |
848 | response.answer.append(rrset) | |
849 | ||
850 | for method in ("sendDOQQueryWrapper", "sendDOH3QueryWrapper"): | |
851 | sender = getattr(self, method) | |
852 | (receivedQuery, receivedResponse) = sender(query, response) | |
853 | ||
854 | self.assertTrue(receivedQuery) | |
855 | self.assertTrue(receivedResponse) | |
856 | receivedQuery.id = query.id | |
857 | self.assertEqual(query, receivedQuery) | |
858 | self.assertEqual(response, receivedResponse) | |
859 | ||
860 | if self._protobufQueue.empty(): | |
861 | # let the protobuf messages the time to get there | |
862 | time.sleep(1) | |
863 | ||
864 | # check the protobuf message corresponding to the query | |
865 | msg = self.getFirstProtobufMessage() | |
866 | ||
867 | if method == "sendDOQQueryWrapper": | |
868 | pbMessageType = dnsmessage_pb2.PBDNSMessage.DOQ | |
869 | elif method == "sendDOH3QueryWrapper": | |
870 | pbMessageType = dnsmessage_pb2.PBDNSMessage.DOH | |
871 | self.assertEqual(msg.httpVersion, dnsmessage_pb2.PBDNSMessage.HTTPVersion.HTTP3) | |
872 | ||
873 | self.checkProtobufQuery(msg, pbMessageType, query, dns.rdataclass.IN, dns.rdatatype.A, name) |