Exemple #1
0
def export_zone(suffix, names, zonefile_dir, server_names, conffile_fh):
    filename = os.path.join(zonefile_dir, 'db._odup.%s' % (suffix.to_text().rstrip('.')))
    conffile_fh.write('zone "_odup.%s" {\n' % suffix.to_text())
    conffile_fh.write('\ttype master;\n')
    conffile_fh.write('\tfile "%s";\n' % filename)
    conffile_fh.write('\tallow-transfer { any; };\n')
    conffile_fh.write('};\n')

    if len(suffix) == 2:
        fetch_str = ' +fetch:axfr://'
    else:
        fetch_str = ''

    with open(filename, 'w+') as fh:
        fh.write('$ORIGIN _odup.%s\n' % (suffix.to_text()))
        fh.write('$TTL 604800\n')
        fh.write('@\tSOA\t%s root.nic.%s 1 1800 900 604800 86400\n' % (server_names[0].to_text(), suffix.to_text()))
        for server_name in server_names:
            fh.write('\tNS\t%s\n' % (server_name.to_text()))
        fh.write('\tTXT\t"v=odup1 +bound%s -all"\n' % fetch_str)

        for name in names:
            name = name.relativize(suffix)
            if name == dns.name.empty:
                continue
            if name[0][0].startswith('!'):
                fh.write('%s\tTXT\t"v=odup1 +org"\n' % (name.to_text().lstrip('!')))
            elif name[0] == '*':
                fh.write('%s\tTXT\t"v=odup1 +bound:%d -all"\n' % (name.to_text(), len(name) - 1))
            else:
                fh.write('%s\tTXT\t"v=odup1 +bound -all"\n' % (name.to_text()))
Exemple #2
0
def humanize_name(name, idn=False, canonicalize=True):
    if canonicalize:
        name = name.canonicalize()
    if idn:
        try:
            name = name.to_unicode()
        except UnicodeError:
            name = lb2s(name.to_text())
    else:
        name = lb2s(name.to_text())
    if name == '.':
        return name
    return name.rstrip('.')
Exemple #3
0
 def domain_exists_in_bind_cache(self, **params):
     try:
         zone = dns.zone.from_xfr(
             dns.query.xfr(params['nameserver'],
                           params['zone_name'],
                           lifetime=params['timeout']))
         for (name, _, _) in zone.iterate_rdatas("SOA"):
             if name.to_text() == (
                     '%s%s' %
                 (params['zone_name'], '.')) or name.to_text() == '@':
                 return params['zone_name']
     except:
         return None
Exemple #4
0
def humanize_name(name, idn=False, canonicalize=True):
    if canonicalize:
        name = name.canonicalize()
    if idn:
        try:
            name = name.to_unicode()
        except UnicodeError:
            name = lb2s(name.to_text())
    else:
        name = lb2s(name.to_text())
    if name == '.':
        return name
    return name.rstrip('.')
Exemple #5
0
 def _send_negative_answer(self, fz, metadata, parsed, rcode):
     self._set_flags(parsed)
     name = parsed.question[0].name
     forged_soa = dns.rrset.from_text(
         name, 3600 * 3, dns.rdataclass.IN, dns.rdatatype.from_text('SOA'),
         'ns1.{} hostmaster.{} 1 {} {} {} {}'.format(
             name.to_text(), name.to_text(), 3 * 3600, 3600, 86400 * 7,
             3 * 3600))
     parsed.set_rcode(rcode)
     parsed.answer = []
     parsed.authority = [forged_soa]
     parsed.additional = []
     self._send('\x00{}{}{}'.format(struct.pack('!H', len(metadata)),
                                    metadata, str(parsed.to_wire())))
Exemple #6
0
    def _refresh_zone(self, zone=None):
        """get the dns zone 'zone'.
           It only lists records which type are in 'self.type_written'.
           'zone' must be correctly configured in 'self.zone_list'

           @str zone: the zone name to refresh
           @rtype: list of hash {'key', 'class', 'type', 'ttl', 'content'}
        """
        # zone is defined by the query string parameter
        # if query string is empty, use the default zone
        if zone is None:
            zone = self.zone_default
        # get the zone from the dns
        self.zone = dns.zone.from_xfr(dns.query.xfr(
            self.zone_list[zone]['ip'], zone))
        records = []
        # get all the records in a list of hash
        # {'key', 'class', 'type', 'ttl', 'content'}
        for name, node in self.zone.nodes.items():
            rdatasets = node.rdatasets
            for rdataset in rdatasets:
                for rdata in rdataset:
                    record = {}
                    record['key'] = name.to_text(name)
                    record['class'] = dns.rdataclass.to_text(rdataset.rdclass)
                    record['type'] = dns.rdatatype.to_text(rdataset.rdtype)
                    record['ttl'] = str(rdataset.ttl)
                    record['content'] = rdata.to_text()
                    # filter by record type
                    if record['type'] in self.type_displayed:
                        records.append(record)
        return records
Exemple #7
0
    def _refresh_zone(self, zone=None):
        """get the dns zone 'zone'.
           It only lists records which type are in 'self.type_written'.
           'zone' must be correctly configured in 'self.zone_list'

           @str zone: the zone name to refresh
           @rtype: list of hash {'key', 'class', 'type', 'ttl', 'content'}
        """
        # zone is defined by the query string parameter
        # if query string is empty, use the default zone
        if zone is None:
            zone = self.zone_default
        # get the zone from the dns
        self.zone = dns.zone.from_xfr(
            dns.query.xfr(self.zone_list[zone]['ip'], zone))
        records = []
        # get all the records in a list of hash
        # {'key', 'class', 'type', 'ttl', 'content'}
        for name, node in self.zone.nodes.items():
            rdatasets = node.rdatasets
            for rdataset in rdatasets:
                for rdata in rdataset:
                    record = {}
                    record['key'] = name.to_text(name)
                    record['class'] = dns.rdataclass.to_text(rdataset.rdclass)
                    record['type'] = dns.rdatatype.to_text(rdataset.rdtype)
                    record['ttl'] = str(rdataset.ttl)
                    record['content'] = rdata.to_text()
                    # filter by record type
                    if record['type'] in self.type_displayed:
                        records.append(record)
        return records
    def _follow_cnames(self, domain, validation_name):
        """
        Performs recursive CNAME lookups in case there exists a CNAME for the given
        validation name.
        If the optional dependency dnspython is not installed, the given name is
        simply returned.
        """
        try:
            import dns.exception
            import dns.resolver
            import dns.name

            if self.conf('follow-cnames') != 'true':
                return validation_name
        except ImportError:
            return validation_name
            
        resolver = dns.resolver.Resolver()
        name = dns.name.from_text(validation_name)
        while 1:
            try:
                answer = resolver.query(name, 'CNAME')
                if 1 <= len(answer):
                    name = answer[0].target
                else:
                    break
            except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
                break
            except (dns.exception.Timeout, dns.resolver.YXDOMAIN, dns.resolver.NoNameservers):
                raise errors.PluginError('Failed to lookup CNAMEs on your requested domain {0}'.format(domain))
        return name.to_text(True)
 def from_wire(cls, otype, wire, cur, olen):
     # Calculate name length based on
     name, n = dns.name.from_wire(wire, cur)
     rate, unit, ttl = struct.unpack('!IHI', wire[cur + n:cur + olen])
     _name = name.to_text()
     _unit = EDNS0_EDomainRate._units_from_int[unit]
     return cls(_name, rate, _unit, ttl)
