Ejemplo n.º 1
0
    def _add_reverse_query(self):
        arg = self._get_arg(True)
        try:
            addr = IPAddr(arg)
        except ValueError:
            sys.stderr.write('Invalid IP address: "%s"\n' % arg)
            sys.exit(1)
        else:
            qname = addr.arpa_name()

        return DVCommandLineQuery(qname, dns.rdatatype.PTR, dns.rdataclass.IN)
Ejemplo n.º 2
0
    def _add_reverse_query(self):
        arg = self._get_arg(True)
        try:
            addr = IPAddr(arg)
        except ValueError:
            sys.stderr.write('Invalid IP address: "%s"\n' % arg)
            sys.exit(1)
        else:
            qname = addr.arpa_name()

        return DVCommandLineQuery(qname, dns.rdatatype.PTR, dns.rdataclass.IN)
Ejemplo n.º 3
0
    def _add_option(self):
        if self.args[self.arg_index].startswith('-h'):
            usage()
            sys.exit(0)
        elif self.args[self.arg_index].startswith('-b'):
            arg = self._get_arg(True)
            try:
                addr = IPAddr(arg)
            except ValueError:
                sys.stderr.write('Invalid IP address: "%s"\n' % arg)
                sys.exit(1)

            if addr.version == 6:
                family = socket.AF_INET6
            else:
                family = socket.AF_INET

            try:
                s = socket.socket(family)
                s.bind((addr, 0))
            except socket.error, e:
                if e.errno == errno.EADDRNOTAVAIL:
                    sys.stderr.write(
                        'Cannot bind to specified IP address: "%s"\n' % addr)
                    sys.exit(1)
            else:
                del s
                if addr.version == 6:
                    self.options['client_ipv6'] = addr
                else:
                    self.options['client_ipv4'] = addr
Ejemplo n.º 4
0
def main(argv):
    global tm
    global resolver

    try:
        try:
            opts, args = getopt.getopt(argv[1:], 'f:d:l:c:r:t:64b:u:kmpo:a:R:x:EAs:Fh')
        except getopt.GetoptError, e:
            usage(str(e))
            sys.exit(1)

        tm = transport.DNSQueryTransportManager()
        resolver = Resolver.from_file('/etc/resolv.conf', StandardRecursiveQueryCD, transport_manager=tm)

        # get all the -x options
        explicit_delegations = {}
        client_ipv4 = None
        client_ipv6 = None
        for opt, arg in opts:
            if opt == '-x':
                try:
                    domain, mappings = arg.split(':', 1)
                except ValueError:
                    usage('Incorrect usage of -x option: "%s"' % arg)
                    sys.exit(1)
                domain = domain.strip()
                mappings = mappings.strip()
                try:
                    domain = dns.name.from_text(domain)
                except dns.exception.DNSException:
                    usage('The domain name was invalid: "%s"' % domain)
                    sys.exit(1)
                if not mappings:
                    usage('Incorrect usage of -x option: "%s"' % arg)
                    sys.exit(1)
                if domain not in explicit_delegations:
                    explicit_delegations[domain] = set()
                explicit_delegations[domain].update(name_addr_mappings_from_string(mappings))

            elif opt == '-b':
                try:
                    addr = IPAddr(arg)
                except ValueError:
                    usage('The IP address was invalid: "%s"' % arg)
                    sys.exit(1)

                if addr.version == 4:
                    client_ipv4 = addr
                    fam = socket.AF_INET
                else:
                    client_ipv6 = addr
                    fam = socket.AF_INET6
                try:
                    s = socket.socket(fam)
                    s.bind((addr, 0))
                    del s
                except socket.error, e:
                    if e.errno == errno.EADDRNOTAVAIL:
                        usage('Cannot bind to specified IP address: "%s"' % addr)
                        sys.exit(1)
