From: Remi Gacogne Date: Wed, 14 Nov 2018 11:14:17 +0000 (+0100) Subject: dnsdist: Add regression tests for the 'ecs-scope-zero' feature X-Git-Tag: rec-4.2.0-alpha1~42^2~3 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=21d2b4c0dbbb9d03cfb619effd515efe5be34ac5;p=pdns dnsdist: Add regression tests for the 'ecs-scope-zero' feature --- diff --git a/regression-tests.dnsdist/test_Caching.py b/regression-tests.dnsdist/test_Caching.py index bddf2cdd9..7cb26a564 100644 --- a/regression-tests.dnsdist/test_Caching.py +++ b/regression-tests.dnsdist/test_Caching.py @@ -1752,3 +1752,258 @@ class TestCachingCollisionWithECSParsing(DNSDistTest): response2.answer.append(rrset) (receivedQuery, receivedResponse) = self.sendUDPQuery(query2, response2) self.assertEquals(receivedResponse, response2) + +class TestCachingScopeZero(DNSDistTest): + + _config_template = """ + -- Be careful to enable ECS parsing in the packet cache, otherwise scope zero is disabled + pc = newPacketCache(100, 86400, 1, 60, 60, false, 1, true, 3600, true) + getPool(""):setCache(pc) + newServer{address="127.0.0.1:%d", useClientSubnet=true} + -- to simulate a second client coming from a different IP address, + -- we will force the ECS value added to the query if RD is set (note that we need + -- to unset it using rules before the first cache lookup) + addAction(RDRule(), SetECSAction("192.0.2.1/32")) + addAction(RDRule(), NoRecurseAction()) + """ + + def testScopeZero(self): + """ + Cache: Test the scope-zero feature, backend returns a scope of zero + """ + ttl = 600 + name = 'scope-zero.cache.tests.powerdns.com.' + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= ~dns.flags.RD + ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24) + expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512) + expectedQuery.flags &= ~dns.flags.RD + ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0) + expectedResponse = dns.message.make_response(query) + scopedResponse = dns.message.make_response(query) + scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse]) + rrset = dns.rrset.from_text(name, + ttl, + dns.rdataclass.IN, + dns.rdatatype.AAAA, + '::1') + scopedResponse.answer.append(rrset) + expectedResponse.answer.append(rrset) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, scopedResponse) + receivedQuery.id = expectedQuery.id + self.checkMessageEDNSWithECS(expectedQuery, receivedQuery) + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + # next query should hit the cache, nothing special about that + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= dns.flags.RD + # next query FROM A DIFFERENT CLIENT since RD is now set should STILL hit the cache + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + receivedResponse.id = expectedResponse.id + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + name = 'scope-zero-with-ecs.cache.tests.powerdns.com.' + ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) + query = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512) + query.flags &= ~dns.flags.RD + expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512) + expectedQuery.flags &= ~dns.flags.RD + expectedResponse = dns.message.make_response(query) + expectedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse]) + expectedResponse.answer.append(rrset) + scopedResponse = dns.message.make_response(query) + scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse]) + scopedResponse.answer.append(rrset) + # this query has ECS, it should NOT be able to use the scope-zero cached entry since the hash will be + # different + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, scopedResponse) + receivedQuery.id = expectedQuery.id + self.checkMessageEDNSWithECS(expectedQuery, receivedQuery) + print(receivedResponse) + print(expectedResponse) + self.checkMessageEDNSWithECS(receivedResponse, expectedResponse) + + # it should still have been cached, though, so the next query should be a hit + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.checkMessageEDNSWithECS(receivedResponse, expectedResponse) + + def testScopeNotZero(self): + """ + Cache: Test the scope-zero feature, backend returns a scope of non-zero + """ + ttl = 600 + name = 'scope-not-zero.cache.tests.powerdns.com.' + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= ~dns.flags.RD + ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24) + expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512) + expectedQuery.flags &= ~dns.flags.RD + ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) + expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512) + expectedQuery2.flags &= ~dns.flags.RD + ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 24) + ecsoResponse2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32, 24) + rrset = dns.rrset.from_text(name, + ttl, + dns.rdataclass.IN, + dns.rdatatype.AAAA, + '::1') + expectedResponse = dns.message.make_response(query) + expectedResponse.answer.append(rrset) + scopedResponse = dns.message.make_response(query) + scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse]) + scopedResponse.answer.append(rrset) + scopedResponse2 = dns.message.make_response(query) + scopedResponse2.use_edns(edns=True, payload=4096, options=[ecsoResponse2]) + scopedResponse2.answer.append(rrset) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, scopedResponse) + receivedQuery.id = expectedQuery.id + self.checkMessageEDNSWithECS(expectedQuery, receivedQuery) + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + # next query should hit the cache, nothing special about that + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= dns.flags.RD + expectedResponse = dns.message.make_response(query) + expectedResponse.answer.append(rrset) + # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, scopedResponse2) + receivedQuery.id = expectedQuery2.id + self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery) + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + def testNoECS(self): + """ + Cache: Test the scope-zero feature, backend returns no ECS at all + """ + ttl = 600 + name = 'scope-zero-no-ecs.cache.tests.powerdns.com.' + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= ~dns.flags.RD + ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24) + expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512) + expectedQuery.flags &= ~dns.flags.RD + ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) + expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512) + expectedQuery2.flags &= ~dns.flags.RD + rrset = dns.rrset.from_text(name, + ttl, + dns.rdataclass.IN, + dns.rdatatype.AAAA, + '::1') + response = dns.message.make_response(query) + response.answer.append(rrset) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, response) + receivedQuery.id = expectedQuery.id + self.checkMessageEDNSWithECS(expectedQuery, receivedQuery) + self.checkMessageNoEDNS(receivedResponse, response) + + # next query should hit the cache, nothing special about that + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.checkMessageNoEDNS(receivedResponse, response) + + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= dns.flags.RD + response = dns.message.make_response(query) + response.answer.append(rrset) + # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, response) + receivedQuery.id = expectedQuery2.id + self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery) + self.checkMessageNoEDNS(receivedResponse, response) + +class TestCachingScopeZeroButNoSubnetcheck(DNSDistTest): + + _config_template = """ + -- We disable ECS parsing in the packet cache, meaning scope zero is disabled + pc = newPacketCache(100, 86400, 1, 60, 60, false, 1, true, 3600, false) + getPool(""):setCache(pc) + newServer{address="127.0.0.1:%d", useClientSubnet=true} + -- to simulate a second client coming from a different IP address, + -- we will force the ECS value added to the query if RD is set (note that we need + -- to unset it using rules before the first cache lookup) + addAction(RDRule(), SetECSAction("192.0.2.1/32")) + addAction(RDRule(), NoRecurseAction()) + """ + + def testScopeZero(self): + """ + Cache: Test that the scope-zero feature is disabled when ECS parsing is not enabled in the cache + """ + ttl = 600 + name = 'scope-zero-no-subnet.cache.tests.powerdns.com.' + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= ~dns.flags.RD + ecso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24) + expectedQuery = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso], payload=512) + expectedQuery.flags &= ~dns.flags.RD + ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) + expectedQuery2 = dns.message.make_query(name, 'AAAA', 'IN', use_edns=True, options=[ecso2], payload=512) + expectedQuery2.flags &= ~dns.flags.RD + ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, 0) + expectedResponse = dns.message.make_response(query) + scopedResponse = dns.message.make_response(query) + scopedResponse.use_edns(edns=True, payload=4096, options=[ecsoResponse]) + rrset = dns.rrset.from_text(name, + ttl, + dns.rdataclass.IN, + dns.rdatatype.AAAA, + '::1') + scopedResponse.answer.append(rrset) + expectedResponse.answer.append(rrset) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, scopedResponse) + receivedQuery.id = expectedQuery.id + self.checkMessageEDNSWithECS(expectedQuery, receivedQuery) + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + # next query should hit the cache, nothing special about that + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (_, receivedResponse) = sender(query, response=None, useQueue=False) + self.checkMessageNoEDNS(receivedResponse, expectedResponse) + + query = dns.message.make_query(name, 'AAAA', 'IN') + query.flags &= dns.flags.RD + response = dns.message.make_response(query) + response.answer.append(rrset) + # next query FROM A DIFFERENT CLIENT since RD is now set should NOT hit the cache + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, response) + receivedQuery.id = expectedQuery2.id + self.checkMessageEDNSWithECS(expectedQuery2, receivedQuery) + self.checkMessageNoEDNS(receivedResponse, response)