10 from dnsdisttests
import DNSDistTest
, pickAvailablePort
12 class OutgoingDOHTests(object):
15 _webServerPort
= pickAvailablePort()
16 _webServerBasicAuthPassword
= 'secret'
17 _webServerAPIKey
= 'apisecret'
18 _webServerBasicAuthPasswordHashed
= '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
19 _webServerAPIKeyHashed
= '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
21 def checkOnlyDOHResponderHit(self
, numberOfDOHQueries
=1):
22 self
.assertNotIn('UDP Responder', self
._responsesCounter
)
23 self
.assertNotIn('TCP Responder', self
._responsesCounter
)
24 self
.assertNotIn('TLS Responder', self
._responsesCounter
)
25 self
.assertEqual(self
._responsesCounter
['DoH Connection Handler'], numberOfDOHQueries
)
27 def getServerStat(self
, key
):
28 headers
= {'x-api-key': self
._webServerAPIKey
}
29 url
= 'http://127.0.0.1:' + str(self
._webServerPort
) + '/api/v1/servers/localhost'
30 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
32 self
.assertEqual(r
.status_code
, 200)
33 self
.assertTrue(r
.json())
35 self
.assertTrue(len(content
['servers']), 1)
36 server
= content
['servers'][0]
37 self
.assertIn(key
, server
)
42 Outgoing DOH: UDP query is sent via DOH
44 name
= 'udp.outgoing-doh.test.powerdns.com.'
45 query
= dns
.message
.make_query(name
, 'A', 'IN')
46 expectedResponse
= dns
.message
.make_response(query
)
47 rrset
= dns
.rrset
.from_text(name
,
52 expectedResponse
.answer
.append(rrset
)
54 connsBefore
= self
.getServerStat('tcpReusedConnections')
56 numberOfUDPQueries
= 10
57 for _
in range(numberOfUDPQueries
):
58 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
59 self
.assertEqual(query
, receivedQuery
)
60 self
.assertEqual(receivedResponse
, expectedResponse
)
62 # there was one TCP query in testTCP (below, but before in alphabetical order)
63 numberOfQueries
= numberOfUDPQueries
+ 1
64 self
.checkOnlyDOHResponderHit(numberOfUDPQueries
)
66 self
.assertEqual(self
.getServerStat('tcpNewConnections'), 1)
67 self
.assertEqual(self
.getServerStat('tcpReusedConnections'), connsBefore
+ numberOfQueries
- 1)
68 self
.assertEqual(self
.getServerStat('tlsResumptions'), 0)
72 Outgoing DOH: TCP query is sent via DOH
74 name
= 'tcp.outgoing-doh.test.powerdns.com.'
75 query
= dns
.message
.make_query(name
, 'A', 'IN')
76 expectedResponse
= dns
.message
.make_response(query
)
77 rrset
= dns
.rrset
.from_text(name
,
82 expectedResponse
.answer
.append(rrset
)
84 connsBefore
= self
.getServerStat('tcpReusedConnections')
86 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
87 self
.assertEqual(query
, receivedQuery
)
88 self
.assertEqual(receivedResponse
, expectedResponse
)
89 self
.checkOnlyDOHResponderHit()
90 self
.assertEqual(self
.getServerStat('tcpNewConnections'), 1)
91 self
.assertEqual(self
.getServerStat('tcpReusedConnections'), connsBefore
)
92 self
.assertEqual(self
.getServerStat('tlsResumptions'), 0)
94 def testUDPCache(self
):
96 Outgoing DOH: UDP query is sent via DOH, should be cached
98 name
= 'udp.cached.outgoing-doh.test.powerdns.com.'
99 query
= dns
.message
.make_query(name
, 'A', 'IN')
100 expectedResponse
= dns
.message
.make_response(query
)
101 rrset
= dns
.rrset
.from_text(name
,
106 expectedResponse
.answer
.append(rrset
)
108 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
109 self
.assertEqual(query
, receivedQuery
)
110 self
.assertEqual(receivedResponse
, expectedResponse
)
112 numberOfUDPQueries
= 10
113 for _
in range(numberOfUDPQueries
):
114 (_
, receivedResponse
) = self
.sendUDPQuery(query
, useQueue
=False, response
=None)
115 self
.assertEqual(receivedResponse
, expectedResponse
)
117 def testTCPCache(self
):
119 Outgoing DOH: TCP query is sent via DOH, should be cached
121 name
= 'tcp.cached.outgoing-doh.test.powerdns.com.'
122 query
= dns
.message
.make_query(name
, 'A', 'IN')
123 expectedResponse
= dns
.message
.make_response(query
)
124 rrset
= dns
.rrset
.from_text(name
,
129 expectedResponse
.answer
.append(rrset
)
131 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
132 self
.assertEqual(query
, receivedQuery
)
133 self
.assertEqual(receivedResponse
, expectedResponse
)
135 numberOfTCPQueries
= 10
136 for _
in range(numberOfTCPQueries
):
137 (_
, receivedResponse
) = self
.sendTCPQuery(query
, useQueue
=False, response
=None)
138 self
.assertEqual(receivedResponse
, expectedResponse
)
140 def testZHealthChecks(self
):
141 # this test has to run last, as it will mess up the TCP connection counter,
142 # hence the 'Z' in the name
143 self
.sendConsoleCommand("getServer(0):setAuto()")
145 status
= self
.sendConsoleCommand("if getServer(0):isUp() then return 'up' else return 'down' end").strip("\n")
146 self
.assertEqual(status
, 'up')
148 class BrokenOutgoingDOHTests(object):
151 _webServerPort
= pickAvailablePort()
152 _webServerBasicAuthPassword
= 'secret'
153 _webServerAPIKey
= 'apisecret'
154 _webServerBasicAuthPasswordHashed
= '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
155 _webServerAPIKeyHashed
= '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
157 def checkNoResponderHit(self
):
158 self
.assertNotIn('UDP Responder', self
._responsesCounter
)
159 self
.assertNotIn('TCP Responder', self
._responsesCounter
)
160 self
.assertNotIn('TLS Responder', self
._responsesCounter
)
161 self
.assertNotIn('DOH Responder', self
._responsesCounter
)
165 Outgoing DOH (broken): UDP query is sent via DOH
167 name
= 'udp.broken-outgoing-doh.test.powerdns.com.'
168 query
= dns
.message
.make_query(name
, 'A', 'IN')
170 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
171 self
.assertEqual(receivedResponse
, None)
172 self
.checkNoResponderHit()
176 Outgoing DOH (broken): TCP query is sent via DOH
178 name
= 'tcp.broken-outgoing-doh.test.powerdns.com.'
179 query
= dns
.message
.make_query(name
, 'A', 'IN')
180 expectedResponse
= dns
.message
.make_response(query
)
181 rrset
= dns
.rrset
.from_text(name
,
186 expectedResponse
.answer
.append(rrset
)
188 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
189 self
.assertEqual(receivedResponse
, None)
190 self
.checkNoResponderHit()
192 class OutgoingDOHBrokenResponsesTests(object):
195 _webServerPort
= pickAvailablePort()
196 _webServerBasicAuthPassword
= 'secret'
197 _webServerAPIKey
= 'apisecret'
198 _webServerBasicAuthPasswordHashed
= '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM='
199 _webServerAPIKeyHashed
= '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso='
203 Outgoing DOH (broken responses): UDP query is sent via DOH
205 name
= '500-status.broken-responses.outgoing-doh.test.powerdns.com.'
206 query
= dns
.message
.make_query(name
, 'A', 'IN')
208 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
209 self
.assertEqual(receivedResponse
, None)
211 name
= 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.'
212 query
= dns
.message
.make_query(name
, 'A', 'IN')
214 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
215 self
.assertEqual(receivedResponse
, None)
217 name
= 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.'
218 query
= dns
.message
.make_query(name
, 'A', 'IN')
220 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
221 self
.assertEqual(receivedResponse
, None)
223 # but a valid response should be successful
224 name
= 'valid.broken-responses.outgoing-doh.test.powerdns.com.'
225 query
= dns
.message
.make_query(name
, 'A', 'IN')
226 response
= dns
.message
.make_response(query
)
228 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
229 # we can't check the received query because the responder does not populate the queue..
230 # self.assertEqual(query, receivedQuery)
231 self
.assertEqual(response
, receivedResponse
)
235 Outgoing DOH (broken responses): TCP query is sent via DOH
237 name
= '500-status.broken-responses.outgoing-doh.test.powerdns.com.'
238 query
= dns
.message
.make_query(name
, 'A', 'IN')
240 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
241 self
.assertEqual(receivedResponse
, None)
243 name
= 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.'
244 query
= dns
.message
.make_query(name
, 'A', 'IN')
246 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
247 self
.assertEqual(receivedResponse
, None)
249 name
= 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.'
250 query
= dns
.message
.make_query(name
, 'A', 'IN')
252 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
253 self
.assertEqual(receivedResponse
, None)
255 # but a valid response should be successful
256 name
= 'valid.broken-responses.outgoing-doh.test.powerdns.com.'
257 query
= dns
.message
.make_query(name
, 'A', 'IN')
258 response
= dns
.message
.make_response(query
)
260 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
261 # we can't check the received query because the responder does not populate the queue..
262 #self.assertEqual(query, receivedQuery)
263 self
.assertEqual(response
, receivedResponse
)
265 class TestOutgoingDOHOpenSSL(DNSDistTest
, OutgoingDOHTests
):
266 _tlsBackendPort
= pickAvailablePort()
267 _tlsProvider
= 'openssl'
268 _consoleKey
= DNSDistTest
.generateConsoleKey()
269 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
270 _config_params
= ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
271 _config_template
= """
273 controlSocket("127.0.0.1:%d")
274 setMaxTCPClientThreads(1)
275 newServer{address="127.0.0.1:%s", tls='%s', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', pool={'', 'cache'}}:setUp()
276 webserver("127.0.0.1:%s")
277 setWebserverConfig({password="%s", apiKey="%s"})
279 pc = newPacketCache(100)
280 getPool('cache'):setCache(pc)
281 smn = newSuffixMatchNode()
282 smn:add('cached.outgoing-doh.test.powerdns.com.')
283 addAction(SuffixMatchNodeRule(smn), PoolAction('cache'))
287 def sniCallback(sslSocket
, sni
, sslContext
):
288 assert(sni
== 'powerdns.com')
292 def startResponders(cls
):
293 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
294 tlsContext
.set_alpn_protocols(["h2"])
295 tlsContext
.load_cert_chain('server.chain', 'server.key')
296 # requires Python 3.7+
297 if hasattr(tlsContext
, 'sni_callback'):
298 tlsContext
.sni_callback
= cls
.sniCallback
300 print("Launching DOH responder..")
301 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, None, tlsContext
])
302 cls
._DOHResponder
.daemon
= True
303 cls
._DOHResponder
.start()
305 class TestOutgoingDOHGnuTLS(DNSDistTest
, OutgoingDOHTests
):
306 _tlsBackendPort
= pickAvailablePort()
307 _tlsProvider
= 'gnutls'
308 _consoleKey
= DNSDistTest
.generateConsoleKey()
309 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
310 _config_params
= ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
311 _config_template
= """
313 controlSocket("127.0.0.1:%d")
314 setMaxTCPClientThreads(1)
315 newServer{address="127.0.0.1:%s", tls='%s', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', pool={'', 'cache'}}:setUp()
316 webserver("127.0.0.1:%s")
317 setWebserverConfig({password="%s", apiKey="%s"})
319 pc = newPacketCache(100)
320 getPool('cache'):setCache(pc)
321 smn = newSuffixMatchNode()
322 smn:add('cached.outgoing-doh.test.powerdns.com.')
323 addAction(SuffixMatchNodeRule(smn), PoolAction('cache'))
327 def startResponders(cls
):
328 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
329 tlsContext
.load_cert_chain('server.chain', 'server.key')
330 tlsContext
.keylog_filename
= "/tmp/keys"
332 print("Launching DOH responder..")
333 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, None, tlsContext
])
334 cls
._DOHResponder
.daemon
= True
335 cls
._DOHResponder
.start()
337 class TestOutgoingDOHOpenSSLWrongCertName(DNSDistTest
, BrokenOutgoingDOHTests
):
338 _tlsBackendPort
= pickAvailablePort()
339 _config_params
= ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
340 _config_template
= """
341 setMaxTCPClientThreads(1)
342 newServer{address="127.0.0.1:%s", tls='openssl', validateCertificates=true, caStore='ca.pem', subjectName='not-powerdns.com', dohPath='/dns-query'}:setUp()
343 webserver("127.0.0.1:%s")
344 setWebserverConfig({password="%s", apiKey="%s"})
348 def startResponders(cls
):
349 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
350 tlsContext
.load_cert_chain('server.chain', 'server.key')
352 print("Launching DOH responder..")
353 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, None, tlsContext
])
354 cls
._DOHResponder
.daemon
= True
355 cls
._DOHResponder
.start()
357 class TestOutgoingDOHGnuTLSWrongCertName(DNSDistTest
, BrokenOutgoingDOHTests
):
358 _tlsBackendPort
= pickAvailablePort()
359 _config_params
= ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
360 _config_template
= """
361 setMaxTCPClientThreads(1)
362 newServer{address="127.0.0.1:%s", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='not-powerdns.com', dohPath='/dns-query'}:setUp()
363 webserver("127.0.0.1:%s")
364 setWebserverConfig({password="%s", apiKey="%s"})
368 def startResponders(cls
):
369 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
370 tlsContext
.load_cert_chain('server.chain', 'server.key')
372 print("Launching DOH responder..")
373 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, None, tlsContext
])
374 cls
._DOHResponder
.daemon
= True
375 cls
._DOHResponder
.start()
377 class TestOutgoingDOHOpenSSLWrongCertNameButNoCheck(DNSDistTest
, OutgoingDOHTests
):
378 _tlsBackendPort
= pickAvailablePort()
379 _tlsProvider
= 'openssl'
380 _consoleKey
= DNSDistTest
.generateConsoleKey()
381 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
382 _config_params
= ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
383 _config_template
= """
385 controlSocket("127.0.0.1:%d")
386 setMaxTCPClientThreads(1)
387 newServer{address="127.0.0.1:%s", tls='%s', validateCertificates=false, caStore='ca.pem', subjectName='not-powerdns.com', dohPath='/dns-query', pool={'', 'cache'}}:setUp()
388 webserver("127.0.0.1:%s")
389 setWebserverConfig({password="%s", apiKey="%s"})
391 pc = newPacketCache(100)
392 getPool('cache'):setCache(pc)
393 smn = newSuffixMatchNode()
394 smn:add('cached.outgoing-doh.test.powerdns.com.')
395 addAction(SuffixMatchNodeRule(smn), PoolAction('cache'))
399 def startResponders(cls
):
400 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
401 tlsContext
.load_cert_chain('server.chain', 'server.key')
403 print("Launching DOH responder..")
404 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, None, tlsContext
])
405 cls
._DOHResponder
.daemon
= True
406 cls
._DOHResponder
.start()
408 class TestOutgoingDOHGnuTLSWrongCertNameButNoCheck(DNSDistTest
, OutgoingDOHTests
):
409 _tlsBackendPort
= pickAvailablePort()
410 _tlsProvider
= 'gnutls'
411 _consoleKey
= DNSDistTest
.generateConsoleKey()
412 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
413 _config_params
= ['_consoleKeyB64', '_consolePort', '_tlsBackendPort', '_tlsProvider', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
414 _config_template
= """
416 controlSocket("127.0.0.1:%d")
417 setMaxTCPClientThreads(1)
418 newServer{address="127.0.0.1:%s", tls='%s', validateCertificates=false, caStore='ca.pem', subjectName='not-powerdns.com', dohPath='/dns-query', pool={'', 'cache'}}:setUp()
419 webserver("127.0.0.1:%s")
420 setWebserverConfig({password="%s", apiKey="%s"})
422 pc = newPacketCache(100)
423 getPool('cache'):setCache(pc)
424 smn = newSuffixMatchNode()
425 smn:add('cached.outgoing-doh.test.powerdns.com.')
426 addAction(SuffixMatchNodeRule(smn), PoolAction('cache'))
430 def startResponders(cls
):
431 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
432 tlsContext
.load_cert_chain('server.chain', 'server.key')
434 print("Launching DOH responder..")
435 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, None, tlsContext
])
436 cls
._DOHResponder
.daemon
= True
437 cls
._DOHResponder
.start()
439 class TestOutgoingDOHBrokenResponsesOpenSSL(DNSDistTest
, OutgoingDOHBrokenResponsesTests
):
440 _tlsBackendPort
= pickAvailablePort()
441 _config_params
= ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
442 _config_template
= """
443 setMaxTCPClientThreads(1)
444 newServer{address="127.0.0.1:%s", tls='openssl', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', pool={'', 'cache'}}:setUp()
445 webserver("127.0.0.1:%s")
446 setWebserverConfig({password="%s", apiKey="%s"})
448 pc = newPacketCache(100)
449 getPool('cache'):setCache(pc)
450 smn = newSuffixMatchNode()
451 smn:add('cached.outgoing-doh.test.powerdns.com.')
452 addAction(SuffixMatchNodeRule(smn), PoolAction('cache'))
455 def callback(request
, headers
, fromQueue
, toQueue
):
457 if str(request
.question
[0].name
) == '500-status.broken-responses.outgoing-doh.test.powerdns.com.':
458 print("returning 500")
459 return 500, b
'Server error'
461 if str(request
.question
[0].name
) == 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.':
462 return 200, b
'not DNS'
464 if str(request
.question
[0].name
) == 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.':
467 print("Returning default for %s" % (request
.question
[0].name
))
468 return 200, dns
.message
.make_response(request
).to_wire()
471 def startResponders(cls
):
472 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
473 tlsContext
.set_alpn_protocols(["h2"])
474 tlsContext
.load_cert_chain('server.chain', 'server.key')
476 print("Launching DOH responder..")
477 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, cls
.callback
, tlsContext
])
478 cls
._DOHResponder
.daemon
= True
479 cls
._DOHResponder
.start()
481 class TestOutgoingDOHBrokenResponsesGnuTLS(DNSDistTest
, OutgoingDOHBrokenResponsesTests
):
482 _tlsBackendPort
= pickAvailablePort()
483 _config_params
= ['_tlsBackendPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
484 _config_template
= """
485 setMaxTCPClientThreads(1)
486 newServer{address="127.0.0.1:%s", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query'}:setUp()
487 webserver("127.0.0.1:%s")
488 setWebserverConfig({password="%s", apiKey="%s"})
492 def callback(request
, headers
, fromQueue
, toQueue
):
494 if str(request
.question
[0].name
) == '500-status.broken-responses.outgoing-doh.test.powerdns.com.':
495 print("returning 500")
496 return 500, b
'Server error'
498 if str(request
.question
[0].name
) == 'invalid-dns-payload.broken-responses.outgoing-doh.test.powerdns.com.':
499 return 200, b
'not DNS'
501 if str(request
.question
[0].name
) == 'closing-connection-id.broken-responses.outgoing-doh.test.powerdns.com.':
504 print("Returning default for %s" % (request
.question
[0].name
))
505 return 200, dns
.message
.make_response(request
).to_wire()
508 def startResponders(cls
):
509 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
510 tlsContext
.set_alpn_protocols(["h2"])
511 tlsContext
.load_cert_chain('server.chain', 'server.key')
513 print("Launching DOH responder..")
514 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, cls
.callback
, tlsContext
])
515 cls
._DOHResponder
.daemon
= True
516 cls
._DOHResponder
.start()
518 class TestOutgoingDOHProxyProtocol(DNSDistTest
):
520 _tlsBackendPort
= pickAvailablePort()
521 _config_params
= ['_tlsBackendPort']
522 _config_template
= """
523 setMaxTCPClientThreads(1)
524 newServer{address="127.0.0.1:%s", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', useProxyProtocol=true}:setUp()
529 def startResponders(cls
):
530 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
531 tlsContext
.set_alpn_protocols(["h2"])
532 tlsContext
.load_cert_chain('server.chain', 'server.key')
534 print("Launching DOH woth Proxy Protocol responder..")
535 cls
._DOHResponder
= threading
.Thread(name
='DOH with Proxy Protocol Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, None, tlsContext
, True])
536 cls
._DOHResponder
.daemon
= True
537 cls
._DOHResponder
.start()
541 Outgoing DOH with Proxy Protocol
543 name
= 'proxy-protocol.outgoing-doh.test.powerdns.com.'
544 query
= dns
.message
.make_query(name
, 'A', 'IN')
545 expectedResponse
= dns
.message
.make_response(query
)
546 rrset
= dns
.rrset
.from_text(name
,
551 expectedResponse
.answer
.append(rrset
)
553 (receivedProxyPayload
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
554 receivedQuery
= self
._fromResponderQueue
.get(True, 1.0)
555 self
.assertEqual(query
, receivedQuery
)
556 self
.assertEqual(receivedResponse
, expectedResponse
)
557 self
.checkMessageProxyProtocol(receivedProxyPayload
, '127.0.0.1', '127.0.0.1', False)
559 (receivedProxyPayload
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
560 receivedQuery
= self
._fromResponderQueue
.get(True, 1.0)
561 self
.assertEqual(query
, receivedQuery
)
562 self
.assertEqual(receivedResponse
, expectedResponse
)
563 self
.checkMessageProxyProtocol(receivedProxyPayload
, '127.0.0.1', '127.0.0.1', True)
565 class TestOutgoingDOHXForwarded(DNSDistTest
):
566 _tlsBackendPort
= pickAvailablePort()
567 _config_params
= ['_tlsBackendPort']
568 _config_template
= """
569 setMaxTCPClientThreads(1)
570 newServer{address="127.0.0.1:%s", tls='gnutls', validateCertificates=true, caStore='ca.pem', subjectName='powerdns.com', dohPath='/dns-query', addXForwardedHeaders=true}
574 def callback(request
, headersList
, fromQueue
, toQueue
):
576 if str(request
.question
[0].name
) == 'a.root-servers.net.':
577 # do not check headers on health-check queries
578 return 200, dns
.message
.make_response(request
).to_wire()
582 for k
,v
in headersList
:
585 if not b
'x-forwarded-for' in headers
:
586 print("missing X-Forwarded-For")
587 return 406, b
'Missing X-Forwarded-For header'
588 if not b
'x-forwarded-port' in headers
:
589 print("missing X-Forwarded-Port")
590 return 406, b
'Missing X-Forwarded-Port header'
591 if not b
'x-forwarded-proto' in headers
:
592 print("missing X-Forwarded-Proto")
593 return 406, b
'Missing X-Forwarded-Proto header'
595 toQueue
.put(request
, True, 1.0)
596 response
= fromQueue
.get(True, 1.0)
598 response
= copy
.copy(response
)
599 response
.id = request
.id
601 return 200, response
.to_wire()
604 def startResponders(cls
):
605 tlsContext
= ssl
.SSLContext(ssl
.PROTOCOL_TLS_SERVER
)
606 tlsContext
.set_alpn_protocols(["h2"])
607 tlsContext
.load_cert_chain('server.chain', 'server.key')
609 print("Launching DOH responder..")
610 cls
._DOHResponder
= threading
.Thread(name
='DOH Responder', target
=cls
.DOHResponder
, args
=[cls
._tlsBackendPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
, False, False, cls
.callback
, tlsContext
])
611 cls
._DOHResponder
.daemon
= True
612 cls
._DOHResponder
.start()
614 def testXForwarded(self
):
616 Outgoing DOH: X-Forwarded
618 name
= 'x-forwarded-for.outgoing-doh.test.powerdns.com.'
619 query
= dns
.message
.make_query(name
, 'A', 'IN')
620 expectedResponse
= dns
.message
.make_response(query
)
621 rrset
= dns
.rrset
.from_text(name
,
626 expectedResponse
.answer
.append(rrset
)
628 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, expectedResponse
)
629 self
.assertEqual(query
, receivedQuery
)
630 self
.assertEqual(receivedResponse
, expectedResponse
)
632 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, expectedResponse
)
633 self
.assertEqual(query
, receivedQuery
)
634 self
.assertEqual(receivedResponse
, expectedResponse
)