Exemple #10
0
 def get_prep_value(self, value):
     if value is None:
         return None
     if isinstance(value, dns.name.Name):
         name = value
     else:
         name = dns.name.from_text(value)
     if self.canonicalize:
         name = name.canonicalize()
     return name.to_text()
Exemple #11
0
 def get_prep_value(self, value):
     if value is None:
         return None
     if isinstance(value, dns.name.Name):
         name = value
     else:
         name = dns.name.from_text(value)
     if self.canonicalize:
         name = name.canonicalize()
     return name.to_text()
Exemple #12
0
def _get_hosts_from_dns(config):
    """Does a DNS zone transfer off the SOA for a given domain to get a list of
    hosts that can be pushed to."""

    try:
        soa_answer = dns.resolver.query(config.dns.domain, "SOA", tcp=True)
        master_answer = dns.resolver.query(soa_answer[0].mname, "A", tcp=True)
        xfr_answer = dns.query.xfr(master_answer[0].address, config.dns.domain)
        zone = dns.zone.from_xfr(xfr_answer)
        return sorted_nicely([name.to_text()
                              for name, ttl, rdata in
                              zone.iterate_rdatas("A")])
    except dns.exception.DNSException, e:
        raise HostLookupError("host lookup by dns failed: %r" % e)
Exemple #13
0
    def get_all_hosts(self):
        """Pull all hosts from DNS by doing a zone transfer."""

        try:
            soa_answer = dns.resolver.query(self.domain, "SOA", tcp=True)
            soa_host = soa_answer[0].mname

            master_answer = dns.resolver.query(soa_host, "A", tcp=True)
            master_addr = master_answer[0].address

            xfr_answer = dns.query.xfr(master_addr, self.domain)
            zone = dns.zone.from_xfr(xfr_answer)
            return [name.to_text()
                    for name, ttl, rdata in zone.iterate_rdatas("A")]
        except dns.exception.DNSException, e:
            raise HostLookupError("host lookup by dns failed: %r" % e)
Exemple #14
0
    def get_all_hosts(self):
        """Pull all hosts from DNS by doing a zone transfer."""

        try:
            soa_answer = dns.resolver.query(self.domain, "SOA", tcp=True)
            soa_host = soa_answer[0].mname

            master_answer = dns.resolver.query(soa_host, "A", tcp=True)
            master_addr = master_answer[0].address

            xfr_answer = dns.query.xfr(master_addr, self.domain)
            zone = dns.zone.from_xfr(xfr_answer)
            return [
                name.to_text() for name, ttl, rdata in zone.iterate_rdatas("A")
            ]
        except dns.exception.DNSException, e:
            raise HostLookupError("host lookup by dns failed: %r" % e)
Exemple #15
0
	def from_file(self, filename):
		z = dns.zone.from_file(filename);
		origin = z.origin.to_text();
		if origin[-1:] == '.':
			origin = origin[:-1];
		if origin != self._zname:
			raise Exception('Invalid origin');
		for name in z.nodes:
			rname = name.to_text();
			self._records[rname] = {};
			for rdataset in z.nodes[name].rdatasets:
				rtype = rdt.to_text(rdataset.rdtype);
				ttl = rdataset.ttl;
				self._records[rname][rtype] = [];
				for rdata in rdataset.items:
					rec = record.from_rdata(rdata, ttl);
					self.add_record(rname, rec);
		self._getSOA();
		self._filename = filename;
Exemple #16
0
def to_text(keyring):
    """Convert a dictionary containing (dns.name.Name, dns.tsig.Key) pairs
    into a text keyring which has (textual DNS name, (textual algorithm,
    base64 secret)) pairs, or a dictionary containing (dns.name.Name, bytes)
    pairs into a text keyring which has (textual DNS name, base64 secret) pairs.
    @rtype: dict"""

    textring = {}

    def b64encode(secret):
        return base64.encodebytes(secret).decode().rstrip()

    for (name, key) in keyring.items():
        name = name.to_text()
        if isinstance(key, bytes):
            textring[name] = b64encode(key)
        else:
            textring[name] = (key.algorithm.to_text(), b64encode(key.secret))
    return textring
Exemple #17
0
    def zone_records(self, zone):
        if zone.name not in self._zone_records:
            try:
                z = self._load_zone_file(zone.name)
                records = []
                for (name, ttl, rdata) in z.iterate_rdatas():
                    rdtype = dns.rdatatype.to_text(rdata.rdtype)
                    records.append({
                        "name": name.to_text(),
                        "ttl": ttl,
                        "type": rdtype,
                        "value": rdata.to_text()
                    })

                self._zone_records[zone.name] = records
            except ZoneFileSourceNotFound:
                return []

        return self._zone_records[zone.name]
Exemple #18
0
    def zone_records(self, zone):
        try:
            z = dns.zone.from_xfr(dns.query.xfr(self.master, zone.name,
                                                relativize=False),
                                  relativize=False)
        except DNSException:
            raise AxfrSourceZoneTransferFailed()

        records = []

        for (name, ttl, rdata) in z.iterate_rdatas():
            rdtype = dns.rdatatype.to_text(rdata.rdtype)
            records.append({
                "name": name.to_text(),
                "ttl": ttl,
                "type": rdtype,
                "value": rdata.to_text()
            })

        return records