Ejemplo n.º 5
0
    def test_delegation_authoritative_aggregation(self):
        args1 = [
            '-A', '-N',
            'example.com:ns1.example.com=192.0.2.1,ns1.example.com=[2001:db8::1]',
            '-x', 'foo.com:ns1.foo.com=192.0.2.3:50503'
        ]

        explicit_delegations1 = {
            (dns.name.from_text('com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['localhost']),
            (dns.name.from_text('foo.com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('foo.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['ns1.foo.com']),
            (dns.name.from_text('ns1.foo.com'), dns.rdatatype.A):
            dns.rrset.from_text_list(dns.name.from_text('ns1.foo.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.A,
                                     ['192.0.2.3']),
        }

        for ex in (explicit_delegations1, ):
            if self.use_ipv6:
                ex[(dns.name.from_text('localhost'), dns.rdatatype.AAAA)] = \
                        dns.rrset.from_text_list(dns.name.from_text('localhost'), 0, dns.rdataclass.IN, dns.rdatatype.AAAA,
                            ['::1'])
                loopback_ip = IPAddr('::1')
            else:
                ex[(dns.name.from_text('localhost'), dns.rdatatype.A)] = \
                        dns.rrset.from_text_list(dns.name.from_text('localhost'), 0, dns.rdataclass.IN, dns.rdatatype.A,
                            ['127.0.0.1'])
                loopback_ip = IPAddr('127.0.0.1')

        odd_ports1 = {
            (dns.name.from_text('com'), loopback_ip): self.first_port,
            (dns.name.from_text('foo.com'), IPAddr('192.0.2.3')): 50503,
        }

        ZoneFileToServe._next_free_port = self.first_port

        arghelper1 = ArgHelper(self.resolver, self.logger)
        arghelper1.build_parser('probe', args1)
        arghelper1.aggregate_delegation_info()
        self.assertEqual(arghelper1.explicit_delegations,
                         explicit_delegations1)
        self.assertEqual(arghelper1.odd_ports, odd_ports1)
Ejemplo n.º 6
0
    def test_client_ip(self):
        args = []
        arghelper = ArgHelper(self.resolver, self.logger)
        arghelper.build_parser('probe', args)
        arghelper.set_kwargs()
        self.assertIsNone(arghelper.client_ipv4)
        self.assertIsNone(arghelper.client_ipv6)

        args = ['-b', '127.0.0.1']
        if self.use_ipv6:
            args.extend(['-b', '::1'])
        arghelper = ArgHelper(self.resolver, self.logger)
        arghelper.build_parser('probe', args)
        arghelper.set_kwargs()
        self.assertEqual(arghelper.client_ipv4, IPAddr('127.0.0.1'))
        if self.use_ipv6:
            self.assertEqual(arghelper.client_ipv6, IPAddr('::1'))
Ejemplo n.º 7
0
def _init_stub_resolver():
    global resolver

    servers = set()
    for rdata in explicit_delegations[(WILDCARD_EXPLICIT_DELEGATION, dns.rdatatype.NS)]:
        for rdtype in (dns.rdatatype.A, dns.rdatatype.AAAA):
            if (rdata.target, rdtype) in explicit_delegations:
                servers.update([IPAddr(r.address) for r in explicit_delegations[(rdata.target, rdtype)]])
    resolver = Resolver(list(servers), StandardRecursiveQueryCD, transport_manager=tm)
Ejemplo n.º 8
0
def _get_nameservers_for_name(addr):
    nameservers = []
    try:
        addrinfo = socket.getaddrinfo(addr, 53, 0, 0, socket.IPPROTO_TCP)
    except socket.gaierror:
        sys.stderr.write('Unable to resolve "%s"\n' % addr)
    else:
        for item in addrinfo:
            nameservers.append(IPAddr(item[4][0]))
    return nameservers
Ejemplo n.º 9
0
    def _add_option(self):
        if self.args[self.arg_index].startswith('-h'):
            usage()
            sys.exit(0)
        elif self.args[self.arg_index].startswith('-b'):
            arg = self._get_arg(True)
            try:
                addr = IPAddr(arg)
            except ValueError:
                sys.stderr.write('Invalid IP address: "%s"\n' % arg)
                sys.exit(1)

            if addr.version == 6:
                family = socket.AF_INET6
            else:
                family = socket.AF_INET

            try:
                s = socket.socket(family)
                s.bind((addr, 0))
            except socket.error as e:
                if e.errno == errno.EADDRNOTAVAIL:
                    sys.stderr.write(
                        'Cannot bind to specified IP address: "%s"\n' % addr)
                    sys.exit(1)
            else:
                del s
                if addr.version == 6:
                    self.options['client_ipv6'] = addr
                else:
                    self.options['client_ipv4'] = addr
        elif self.args[self.arg_index].startswith('-c'):
            arg = self._get_arg(True)
            try:
                self.options['rdclass'] = dns.rdataclass.from_text(arg)
            except dns.rdataclass.UnknownRdataclass:
                sys.stderr.write('Unknown class: "%s".\n' % arg)
                sys.exit(1)
        elif self.args[self.arg_index].startswith('-t'):
            arg = self._get_arg(True)
            try:
                self.options['rdtype'] = dns.rdatatype.from_text(arg)
            except dns.rdatatype.UnknownRdatatype:
                sys.stderr.write('Unknown type: "%s".\n' % arg)
                sys.exit(1)
        elif self.args[self.arg_index].startswith('-6'):
            self._get_arg(False)
            self.options['use_ipv6'] = True
        elif self.args[self.arg_index].startswith('-4'):
            self._get_arg(False)
            self.options['use_ipv4'] = True
        else:
            sys.stderr.write('Option "%s" not recognized.\n' %
                             self.args[self.arg_index][:2])
            sys.exit(1)
    def test_bindable_ip(self):
        self.assertEqual(ArgHelper.bindable_ip('127.0.0.1'), IPAddr('127.0.0.1'))
        if self.use_ipv6:
            self.assertEqual(ArgHelper.bindable_ip('::1'), IPAddr('::1'))

        # invalid IPv4 address
        with self.assertRaises(argparse.ArgumentTypeError):
            ArgHelper.bindable_ip('192.')

        # invalid IPv6 address
        with self.assertRaises(argparse.ArgumentTypeError):
            ArgHelper.bindable_ip('2001:')

        # invalid IPv4 to bind to
        with self.assertRaises(argparse.ArgumentTypeError):
            ArgHelper.bindable_ip('192.0.2.1')

        # invalid IPv6 to bind to
        with self.assertRaises(argparse.ArgumentTypeError):
            ArgHelper.bindable_ip('2001:db8::1')
    def test_delegation_option(self):
        arg1 = 'example.com:ns1.example.com=192.0.2.1:1234,ns1.example.com=[2001:db8::1],' + \
                'ns1.example.com=192.0.2.2,ns2.example.com=[2001:db8::2]'

        arg1_with_spaces = ' example.com : ns1.example.com = [192.0.2.1]:1234 , ns1.example.com = [2001:db8::1], ' + \
                'ns1.example.com = [192.0.2.2] , ns2.example.com = [2001:db8::2] '

        arg2 = 'example.com:%s' % EXAMPLE_COM_DELEGATION

        delegation_mapping1 = {
                (dns.name.from_text('example.com'), dns.rdatatype.NS):
                        dns.rrset.from_text_list(dns.name.from_text('example.com'), 0, dns.rdataclass.IN, dns.rdatatype.NS,
                            ['ns1.example.com', 'ns2.example.com']),
                (dns.name.from_text('ns1.example.com'), dns.rdatatype.A):
                        dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0, dns.rdataclass.IN, dns.rdatatype.A,
                            ['192.0.2.1', '192.0.2.2']),
                (dns.name.from_text('ns1.example.com'), dns.rdatatype.AAAA):
                        dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0, dns.rdataclass.IN, dns.rdatatype.AAAA,
                            ['2001:db8::1']),
                (dns.name.from_text('ns2.example.com'), dns.rdatatype.AAAA):
                        dns.rrset.from_text_list(dns.name.from_text('ns2.example.com'), 0, dns.rdataclass.IN, dns.rdatatype.AAAA,
                            ['2001:db8::2']),
                }
        stop_at1 = False
        odd_ports1 = { (dns.name.from_text('example.com'), IPAddr('192.0.2.1')): 1234 }

        delegation_mapping2 = {
                (dns.name.from_text('example.com'), dns.rdatatype.NS):
                        dns.rrset.from_text_list(dns.name.from_text('example.com'), 0, dns.rdataclass.IN, dns.rdatatype.NS,
                            ['ns1.example.com']),
                (dns.name.from_text('ns1.example.com'), dns.rdatatype.A):
                        dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0, dns.rdataclass.IN, dns.rdatatype.A,
                            ['127.0.0.1'])
                }
        stop_at2 = False
        odd_ports2 = {}

        obj = self.helper.delegation_name_server_mappings(arg1)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping1)
        self.assertEqual(obj.stop_at, stop_at1)
        self.assertEqual(obj.odd_ports, odd_ports1)

        obj = self.helper.delegation_name_server_mappings(arg1_with_spaces)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping1)
        self.assertEqual(obj.stop_at, stop_at1)
        self.assertEqual(obj.odd_ports, odd_ports1)

        obj = self.helper.delegation_name_server_mappings(arg2)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping2)
        self.assertEqual(obj.stop_at, stop_at2)
        self.assertEqual(obj.odd_ports, odd_ports2)
Ejemplo n.º 12
0
    def test_recursive_option(self):
        arg1 = 'ns1.example.com=192.0.2.1:1234,ns1.example.com=[2001:db8::1],' + \
                'ns1.example.com=192.0.2.2,ns2.example.com=[2001:db8::2],a.root-servers.net'

        arg1_with_spaces = ' ns1.example.com = [192.0.2.1]:1234 , ns1.example.com = [2001:db8::1], ' + \
                'ns1.example.com = [192.0.2.2] , ns2.example.com = [2001:db8::2] , a.root-servers.net '

        delegation_mapping1 = {
            (WILDCARD_EXPLICIT_DELEGATION, dns.rdatatype.NS):
            dns.rrset.from_text_list(
                WILDCARD_EXPLICIT_DELEGATION, 0, dns.rdataclass.IN,
                dns.rdatatype.NS,
                ['ns1.example.com', 'ns2.example.com', 'a.root-servers.net']),
            (dns.name.from_text('ns1.example.com'), dns.rdatatype.A):
            dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.A,
                                     ['192.0.2.1', '192.0.2.2']),
            (dns.name.from_text('ns1.example.com'), dns.rdatatype.AAAA):
            dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.AAAA,
                                     ['2001:db8::1']),
            (dns.name.from_text('ns2.example.com'), dns.rdatatype.AAAA):
            dns.rrset.from_text_list(dns.name.from_text('ns2.example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.AAAA,
                                     ['2001:db8::2']),
            (dns.name.from_text('a.root-servers.net'), dns.rdatatype.A):
            dns.rrset.from_text_list(dns.name.from_text('a.root-servers.net'),
                                     0, dns.rdataclass.IN, dns.rdatatype.A,
                                     ['198.41.0.4']),
            (dns.name.from_text('a.root-servers.net'), dns.rdatatype.AAAA):
            dns.rrset.from_text_list(dns.name.from_text('a.root-servers.net'),
                                     0, dns.rdataclass.IN, dns.rdatatype.AAAA,
                                     ['2001:503:ba3e::2:30'])
        }
        stop_at1 = False
        odd_ports1 = {
            (WILDCARD_EXPLICIT_DELEGATION, IPAddr('192.0.2.1')): 1234
        }

        obj = self.helper.recursive_servers_for_domain(arg1)
        self.assertEqual(obj.domain, WILDCARD_EXPLICIT_DELEGATION)
        self.assertEqual(obj.delegation_mapping, delegation_mapping1)
        self.assertEqual(obj.stop_at, stop_at1)
        self.assertEqual(obj.odd_ports, odd_ports1)

        obj = self.helper.recursive_servers_for_domain(arg1_with_spaces)
        self.assertEqual(obj.domain, WILDCARD_EXPLICIT_DELEGATION)
        self.assertEqual(obj.delegation_mapping, delegation_mapping1)
        self.assertEqual(obj.stop_at, stop_at1)
        self.assertEqual(obj.odd_ports, odd_ports1)
Ejemplo n.º 13
0
        def clean_ecs(self):
            if not self.cleaned_data['ecs']:
                return None

            s = self.cleaned_data['ecs']
            try:
                addr, prefix = s.split('/', 1)
            except ValueError:
                addr = s
                prefix = None

            try:
                addr = IPAddr(addr)
            except ValueError:
                raise forms.ValidationError('Please enter a valid IP address.')

            if addr.version == 4:
                addrlen = 4
                family = 1
            else:
                addrlen = 16
                family = 2

            if prefix is None:
                prefix = addrlen << 3
            else:
                try:
                    prefix = int(prefix)
                except ValueError:
                    raise forms.ValidationError(
                        'Please enter a valid prefix length.')

                if prefix < 0 or prefix > (addrlen << 3):
                    raise forms.ValidationError(
                        'Please enter a valid prefix length.')

            bytes_masked, remainder = divmod(prefix, 8)
            if remainder:
                bytes_masked += 1

            wire = struct.pack('!H', family)
            wire += struct.pack('!B', prefix)
            wire += struct.pack('!B', 0)
            wire += addr._ipaddr_bytes[:bytes_masked]

            return dns.edns.GenericOption(8, wire)
