def test_followCanonicalName(self): """ If no record of the requested type is included in a response, but a I{CNAME} record for the query name is included, queries are made to resolve the value of the I{CNAME}. """ servers = { ('1.1.2.3', 53): { ('example.com', A): { 'answers': [('example.com', Record_CNAME('example.net'))], }, ('example.net', A): { 'answers': [('example.net', Record_A('10.0.0.5'))], }, }, } resolver = self._getResolver(servers) d = resolver.lookupAddress('example.com') d.addCallback(lambda (ans, auth, add): ans) d.addCallback(self.assertEquals, [ RRHeader('example.com', CNAME, payload=Record_CNAME('example.net')), RRHeader('example.net', A, payload=Record_A('10.0.0.5')) ]) return d
def test_returnCanonicalName(self): """ If a I{CNAME} record is encountered as the answer to a query for another record type, that record is returned as the answer. """ servers = { ("1.1.2.3", 53): { (b"example.com", A): { "answers": [ (b"example.com", Record_CNAME(b"example.net")), (b"example.net", Record_A("10.0.0.7")), ], }, }, } resolver = self._getResolver(servers) d = resolver.lookupAddress(b"example.com") d.addCallback(lambda results: results[0]) # Get the answer section d.addCallback( self.assertEqual, [ RRHeader(b"example.com", CNAME, payload=Record_CNAME(b"example.net")), RRHeader(b"example.net", A, payload=Record_A("10.0.0.7")), ], ) return d
def test_followCanonicalName(self): """ If no record of the requested type is included in a response, but a I{CNAME} record for the query name is included, queries are made to resolve the value of the I{CNAME}. """ servers = { ("1.1.2.3", 53): { (b"example.com", A): { "answers": [(b"example.com", Record_CNAME(b"example.net"))], }, (b"example.net", A): { "answers": [(b"example.net", Record_A("10.0.0.5"))], }, }, } resolver = self._getResolver(servers) d = resolver.lookupAddress(b"example.com") d.addCallback(lambda results: results[0]) # Get the answer section d.addCallback( self.assertEqual, [ RRHeader(b"example.com", CNAME, payload=Record_CNAME(b"example.net")), RRHeader(b"example.net", A, payload=Record_A("10.0.0.5")), ], ) return d
def resolved(results): answers, authority, additional = results self.assertEqual((RRHeader(b"multiple", A, IN, self.ttl, Record_A("1.1.1.3", self.ttl)), RRHeader(b"multiple", A, IN, self.ttl, Record_A("1.1.1.4", self.ttl))), answers)
def resolved(results): answers, authority, additional = results self.assertEqual( (RRHeader(b"ip6-multiple", AAAA, IN, self.ttl, Record_AAAA("::3", self.ttl)), RRHeader(b"ip6-multiple", AAAA, IN, self.ttl, Record_AAAA("::4", self.ttl))), answers)
def test_lookupIPV6Address(self): """ L{hosts.Resolver.lookupIPV6Address} returns a L{Deferred} which fires with AAAA records from the hosts file. """ d = self.resolver.lookupIPV6Address(b'ip6-multiple') answers, authority, additional = self.successResultOf(d) self.assertEqual((RRHeader(b"ip6-multiple", AAAA, IN, self.ttl, Record_AAAA("::3", self.ttl)), RRHeader(b"ip6-multiple", AAAA, IN, self.ttl, Record_AAAA("::4", self.ttl))), answers)
def test_infer_search_domains(resolver): """ ``LocalResolver`` uses a number of DNS queries sent to it as probes to infer the search domains configured for the client. """ probe = u"hellotelepresence" counter = count(0) for search in [u".foo", u".foo.bar", u".alternate"]: for i in range(3): name = u"{}{}{}".format( probe, next(counter), search, ).encode("ascii") rrheader = RRHeader( name=name, payload=Record_A(address=b"127.0.0.1"), ) expected = ([rrheader], [], []) result = resolver.query(Query(name)) assert expected == result for search in [u".foo", u".foo.bar", u".alternate"]: mangled = (u"example.com" + search).encode("ascii").split(b".") assert [b"example", b"com"] == resolver._strip_search_suffix(mangled)
def _respond(self, answers=[], authority=[], additional=[], rCode=OK): """ Create a L{Message} suitable for use as a response to a query. @param answers: A C{list} of two-tuples giving data for the answers section of the message. The first element of each tuple is a name for the L{RRHeader}. The second element is the payload. @param authority: A C{list} like C{answers}, but for the authority section of the response. @param additional: A C{list} like C{answers}, but for the additional section of the response. @param rCode: The response code the message will be created with. @return: A new L{Message} initialized with the given values. """ response = Message(rCode=rCode) for (section, data) in [ (response.answers, answers), (response.authority, authority), (response.additional, additional), ]: section.extend([ RRHeader(name, record.TYPE, getattr(record, "CLASS", IN), payload=record) for (name, record) in data ]) return response
def test_lookupAddress(self): """ L{SecondaryAuthority.lookupAddress} returns a L{Deferred} that fires with the I{A} records the authority has cached from the primary. """ secondary = SecondaryAuthority.fromServerAddressAndDomain( ("192.168.1.2", 1234), b"example.com") secondary._reactor = reactor = MemoryReactorClock() secondary.transfer() host, port, factory, timeout, bindAddress = reactor.tcpClients.pop(0) proto = factory.buildProtocol((host, port)) transport = StringTransport() proto.makeConnection(transport) query = Message(answer=1, auth=1) query.decode(BytesIO(transport.value()[2:])) # Generate a response with some data we can check. soa = Record_SOA( mname=b"ns1.example.com", rname="admin.example.com", serial=123456, refresh=3600, minimum=4800, expire=7200, retry=9600, ttl=12000, ) a = Record_A(b"192.168.1.2", ttl=0) answer = Message(id=query.id, answer=1, auth=1) answer.answers.extend([ RRHeader(b"example.com", type=SOA, payload=soa), RRHeader(b"example.com", payload=a), RRHeader(b"example.com", type=SOA, payload=soa), ]) data = answer.toStr() proto.dataReceived(pack("!H", len(data)) + data) result = self.successResultOf(secondary.lookupAddress("example.com")) self.assertEqual( ([RRHeader(b"example.com", payload=a, auth=True)], [], []), result)
def test_lookupAllRecords(self): """ L{hosts.Resolver.lookupAllRecords} returns a L{Deferred} which fires with A records from the hosts file. """ d = self.resolver.lookupAllRecords(b'mixed') answers, authority, additional = self.successResultOf(d) self.assertEqual((RRHeader(b"mixed", A, IN, self.ttl, Record_A("1.1.1.2", self.ttl)), ), answers)
def getMX(self, domain): return defer.succeed([ RRHeader(name=domain, type=Record_MX.TYPE, payload=Record_MX( 1, 'localhost', )) ])
def test_lookupIPV6Malformed(self): """ Like L{test_lookupAddressMalformed}, but for L{hosts.Resolver.lookupIPV6Address}. """ d = self.resolver.lookupIPV6Address(b"malformed") [answer], authority, additional = self.successResultOf(d) self.assertEqual( RRHeader(b"malformed", AAAA, IN, self.ttl, Record_AAAA("::5", self.ttl)), answer, )
def test_lookupMalformed(self): """ L{hosts.Resolver.lookupAddress} returns a L{Deferred} which fires with the valid addresses from the hosts file, ignoring any entries that aren't valid IP addresses. """ d = self.resolver.lookupAddress(b"malformed") [answer], authority, additional = self.successResultOf(d) self.assertEqual( RRHeader(b"malformed", A, IN, self.ttl, Record_A("1.1.1.5", self.ttl)), answer, )
def test_filteredQuery(self): """ L{Resolver._query} accepts a L{Query} instance and an address, issues the query, and returns a L{Deferred} which fires with the response to the query. If a true value is passed for the C{filter} parameter, the result is a three-tuple of lists of records. """ answer, authority, additional = self._queryTest(True) self.assertEqual(answer, [ RRHeader(b"foo.example.com", payload=Record_A("5.8.13.21", ttl=0)) ]) self.assertEqual(authority, []) self.assertEqual(additional, [])
def test_returnCanonicalName(self): """ If a I{CNAME} record is encountered as the answer to a query for another record type, that record is returned as the answer. """ servers = { ('1.1.2.3', 53): { ('example.com', A): { 'answers': [('example.com', Record_CNAME('example.net')), ('example.net', Record_A('10.0.0.7'))], }, }, } resolver = self._getResolver(servers) d = resolver.lookupAddress('example.com') d.addCallback(lambda (ans, auth, add): ans) d.addCallback(self.assertEquals, [ RRHeader('example.com', CNAME, payload=Record_CNAME('example.net')), RRHeader('example.net', A, payload=Record_A('10.0.0.7')) ]) return d
def test_unfilteredQuery(self): """ Similar to L{test_filteredQuery}, but for the case where a false value is passed for the C{filter} parameter. In this case, the result is a L{Message} instance. """ message = self._queryTest(False) self.assertIsInstance(message, Message) self.assertEqual(message.queries, []) self.assertEqual(message.answers, [ RRHeader(b'foo.example.com', payload=Record_A('5.8.13.21', ttl=0)) ]) self.assertEqual(message.authority, []) self.assertEqual(message.additional, [])
def _queryTest(self, filter): """ Invoke L{Resolver._query} and verify that it sends the correct DNS query. Deliver a canned response to the query and return whatever the L{Deferred} returned by L{Resolver._query} fires with. @param filter: The value to pass for the C{filter} parameter to L{Resolver._query}. """ reactor = MemoryReactor() resolver = Resolver([], reactor=reactor) d = resolver._query( Query(b'foo.example.com', A, IN), [('1.1.2.3', 1053)], (30,), filter) # A UDP port should have been started. portNumber, transport = reactor.udpPorts.popitem() # And a DNS packet sent. [(packet, address)] = transport._sentPackets message = Message() message.fromStr(packet) # It should be a query with the parameters used above. self.assertEqual(message.queries, [Query(b'foo.example.com', A, IN)]) self.assertEqual(message.answers, []) self.assertEqual(message.authority, []) self.assertEqual(message.additional, []) response = [] d.addCallback(response.append) self.assertEqual(response, []) # Once a reply is received, the Deferred should fire. del message.queries[:] message.answer = 1 message.answers.append(RRHeader( b'foo.example.com', payload=Record_A('5.8.13.21'))) transport._protocol.datagramReceived( message.toStr(), ('1.1.2.3', 1053)) return response[0]
async def get_response(query: Query): name = query.name.name if name.endswith(b'.'): name = name[:-1] if not name.endswith(b'.lo0.wtf'): raise DomainError() results = [] if query.type in (AAAA, ALL_RECORDS): name = name[:-8] vhost, username = name.rsplit(b'.', 1) vhost_hash = CityHash32(vhost) username_hash = CityHash32(username) ip = NETWORK[(username_hash << 32) + vhost_hash] results.append( RRHeader( name=query.name.name, type=AAAA, ttl=60, payload=Record_AAAA(address=str(ip).encode('ascii')), )) if query.type in (SOA, ALL_RECORDS) and name == b'': results.append(SOA_OBJ) return results
return [], [], [] return answers, [], [] @ensure_returns_deferred async def lookupAllRecords(self, name, timeout=None): query = Query(name, type=ALL_RECORDS) answers = await self.callback(query) return answers, [], [] SOA_OBJ = RRHeader(name='lo0.wtf', type=SOA, ttl=60, payload=Record_SOA( mname='lo0-ns1.leigh.party', rname='l.leigh.net.au', serial=int(time() / 10), refresh=60, retry=60, expire=60, minimum=60, )) async def get_response(query: Query): name = query.name.name if name.endswith(b'.'): name = name[:-1] if not name.endswith(b'.lo0.wtf'): raise DomainError() results = [] if query.type in (AAAA, ALL_RECORDS): name = name[:-8] vhost, username = name.rsplit(b'.', 1)
def make_soa_result(self, serial): return RRHeader(type=SOA, cls=A, ttl=30, payload=Record_SOA(serial=serial))
def resolved(results): answers, authority, additional = results self.assertEqual( (RRHeader(b"mixed", A, IN, self.ttl, Record_A("1.1.1.2", self.ttl)),), answers)
def resolved((results, authority, additional)): self.assertEqual((RRHeader("multiple", A, IN, self.ttl, Record_A("1.1.1.3", self.ttl)), RRHeader("multiple", A, IN, self.ttl, Record_A("1.1.1.4", self.ttl))), results)
def resolved((results, authority, additional)): self.assertEqual((RRHeader("mixed", A, IN, self.ttl, Record_A("1.1.1.2", self.ttl)), ), results)
def resolved((results, authority, additional)): self.assertEqual((RRHeader("ip6-multiple", AAAA, IN, self.ttl, Record_AAAA("::3", self.ttl)), RRHeader("ip6-multiple", AAAA, IN, self.ttl, Record_AAAA("::4", self.ttl))), results)