Exemple #19
0
def main(argv):
    try:
        test_pygraphviz()

        try:
            opts, args = getopt.getopt(argv[1:], 'f:r:R:et:a:d:CPOo:T:h')
        except getopt.GetoptError as e:
            sys.stderr.write('%s\n' % str(e))
            sys.exit(1)

        # collect trusted keys
        trusted_keys = []
        for opt, arg in opts:
            if opt == '-t':
                try:
                    with io.open(arg, 'r', encoding='utf-8') as fh:
                        tk_str = fh.read()
                except IOError as e:
                    logger.error('%s: "%s"' % (e.strerror, arg))
                    sys.exit(3)
                try:
                    trusted_keys.extend(get_trusted_keys(tk_str))
                except dns.exception.DNSException:
                    logger.error(
                        'There was an error parsing the trusted keys file: "%s"'
                        % arg)
                    sys.exit(3)

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

        if '-f' in opts and args:
            sys.stderr.write(
                'If -f is used, then domain names may not supplied as command line arguments.\n'
            )
            sys.exit(1)

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

        if '-a' in opts:
            try:
                supported_algs = opts['-a'].split(',')
            except ValueError:
                sys.stderr.write('The list of algorithms was invalid: "%s"\n' %
                                 opts['-a'])
                sys.exit(1)
            try:
                supported_algs = set([int(x) for x in supported_algs])
            except ValueError:
                sys.stderr.write('The list of algorithms was invalid: "%s"\n' %
                                 opts['-a'])
                sys.exit(1)
        else:
            supported_algs = None

        if '-d' in opts:
            try:
                supported_digest_algs = opts['-d'].split(',')
            except ValueError:
                sys.stderr.write(
                    'The list of digest algorithms was invalid: "%s"\n' %
                    opts['-d'])
                sys.exit(1)
            try:
                supported_digest_algs = set(
                    [int(x) for x in supported_digest_algs])
            except ValueError:
                sys.stderr.write(
                    'The list of digest algorithms was invalid: "%s"\n' %
                    opts['-d'])
                sys.exit(1)
        else:
            supported_digest_algs = None

        strict_cookies = '-C' in opts
        allow_private = '-P' in opts

        remove_edges = '-e' not in opts

        if '-T' in opts:
            fmt = opts['-T']
        elif '-o' in opts:
            fmt = opts['-o'].split('.')[-1]
        else:
            fmt = 'dot'
        if fmt not in ('dot', 'png', 'jpg', 'svg', 'html'):
            sys.stderr.write('Image format unrecognized: "%s"\n' % fmt)
            sys.exit(1)

        if '-o' in opts and '-O' in opts:
            sys.stderr.write(
                'The -o and -O options may not be used together.\n')
            sys.exit(1)

        if '-r' not in opts or opts['-r'] == '-':
            opt_r = sys.stdin.fileno()
        else:
            opt_r = opts['-r']
        try:
            with io.open(opt_r, 'r', encoding='utf-8') as fh:
                analysis_str = fh.read()
        except IOError as e:
            logger.error('%s: "%s"' % (e.strerror, opts.get('-r', '-')))
            sys.exit(3)
        if not analysis_str:
            if opt_r != sys.stdin.fileno():
                logger.error('No input.')
            sys.exit(3)
        try:
            analysis_structured = json.loads(analysis_str)
        except ValueError:
            logger.error('There was an error parsing the JSON input: "%s"' %
                         opts.get('-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 = OrderedDict()
        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:
                    if name not in names:
                        names[name] = None
            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:
                    if name not in names:
                        names[name] = None

        latest_analysis_date = None
        name_objs = []
        cache = {}
        for name in names:
            name_str = lb2s(name.canonicalize().to_text())
            if name_str not in analysis_structured or analysis_structured[
                    name_str].get('stub', True):
                logger.error(
                    'The analysis of "%s" was not found in the input.' %
                    lb2s(name.to_text()))
                continue
            name_obj = OfflineDomainNameAnalysis.deserialize(
                name,
                analysis_structured,
                cache,
                strict_cookies=strict_cookies,
                allow_private=allow_private)
            name_objs.append(name_obj)

            if latest_analysis_date is None or latest_analysis_date > name_obj.analysis_end:
                latest_analysis_date = name_obj.analysis_end

        if not name_objs:
            sys.exit(4)

        if '-t' not in opts:
            trusted_keys = get_default_trusted_keys(latest_analysis_date)

        G = DNSAuthGraph()
        for name_obj in name_objs:
            name_obj.populate_status(
                trusted_keys,
                supported_algs=supported_algs,
                supported_digest_algs=supported_digest_algs)
            for qname, rdtype in name_obj.queries:
                if rdtypes is None:
                    # if rdtypes was not specified, then graph all, with some
                    # exceptions
                    if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY,
                                                         dns.rdatatype.DS,
                                                         dns.rdatatype.DLV):
                        continue
                else:
                    # if rdtypes was specified, then only graph rdtypes that
                    # were specified
                    if qname != name_obj.name or rdtype not in rdtypes:
                        continue
                G.graph_rrset_auth(name_obj, qname, rdtype)

            if rdtypes is not None:
                for rdtype in rdtypes:
                    if (name_obj.name, rdtype) not in name_obj.queries:
                        logger.error(
                            'No query for "%s/%s" was included in the analysis.'
                            % (lb2s(name_obj.name.to_text()),
                               dns.rdatatype.to_text(rdtype)))

            if '-O' in opts:
                if name_obj.name == dns.name.root:
                    name = 'root'
                else:
                    name = lb2s(
                        name_obj.name.canonicalize().to_text()).rstrip('.')
                finish_graph(G, [name_obj], rdtypes, trusted_keys,
                             supported_algs, fmt, '%s.%s' % (name, fmt),
                             remove_edges)
                G = DNSAuthGraph()

        if '-O' not in opts:
            if '-o' not in opts or opts['-o'] == '-':
                finish_graph(G, name_objs, rdtypes, trusted_keys,
                             supported_algs, fmt, None, remove_edges)
            else:
                finish_graph(G, name_objs, rdtypes, trusted_keys,
                             supported_algs, fmt, opts['-o'], remove_edges)

    except KeyboardInterrupt:
        logger.error('Interrupted.')
        sys.exit(4)
Exemple #20
0
def main(argv):
    try:
        test_m2crypto()

        #TODO remove -p option (it is now the default, and -c is used to change it)
        try:
            opts, args = getopt.getopt(argv[1:], 'f:r:t:o:cpl:h')
        except getopt.GetoptError as e:
            usage(str(e))
            sys.exit(1)

        # collect trusted keys
        trusted_keys = []
        for opt, arg in opts:
            if opt == '-t':
                try:
                    tk_str = io.open(arg, 'r', encoding='utf-8').read()
                except IOError as e:
                    sys.stderr.write('%s: "%s"\n' % (e.strerror, arg))
                    sys.exit(3)
                try:
                    trusted_keys.extend(get_trusted_keys(tk_str))
                except dns.exception.DNSException:
                    sys.stderr.write('There was an error parsing the trusted keys file: "%s"\n' % arg)
                    sys.exit(3)

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

        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 '-l' in opts:
            if opts['-l'] == 'error':
                loglevel = logging.ERROR
            elif opts['-l'] == 'warning':
                loglevel = logging.WARNING
            elif opts['-l'] == 'info':
                loglevel = logging.INFO
            elif opts['-l'] == 'debug':
                loglevel = logging.DEBUG
            else:
                usage('Invalid log level: "%s"' % opts['-l'])
                sys.exit(1)
        else:
            loglevel = logging.DEBUG
        handler = logging.StreamHandler()
        handler.setLevel(logging.WARNING)
        logger.addHandler(handler)
        logger.setLevel(logging.WARNING)

        if '-r' not in opts or 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.get('-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 '-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)

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

        # if trusted keys were supplied, check that pygraphviz is installed
        if trusted_keys:
            test_pygraphviz()

        name_objs = []
        cache = {}
        for name in names:
            name_str = lb2s(name.canonicalize().to_text())
            if name_str not in analysis_structured or analysis_structured[name_str].get('stub', True):
                logger.error('The analysis of "%s" was not found in the input.' % lb2s(name.to_text()))
                continue
            name_objs.append(OfflineDomainNameAnalysis.deserialize(name, analysis_structured, cache))

        if not name_objs:
            sys.exit(4)

        d = OrderedDict()
        for name_obj in name_objs:
            name_obj.populate_status(trusted_keys)

            if trusted_keys:
                G = DNSAuthGraph()
                for qname, rdtype in name_obj.queries:
                    if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY, dns.rdatatype.DS, dns.rdatatype.DLV):
                        continue
                    G.graph_rrset_auth(name_obj, qname, rdtype)
                for target, mx_obj in name_obj.mx_targets.items():
                    if mx_obj is not None:
                        G.graph_rrset_auth(mx_obj, target, dns.rdatatype.A)
                        G.graph_rrset_auth(mx_obj, target, dns.rdatatype.AAAA)
                for target, ns_obj in name_obj.ns_dependencies.items():
                    if ns_obj is not None:
                        G.graph_rrset_auth(ns_obj, target, dns.rdatatype.A)
                        G.graph_rrset_auth(ns_obj, target, dns.rdatatype.AAAA)
                G.add_trust(trusted_keys)
                name_obj.populate_response_component_status(G)

            name_obj.serialize_status(d, loglevel=loglevel)

        if d:
            s = json.dumps(d, ensure_ascii=False, **kwargs)
            if '-c' not in opts and fh.isatty() and os.environ.get('TERM', 'dumb') != 'dumb':
                s = color_json(s)
            fh.write(s.encode('utf-8'))

    except KeyboardInterrupt:
        logger.error('Interrupted.')
        sys.exit(4)