Ejemplo n.º 14
0
def _get_ecs_option(s):
    try:
        addr, prefix = s.split('/', 1)
    except ValueError:
        addr = s
        prefix = None

    try:
        addr = IPAddr(addr)
    except ValueError:
        usage('The IP address was invalid: "%s"' % addr)
        sys.exit(1)

    if addr.version == 4:
        addrlen = 4
        family = 1
    else:
        addrlen = 16
        family = 2

    if prefix is None:
        prefix = addrlen << 3
    else:
        try:
            prefix = int(prefix)
        except ValueError:
            usage('The mask length was invalid: "%s"' % prefix)
            sys.exit(1)

        if prefix < 0 or prefix > (addrlen << 3):
            usage('The mask length was invalid: "%d"' % prefix)
            sys.exit(1)

    bytes_masked, remainder = divmod(prefix, 8)
    if remainder:
        bytes_masked += 1

    wire = struct.pack('!H', family)
    wire += struct.pack('!B', prefix)
    wire += struct.pack('!B', 0)
    wire += addr._ipaddr_bytes[:bytes_masked]

    return dns.edns.GenericOption(8, wire)
Ejemplo n.º 15
0
def get_explicit_delegations(tm, query_authoritative_servers: bool,
                             server_list: typing.Optional[str]):
    explicit_delegations = {}
    odd_ports = {}
    if not query_authoritative_servers:
        bootstrap_resolver = Resolver.from_file("/etc/resolv.conf",
                                                StandardRecursiveQueryCD,
                                                transport_manager=tm)
        explicit_delegations[(WILDCARD_EXPLICIT_DELEGATION,
                              dns.rdatatype.NS)] = dns.rrset.RRset(
                                  WILDCARD_EXPLICIT_DELEGATION,
                                  dns.rdataclass.IN, dns.rdatatype.NS)
        if server_list:
            _name_addr_mappings_from_string(
                WILDCARD_EXPLICIT_DELEGATION,
                server_list,
                explicit_delegations,
                odd_ports,
            )
        else:
            for i, server in enumerate(bootstrap_resolver._servers):
                if IPAddr(server).version == 6:
                    rdtype = dns.rdatatype.AAAA
                else:
                    rdtype = dns.rdatatype.A
                name = dns.name.from_text("ns%d" % i)
                explicit_delegations[(WILDCARD_EXPLICIT_DELEGATION,
                                      dns.rdatatype.NS)].add(
                                          dns.rdtypes.ANY.NS.NS(
                                              dns.rdataclass.IN,
                                              dns.rdatatype.NS, name))
                if (name, rdtype) not in explicit_delegations:
                    explicit_delegations[(name, rdtype)] = dns.rrset.RRset(
                        name, dns.rdataclass.IN, rdtype)
                explicit_delegations[(name, rdtype)].add(
                    dns.rdata.from_text(dns.rdataclass.IN, rdtype, server))
    return (explicit_delegations, odd_ports)
Ejemplo n.º 16
0
def name_addr_mappings_from_string(mappings):
    mappings_set = set()
    mappings = mappings.split(',')
    i = 1
    for mapping in mappings:
        try:
            name, addr = mapping.rsplit('=', 1)
        except ValueError:
            # first see if it's a plain IP address
            try:
                addr = IPAddr(mapping.strip())
            except ValueError:
                # if not, then assign name to mapping
                name = mapping
                addr = None
            else:
                # if it's an IP with no name specified, then create
                # a name
                name = 'ns%d' % i
                i += 1
        else:
            addr = addr.strip()
        name = name.strip()
        try:
            name = dns.name.from_text(name)
        except dns.exception.DNSException:
            usage('The domain name was invalid: "%s"' % name)
            sys.exit(1)

        # no address is provided, so query A/AAAA records for the name
        if addr is None:
            query_tuples = ((name, dns.rdatatype.A, dns.rdataclass.IN), (name, dns.rdatatype.AAAA, dns.rdataclass.IN))
            answer_map = resolver.query_multiple_for_answer(*query_tuples)
            found_answer = False
            for a in answer_map.values():
                if isinstance(a, DNSAnswer):
                    found_answer = True
                    for a_rr in a.rrset:
                        mappings_set.add((name, IPAddr(a_rr.to_text())))
                # negative responses
                elif isinstance(a, (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer)):
                    pass
                # error responses
                elif isinstance(a, (dns.exception.Timeout, dns.resolver.NoNameservers)):
                    usage('There was an error resolving "%s".  Please specify an address or use a name that resolves properly.' % fmt.humanize_name(name))
                    sys.exit(1)

            if not found_answer:
                usage('"%s" did not resolve to an address.  Please specify an address or use a name that resolves properly.' % fmt.humanize_name(name))
                sys.exit(1)

        # otherwise, add the address
        elif not addr:
            usage('No IP address was supplied.')
            sys.exit(1)
        else:
            if addr and addr[0] == '[' and addr[-1] == ']':
                addr = addr[1:-1]
            try:
                addr = IPAddr(addr)
            except ValueError:
                usage('The IP address was invalid: "%s"' % addr)
                sys.exit(1)
            mappings_set.add((name, addr))
    return mappings_set
Ejemplo n.º 17
0
        def clean_explicit_delegation(self):
            resolver = Resolver.from_file('/etc/resolv.conf',
                                          StandardRecursiveQueryCD)

            s = self.cleaned_data['explicit_delegation']
            mappings = set()
            i = 1
            for line in s.splitlines():
                line = line.strip()
                if not line:
                    continue
                # get ride of extra columns
                cols = line.split()
                if len(cols) > 1:
                    line = '%s %s' % (cols[0], cols[-1])
                try:
                    name, addr = line.split()
                except ValueError:
                    # first see if it's a plain IP address
                    try:
                        addr = IPAddr(line.strip())
                    except ValueError:
                        # if not, then assign name to mapping
                        name = line
                        addr = None
                    else:
                        # if it's an IP with no name specified, then create
                        # a name
                        name = 'ns%d' % i
                        i += 1
                try:
                    name = dns.name.from_text(name)
                except:
                    raise forms.ValidationError(
                        'The domain name was invalid: "%s"' % name)

                # no address is provided, so query A/AAAA records for the name
                if addr is None:
                    query_tuples = ((name, dns.rdatatype.A, dns.rdataclass.IN),
                                    (name, dns.rdatatype.AAAA,
                                     dns.rdataclass.IN))
                    answer_map = resolver.query_multiple_for_answer(
                        *query_tuples)
                    found_answer = False
                    for a in answer_map.values():
                        if isinstance(a, DNSAnswer):
                            found_answer = True
                            for a_rr in a.rrset:
                                mappings.add((name, IPAddr(a_rr.to_text())))
                        # negative responses
                        elif isinstance(
                                a,
                            (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer)):
                            pass
                        # error responses
                        elif isinstance(a, (dns.exception.Timeout,
                                            dns.resolver.NoNameservers)):
                            raise forms.ValidationError(
                                'There was an error resolving "%s".  Please specify an address or use a name that resolves properly.'
                                % fmt.humanize_name(name))

                    if not found_answer:
                        raise forms.ValidationError(
                            '"%s" did not resolve to an address.  Please specify an address or use a name that resolves properly.'
                            % fmt.humanize_name(name))

                # otherwise, add the address
                else:
                    if addr and addr[0] == '[' and addr[-1] == ']':
                        addr = addr[1:-1]
                    try:
                        addr = IPAddr(addr)
                    except ValueError:
                        raise forms.ValidationError(
                            'The IP address was invalid: "%s"' % addr)
                    mappings.add((name, addr))

            # if there something in the box, yet no mappings resulted, then raise a
            # validation error
            if self.cleaned_data['explicit_delegation'] and not mappings:
                raise forms.ValidationError(
                    'Unable to process address records!')

            return mappings
