#!/usr/bin/env python2
-import dns
-import dns.message
import socket
import struct
import time
+import dns
+import dns.message
import libnacl
import libnacl.utils
-class DNSCryptResolverCertificate:
+class DNSCryptResolverCertificate(object):
DNSCRYPT_CERT_MAGIC = '\x44\x4e\x53\x43'
DNSCRYPT_ES_VERSION = '\x00\x01'
DNSCRYPT_PROTOCOL_MIN_VERSION = '\x00\x00'
validUntil = struct.unpack_from("!I", orig[48:52])[0]
return DNSCryptResolverCertificate(serial, validFrom, validUntil, resolverPK, clientMagic)
-class DNSCryptClient:
+class DNSCryptClient(object):
DNSCRYPT_NONCE_SIZE = 24
DNSCRYPT_MAC_SIZE = 16
DNSCRYPT_PADDED_BLOCK_SIZE = 64
cls._dnsdist.wait()
@classmethod
- def ResponderIncrementCounter(cls):
+ def _ResponderIncrementCounter(cls):
if threading.currentThread().name in cls._responsesCounter:
cls._responsesCounter[threading.currentThread().name] += 1
else:
cls._responsesCounter[threading.currentThread().name] = 1
+ @classmethod
+ def _getResponse(cls, request):
+ response = None
+ if len(request.question) != 1:
+ print("Skipping query with question count %d" % (len(request.question)))
+ return None
+ healthcheck = not str(request.question[0].name).endswith('tests.powerdns.com.')
+ if not healthcheck:
+ cls._ResponderIncrementCounter()
+ if not cls._toResponderQueue.empty():
+ response = cls._toResponderQueue.get(True, cls._queueTimeout)
+ if response:
+ response = copy.copy(response)
+ response.id = request.id
+ cls._fromResponderQueue.put(request, True, cls._queueTimeout)
+
+ if not response:
+ # unexpected query, or health check
+ response = dns.message.make_response(request)
+
+ return response
+
@classmethod
def UDPResponder(cls, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
data, addr = sock.recvfrom(4096)
request = dns.message.from_wire(data)
- answered = False
- if len(request.question) != 1:
- print("Skipping query with question count %d" % (len(request.question)))
+ response = cls._getResponse(request)
+ if not response:
continue
- if str(request.question[0].name).endswith('tests.powerdns.com.') and not cls._toResponderQueue.empty():
- response = cls._toResponderQueue.get(True, cls._queueTimeout)
- if response:
- response = copy.copy(response)
- response.id = request.id
- cls._fromResponderQueue.put(request, True, cls._queueTimeout)
- cls.ResponderIncrementCounter()
- answered = True
-
- if not answered:
- # unexpected query, or health check
- response = dns.message.make_response(request)
- rrset = None
- if request.question[0].rdclass == dns.rdataclass.IN:
- if request.question[0].rdtype == dns.rdatatype.A:
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- request.question[0].rdclass,
- request.question[0].rdtype,
- '127.0.0.1')
- elif request.question[0].rdtype == dns.rdatatype.AAAA:
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- request.question[0].rdclass,
- request.question[0].rdtype,
- '::1')
- if rrset:
- response.answer.append(rrset)
sock.settimeout(2.0)
sock.sendto(response.to_wire(), addr)
sock.listen(100)
while True:
- answered = False
(conn, _) = sock.accept()
conn.settimeout(2.0)
data = conn.recv(2)
(datalen,) = struct.unpack("!H", data)
data = conn.recv(datalen)
request = dns.message.from_wire(data)
- if len(request.question) != 1:
- print("Skipping query with question count %d" % (len(request.question)))
+ response = cls._getResponse(request)
+ if not response:
continue
- if str(request.question[0].name).endswith('tests.powerdns.com.') and not cls._toResponderQueue.empty():
- response = cls._toResponderQueue.get(True, cls._queueTimeout)
- if response:
- response = copy.copy(response)
- response.id = request.id
- cls._fromResponderQueue.put(request, True, cls._queueTimeout)
- cls.ResponderIncrementCounter()
- answered = True
-
- if not answered:
- # unexpected query, or health check
- response = dns.message.make_response(request)
- rrset = None
- if request.question[0].rdclass == dns.rdataclass.IN:
- if request.question[0].rdtype == dns.rdatatype.A:
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- request.question[0].rdclass,
- request.question[0].rdtype,
- '127.0.0.1')
- elif request.question[0].rdtype == dns.rdatatype.AAAA:
- rrset = dns.rrset.from_text(request.question[0].name,
- 3600,
- request.question[0].rdclass,
- request.question[0].rdtype,
- '::1')
- if rrset:
- response.answer.append(rrset)
wire = response.to_wire()
conn.send(struct.pack("!H", len(wire)))
self._toResponderQueue.get(False)
while not self._fromResponderQueue.empty():
- self._toResponderQueue.get(False)
+ self._fromResponderQueue.get(False)
--- /dev/null
+[MESSAGES CONTROL]
+disable=invalid-name, missing-docstring, line-too-long, superfluous-parens
\ No newline at end of file
class TestCaching(DNSDistTest):
_config_template = """
- pc = newPacketCache(5, 86400, 1)
+ pc = newPacketCache(100, 86400, 1)
getPool(""):setCache(pc)
addAction(makeRule("nocache.cache.tests.powerdns.com."), SkipCacheAction())
newServer{address="127.0.0.1:%s"}
"""
+
def testCached(self):
"""
Cache: Served from cache
total += TestCachingWithExistingEDNS._responsesCounter[key]
self.assertEquals(total, misses)
+
+class TestCachingCacheFull(DNSDistTest):
+
+ _config_template = """
+ pc = newPacketCache(1, 86400, 1)
+ getPool(""):setCache(pc)
+ newServer{address="127.0.0.1:%s"}
+ """
+ def testCacheFull(self):
+ """
+ Cache: No new entries are cached when the cache is full
+
+ """
+ misses = 0
+ name = 'cachenotfullyet.cache.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+
+ # Miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ # next queries should hit the cache
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, response)
+
+ # ok, now the cache is full, send another query
+ name = 'cachefull.cache.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'AAAA', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.AAAA,
+ '::1')
+ response.answer.append(rrset)
+
+ # Miss
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ # next queries should NOT hit the cache
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ misses += 1
+
+ total = 0
+ for key in TestCachingCacheFull._responsesCounter:
+ total += TestCachingCacheFull._responsesCounter[key]
+
+ self.assertEquals(total, misses)
class TestCheckConfig(unittest.TestCase):
- def tryDNSDist(self, configTemplate, shouldBeSuccessful=True):
+ @staticmethod
+ def tryDNSDist(configTemplate, shouldBeSuccessful=True):
conffile = 'dnsdist_test.conf'
with open(conffile, 'w') as conf:
conf.write("-- Autogenerated by dnsdisttests.py\n")
_config_template = """
truncateTC(true)
- block=newDNSName("powerdns.org.")
- function blockFilter(dq)
- if(dq.qname:isPartOf(block))
- then
- print("Blocking *.powerdns.org")
- return true
- end
- return false
- end
newServer{address="127.0.0.1:%s", useClientSubnet=true}
"""
_config_template = """
truncateTC(true)
- block=newDNSName("powerdns.org.")
- function blockFilter(dq)
- if(dq.qname:isPartOf(block))
- then
- print("Blocking *.powerdns.org")
- return true
- end
- return false
- end
setECSOverride(true)
setECSSourcePrefixV4(24)
setECSSourcePrefixV6(56)
# dnsdist set RA = RD for spoofed responses
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
-
+
rrset = dns.rrset.from_text(name,
60,
dns.rdataclass.IN,
dns.rdatatype.A,
'192.0.2.2', '192.0.2.1')
expectedResponse.answer.append(rrset)
-
+
rrset = dns.rrset.from_text(name,
60,
dns.rdataclass.IN,