Exemple #21
0
    class DomainNameAnalysisForm(forms.Form):
        EXTRA_TYPES = ((dns.rdatatype.A, dns.rdatatype.to_text(
            dns.rdatatype.A)), (dns.rdatatype.AAAA,
                                dns.rdatatype.to_text(dns.rdatatype.AAAA)),
                       (dns.rdatatype.TXT,
                        dns.rdatatype.to_text(dns.rdatatype.TXT)),
                       (dns.rdatatype.PTR,
                        dns.rdatatype.to_text(dns.rdatatype.PTR)),
                       (dns.rdatatype.MX,
                        dns.rdatatype.to_text(dns.rdatatype.MX)),
                       (dns.rdatatype.SOA,
                        dns.rdatatype.to_text(dns.rdatatype.SOA)),
                       (dns.rdatatype.CNAME,
                        dns.rdatatype.to_text(dns.rdatatype.CNAME)),
                       (dns.rdatatype.SRV,
                        dns.rdatatype.to_text(dns.rdatatype.SRV)),
                       (dns.rdatatype.NAPTR,
                        dns.rdatatype.to_text(dns.rdatatype.NAPTR)),
                       (dns.rdatatype.TLSA,
                        dns.rdatatype.to_text(dns.rdatatype.TLSA)))

        ANALYSIS_TYPES = ((ANALYSIS_TYPE_AUTHORITATIVE,
                           'Authoritative servers'), (ANALYSIS_TYPE_RECURSIVE,
                                                      'Recursive servers'))

        PERSPECTIVE = (('server', 'DNSViz server (me)'), ('client',
                                                          'Web client (you)'))

        force_ancestor = forms.TypedChoiceField(
            label='Force ancestor analysis',
            choices=ANCESTOR_CHOICES,
            initial=name.to_text(),
            required=True,
            coerce=dns.name.from_text,
            help_text=
            'Usually it is sufficient to select the name itself (%s) or its zone, in which case cached values will be used for the analysis of any ancestor names (unless it is determined that they are out of date).  Occasionally it is useful to re-analyze some portion of the ancestry, in which case the desired ancestor can be selected.  However, the overall analysis will take longer.'
            % (fmt.humanize_name(name, True)))
        extra_types = forms.TypedMultipleChoiceField(
            choices=EXTRA_TYPES,
            initial=(),
            required=False,
            coerce=int,
            help_text=
            'Select any extra RR types to query as part of this analysis.  A default set of types will already be queried based on the nature of the name, but any types selected here will assuredly be included.'
        )
        edns_diagnostics = forms.BooleanField(
            label='EDNS diagnostics',
            initial=False,
            required=False,
            help_text='Issue queries specific to EDNS diagnostics.')
        explicit_delegation = forms.CharField(
            initial='',
            required=False,
            widget=forms.Textarea(attrs={
                'cols': 50,
                'rows': 5
            }),
            help_text=
            'If you wish to designate servers explicitly for the "force ancestor" zone (rather than following delegation from the IANA root), enter the server names, one per line.  You may optionally include an IPv4 or IPv6 address on the same line as the name.'
        )
        analysis_type = forms.TypedChoiceField(
            choices=ANALYSIS_TYPES,
            initial=ANALYSIS_TYPE_AUTHORITATIVE,
            coerce=int,
            widget=forms.RadioSelect(),
            help_text=
            'If authoritative analysis is selected, then the authoritative servers will be analyzed, beginning at the root servers--or the servers explicitly designated; if recursive analysis is selected, then the designated recursive servers will be analyzed.'
        )
        perspective = forms.TypedChoiceField(
            choices=PERSPECTIVE,
            initial='server',
            widget=forms.RadioSelect(),
            help_text=
            'If \'DNSViz server\' is selected, then the diagnostic queries will be issued from the DNSViz server.  If \'Web client\' is selected, they will be issued from the browser (requires the use of a Java applet).'
        )
        sockname = forms.CharField(widget=forms.HiddenInput(), required=False)

        def clean(self):
            cleaned_data = super(DomainNameAnalysisForm, self).clean()
            if cleaned_data.get('analysis_type', None) == ANALYSIS_TYPE_RECURSIVE and \
                    not cleaned_data.get('explicit_delegation', None):
                raise forms.ValidationError(
                    'If recursive analysis is desired, then servers names and/or addresses must be specified.'
                )
            if cleaned_data.get('perspective', None) == 'client' and \
                    not cleaned_data.get('sockname', None):
                raise forms.ValidationError(
                    'No address supplied for WebSocket')
            return cleaned_data

        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