Ejemplo n.º 18
0
def name_addr_mappings_from_string(mappings):
    mappings_set = set()
    mappings = mappings.split(',')
    i = 1
    for mapping in mappings:
        try:
            name, addr = mapping.rsplit('=', 1)
        except ValueError:
            # first see if it's a plain IP address
            try:
                addr = IPAddr(mapping.strip())
            except ValueError:
                # if not, then assign name to mapping
                name = mapping
                addr = None
            else:
                # if it's an IP with no name specified, then create
                # a name
                name = 'ns%d' % i
                i += 1
        else:
            addr = addr.strip()
        name = name.strip()
        try:
            name = dns.name.from_text(name)
        except dns.exception.DNSException:
            usage('The domain name was invalid: "%s"' % name)
            sys.exit(1)

        # no address is provided, so query A/AAAA records for the name
        if addr is None:
            query_tuples = ((name, dns.rdatatype.A, dns.rdataclass.IN), (name, dns.rdatatype.AAAA, dns.rdataclass.IN))
            answer_map = resolver.query_multiple_for_answer(*query_tuples)
            found_answer = False
            for a in answer_map.values():
                if isinstance(a, DNSAnswer):
                    found_answer = True
                    for a_rr in a.rrset:
                        mappings_set.add((name, IPAddr(a_rr.to_text())))
                # negative responses
                elif isinstance(a, (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer)):
                    pass
                # error responses
                elif isinstance(a, (dns.exception.Timeout, dns.resolver.NoNameservers)):
                    usage('There was an error resolving "%s".  Please specify an address or use a name that resolves properly.' % fmt.humanize_name(name))
                    sys.exit(1)

            if not found_answer:
                usage('"%s" did not resolve to an address.  Please specify an address or use a name that resolves properly.' % fmt.humanize_name(name))
                sys.exit(1)

        # otherwise, add the address
        elif not addr:
            usage('No IP address was supplied.')
            sys.exit(1)
        else:
            if addr and addr[0] == '[' and addr[-1] == ']':
                addr = addr[1:-1]
            try:
                addr = IPAddr(addr)
            except ValueError:
                usage('The IP address was invalid: "%s"' % addr)
                sys.exit(1)
            mappings_set.add((name, addr))
    return mappings_set
Ejemplo n.º 19
0
def _name_addr_mappings_from_string(domain, addr_mappings: typing.List[str],
                                    delegation_mapping, odd_ports):
    require_name = False
    i = 1
    for mapping in addr_mappings:
        # get rid of whitespace
        mapping = mapping.strip()

        # Name
        try:
            name = dns.name.from_text("ns%d" % i)
            i += 1
        except dns.exception.DNSException:
            usage('The domain name was invalid: "%s"' % name)
            sys.exit(1)

        # Add the name to the NS RRset
        delegation_mapping[(domain, dns.rdatatype.NS)].add(
            dns.rdtypes.ANY.NS.NS(dns.rdataclass.IN, dns.rdatatype.NS, name))

        # addr, port
        match = PORT_RE.search(mapping)
        if match is not None:
            mapping = match.group(1)
            port = int(match.group(2))
            port_str = ":%d" % port
        else:
            port = 53
            port_str = ""

        # value is an address
        addr, num_replacements = BRACKETS_RE.subn(r"\1", mapping)
        try:
            IPAddr(addr)
        except ValueError:
            # see if this was an IPv6 address without a port
            try:
                IPAddr(addr + port_str)
            except ValueError:
                usage('The IP address was invalid: "%s"' % addr)
                sys.exit(1)
            else:
                usage("Brackets are required around IPv6 addresses.")
                sys.exit(1)

        if IPAddr(addr).version == 6:
            if num_replacements < 1:
                usage("Brackets are required around IPv6 addresses.")
                sys.exit(1)

            a_rdtype = dns.rdatatype.AAAA
            rdtype_cls = dns.rdtypes.IN.AAAA.AAAA
        else:
            a_rdtype = dns.rdatatype.A
            rdtype_cls = dns.rdtypes.IN.A.A
        if (name, a_rdtype) not in delegation_mapping:
            delegation_mapping[(name, a_rdtype)] = dns.rrset.RRset(
                name, dns.rdataclass.IN, a_rdtype)
        delegation_mapping[(name, a_rdtype)].add(
            rdtype_cls(dns.rdataclass.IN, a_rdtype, addr))
        if port != 53:
            odd_ports[(domain, IPAddr(addr))] = port
Ejemplo n.º 20
0
from dnsviz.resolver import DNSAnswer, Resolver, FullResolver
from dnsviz import transport
from dnsviz.util import get_client_address, get_root_hints
lb2s = fmt.latin1_binary_to_string

logger = logging.getLogger('dnsviz.analysis.online')

# this needs to be global because of multiprocessing
tm = None
full_resolver = None
stub_resolver = None
explicit_delegations = None
odd_ports = None
next_port = 50053

A_ROOT_IPV4 = IPAddr('198.41.0.4')
A_ROOT_IPV6 = IPAddr('2001:503:ba3e::2:30')

BRACKETS_RE = re.compile(r'^\[(.*)\]$')
PORT_RE = re.compile(r'^(.*):(\d+)$')
STOP_RE = re.compile(r'^(.*)\+$')
NAME_VAL_DELIM_RE = re.compile(r'\s*=\s*')


#XXX this is a hack required for inter-process sharing of dns.name.Name
# instances using multiprocess
def _setattr_dummy(self, name, value):
    return super(dns.name.Name, self).__setattr__(name, value)


