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)
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
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)
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)
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'))
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)
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
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)
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)
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)
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)
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)
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
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
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
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
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
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)
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)
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()