Exemple #22
0
                logger.error('%s: "%s"' % (e.strerror, opts['-t']))
                sys.exit(3)
            try:
                trusted_keys = get_trusted_keys(tk_str)
            except dns.exception.DNSException:
                logger.error('There was an error parsing the trusted keys file: "%s"' % opts['-t'])
                sys.exit(3)
        else:
            trusted_keys = ()

        name_objs = []
        cache = {}
        for name in names:
            name_str = name.canonicalize().to_text()
            if name_str not in analysis_structured or analysis_structured[name_str].get('stub', True):
                logger.error('The analysis of "%s" was not found in the input.' % name.to_text())
                continue
            name_objs.append(OfflineDomainNameAnalysis.deserialize(name, analysis_structured, cache))

        if not name_objs:
            sys.exit(4)

        G = DNSAuthGraph()
        for name_obj in name_objs:
            name_obj.populate_status(trusted_keys)
            for qname, rdtype in name_obj.queries:
                if rdtypes is None:
                    # if rdtypes was not specified, then graph all, with some
                    # exceptions
                    if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY, dns.rdatatype.DS, dns.rdatatype.DLV):
                        continue
Exemple #23
0
def main(argv):
    try:
        test_m2crypto()
        test_pygraphviz()

        try:
            opts, args = getopt.getopt(argv[1:], 'f:r:R:t:Oo:h')
        except getopt.GetoptError as e:
            usage(str(e))
            sys.exit(1)

        # collect trusted keys
        trusted_keys = []
        for opt, arg in opts:
            if opt == '-t':
                try:
                    tk_str = io.open(arg, 'r', encoding='utf-8').read()
                except IOError as e:
                    sys.stderr.write('%s: "%s"\n' % (e.strerror, arg))
                    sys.exit(3)
                try:
                    trusted_keys.extend(get_trusted_keys(tk_str))
                except dns.exception.DNSException:
                    sys.stderr.write(
                        'There was an error parsing the trusted keys file: "%s"\n'
                        % arg)
                    sys.exit(3)

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

        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 '-R' in opts:
            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

        if '-o' in opts and '-O' in opts:
            usage('The -o and -O options may not be used together.')
            sys.exit(1)

        handler = logging.StreamHandler()
        handler.setLevel(logging.WARNING)
        logger.addHandler(handler)
        logger.setLevel(logging.WARNING)

        if '-r' not in opts or 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.get('-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 '-t' not in opts:
            try:
                tk_str = io.open(TRUSTED_KEYS_ROOT, 'r',
                                 encoding='utf-8').read()
            except IOError as e:
                logger.error('Error reading trusted keys file "%s": %s' %
                             (TRUSTED_KEYS_ROOT, e.strerror))
                sys.exit(3)
            try:
                trusted_keys.extend(get_trusted_keys(tk_str))
            except dns.exception.DNSException:
                logger.error(
                    'There was an error parsing the trusted keys file: "%s"' %
                    arg)
                sys.exit(3)

        name_objs = []
        cache = {}
        for name in names:
            name_str = lb2s(name.canonicalize().to_text())
            if name_str not in analysis_structured or analysis_structured[
                    name_str].get('stub', True):
                logger.error(
                    'The analysis of "%s" was not found in the input.' %
                    lb2s(name.to_text()))
                continue
            name_objs.append(
                TTLAgnosticOfflineDomainNameAnalysis.deserialize(
                    name, analysis_structured, cache))

        if not name_objs:
            sys.exit(4)

        G = DNSAuthGraph()
        for name_obj in name_objs:
            name_obj.populate_status(trusted_keys)
            for qname, rdtype in name_obj.queries:
                if rdtypes is None:
                    # if rdtypes was not specified, then graph all, with some
                    # exceptions
                    if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY,
                                                         dns.rdatatype.DS,
                                                         dns.rdatatype.DLV):
                        continue
                else:
                    # if rdtypes was specified, then only graph rdtypes that
                    # were specified
                    if qname != name_obj.name or rdtype not in rdtypes:
                        continue
                G.graph_rrset_auth(name_obj, qname, rdtype)

            if rdtypes is not None:
                for rdtype in rdtypes:
                    if (name_obj.name, rdtype) not in name_obj.queries:
                        logger.error(
                            'No query for "%s/%s" was included in the analysis.'
                            % (lb2s(name_obj.name.to_text()),
                               dns.rdatatype.to_text(rdtype)))

            if '-O' in opts:
                if name_obj.name == dns.name.root:
                    name = 'root'
                else:
                    name = lb2s(
                        name_obj.name.canonicalize().to_text()).rstrip('.')
                finish_graph(G, [name_obj], rdtypes, trusted_keys,
                             '%s.txt' % name)
                G = DNSAuthGraph()

        if '-O' not in opts:
            if '-o' not in opts or opts['-o'] == '-':
                finish_graph(G, name_objs, rdtypes, trusted_keys, None)
            else:
                finish_graph(G, name_objs, rdtypes, trusted_keys, opts['-o'])

    except KeyboardInterrupt:
        logger.error('Interrupted.')
        sys.exit(4)
Exemple #24
0
def main(argv):
    try:
        test_m2crypto()
        test_pygraphviz()

        try:
            opts, args = getopt.getopt(argv[1:], 'f:r:R:t:Oo:T:h')
        except getopt.GetoptError as e:
            usage(str(e))
            sys.exit(1)

        # collect trusted keys
        trusted_keys = []
        for opt, arg in opts:
            if opt == '-t':
                try:
                    tk_str = io.open(arg, 'r', encoding='utf-8').read()
                except IOError as e:
                    sys.stderr.write('%s: "%s"\n' % (e.strerror, arg))
                    sys.exit(3)
                try:
                    trusted_keys.extend(get_trusted_keys(tk_str))
                except dns.exception.DNSException:
                    sys.stderr.write('There was an error parsing the trusted keys file: "%s"\n' % arg)
                    sys.exit(3)

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

        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 '-R' in opts:
            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

        if '-T' in opts:
            fmt = opts['-T']
        elif '-o' in opts:
            fmt = opts['-o'].split('.')[-1]
        else:
            fmt = 'dot'
        if fmt not in ('dot','png','jpg','svg','html'):
            usage('Image format unrecognized: "%s"' % fmt)
            sys.exit(1)

        if '-o' in opts and '-O' in opts:
            usage('The -o and -O options may not be used together.')
            sys.exit(1)

        handler = logging.StreamHandler()
        handler.setLevel(logging.WARNING)
        logger.addHandler(handler)
        logger.setLevel(logging.WARNING)

        if '-r' not in opts or 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.get('-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 '-t' not in opts:
            try:
                tk_str = io.open(TRUSTED_KEYS_ROOT, 'r', encoding='utf-8').read()
            except IOError as e:
                logger.error('Error reading trusted keys file "%s": %s' % (TRUSTED_KEYS_ROOT, e.strerror))
                sys.exit(3)
            try:
                trusted_keys.extend(get_trusted_keys(tk_str))
            except dns.exception.DNSException:
                logger.error('There was an error parsing the trusted keys file: "%s"' % arg)
                sys.exit(3)

        name_objs = []
        cache = {}
        for name in names:
            name_str = lb2s(name.canonicalize().to_text())
            if name_str not in analysis_structured or analysis_structured[name_str].get('stub', True):
                logger.error('The analysis of "%s" was not found in the input.' % lb2s(name.to_text()))
                continue
            name_objs.append(OfflineDomainNameAnalysis.deserialize(name, analysis_structured, cache))

        if not name_objs:
            sys.exit(4)

        G = DNSAuthGraph()
        for name_obj in name_objs:
            name_obj.populate_status(trusted_keys)
            for qname, rdtype in name_obj.queries:
                if rdtypes is None:
                    # if rdtypes was not specified, then graph all, with some
                    # exceptions
                    if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY, dns.rdatatype.DS, dns.rdatatype.DLV):
                        continue
                else:
                    # if rdtypes was specified, then only graph rdtypes that
                    # were specified
                    if qname != name_obj.name or rdtype not in rdtypes:
                        continue
                G.graph_rrset_auth(name_obj, qname, rdtype)

            if rdtypes is not None:
                for rdtype in rdtypes:
                    if (name_obj.name, rdtype) not in name_obj.queries:
                        logger.error('No query for "%s/%s" was included in the analysis.' % (lb2s(name_obj.name.to_text()), dns.rdatatype.to_text(rdtype)))

            if '-O' in opts:
                if name_obj.name == dns.name.root:
                    name = 'root'
                else:
                    name = lb2s(name_obj.name.canonicalize().to_text()).rstrip('.')
                finish_graph(G, [name_obj], rdtypes, trusted_keys, fmt, '%s.%s' % (name, fmt))
                G = DNSAuthGraph()

        if '-O' not in opts:
            if '-o' not in opts or opts['-o'] == '-':
                finish_graph(G, name_objs, rdtypes, trusted_keys, fmt, None)
            else:
                finish_graph(G, name_objs, rdtypes, trusted_keys, fmt, opts['-o'])

    except KeyboardInterrupt:
        logger.error('Interrupted.')
        sys.exit(4)
