Example #1
0
File: dns.py Project: loryka/DNSpy
    def record_reader(self, zone_file):
        with open(zone_file, 'r') as root_hints_file:
            for line in root_hints_file:
                if not line or line[0] == ';':
                    continue
                tokens = re.split(r'\s*\s', line.rstrip())
                if len(tokens) == 5:
                    (name, ttl, rclass, rtype, rdata) = tokens
                    rclass = DnsRClass[rclass]
                elif len(tokens) == 4:
                    (name, ttl, rtype, rdata) = tokens
                    rclass = DnsRClass.IN  # lets just assume
                else:
                    # Don't know what this is
                    continue
                try:
                    rtype = DnsRType[rtype]
                    ttl = int(ttl)
                except ValueError:
                    self.log.critical('Malformed glue records.')
                    continue

                name = DomainName.from_string(name)
                if rtype == DnsRType.NS:
                    record = DnsRecord(name, rtype, rclass, ttl, rdata=DomainName.from_string(rdata).encode())
                elif rtype == DnsRType.A:
                    record = DnsRecord(name, rtype, rclass, ttl, rdata=ipaddress.IPv4Address(rdata).packed)
                elif rtype == DnsRType.AAAA:
                    record = DnsRecord(name, rtype, rclass, ttl, rdata=ipaddress.IPv6Address(rdata).packed)
                self.log.debug('Glue record: %s' % record)
                yield record
Example #2
0
File: dns.py Project: loryka/DNSpy
 def get_name(self, name_id):
     with closing(self.db.cursor()) as cursor:
         sequence = []
         for foo in range(256):  # dns labels are limited to 256 depths anyway
             cursor.execute('SELECT parent,name FROM name WHERE `id`=%s LIMIT 1', (name_id,))
             self.queries += 1
             (name_id, label) = cursor.fetchone()
             sequence.append(label)
             if name_id is None:
                 return DomainName(sequence)
Example #3
0
File: dns.py Project: loryka/DNSpy
    def enumerate_nameserver_addresses(nameserver_records, additional_records):
        nameserver_records = list(nameserver_records)
        additional_records = list(additional_records)
        random.shuffle(nameserver_records)
        random.shuffle(additional_records)

        # TODO: consider moving away from ns,addr pairs, and just using addr soley
        for nameserver_record in nameserver_records:
            assert isinstance(nameserver_record, DnsRecord)
            if nameserver_record.rtype == DnsRType.NS:
                # TODO: support domain name label compression for NS rdata
                ns_name = DomainName.parse(nameserver_record.rdata)
                for address_record in additional_records:
                    assert isinstance(address_record, DnsRecord)
                    if address_record.name == ns_name and address_record.rtype in [DnsRType.A]:
                        yield (nameserver_record, address_record)
Example #4
0
File: dns.py Project: loryka/DNSpy
    def resolve(self, dns_packet):
        assert isinstance(dns_packet, DnsPacket)
        assert hasattr(dns_packet, 'pk')

        # TODO: consider http://tools.ietf.org/html/draft-ietf-dnsext-edns1-03
        # TODO: try/except to set proper RCODE
        assert dns_packet.QDCOUNT > 0
        if dns_packet.QDCOUNT > 1:
            assert all(map(lambda question: question.name == dns_packet.questions[0].name, dns_packet.questions[1:]))
            assert all(
                map(lambda question: question.qclass == dns_packet.questions[0].qclass, dns_packet.questions[1:]))

        response = self.db.lookup_response(dns_packet.questions)
        if response:
            return response

        # TODO: deduplicate at the packet level?
        self.db.create_query(dns_packet.pk)

        # TODO: if not zone_cut bootstrap() ?

        root_hints = self.db.lookup_response(DnsQuestion(root_label, DnsQType.NS))
        next_zone_cut = root_hints

        # Determine SOA
        for zone in dns_packet.questions[0].name.enumerate_hierarchy():
            # TODO: opportunity here for async operation
            question = DnsQuestion(zone, DnsQType.SOA)
            zone_soa = self.db.lookup_response(question)

            #next_zone_cut = root_hints ???

            while next_zone_cut and not zone_soa:
                assert isinstance(next_zone_cut, Response)

                zone_cut = next_zone_cut
                next_zone_cut = None

                for (nameserver, additional_record) in self.enumerate_nameserver_addresses(zone_cut.nameservers,zone_cut.additional_records):
                    # TODO: Use/Check resolved AA queries from cached records, from zone_cut
                    # As fallback, use glue
                    # TODO: Nameserver(nameserver_record)?
                    ns_name = DomainName.parse(nameserver.rdata)
                    try:
                        response = yield from self.query([question], nameserver, additional_record,parent_id=dns_packet.pk)
                        assert isinstance(response, Response)
                    except asyncio.CancelledError:
                        pass
                    except asyncio.InvalidStateError:
                        pass
                    except AssertionError:
                        pass
                    else:
                        self.log.debug('resolve() %s' % response)
                        if response.RCODE == DnsResponseCode.no_error:
                            # TODO: and rtype == SOA etc
                            if response.ANCOUNT == 0:
                                if response.NSCOUNT > 0:
                                    # TODO: lame delegation
                                    # TODO: if RD
                                    # TODO: when we ask com for example.com our zone=example.com, when zone_cut is a.iana-servers.net, should attempt to resolve the address of the NS first, then rely on glue... this will require another recursion
                                    # for _ns in response.nameservers:
                                    #     #TODO: ask for A or AAAA based on the ipv4/6 of socket
                                    #     #TODO: check for a cached record, if one is not found, attempt to resolve it, if that fails, use glue
                                    #     #cached_response = yield from self.resolve(Query(questions=[DnsQuestion(DomainName.parse(_ns.rdata), DnsQType.A)]))
                                    #     # if cached_response:
                                    #     #     next_zone_cut = cached_response
                                    #     #     break # We found a cached address for the ns, break out so we can use this as next zone cut
                                    #         # TODO: this whole thing is a mess, and needs rewritting... the break here will prevent continuing in the event that the address we have cached is non responsive... a stack type mechanism would be much more suitable, then recursion depth could be controlled in a sane fashion
                                    # else:
                                    # Use the glue
                                    next_zone_cut = response
                                    break
                            elif response.ANCOUNT >= 1:
                                # TODO: ANCOUNT == 1 probably shouldn't be a requirement
                                next_zone_cut = zone_cut # Reuse the zone_cut that produced this authorative response
                                zone_soa = RData_SOA.parse(response.answers[0].rdata)
                                if ns_name != zone_soa.mname:
                                    pass

                                if response.questions[0].name == dns_packet.questions[0].name:
                                    result = yield from self.query(dns_packet.questions, nameserver, additional_record, parent_id=dns_packet.pk)
                                    if isinstance(result, Response):
                                        result.ID = dns_packet.ID
                                        return result
                                    # We received an authorative SOA response, but not from the preferred NS
                                    # TODO: consider using zone_soa as parent_id?
                                    # TODO: enforce expires/refresh/minimum TTL values
                                    # TODO: support negative caching (if response.AA)

                            else:
                                pass

                        # zone_cut = response
                        break # TODO: parallel requests, this line skips the rest of the ns,addr pairs

                if not response:
                    pass