dns.name.Name.__setattr__ = _setattr_dummy
Ejemplo n.º 21
0
def name_addr_mappings_from_string(domain, addr_mappings, delegation_mapping,
                                   require_name):
    global next_port

    addr_mappings = addr_mappings.split(',')
    i = 1
    for mapping in addr_mappings:

        # get rid of whitespace
        mapping = mapping.strip()

        # Determine whether there is a port stuck on there
        match = PORT_RE.search(mapping)
        if match is not None:
            mapping = match.group(1)
            port = int(match.group(2))
            port_str = ':%d' % port
        else:
            port = 53
            port_str = ''

        num_replacements = None

        # if the value is actually a path, then check it as a zone file
        if os.path.isfile(mapping):
            # if this is a file containing delegation records, then read the
            # file, create a name=value string, and call name_addr_mappings_from_string()
            if require_name:
                mappings_from_file = []
                try:
                    s = io.open(mapping, 'r', encoding='utf-8').read()
                except IOError as e:
                    usage('%s: "%s"' % (e.strerror, mapping))
                    sys.exit(3)

                try:
                    m = dns.message.from_text(str(';ANSWER\n' + s))
                except dns.exception.DNSException as e:
                    usage('Error reading delegation records from %s: "%s"' %
                          (mapping, e))
                    sys.exit(3)

                try:
                    ns_rrset = m.find_rrset(m.answer, domain,
                                            dns.rdataclass.IN,
                                            dns.rdatatype.NS)
                except KeyError:
                    usage('No NS records for %s found in %s' %
                          (lb2s(domain.canonicalize().to_text()), mapping))
                    sys.exit(3)

                for rdata in ns_rrset:
                    a_rrsets = [
                        r for r in m.answer
                        if r.name == rdata.target and r.rdtype in (
                            dns.rdatatype.A, dns.rdatatype.AAAA)
                    ]
                    if not a_rrsets or not rdata.target.is_subdomain(
                            domain.parent()):
                        mappings_from_file.append(
                            lb2s(rdata.target.canonicalize().to_text()))
                    else:
                        for a_rrset in a_rrsets:
                            for a_rdata in a_rrset:
                                mappings_from_file.append(
                                    '%s=%s' % (lb2s(
                                        rdata.target.canonicalize().to_text()),
                                               IPAddr(a_rdata.address)))

                name_addr_mappings_from_string(domain,
                                               ','.join(mappings_from_file),
                                               delegation_mapping,
                                               require_name)
                continue

            # otherwise (it is the zone proper), just serve the file
            else:
                if port_str == '':
                    #TODO assign random port here
                    port = next_port
                    next_port += 1
                _serve_zone(domain, mapping, port)
                name = 'localhost'
                addr = '127.0.0.1'

        else:
            # First determine whether the argument is name=value or simply value
            try:
                name, addr = NAME_VAL_DELIM_RE.split(mapping, 1)
            except ValueError:
                # Argument is a single value.  Now determine whether that value is
                # a name or an address.
                try:
                    IPAddr(BRACKETS_RE.sub(r'\1', mapping))
                except ValueError:
                    # see if this was an IPv6 address without a port
                    try:
                        IPAddr(mapping + port_str)
                    except ValueError:
                        pass
                    else:
                        usage('Brackets are required around IPv6 addresses.')
                        sys.exit(1)

                    # value is not an address
                    name = mapping
                    addr = None
                else:
                    if require_name:
                        usage(
                            'A name is required to accompany the address for this option.'
                        )
                        sys.exit(1)

                    # value is an address
                    name = 'ns%d' % i
                    addr, num_replacements = BRACKETS_RE.subn(r'\1', mapping)
                    i += 1
            else:
                # Argument is name=value
                addr, num_replacements = BRACKETS_RE.subn(r'\1', addr)

            if not name:
                usage('The domain name was empty.')
                sys.exit(1)

        # At this point, name is defined, and addr may or may not be defined.
        # Both are of type str.

        # Check that the name is valid
        try:
            name = dns.name.from_text(name)
        except dns.exception.DNSException:
            usage('The domain name was invalid: "%s"' % name)
            sys.exit(1)

        # Add the name to the NS RRset
        delegation_mapping[(domain, dns.rdatatype.NS)].add(
            dns.rdtypes.ANY.NS.NS(dns.rdataclass.IN, dns.rdatatype.NS, name))

        if addr is None:
            if not require_name:
                # If no address is provided, query A/AAAA records for the name
                query_tuples = ((name, dns.rdatatype.A, dns.rdataclass.IN),
                                (name, dns.rdatatype.AAAA, dns.rdataclass.IN))
                answer_map = stub_resolver.query_multiple_for_answer(
                    *query_tuples)
                found_answer = False
                for (n, rdtype, rdclass) in answer_map:
                    a = answer_map[(n, rdtype, rdclass)]
                    if isinstance(a, DNSAnswer):
                        found_answer = True
                        delegation_mapping[(
                            name, rdtype)] = dns.rrset.from_text_list(
                                name, 0, dns.rdataclass.IN, rdtype,
                                [IPAddr(r.address) for r in a.rrset])
                        if port != 53:
                            for r in a.rrset:
                                odd_ports[(domain, IPAddr(r.address))] = port
                    # negative responses
                    elif isinstance(
                            a, (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer)):
                        pass
                    # error responses
                    elif isinstance(
                            a,
                        (dns.exception.Timeout, dns.resolver.NoNameservers)):
                        usage(
                            'There was an error resolving "%s".  Please specify an address or use a name that resolves properly.'
                            % fmt.humanize_name(name))
                        sys.exit(1)

                if not found_answer:
                    usage(
                        '"%s" did not resolve to an address.  Please specify an address or use a name that resolves properly.'
                        % fmt.humanize_name(name))
                    sys.exit(1)

        elif not addr:
            if not require_name:
                usage('The IP address was empty.')
                sys.exit(1)

        else:
            try:
                IPAddr(addr)
            except ValueError:
                # see if this was an IPv6 address without a port
                try:
                    IPAddr(addr + port_str)
                except ValueError:
                    usage('The IP address was invalid: "%s"' % addr)
                    sys.exit(1)
                else:
                    usage('Brackets are required around IPv6 addresses.')
                    sys.exit(1)

            if IPAddr(addr).version == 6:
                if num_replacements < 1:
                    usage('Brackets are required around IPv6 addresses.')
                    sys.exit(1)

                a_rdtype = dns.rdatatype.AAAA
                rdtype_cls = dns.rdtypes.IN.AAAA.AAAA
            else:
                a_rdtype = dns.rdatatype.A
                rdtype_cls = dns.rdtypes.IN.A.A
            if (name, a_rdtype) not in delegation_mapping:
                delegation_mapping[(name, a_rdtype)] = dns.rrset.RRset(
                    name, dns.rdataclass.IN, a_rdtype)
            delegation_mapping[(name, a_rdtype)].add(
                rdtype_cls(dns.rdataclass.IN, a_rdtype, addr))
            if port != 53:
                odd_ports[(domain, IPAddr(addr))] = port