Exemple #25
0
 ns = str(n).rstrip('.')
 if not ns:
     continue
 server = {"ns":ns, "axfr":False}
 zone_content = []
 try:
     axfr_rr = dns.query.xfr(ns, domain, lifetime=timeout)
     axfr_zone = dns.zone.from_xfr(axfr_rr)
 except Exception, e:
     server["error"] = str(e)
     retv["servers"].append(server)
     continue
 server["axfr"] = True
 for name, ttl, rdata in axfr_zone.iterate_rdatas():
     entry = {
         'name':    name.to_text(),
         'ttl':     ttl,
         'rdclass': rdata.rdclass,
         'rdtype':  rdata.rdtype,
         'rdata':   rdata.to_text()
     }
     try:
         entry['pretty_rdclass'] = dns.rdataclass.to_text(rdata.rdclass)
         entry['pretty_rdtype'] = dns.rdatatype.to_text(rdata.rdtype)
         parent = dns.name.Name(domain.split('.'))
         if name == dns.name.empty:
             entry['pretty_name'] = parent.to_text()
         else:
             entry['pretty_name'] = name.concatenate(parent).to_text()
     except Exception:
         pass
Exemple #26
0
def main(argv):
    global tm
    global th_factories
    global resolver
    global bootstrap_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:ne:EAs:Fh')
        except getopt.GetoptError as e:
            usage(str(e))
            sys.exit(1)

        _init_tm()
        bootstrap_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(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))
        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.DNSQueryTransportHandlerWebSocketServerFactory(url.path),)
            elif url.scheme == 'ssh':
                th_factories = (transport.DNSQueryTransportHandlerRemoteCmdFactory(opts['-u']),)
            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 = 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 '-n' in opts or '-e' in opts:
            CustomQueryMixin.edns_options = []
            if '-e' in opts:
                CustomQueryMixin.edns_options.append(_get_ecs_option(opts['-e']))
            if '-n' in opts:
                CustomQueryMixin.edns_options.append(_get_nsid_option())
            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, processes)
            else:
                if cls.use_full_resolver:
                    _init_full_resolver()
                else:
                    _init_stub_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)
                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 = 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()
Exemple #27
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()
Exemple #28
0
                trusted_keys.extend(get_trusted_keys(tk_str))
            except dns.exception.DNSException:
                logger.error(
                    'There was an error parsing the trusted keys file: "%s"' %
                    arg)
                sys.exit(3)

        name_objs = []
        cache = {}
        for name in names:
            name_str = name.canonicalize().to_text()
            if name_str not in analysis_structured or analysis_structured[
                    name_str].get('stub', True):
                logger.error(
                    'The analysis of "%s" was not found in the input.' %
                    name.to_text())
                continue
            name_objs.append(
                OfflineDomainNameAnalysis.deserialize(name,
                                                      analysis_structured,
                                                      cache))

        if not name_objs:
            sys.exit(4)

        G = DNSAuthGraph()
        for name_obj in name_objs:
            name_obj.populate_status(trusted_keys)
            for qname, rdtype in name_obj.queries:
                if rdtypes is None:
                    # if rdtypes was not specified, then graph all, with some
Exemple #29
0
def main(argv):
    try:

        arghelper = GrokArgHelper(logger)
        arghelper.build_parser('%s %s' % (sys.argv[0], argv[0]), argv[1:])
        logger.setLevel(logging.WARNING)

        try:
            arghelper.check_args()
            arghelper.set_kwargs()
            arghelper.set_buffers()
            arghelper.aggregate_trusted_key_info()
            arghelper.ingest_input()
            arghelper.ingest_names()
        except argparse.ArgumentTypeError as e:
            arghelper.parser.error(str(e))
        except AnalysisInputError as e:
            s = str(e)
            if s:
                logger.error(s)
            sys.exit(3)

        if arghelper.args.minimize_output:
            kwargs = {}
        else:
            kwargs = {'indent': 4, 'separators': (',', ': ')}

        # if trusted keys were supplied, check that pygraphviz is installed
        if arghelper.trusted_keys:
            test_pygraphviz()

        name_objs = []
        cache = {}
        for name in arghelper.names:
            name_str = lb2s(name.canonicalize().to_text())
            if name_str not in arghelper.analysis_structured or arghelper.analysis_structured[
                    name_str].get('stub', True):
                logger.error(
                    'The analysis of "%s" was not found in the input.' %
                    lb2s(name.to_text()))
                continue
            name_obj = OfflineDomainNameAnalysis.deserialize(
                name,
                arghelper.analysis_structured,
                cache,
                strict_cookies=arghelper.args.enforce_cookies,
                allow_private=arghelper.args.allow_private)
            name_objs.append(name_obj)

        if not name_objs:
            sys.exit(4)

        arghelper.update_trusted_key_info()

        d = OrderedDict()
        for name_obj in name_objs:
            name_obj.populate_status(
                arghelper.trusted_keys,
                supported_algs=arghelper.args.algorithms,
                supported_digest_algs=arghelper.args.digest_algorithms)

            if arghelper.trusted_keys:
                G = DNSAuthGraph()
                for qname, rdtype in name_obj.queries:
                    if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY,
                                                         dns.rdatatype.DS,
                                                         dns.rdatatype.DLV):
                        continue
                    G.graph_rrset_auth(name_obj, qname, rdtype)
                for target, mx_obj in name_obj.mx_targets.items():
                    if mx_obj is not None:
                        G.graph_rrset_auth(mx_obj, target, dns.rdatatype.A)
                        G.graph_rrset_auth(mx_obj, target, dns.rdatatype.AAAA)
                for target, ns_obj in name_obj.ns_dependencies.items():
                    if ns_obj is not None:
                        G.graph_rrset_auth(ns_obj, target, dns.rdatatype.A)
                        G.graph_rrset_auth(ns_obj, target, dns.rdatatype.AAAA)
                G.add_trust(arghelper.trusted_keys,
                            supported_algs=arghelper.args.algorithms)
                name_obj.populate_response_component_status(G)

            name_obj.serialize_status(d, loglevel=arghelper.log_level)

        if d:
            s = json.dumps(d, ensure_ascii=False, **kwargs)
            if not arghelper.args.minimize_output and arghelper.args.output_file.isatty(
            ) and os.environ.get('TERM', 'dumb') != 'dumb':
                s = color_json(s)
            arghelper.args.output_file.write(s.encode('utf-8'))

    except KeyboardInterrupt:
        logger.error('Interrupted.')
        sys.exit(4)
