def testSendECS(self): expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.1/32') ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) # check that a query in a different range is a miss ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected)
def testRequireNoECS(self): expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.168.0.1/32') ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected, ttlECS)
def testSendECS(self): expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', emptyECSText) ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected)
def testSendECS(self): expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24') ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) # check that a query in a different ECS range is a hit, because we don't use the incoming ECS ecso = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.checkECSQueryHit(query, expected)
def testWithEDNSNoECSResponseWithECS(self): """ ECS: Existing EDNS without ECS (BE returning only the ECS option) Send a query with EDNS but no ECS value. Check that the query received by the responder has a valid ECS value and that the response received from dnsdist contains an EDNS pseudo-RR. This time the response returned by the backend contains an ECS option with scope set. """ name = 'withednsnoecs.bereturnsecs.ecs.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096) expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso]) response = dns.message.make_response(expectedQuery) ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24) response.use_edns(edns=True, payload=4096, options=[ecsoResponse]) expectedResponse = 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) expectedResponse.answer.append(rrset) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseEDNSWithoutECS(expectedResponse, receivedResponse) (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseEDNSWithoutECS(expectedResponse, receivedResponse)
def testRequireNoECS(self): # we will get ::1 because ecs-scope-zero-addr is set to ::1 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '::1/128') ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected, ttlECS)
def testWithoutEDNSResponseWithECS(self): """ ECS: No existing EDNS (BE returning ECS) Send a query without EDNS, check that the query received by the responder has the correct ECS value and that the response received from dnsdist does not have an EDNS pseudo-RR. This time the response returned by the backend contains an ECS option with scope set. """ name = 'withoutedns.bereturnsecs.ecs.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) query = dns.message.make_query(name, 'A', 'IN') expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512) response = dns.message.make_response(expectedQuery) ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24) response.use_edns(edns=True, payload=4096, options=[ecsoResponse]) expectedResponse = 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) expectedResponse.answer.append(rrset) for method in ("sendUDPQuery", "sendTCPQuery"): sender = getattr(self, method) (receivedQuery, receivedResponse) = sender(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseNoEDNS(expectedResponse, receivedResponse)
def testRequireNoECS(self): expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '127.0.0.0/24') # the request for no ECS is ignored because use-incoming-edns-subnet is not set ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected)
def testLatlonloc(self): """ Basic latlonloc() test """ name = 'latlonloc.geo.example.org.' expected = dns.rrset.from_text(name, 0,dns.rdataclass.IN, 'TXT', '"47 54 46.8 N 122 18 15.12 W 0.00m 1.00m 10000.00m 10.00m"') ecso = clientsubnetoption.ClientSubnetOption('1.2.3.0', 24) query = dns.message.make_query(name, 'TXT', 'IN', use_edns=True, payload=4096, options=[ecso]) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected)
def testWithEDNSSameSizeInitialECS(self): """ ECS Override: Existing EDNS with ECS (same) Send a query with EDNS and a crafted ECS value. Check that the query received by the responder has an overwritten ECS value (not the initial one) and that the response received from dnsdist contains an EDNS pseudo-RR. The initial ECS value is exactly the same size as the one it will replaced with. """ name = 'withednsecs.overridden.ecs.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24) rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso]) expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso]) response = dns.message.make_response(query) response.use_edns(edns=True, payload=4096, options=[rewrittenEcso]) rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1') response.answer.append(rrset) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseEDNSWithECS(response, receivedResponse) (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseEDNSWithECS(response, receivedResponse)
def testTeeWithECS(self): """ TeeAction: ECS """ name = 'ecs.tee.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, '192.0.2.1') response.answer.append(rrset) numberOfQueries = 10 for _ in range(numberOfQueries): # push the response to the Tee server self._toTeeQueue.put(response, True, 2.0) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = query.id self.assertEquals(query, receivedQuery) self.assertEquals(response, receivedResponse) # retrieve the query from the Tee server teedQuery = self._fromTeeQueue.get(True, 2.0) ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512) expectedQuery.id = query.id self.checkQueryEDNSWithECS(expectedQuery, teedQuery) # check the TeeAction stats stats = self.sendConsoleCommand("getAction(0):printStats()") self.assertEquals( stats, """noerrors\t%d nxdomains\t0 other-rcode\t0 queries\t%d recv-errors\t0 refuseds\t0 responses\t%d send-errors\t0 servfails\t0 tcp-drops\t0 """ % (numberOfQueries, numberOfQueries, numberOfQueries))
def testWithECSOverrideSetViaLua(self): """ ECS Override: set via Lua """ name = 'overriddenvialua.ecsrules.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24) rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso]) expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[rewrittenEcso]) response = dns.message.make_response(query) response.use_edns(edns=True, payload=4096, options=[rewrittenEcso]) rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1') response.answer.append(rrset) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseEDNSWithECS(response, receivedResponse) (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseEDNSWithECS(response, receivedResponse)
def testRequireNoECS(self): # we should get ::1/128 because neither ecs-scope-zero-addr nor query-local-address are set, # but query-local-address6 is set to ::1 expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "::1/128") ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0) query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected, ttlECS)
def testSubnet(self): name = 'gettag-subnet.lua.' subnet = '192.0.2.255' subnetMask = 32 ecso = clientsubnetoption.ClientSubnetOption(subnet, subnetMask) expected = [ dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.1'), dns.rrset.from_text_list(name, 0, dns.rdataclass.IN, 'TXT', [name, 'gettag-qtype-1', 'edns-subnet-' + subnet + '/' + str(subnetMask), 'ednsoption-8-count-1', 'ednsoption-8-total-len-8']), ] query = dns.message.make_query(name, 'A', want_dnssec=True, options=[ecso]) query.flags |= dns.flags.CD res = self.sendUDPQuery(query) self.assertResponseMatches(query, expected, res)
def testSendECSInvalidScope(self): # test that the recursor does not cache with a more specific scope than the source it sent expected = dns.rrset.from_text(nameECSInvalidScope, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.2.0/24') ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) query = dns.message.make_query(nameECSInvalidScope, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected)
def testWithEDNSNoECS(self): """ ECS: Existing EDNS without ECS Send a query with EDNS but no ECS value. Check that the query received by the responder has a valid ECS value and that the response received from dnsdist contains an EDNS pseudo-RR. """ name = 'withednsnoecs.ecs.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096) expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso]) response = dns.message.make_response(expectedQuery) expectedResponse = 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) expectedResponse.answer.append(rrset) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.assertEquals(expectedQuery, receivedQuery) self.assertEquals(expectedResponse, receivedResponse) self.assertEquals(receivedResponse.edns, 0) self.assertEquals(len(receivedResponse.options), 0) (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.assertEquals(expectedQuery, receivedQuery) self.assertEquals(expectedResponse, receivedResponse) self.assertEquals(receivedResponse.edns, 0) self.assertEquals(len(receivedResponse.options), 0)
def testClosest(self): """ Basic pickclosest() test """ queries = [ ('1.1.1.0', 24, '1.1.1.2'), ('1.2.3.0', 24, '1.2.3.4'), ('17.1.0.0', 16, '1.1.1.2') ] name = 'closest.geo.example.org.' for (subnet, mask, ip) in queries: ecso = clientsubnetoption.ClientSubnetOption(subnet, mask) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso]) expected = dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', ip) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected)
def testTooLarge(self): qname = 'toolarge.ecs.' ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 24) query = dns.message.make_query(qname, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) # should not have an ECS Option since the packet is too large already res = self.sendUDPQuery(query, timeout=5.0) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertEqual(len(res.answer), 1) self.assertEqual(res.edns, 0) self.assertEqual(len(res.options), 0) res = self.sendTCPQuery(query, timeout=5.0) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertEqual(len(res.answer), 1) self.assertEqual(res.edns, 0) self.assertEqual(len(res.options), 1) self.assertEqual(res.options[0].otype, 8) self.assertEqual(res.options[0].scope, 0)
def query_nameserver(nameserver, name): """ Do a DNS query against a specified nameserver """ LOG.debug("Querying {0} for {1}".format(nameserver, name)) qname = dns.name.from_text(name) cso = clientsubnetoption.ClientSubnetOption(get_external_ip()) message = dns.message.make_query(qname, 'A') message.use_edns(options=[cso]) try: r = dns.query.udp(message, nameserver, timeout=DNS_TIMEOUT, port=53) except dns.exception.Timeout: return 'TIMEOUT' ns_rrset = r.find_rrset(r.answer, qname, dns.rdataclass.IN, dns.rdatatype.A) for rr in ns_rrset: DNS_REQUESTS.inc() return rr
def testWithoutEDNS(self): """ ECS Override: No existing EDNS Send a query without EDNS, check that the query received by the responder has the correct ECS value and that the response received from dnsdist does not have an EDNS pseudo-RR. """ name = 'withoutedns.overriden.ecs.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) query = dns.message.make_query(name, 'A', 'IN') expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512) response = dns.message.make_response(expectedQuery) expectedResponse = 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) expectedResponse.answer.append(rrset) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.assertEquals(expectedQuery, receivedQuery) self.assertEquals(expectedResponse, receivedResponse) self.assertEquals(receivedResponse.edns, -1) self.assertEquals(len(receivedResponse.options), 0) (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.assertEquals(expectedQuery, receivedQuery) self.assertEquals(expectedResponse, receivedResponse) self.assertEquals(receivedResponse.edns, -1) self.assertEquals(len(receivedResponse.options), 0)
def testAWithECS(self): """ Basics: A query with an ECS value """ name = 'awithecs.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4') query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso]) response = dns.message.make_response(query) rrset = dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1') response.answer.append(rrset) for method in ("sendUDPQuery", "sendTCPQuery"): sender = getattr(self, method) (receivedQuery, receivedResponse) = sender(query, response) receivedQuery.id = query.id self.assertEquals(query, receivedQuery) self.assertEquals(response, receivedResponse)
def testWithEDNSECS(self): """ ECS: Existing EDNS with ECS Send a query with EDNS and a crafted ECS value. Check that the query received by the responder has the initial ECS value (not overwritten) and that the response received from dnsdist contains an EDNS pseudo-RR. """ name = 'withednsecs.ecs.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('1.2.3.4', 24) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso]) 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) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = query.id receivedResponse.id = response.id self.assertEquals(query, receivedQuery) self.assertEquals(response, receivedResponse) (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = query.id receivedResponse.id = response.id self.assertEquals(query, receivedQuery) self.assertEquals(response, receivedResponse)
def responseCallback(request): if len(request.question) != 1: print("Skipping query with question count %d" % (len(request.question))) return None healthCheck = str(request.question[0].name).endswith('a.root-servers.net.') if healthCheck: response = dns.message.make_response(request) return response.to_wire() # now we create a broken response response = dns.message.make_response(request) ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 32) response.use_edns(edns=True, payload=4096, options=[ecso]) rrset = dns.rrset.from_text(request.question[0].name, 3600, dns.rdataclass.IN, dns.rdatatype.A, '127.0.0.1') response.answer.append(rrset) raw = response.to_wire() # first label length of this rrset is at 12 (dnsheader) + length(qname) + 2 (leading label length + trailing 0) + 2 (qtype) + 2 (qclass) offset = 12 + len(str(request.question[0].name)) + 2 + 2 + 2 altered = raw[:offset] + b'\xff' + raw[offset+1:] return altered
def testCookie(self): """ EDNS Options: Cookie (adding ECS) """ name = 'cookie.ednsoptions-ecs.tests.powerdns.com.' eco = cookiesoption.CookiesOption('deadbeef', 'deadbeef') query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[eco]) ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[eco, ecso], payload=512) 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) (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery, 1) self.checkResponseEDNSWithoutECS(response, receivedResponse) (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.checkQueryEDNSWithECS(expectedQuery, receivedQuery, 1) self.checkResponseEDNSWithoutECS(response, receivedResponse)
def testDOHExistingEDNS(self): """ DOH with ECS: Existing EDNS """ name = 'existing-edns.doh-ecs.tests.powerdns.com.' query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=8192) query.id = 0 rewrittenEcso = clientsubnetoption.ClientSubnetOption('127.0.0.0', 24) expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=8192, options=[rewrittenEcso]) 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) (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert) self.assertTrue(receivedQuery) self.assertTrue(receivedResponse) receivedQuery.id = expectedQuery.id self.assertEquals(expectedQuery, receivedQuery) self.assertEquals(response, receivedResponse) self.checkQueryEDNSWithECS(expectedQuery, receivedQuery) self.checkResponseEDNSWithoutECS(response, receivedResponse)
def testClosestMagic(self): """ Basic closestMagic() test """ name = 'www-balanced.example.org.' cname = '1-1-1-3.17-1-2-4.1-2-3-5.magic.example.org.' queries = [ ('1.1.1.0', 24, '1.1.1.3'), ('1.2.3.0', 24, '1.2.3.5'), ('17.1.0.0', 16, '17.1.2.4') ] for (subnet, mask, ip) in queries: ecso = clientsubnetoption.ClientSubnetOption(subnet, mask) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso]) response = dns.message.make_response(query) response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, dns.rdatatype.CNAME, cname)) response.answer.append(dns.rrset.from_text(cname, 0, dns.rdataclass.IN, 'A', ip)) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertEqual(res.answer, response.answer)
def testWhoAmI(self): """ See if LUA who picks up the inner address from the PROXY protocol """ for testWithECS in True, False: # first test with an unproxied query - should get ignored options = [] expectedText = '192.0.2.1/192.0.2.1' if testWithECS: ecso = clientsubnetoption.ClientSubnetOption('192.0.2.5', 32) options.append(ecso) expectedText = '192.0.2.1/192.0.2.5' query = dns.message.make_query('myip.example.org', 'TXT', 'IN', use_edns=testWithECS, options=options, payload=512) res = self.sendUDPQuery(query) self.assertEqual(res, None) # query was ignored correctly # now send a proxied query queryPayload = query.to_wire() ppPayload = ProxyProtocol.getPayload(False, False, False, "192.0.2.1", "10.1.2.3", 12345, 53, []) payload = ppPayload + queryPayload # UDP self._sock.settimeout(2.0) try: self._sock.send(payload) data = self._sock.recv(4096) except socket.timeout: data = None finally: self._sock.settimeout(None) res = None if data: res = dns.message.from_wire(data) expected = [ dns.rrset.from_text('myip.example.org.', 0, dns.rdataclass.IN, 'TXT', expectedText) ] self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertEqual(res.answer, expected) # TCP sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(2.0) sock.connect(("127.0.0.1", self._authPort)) try: sock.send(ppPayload) sock.send(struct.pack("!H", len(queryPayload))) sock.send(queryPayload) data = sock.recv(2) if data: (datalen, ) = struct.unpack("!H", data) data = sock.recv(datalen) except socket.timeout as e: print("Timeout: %s" % (str(e))) data = None except socket.error as e: print("Network error: %s" % (str(e))) data = None finally: sock.close() res = None if data: res = dns.message.from_wire(data) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertEqual(res.answer, expected)
def main(arguments): # default: start from 1.0.0.0 IP_binary = struct.unpack('!L', socket.inet_aton(arguments.start))[0] step = 24 # /24 is the minium step mask = step suggested_mask = step # the mask suggested by DNS response pool = {} # the mapping results count = 1 timeout = 0 failed = 0 isFailed = False # # first, restore the current states from last time # if os.path.isfile(CACHE_FILE): # with open(CACHE_FILE, 'rb') as f: # pool = pickle.load(f) if os.path.isfile(NOW_FILE): with open(NOW_FILE, 'rb') as f: IP_binary = pickle.load(f) # load geo ip data READER = geoip2.database.Reader('./geoip/GeoLite2-City.mmdb') print("准备就绪") # main loop over all IPs # while IP_binary < 255*(1<<24): #255.0.0.0 while IP_binary < 2 * (1 << 24): # 1.0.0.0 IP_pack = struct.pack('!L', IP_binary) IP = socket.inet_ntoa(IP_pack) # skip private IP if not good_IP(IP, READER): IP_binary = advance(IP_binary, mask) continue # skip google IP as their locations are all reported as MTV googlemask = isGoogleIP(IP_binary) if googlemask > 0: IP_binary = advance(IP_binary, googlemask) continue # creat the query message cso = clientsubnetoption.ClientSubnetOption(IP, bits=mask) print(IP, cso) print("CSO打印完啦") # default: 'google' message = dns.message.make_query(CDN[arguments.name], 'A') message.use_edns(options=[cso]) print(message) print("Message打印完啦") try: # rotating the DNS server we use r = dns.query.udp(message, DNS_servers[count % len(DNS_servers)], timeout=arguments.timeout) print(r) print("打印完啦") except dns.exception.Timeout: timeout += 1 print('timeout:', DNS_servers[count % len(DNS_servers)], IP) IP_binary = advance(IP_binary, mask) continue # only use A record here because it seems enough servers = {} for ans in r.answer: if ans.to_rdataset().rdtype == dns.rdatatype.A: # use /24 prefix to represent # all the servers in the same location server = networkMask(ans[0].to_text(), 24) servers[server] = 1 print(servers) # if ENDS0 is used, get the suggested mask. for options in r.options: if isinstance(options, clientsubnetoption.ClientSubnetOption): suggested_mask = int(options.scope) if len(servers) > 0: for server in servers: clients = pool.get(server, []) clients.append((IP, suggested_mask)) pool[server] = clients isFailed = False else: failed += 1 print(r.answer) print ('failed:', DNS_servers[count%len(DNS_servers)],\ IP, suggested_mask) isFailed = True if suggested_mask == 0: # suggested == 0 *might* imply EDNS0 is not supported mask = step elif suggested_mask == 32: # suggested == 0 *might* imply # the EDNS0 record for this prefix is not set mask = 17 # rule of thumb: take a big step forward else: mask = suggested_mask IP_binary = advance(IP_binary, mask) count += 1 print(pool) break # save and report every 500 prefixes if count % 500 == 0: print(IP, mask, suggested_mask) print("Servers %d, errors %d/%d" % (len(pool), timeout, failed)) # start another thread to save so that it will not be interrupted # by signals such as ctrl+c a = Thread(target=save_states, args=(pool, IP_binary)) a.start() a.join() if isFailed: # might need to slow down because we sent too many requests # rule of thumb: 90 seconds should be enough time.sleep(arguments.cooldown) else: time.sleep(arguments.delay)
def testWithEDNSNoOptions(self): """ EDNS on Self-Generated: EDNS with options in the query """ name = 'edns-options.rcode.edns-self.tests.powerdns.com.' ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24) query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True) expectedResponse = dns.message.make_response(query) expectedResponse.set_rcode(dns.rcode.REFUSED) (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042) (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042) name = 'edns-options.tc.edns-self.tests.powerdns.com.' query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True) expectedResponse = dns.message.make_response(query) expectedResponse.flags |= dns.flags.TC (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042) (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042) name = 'edns-options.lua.edns-self.tests.powerdns.com.' query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True) expectedResponse = dns.message.make_response(query) expectedResponse.set_rcode(dns.rcode.NXDOMAIN) (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042) (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042) name = 'edns-options.spoof.edns-self.tests.powerdns.com.' query = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512, want_dnssec=True) # dnsdist set RA = RD for spoofed responses query.flags &= ~dns.flags.RD expectedResponse = dns.message.make_response(query) expectedResponse.answer.append( dns.rrset.from_text(name, 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1', '192.0.2.2')) (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042) (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) self.checkMessageEDNSWithoutOptions(expectedResponse, receivedResponse) self.assertTrue(receivedResponse.ednsflags & dns.flags.DO) self.assertEquals(receivedResponse.payload, 1042)
def testPacketCache(self): # first query, no cookie qname = 'a.example.' query = dns.message.make_query(qname, 'A', want_dnssec=True) expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.42') for method in ("sendUDPQuery", "sendTCPQuery"): sender = getattr(self, method) res = sender(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(0, 1) # we should get a hit over UDP this time res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(1, 1) eco1 = cookiesoption.CookiesOption(b'deadbeef', b'deadbeef') eco2 = cookiesoption.CookiesOption(b'deadc0de', b'deadc0de') ecso1 = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) ecso2 = clientsubnetoption.ClientSubnetOption('192.0.2.2', 32) # we add a cookie, should not match anymore query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1]) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(1, 2) # same cookie, should match query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1]) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(2, 2) # different cookie, should still match query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2]) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(3, 2) # first cookie but with an ECS option, should not match query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso1]) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(3, 3) # different cookie but same ECS option, should match query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco2, ecso1]) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(4, 3) # first cookie but different ECS option, should still match (we ignore EDNS Client Subnet # in the recursor's packet cache, but ECS-specific responses are not cached query = dns.message.make_query(qname, 'A', want_dnssec=True, options=[eco1, ecso2]) res = self.sendUDPQuery(query) self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) self.checkPacketCacheMetrics(5, 3)