Ejemplo n.º 22
0
    def test_delegation_aggregation(self):
        args1 = [
            '-A', '-N',
            'example.com:ns1.example.com=192.0.2.1,ns1.example.com=[2001:db8::1]',
            '-N', 'example.com:ns1.example.com=192.0.2.4', '-N',
            'example.com:ns2.example.com=192.0.2.2', '-N',
            'example.com:ns3.example.com=192.0.2.3'
        ]
        args2 = [
            '-A', '-N', 'example.com:ns1.example.com=192.0.2.1', '-D',
            'example.com:34983 10 1 EC358CFAAEC12266EF5ACFC1FEAF2CAFF083C418',
            '-D',
            'example.com:34983 10 2 608D3B089D79D554A1947BD10BEC0A5B1BDBE67B4E60E34B1432ED00 33F24B49'
        ]
        args3 = [
            '-A', '-N', 'example.com:ns1.example.com=192.0.2.1', '-N',
            'example1.com:ns1.example1.com=192.0.2.2'
        ]
        args4 = [
            '-A', '-N', 'example.com:ns1.example.com=192.0.2.1', '-N',
            'example.net:ns1.example.net=192.0.2.2'
        ]

        explicit_delegations1 = {
            (dns.name.from_text('com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['localhost']),
        }
        explicit_delegations2 = {
            (dns.name.from_text('com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['localhost']),
        }
        explicit_delegations3 = {
            (dns.name.from_text('com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['localhost']),
        }
        explicit_delegations4 = {
            (dns.name.from_text('com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['localhost']),
            (dns.name.from_text('net'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('net'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['localhost']),
        }

        for ex in (explicit_delegations1, explicit_delegations2,
                   explicit_delegations3, explicit_delegations4):
            if self.use_ipv6:
                ex[(dns.name.from_text('localhost'), dns.rdatatype.AAAA)] = \
                        dns.rrset.from_text_list(dns.name.from_text('localhost'), 0, dns.rdataclass.IN, dns.rdatatype.AAAA,
                            ['::1'])
                loopback_ip = IPAddr('::1')
            else:
                ex[(dns.name.from_text('localhost'), dns.rdatatype.A)] = \
                        dns.rrset.from_text_list(dns.name.from_text('localhost'), 0, dns.rdataclass.IN, dns.rdatatype.A,
                            ['127.0.0.1'])
                loopback_ip = IPAddr('127.0.0.1')

        odd_ports1 = {
            (dns.name.from_text('com'), loopback_ip): self.first_port
        }
        odd_ports2 = {
            (dns.name.from_text('com'), loopback_ip): self.first_port
        }
        odd_ports3 = {
            (dns.name.from_text('com'), loopback_ip): self.first_port
        }
        odd_ports4 = {
            (dns.name.from_text('com'), loopback_ip): self.first_port,
            (dns.name.from_text('net'), loopback_ip): self.first_port + 1,
        }

        if self.use_ipv6:
            rdata = b'AAAA ::1'
        else:
            rdata = b'A 127.0.0.1'

        zone_contents1 = b'''@ 600 IN SOA localhost. root.localhost. 1 1800 900 86400 600
@ 600 IN NS @
@ 600 IN ''' + rdata + \
b'''
example 0 IN NS ns1.example
example 0 IN NS ns2.example
example 0 IN NS ns3.example
ns1.example 0 IN A 192.0.2.1
ns1.example 0 IN A 192.0.2.4
ns1.example 0 IN AAAA 2001:db8::1
ns2.example 0 IN A 192.0.2.2
ns3.example 0 IN A 192.0.2.3
'''
        zone_contents2 = b'''@ 600 IN SOA localhost. root.localhost. 1 1800 900 86400 600
@ 600 IN NS @
@ 600 IN ''' + rdata + \
b'''
example 0 IN DS 34983 10 1 ec358cfaaec12266ef5acfc1feaf2caff083c418
example 0 IN DS 34983 10 2 608d3b089d79d554a1947bd10bec0a5b1bdbe67b4e60e34b1432ed0033f24b49
example 0 IN NS ns1.example
ns1.example 0 IN A 192.0.2.1
'''

        ZoneFileToServe._next_free_port = self.first_port

        arghelper1 = ArgHelper(self.resolver, self.logger)
        arghelper1.build_parser('probe', args1)
        arghelper1.aggregate_delegation_info()
        zone_to_serve = arghelper1._zones_to_serve[0]
        zone_obj = dns.zone.from_file(zone_to_serve.filename,
                                      dns.name.from_text('com'))
        zone_obj_other = dns.zone.from_text(zone_contents1,
                                            dns.name.from_text('com'))
        self.assertEqual(zone_obj, zone_obj_other)
        self.assertEqual(arghelper1.explicit_delegations,
                         explicit_delegations1)
        self.assertEqual(arghelper1.odd_ports, odd_ports1)

        ZoneFileToServe._next_free_port = self.first_port

        arghelper2 = ArgHelper(self.resolver, self.logger)
        arghelper2.build_parser('probe', args2)
        arghelper2.aggregate_delegation_info()
        zone_to_serve = arghelper2._zones_to_serve[0]
        zone_obj = dns.zone.from_file(zone_to_serve.filename,
                                      dns.name.from_text('com'))
        zone_obj_other = dns.zone.from_text(zone_contents2,
                                            dns.name.from_text('com'))
        self.assertEqual(zone_obj, zone_obj_other)
        self.assertEqual(arghelper2.explicit_delegations,
                         explicit_delegations2)
        self.assertEqual(arghelper2.odd_ports, odd_ports2)

        ZoneFileToServe._next_free_port = self.first_port

        arghelper3 = ArgHelper(self.resolver, self.logger)
        arghelper3.build_parser('probe', args3)
        arghelper3.aggregate_delegation_info()
        self.assertEqual(arghelper3.explicit_delegations,
                         explicit_delegations3)
        self.assertEqual(arghelper3.odd_ports, odd_ports3)

        ZoneFileToServe._next_free_port = self.first_port

        arghelper4 = ArgHelper(self.resolver, self.logger)
        arghelper4.build_parser('probe', args4)
        arghelper4.aggregate_delegation_info()
        self.assertEqual(arghelper4.explicit_delegations,
                         explicit_delegations4)
        self.assertEqual(arghelper4.odd_ports, odd_ports4)
Ejemplo n.º 23
0
    def test_authoritative_option(self):
        arg1 = 'example.com+:ns1.example.com=192.0.2.1:1234,ns1.example.com=[2001:db8::1],' + \
                'ns1.example.com=192.0.2.2,ns2.example.com=[2001:db8::2],a.root-servers.net,192.0.2.3'

        arg1_with_spaces = ' example.com+ : ns1.example.com = [192.0.2.1]:1234 , ns1.example.com = [2001:db8::1], ' + \
                'ns1.example.com = [192.0.2.2] , ns2.example.com = [2001:db8::2] , a.root-servers.net , 192.0.2.3 '

        arg2 = 'example.com:ns1.example.com=192.0.2.1'

        arg3 = 'example.com:%s' % EXAMPLE_COM_ZONE

        arg4 = 'example.com+:%s' % EXAMPLE_COM_ZONE

        delegation_mapping1 = {
            (dns.name.from_text('example.com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(
                dns.name.from_text('example.com'), 0, dns.rdataclass.IN,
                dns.rdatatype.NS, [
                    'ns1.example.com', 'ns2.example.com', 'a.root-servers.net',
                    'ns1._dnsviz.example.com'
                ]),
            (dns.name.from_text('ns1.example.com'), dns.rdatatype.A):
            dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.A,
                                     ['192.0.2.1', '192.0.2.2']),
            (dns.name.from_text('ns1.example.com'), dns.rdatatype.AAAA):
            dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.AAAA,
                                     ['2001:db8::1']),
            (dns.name.from_text('ns1._dnsviz.example.com'), dns.rdatatype.A):
            dns.rrset.from_text_list(
                dns.name.from_text('ns1._dnsviz.example.com'), 0,
                dns.rdataclass.IN, dns.rdatatype.A, ['192.0.2.3']),
            (dns.name.from_text('ns2.example.com'), dns.rdatatype.AAAA):
            dns.rrset.from_text_list(dns.name.from_text('ns2.example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.AAAA,
                                     ['2001:db8::2']),
            (dns.name.from_text('a.root-servers.net'), dns.rdatatype.A):
            dns.rrset.from_text_list(dns.name.from_text('a.root-servers.net'),
                                     0, dns.rdataclass.IN, dns.rdatatype.A,
                                     ['198.41.0.4']),
            (dns.name.from_text('a.root-servers.net'), dns.rdatatype.AAAA):
            dns.rrset.from_text_list(dns.name.from_text('a.root-servers.net'),
                                     0, dns.rdataclass.IN, dns.rdatatype.AAAA,
                                     ['2001:503:ba3e::2:30'])
        }
        stop_at1 = True
        odd_ports1 = {
            (dns.name.from_text('example.com'), IPAddr('192.0.2.1')): 1234
        }
        zone_filename1 = None

        delegation_mapping2 = {
            (dns.name.from_text('example.com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS,
                                     ['ns1.example.com']),
            (dns.name.from_text('ns1.example.com'), dns.rdatatype.A):
            dns.rrset.from_text_list(dns.name.from_text('ns1.example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.A,
                                     ['192.0.2.1'])
        }
        stop_at2 = False
        odd_ports2 = {}
        zone_filename2 = None

        delegation_mapping3 = {
            (dns.name.from_text('example.com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS, []),
        }
        stop_at3 = False
        odd_ports3 = {}
        zone_filename3 = EXAMPLE_COM_ZONE

        delegation_mapping4 = {
            (dns.name.from_text('example.com'), dns.rdatatype.NS):
            dns.rrset.from_text_list(dns.name.from_text('example.com'), 0,
                                     dns.rdataclass.IN, dns.rdatatype.NS, []),
        }
        stop_at4 = True
        odd_ports4 = {}
        zone_filename4 = EXAMPLE_COM_ZONE

        obj = self.helper.authoritative_name_server_mappings(arg1)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping1)
        self.assertEqual(obj.stop_at, stop_at1)
        self.assertEqual(obj.odd_ports, odd_ports1)
        self.assertEqual(obj.filename, zone_filename1)

        obj = self.helper.authoritative_name_server_mappings(arg1_with_spaces)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping1)
        self.assertEqual(obj.stop_at, stop_at1)
        self.assertEqual(obj.odd_ports, odd_ports1)
        self.assertEqual(obj.filename, zone_filename1)

        obj = self.helper.authoritative_name_server_mappings(arg2)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping2)
        self.assertEqual(obj.stop_at, stop_at2)
        self.assertEqual(obj.odd_ports, odd_ports2)
        self.assertEqual(obj.filename, zone_filename2)

        obj = self.helper.authoritative_name_server_mappings(arg3)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping3)
        self.assertEqual(obj.stop_at, stop_at3)
        self.assertEqual(obj.odd_ports, odd_ports3)
        self.assertEqual(obj.filename, zone_filename3)

        obj = self.helper.authoritative_name_server_mappings(arg4)
        self.assertEqual(obj.domain, dns.name.from_text('example.com'))
        self.assertEqual(obj.delegation_mapping, delegation_mapping4)
        self.assertEqual(obj.stop_at, stop_at4)
        self.assertEqual(obj.odd_ports, odd_ports4)
        self.assertEqual(obj.filename, zone_filename4)
Ejemplo n.º 24
0
def main(argv):
    global tm
    global full_resolver
    global stub_resolver
    global explicit_delegations
    global odd_ports
    global next_port

    try:
        try:
            opts, args = getopt.getopt(
                argv[1:], 'f:d:l:c:r:t:64b:u:kmpo:a:R:x:N:D:e:EAs:Fh')
        except getopt.GetoptError as e:
            usage(str(e))
            sys.exit(1)

        _init_tm()
        stub_resolver = Resolver.from_file('/etc/resolv.conf',
                                           StandardRecursiveQueryCD,
                                           transport_manager=tm)

        # get all the options for which there might be multiple values
        explicit_delegations = {}
        odd_ports = {}
        stop_at_explicit = {}
        client_ipv4 = None
        client_ipv6 = None
        delegation_info = {}
        for opt, arg in opts:
            if opt in ('-x', '-N'):
                try:
                    domain, mappings = arg.split(':', 1)
                except ValueError:
                    usage('Incorrect usage of %s option: "%s"' % (opt, arg))
                    sys.exit(1)
                domain = domain.strip()
                mappings = mappings.strip()

                match = STOP_RE.search(domain)
                if match is not None:
                    if opt == '-N':
                        usage('Incorrect usage of %s option: "%s"' %
                              (opt, arg))
                        sys.exit(1)
                    domain = match.group(1)

                try:
                    domain = dns.name.from_text(domain)
                except dns.exception.DNSException:
                    usage('The domain name was invalid: "%s"' % domain)
                    sys.exit(1)

                if opt == '-N' and domain == dns.name.root:
                    usage('The root zone cannot be used with option -N.')
                    sys.exit(1)

                if match is not None:
                    stop_at_explicit[domain] = True
                else:
                    stop_at_explicit[domain] = False

                if opt == '-N':
                    if domain == dns.name.root:
                        usage('The root zone cannot be used with option -N.')
                        sys.exit(1)

                    parent = domain.parent()
                    if parent not in delegation_info:
                        delegation_info[parent] = {}
                    delegation_mapping = delegation_info[parent]
                else:
                    delegation_mapping = explicit_delegations

                if not mappings:
                    usage('Incorrect usage of %s option: "%s"' % (arg, opt))
                    sys.exit(1)
                if (domain, dns.rdatatype.NS) not in delegation_mapping:
                    delegation_mapping[(domain,
                                        dns.rdatatype.NS)] = dns.rrset.RRset(
                                            domain, dns.rdataclass.IN,
                                            dns.rdatatype.NS)
                name_addr_mappings_from_string(domain, mappings,
                                               delegation_mapping, opt == '-N')

            elif opt == '-D':
                try:
                    domain, ds_str = arg.split(':', 1)
                except ValueError:
                    usage('Incorrect usage of %s option: "%s"' % (opt, arg))
                    sys.exit(1)
                domain = domain.strip()
                ds_str = ds_str.strip()

                try:
                    domain = dns.name.from_text(domain)
                except dns.exception.DNSException:
                    usage('The domain name was invalid: "%s"' % domain)
                    sys.exit(1)

                parent = domain.parent()
                if parent not in delegation_info:
                    delegation_info[parent] = {}
                delegation_mapping = delegation_info[parent]

                if not ds_str:
                    usage('Incorrect usage of %s option: "%s"' % (arg, opt))
                    sys.exit(1)
                if (domain, dns.rdatatype.DS) not in delegation_mapping:
                    delegation_mapping[(domain,
                                        dns.rdatatype.DS)] = dns.rrset.RRset(
                                            domain, dns.rdataclass.IN,
                                            dns.rdatatype.DS)
                ds_from_string(domain, ds_str.strip(), delegation_mapping)

            elif opt == '-b':
                try:
                    addr = IPAddr(arg)
                except ValueError:
                    usage('The IP address was invalid: "%s"' % arg)
                    sys.exit(1)

                if addr.version == 4:
                    client_ipv4 = addr
                    fam = socket.AF_INET
                else:
                    client_ipv6 = addr
                    fam = socket.AF_INET6
                try:
                    s = socket.socket(fam)
                    s.bind((addr, 0))
                    del s
                except socket.error as e:
                    if e.errno == errno.EADDRNOTAVAIL:
                        usage('Cannot bind to specified IP address: "%s"' %
                              addr)
                        sys.exit(1)

        opts = dict(opts)
        if '-h' in opts:
            usage()
            sys.exit(0)

        if not ('-f' in opts or args) and '-r' not in opts:
            usage(
                'When -r is not used, either -f must be used or domain names must be supplied as command line arguments.'
            )
            sys.exit(1)
        if '-f' in opts and args:
            usage(
                'If -f is used, then domain names may not supplied as command line arguments.'
            )
            sys.exit(1)

        if '-A' in opts and '-s' in opts:
            usage('If -A is used, then -s cannot be used.')
            sys.exit(1)

        if '-x' in opts and '-A' not in opts:
            usage('-x may only be used in conjunction with -A.')
            sys.exit(1)

        if '-N' in opts and '-A' not in opts:
            usage('-N may only be used in conjunction with -A.')
            sys.exit(1)

        if '-D' in opts and '-N' not in opts:
            #TODO retrieve NS/A/AAAA if -D is specified but -N is not
            usage('-D may only be used in conjunction with -N.')
            sys.exit(1)

        if '-4' in opts and '-6' in opts:
            usage('-4 and -6 may not be used together.')
            sys.exit(1)

        if '-a' in opts:
            try:
                ceiling = dns.name.from_text(opts['-a'])
            except dns.exception.DNSException:
                usage('The domain name was invalid: "%s"' % opts['-a'])
                sys.exit(1)
        elif '-A' in opts:
            ceiling = None
        else:
            ceiling = dns.name.root

        if '-R' in opts:
            explicit_only = True
            try:
                rdtypes = opts['-R'].split(',')
            except ValueError:
                usage('The list of types was invalid: "%s"' % opts['-R'])
                sys.exit(1)
            try:
                rdtypes = [dns.rdatatype.from_text(x) for x in rdtypes]
            except dns.rdatatype.UnknownRdatatype:
                usage('The list of types was invalid: "%s"' % opts['-R'])
                sys.exit(1)
        else:
            rdtypes = None
            explicit_only = False

        # if neither is specified, then they're both tried
        if '-4' not in opts and '-6' not in opts:
            try_ipv4 = True
            try_ipv6 = True
        # if one or the other is specified, then only the one specified is
        # tried
        else:
            if '-4' in opts:
                try_ipv4 = True
                try_ipv6 = False
            else:  # -6 in opts
                try_ipv4 = False
                try_ipv6 = True

        for domain in delegation_info:
            if (domain, dns.rdatatype.NS) in explicit_delegations:
                usage(
                    'Cannot use "%s" with -x if its child is specified with -N'
                    % lb2s(domain.canonicalize().to_text()))
                sys.exit(1)

            port = next_port
            next_port += 1
            _create_and_serve_zone(domain, delegation_info[domain], port)
            localhost = dns.name.from_text('localhost')
            loopback = IPAddr('127.0.0.1')
            explicit_delegations[(domain, dns.rdatatype.NS)] = dns.rrset.RRset(
                domain, dns.rdataclass.IN, dns.rdatatype.NS)
            explicit_delegations[(domain, dns.rdatatype.NS)].add(
                dns.rdtypes.ANY.NS.NS(dns.rdataclass.IN, dns.rdatatype.NS,
                                      localhost))
            explicit_delegations[(localhost,
                                  dns.rdatatype.A)] = dns.rrset.RRset(
                                      localhost, dns.rdataclass.IN,
                                      dns.rdatatype.A)
            explicit_delegations[(localhost, dns.rdatatype.A)].add(
                dns.rdtypes.IN.A.A(dns.rdataclass.IN, dns.rdatatype.A,
                                   loopback))
            odd_ports[(domain, loopback)] = port
            stop_at_explicit[domain] = True

        if '-A' not in opts:
            if '-t' in opts:
                cls = RecursiveParallelAnalyst
            else:
                cls = RecursiveBulkAnalyst
            explicit_delegations[(WILDCARD_EXPLICIT_DELEGATION,
                                  dns.rdatatype.NS)] = dns.rrset.RRset(
                                      WILDCARD_EXPLICIT_DELEGATION,
                                      dns.rdataclass.IN, dns.rdatatype.NS)
            if '-s' in opts:
                name_addr_mappings_from_string(WILDCARD_EXPLICIT_DELEGATION,
                                               opts['-s'],
                                               explicit_delegations, False)
            else:
                for i, server in enumerate(stub_resolver._servers):
                    if IPAddr(server).version == 6:
                        rdtype = dns.rdatatype.AAAA
                    else:
                        rdtype = dns.rdatatype.A
                    name = dns.name.from_text('ns%d' % i)
                    explicit_delegations[(WILDCARD_EXPLICIT_DELEGATION,
                                          dns.rdatatype.NS)].add(
                                              dns.rdtypes.ANY.NS.NS(
                                                  dns.rdataclass.IN,
                                                  dns.rdatatype.NS, name))
                    if (name, rdtype) not in explicit_delegations:
                        explicit_delegations[(name, rdtype)] = dns.rrset.RRset(
                            name, dns.rdataclass.IN, rdtype)
                    explicit_delegations[(name, rdtype)].add(
                        dns.rdata.from_text(dns.rdataclass.IN, rdtype, server))
        else:
            if '-t' in opts:
                cls = ParallelAnalyst
            else:
                cls = BulkAnalyst

        edns_diagnostics = '-E' in opts

        if '-u' in opts:

            # check that version is >= 2.7.9 if HTTPS is requested
            if opts['-u'].startswith('https'):
                vers0, vers1, vers2 = sys.version_info[:3]
                if (2, 7, 9) > (vers0, vers1, vers2):
                    sys.stderr.write(
                        'python version >= 2.7.9 is required to use a DNS looking glass with HTTPS.\n'
                    )
                    sys.exit(1)

            url = urlparse.urlparse(opts['-u'])
            if url.scheme in ('http', 'https'):
                th_factories = (transport.DNSQueryTransportHandlerHTTPFactory(
                    opts['-u'], insecure='-k' in opts), )
            elif url.scheme == 'ws':
                if url.hostname is not None:
                    usage(
                        'WebSocket URL must designate a local UNIX domain socket.'
                    )
                    sys.exit(1)
                th_factories = (
                    transport.DNSQueryTransportHandlerWebSocketFactory(
                        url.path), )
            else:
                usage('Unsupported URL scheme: "%s"' % opts['-u'])
                sys.exit(1)
        else:
            th_factories = None

        if '-l' in opts:
            try:
                dlv_domain = dns.name.from_text(opts['-l'])
            except dns.exception.DNSException:
                usage('The domain name was invalid: "%s"' % opts['-l'])
                sys.exit(1)
        else:
            dlv_domain = None

        # the following option is not documented in usage, as it doesn't
        # apply to most users
        try:
            cache_level = int(opts['-c'])
        except (KeyError, ValueError):
            cache_level = None

        try:
            processes = int(opts.get('-t', 1))
        except ValueError:
            usage('The number of threads used must be greater than 0.')
            sys.exit(1)
        if processes < 1:
            usage('The number of threads used must be greater than 0.')
            sys.exit(1)

        try:
            val = int(opts.get('-d', 2))
        except ValueError:
            usage('The debug value must be an integer between 0 and 3.')
            sys.exit(1)
        if val < 0 or val > 3:
            usage('The debug value must be an integer between 0 and 3.')
            sys.exit(1)

        if val > 2:
            debug_level = logging.DEBUG
        elif val > 1:
            debug_level = logging.INFO
        elif val > 0:
            debug_level = logging.WARNING
        else:
            debug_level = logging.ERROR
        handler = logging.StreamHandler()
        handler.setLevel(debug_level)
        logger.addHandler(handler)
        logger.setLevel(debug_level)

        if '-A' in opts:
            if try_ipv4 and get_client_address(A_ROOT_IPV4) is None:
                logger.warning('No global IPv4 connectivity detected')
            if try_ipv6 and get_client_address(A_ROOT_IPV6) is None:
                logger.warning('No global IPv6 connectivity detected')

        if '-r' in opts:
            if opts['-r'] == '-':
                opt_r = sys.stdin.fileno()
            else:
                opt_r = opts['-r']
            try:
                analysis_str = io.open(opt_r, 'r', encoding='utf-8').read()
            except IOError as e:
                logger.error('%s: "%s"' % (e.strerror, opts.get('-r', '-')))
                sys.exit(3)
            try:
                analysis_structured = json.loads(analysis_str)
            except ValueError:
                logger.error(
                    'There was an error parsing the json input: "%s"' %
                    opts['-r'])
                sys.exit(3)

            # check version
            if '_meta._dnsviz.' not in analysis_structured or 'version' not in analysis_structured[
                    '_meta._dnsviz.']:
                logger.error('No version information in JSON input.')
                sys.exit(3)
            try:
                major_vers, minor_vers = [
                    int(x) for x in str(analysis_structured['_meta._dnsviz.']
                                        ['version']).split('.', 1)
                ]
            except ValueError:
                logger.error('Version of JSON input is invalid: %s' %
                             analysis_structured['_meta._dnsviz.']['version'])
                sys.exit(3)
            # ensure major version is a match and minor version is no greater
            # than the current minor version
            curr_major_vers, curr_minor_vers = [
                int(x) for x in str(DNS_RAW_VERSION).split('.', 1)
            ]
            if major_vers != curr_major_vers or minor_vers > curr_minor_vers:
                logger.error(
                    'Version %d.%d of JSON input is incompatible with this software.'
                    % (major_vers, minor_vers))
                sys.exit(3)

        names = []
        if '-f' in opts:
            if opts['-f'] == '-':
                opts['-f'] = sys.stdin.fileno()
            try:
                f = io.open(opts['-f'], 'r', encoding='utf-8')
            except IOError as e:
                logger.error('%s: "%s"' % (e.strerror, opts['-f']))
                sys.exit(3)
            for line in f:
                name = line.strip()
                try:
                    name = dns.name.from_text(name)
                except UnicodeDecodeError as e:
                    logger.error('%s: "%s"' % (e, name))
                except dns.exception.DNSException:
                    logger.error('The domain name was invalid: "%s"' % name)
                else:
                    names.append(name)
            f.close()
        else:
            if args:
                # python3/python2 dual compatibility
                if isinstance(args[0], bytes):
                    args = [
                        codecs.decode(x, sys.getfilesystemencoding())
                        for x in args
                    ]
            else:
                try:
                    args = analysis_structured['_meta._dnsviz.']['names']
                except KeyError:
                    logger.error('No names found in json input!')
                    sys.exit(3)
            for name in args:
                try:
                    name = dns.name.from_text(name)
                except UnicodeDecodeError as e:
                    logger.error('%s: "%s"' % (e, name))
                except dns.exception.DNSException:
                    logger.error('The domain name was invalid: "%s"' % name)
                else:
                    names.append(name)

        if '-p' in opts:
            kwargs = {'indent': 4, 'separators': (',', ': ')}
        else:
            kwargs = {}

        meta_only = '-m' in opts

        if '-o' not in opts or opts['-o'] == '-':
            opts['-o'] = sys.stdout.fileno()
        try:
            fh = io.open(opts['-o'], 'wb')
        except IOError as e:
            logger.error('%s: "%s"' % (e.strerror, opts['-o']))
            sys.exit(3)

        def _flush(name_obj):
            d = collections.OrderedDict()
            name_obj.serialize(d)
            s = json.dumps(d, **kwargs)
            lindex = s.index('{')
            rindex = s.rindex('}')
            fh.write(s[lindex + 1:rindex] + ',')

        dnsviz_meta = {
            'version': DNS_RAW_VERSION,
            'names': [lb2s(n.to_text()) for n in names]
        }

        flush = '-F' in opts

        if '-e' in opts:
            CustomQueryMixin.edns_options = [_get_ecs_option(opts['-e'])]
            query_class_mixin = CustomQueryMixin
        else:
            query_class_mixin = None

        name_objs = []
        if '-r' in opts:
            cache = {}
            for name in names:
                if name.canonicalize().to_text() not in analysis_structured:
                    logger.error(
                        'The domain name was not found in the analysis input: "%s"'
                        % name.to_text())
                    continue
                name_objs.append(
                    OnlineDomainNameAnalysis.deserialize(
                        name, analysis_structured, cache))
        else:
            if '-t' in opts:
                a = cls(try_ipv4, try_ipv6, client_ipv4, client_ipv6,
                        query_class_mixin, ceiling, edns_diagnostics,
                        stop_at_explicit, cache_level, rdtypes, explicit_only,
                        dlv_domain, th_factories, processes)
            else:
                _init_resolver()
                a = cls(try_ipv4, try_ipv6, client_ipv4, client_ipv6,
                        query_class_mixin, ceiling, edns_diagnostics,
                        stop_at_explicit, cache_level, rdtypes, explicit_only,
                        dlv_domain, th_factories)
                if flush:
                    fh.write('{')
                    a.analyze(names, _flush)
                    fh.write('"_meta._dnsviz.":%s}' %
                             json.dumps(dnsviz_meta, **kwargs))
                    sys.exit(0)

            name_objs = a.analyze(names)

        name_objs = [x for x in name_objs if x is not None]

        if not name_objs:
            sys.exit(4)

        d = collections.OrderedDict()
        for name_obj in name_objs:
            name_obj.serialize(d, meta_only)
        d['_meta._dnsviz.'] = dnsviz_meta

        try:
            fh.write(
                json.dumps(d, ensure_ascii=False, **kwargs).encode('utf-8'))
        except IOError as e:
            logger.error('Error writing analysis: %s' % e)
            sys.exit(3)

    except KeyboardInterrupt:
        logger.error('Interrupted.')
        sys.exit(4)

    # tm is global (because of possible multiprocessing), so we need to
    # explicitly close it here
    finally:
        if tm is not None:
            tm.close()