Exemple #30
0
            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': [n.to_text() for n in names] }

        flush = '-F' in opts

        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(client_ipv4, client_ipv6, ceiling, edns_diagnostics, cache_level, explicit_delegations, rdtypes, explicit_only, dlv_domain, processes)
            else:
                a = cls(client_ipv4, client_ipv6, ceiling, edns_diagnostics, cache_level, explicit_delegations, rdtypes, explicit_only, dlv_domain)
                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)
Exemple #31
0
 ns = str(n).rstrip('.')
 if not ns:
     continue
 server = {"ns": ns, "axfr": False}
 zone_content = []
 try:
     axfr_rr = dns.query.xfr(ns, domain, lifetime=timeout)
     axfr_zone = dns.zone.from_xfr(axfr_rr)
 except Exception, e:
     server["error"] = str(e)
     retv["servers"].append(server)
     continue
 server["axfr"] = True
 for name, ttl, rdata in axfr_zone.iterate_rdatas():
     entry = {
         'name': name.to_text(),
         'ttl': ttl,
         'rdclass': rdata.rdclass,
         'rdtype': rdata.rdtype,
         'rdata': rdata.to_text()
     }
     try:
         entry['pretty_rdclass'] = dns.rdataclass.to_text(rdata.rdclass)
         entry['pretty_rdtype'] = dns.rdatatype.to_text(rdata.rdtype)
         parent = dns.name.Name(domain.split('.'))
         if name == dns.name.empty:
             entry['pretty_name'] = parent.to_text()
         else:
             entry['pretty_name'] = name.concatenate(parent).to_text()
     except Exception:
         pass
Exemple #32
0
def print_result(analysis_structured):
    latest_analysis_date = None
    name_objs = []
    cache = {}
    names = OrderedDict()

    strict_cookies = False
    allow_private = False
    supported_digest_algs = None
    supported_algs = None
    rdtypes = None

    args = analysis_structured['_meta._dnsviz.']['names']

    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:
            if name not in names:
                names[name] = None

    for name in names:
        name_str = lb2s(name.canonicalize().to_text())
        print('* %s' % name_str)
        if name_str not in analysis_structured or analysis_structured[name_str].get('stub', True):
            logger.error('The analysis of "%s" was not found in the input.' % lb2s(name.to_text()))
            continue
        name_obj = TTLAgnosticOfflineDomainNameAnalysis.deserialize(name, analysis_structured, cache, strict_cookies=strict_cookies, allow_private=allow_private)
        name_objs.append(name_obj)

        if latest_analysis_date is None or latest_analysis_date > name_obj.analysis_end:
            latest_analysis_date = name_obj.analysis_end

    if latest_analysis_date is None:
        logger.error('The analysis of "%s" doesn\'t include at least one analysis.' % lb2s(name.to_text()))
        print(names)
        return

    trusted_keys = get_default_trusted_keys(latest_analysis_date)

    G = DNSAuthGraph()
    for name_obj in name_objs:
        name_obj.populate_status(trusted_keys, supported_algs=supported_algs, supported_digest_algs=supported_digest_algs)
        for qname, rdtype in name_obj.queries:
            if rdtypes is None:
                # if rdtypes was not specified, then graph all, with some
                # exceptions
                if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY, dns.rdatatype.DS, dns.rdatatype.DLV):
                    continue
            else:
                # if rdtypes was specified, then only graph rdtypes that
                # were specified
                if qname != name_obj.name or rdtype not in rdtypes:
                    continue
            G.graph_rrset_auth(name_obj, qname, rdtype)

        if rdtypes is not None:
            for rdtype in rdtypes:
                if (name_obj.name, rdtype) not in name_obj.queries:
                    logger.error('No query for "%s/%s" was included in the analysis.' % (lb2s(name_obj.name.to_text()), dns.rdatatype.to_text(rdtype)))

    finish_graph(G, name_objs, rdtypes, trusted_keys, supported_algs, None)
Exemple #33
0
def domain_analysis_form(name):
    ANCESTOR_CHOICES = [(name.to_text(), fmt.humanize_name(name, True))]
    n = name
    while n != dns.name.root:
        n = n.parent()
        ANCESTOR_CHOICES.append((n.to_text(), fmt.humanize_name(n, True)))
    ANCESTOR_CHOICES.reverse()

    class DomainNameAnalysisForm(forms.Form):
        EXTRA_TYPES = (
            (dns.rdatatype.A, dns.rdatatype.to_text(dns.rdatatype.A)),
            (dns.rdatatype.AAAA, dns.rdatatype.to_text(dns.rdatatype.AAAA)),
            (dns.rdatatype.TXT, dns.rdatatype.to_text(dns.rdatatype.TXT)),
            (dns.rdatatype.PTR, dns.rdatatype.to_text(dns.rdatatype.PTR)),
            (dns.rdatatype.MX, dns.rdatatype.to_text(dns.rdatatype.MX)),
            (dns.rdatatype.SOA, dns.rdatatype.to_text(dns.rdatatype.SOA)),
            (dns.rdatatype.CNAME, dns.rdatatype.to_text(dns.rdatatype.CNAME)),
            (dns.rdatatype.SRV, dns.rdatatype.to_text(dns.rdatatype.SRV)),
            (dns.rdatatype.NAPTR, dns.rdatatype.to_text(dns.rdatatype.NAPTR)),
            (dns.rdatatype.TLSA, dns.rdatatype.to_text(dns.rdatatype.TLSA)),
        )

        ANALYSIS_TYPES = (
            (ANALYSIS_TYPE_AUTHORITATIVE, "Authoritative servers"),
            (ANALYSIS_TYPE_RECURSIVE, "Recursive servers"),
        )

        force_ancestor = forms.TypedChoiceField(
            label="Force ancestor analysis",
            choices=ANCESTOR_CHOICES,
            initial=name.to_text(),
            required=True,
            coerce=dns.name.from_text,
            help_text="Usually it is sufficient to select the name itself (%s) or its zone, in which case cached values will be used for the analysis of any ancestor names (unless it is determined that they are out of date).  Occasionally it is useful to re-analyze some portion of the ancestry, in which case the desired ancestor can be selected.  However, the overall analysis will take longer."
            % (fmt.humanize_name(name, True)),
        )
        extra_types = forms.TypedMultipleChoiceField(
            choices=EXTRA_TYPES,
            initial=(),
            required=False,
            coerce=int,
            help_text="Select any extra RR types to query as part of this analysis.  A default set of types will already be queried based on the nature of the name, but any types selected here will assuredly be included.",
        )
        edns_diagnostics = forms.BooleanField(
            label="EDNS diagnostics",
            initial=False,
            required=False,
            help_text="Issue queries specific to EDNS diagnostics.",
        )
        explicit_delegation = forms.CharField(
            initial="",
            required=False,
            widget=forms.Textarea(attrs={"cols": 50, "rows": 5}),
            help_text='If you wish to designate servers explicitly for the "force ancestor" zone (rather than following delegation from the IANA root), enter the server names, one per line.  You may optionally include an IPv4 or IPv6 address on the same line as the name.',
        )
        analysis_type = forms.TypedChoiceField(
            choices=ANALYSIS_TYPES,
            initial=ANALYSIS_TYPE_AUTHORITATIVE,
            coerce=int,
            widget=forms.RadioSelect(),
            help_text="If authoritative analysis is selected, then the authoritative servers will be analyzed, beginning at the root servers--or the servers explicitly designated; if recursive analysis is selected, then the designated recursive servers will be analyzed.",
        )

        def clean(self):
            cleaned_data = super(DomainNameAnalysisForm, self).clean()
            if cleaned_data.get("analysis_type", None) == ANALYSIS_TYPE_RECURSIVE and not cleaned_data.get(
                "explicit_delegation", None
            ):
                raise forms.ValidationError(
                    "If recursive analysis is desired, then servers names and/or addresses must be specified."
                )
            return cleaned_data

        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

    return DomainNameAnalysisForm
Exemple #34
0
 def _printcache(self, cache):
     for name, values in cache.items():
         print(name.to_text() + ' :')
         for type, data in values.items():
             print(dns.rdatatype.to_text(type), end=' :  ')
             print('[' + ', '.join("'" + r.to_text() + "'" for r in data) + ']\n')
Exemple #35
0
def main(argv):
    try:
        test_pygraphviz()

        arghelper = build_helper(logger, sys.argv[0], argv[0])
        arghelper.parse_args(argv[1:])
        logger.setLevel(logging.WARNING)

        try:
            arghelper.check_args()
            arghelper.set_buffers()
            arghelper.aggregate_trusted_key_info()
            arghelper.ingest_input()
            arghelper.ingest_names()
        except argparse.ArgumentTypeError as e:
            arghelper.parser.error(str(e))
        except AnalysisInputError as e:
            s = str(e)
            if s:
                logger.error(s)
            sys.exit(3)

        latest_analysis_date = None
        name_objs = []
        cache = {}
        for name in arghelper.names:
            name_str = lb2s(name.canonicalize().to_text())
            if name_str not in arghelper.analysis_structured or arghelper.analysis_structured[
                    name_str].get('stub', True):
                logger.error(
                    'The analysis of "%s" was not found in the input.' %
                    lb2s(name.to_text()))
                continue
            name_obj = TTLAgnosticOfflineDomainNameAnalysis.deserialize(
                name,
                arghelper.analysis_structured,
                cache,
                strict_cookies=arghelper.args.enforce_cookies,
                allow_private=arghelper.args.allow_private)
            name_objs.append(name_obj)

            if latest_analysis_date is None or latest_analysis_date > name_obj.analysis_end:
                latest_analysis_date = name_obj.analysis_end

        if not name_objs:
            sys.exit(4)

        arghelper.update_trusted_key_info(latest_analysis_date)

        G = DNSAuthGraph()
        for name_obj in name_objs:
            name_obj.populate_status(
                arghelper.trusted_keys,
                supported_algs=arghelper.args.algorithms,
                supported_digest_algs=arghelper.args.digest_algorithms,
                validate_prohibited_algs=arghelper.args.
                validate_prohibited_algs)
            for qname, rdtype in name_obj.queries:
                if arghelper.args.rr_types is None:
                    # if rdtypes was not specified, then graph all, with some
                    # exceptions
                    if name_obj.is_zone() and rdtype in (dns.rdatatype.DNSKEY,
                                                         dns.rdatatype.DS,
                                                         dns.rdatatype.DLV):
                        continue
                else:
                    # if rdtypes was specified, then only graph rdtypes that
                    # were specified
                    if qname != name_obj.name or rdtype not in arghelper.args.rr_types:
                        continue
                G.graph_rrset_auth(name_obj, qname, rdtype)

            if arghelper.args.rr_types is not None:
                for rdtype in arghelper.args.rr_types:
                    if (name_obj.name, rdtype) not in name_obj.queries:
                        logger.error(
                            'No query for "%s/%s" was included in the analysis.'
                            % (lb2s(name_obj.name.to_text()),
                               dns.rdatatype.to_text(rdtype)))

            if arghelper.args.derive_filename:
                if name_obj.name == dns.name.root:
                    name = 'root'
                else:
                    name = lb2s(
                        name_obj.name.canonicalize().to_text()).rstrip('.')
                    name = name.replace(os.sep, '--')
                finish_graph(G, [name_obj], arghelper.args.rr_types,
                             arghelper.trusted_keys, arghelper.args.algorithms,
                             '%s.txt' % name)
                G = DNSAuthGraph()

        if not arghelper.args.derive_filename:
            finish_graph(G, name_objs, arghelper.args.rr_types,
                         arghelper.trusted_keys, arghelper.args.algorithms,
                         arghelper.args.output_file.fileno())

    except KeyboardInterrupt:
        logger.error('Interrupted.')
        sys.exit(4)
Exemple #36
0
            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': [n.to_text() for n in names] }

        flush = '-F' in opts

        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, ceiling, edns_diagnostics, cache_level, explicit_delegations, rdtypes, explicit_only, dlv_domain, th_factories, processes)
            else:
                a = cls(try_ipv4, try_ipv6, client_ipv4, client_ipv6, ceiling, edns_diagnostics, cache_level, explicit_delegations, 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)