Import upstream version 0.9.3+git20210817.1.039c509
Debian Janitor
2 years ago
23 | 23 | The remainer of this section covers other methods of installation, including a |
24 | 24 | list of [dependencies](#dependencies), installation to a |
25 | 25 | [virtual environment](#installation-in-a-virtual-environment), and installation |
26 | on [Fedora](#fedora-rpm-build-and-install) and | |
27 | [RHEL7](#rhel7-rpm-build-and-install). | |
26 | on [Fedora, RHEL 8, CentOS 8,](#fedora--rhel-8--centos-8-rpm-build-and-install) and | |
27 | [RHEL 7](#rhel-7-rpm-build-and-install). | |
28 | 28 | |
29 | 29 | Instructions for running in a Docker container are also available |
30 | 30 | [later in this document](#docker-container). |
93 | 93 | ``` |
94 | 94 | |
95 | 95 | |
96 | ### Fedora RPM Build and Install | |
96 | ### Fedora / RHEL 8 / CentOS 8 RPM Build and Install | |
97 | ||
98 | *RHEL 8 only*: Enable CodeReady Linux Builder by following the instructions [here](https://access.redhat.com/articles/4348511). | |
99 | ||
100 | *CentOS 8 only*: Enable PowerTools and EPEL with the following two commands: | |
101 | ``` | |
102 | $ sudo dnf config-manager --set-enabled powertools | |
103 | $ sudo dnf install epel-release | |
104 | ``` | |
105 | ||
106 | The remaining instructions are for Fedora, RHEL 8, and CentOS 8. | |
97 | 107 | |
98 | 108 | Install the tools for building an RPM, and set up the rpmbuild tree. |
99 | 109 | ``` |
114 | 124 | ``` |
115 | 125 | $ sudo dnf install python3-dns python3-pygraphviz python3-m2crypto |
116 | 126 | ``` |
117 | (Note that as of Fedora 33, the latest version of M2Crypto is 0.35.2. If you | |
127 | (Note that as of Fedora 33 / RHEL 8 / CentOS 8, the latest version of M2Crypto is 0.35.2. If you | |
118 | 128 | would like support for DNSSEC algorithms 15 (Ed25519) and 16 (Ed448), you will |
119 | 129 | need to install M2Crypto using `pip3`. For example, see [installation to a |
120 | 130 | virtual environment](#installation-in-a-virtual-environment).) |
126 | 136 | ``` |
127 | 137 | |
128 | 138 | |
129 | ### RHEL7 RPM Build and Install | |
139 | ### RHEL 7 RPM Build and Install | |
130 | 140 | |
131 | 141 | Install pygraphviz, M2Crypto, and dnspython, after installing their build dependencies. |
132 | 142 | ``` |
1628 | 1628 | required_params = [] |
1629 | 1629 | |
1630 | 1630 | class MissingRRSIGForAlg(ResponseError): |
1631 | description_template = 'The %(source)s RRset for the zone included algorithm %(algorithm)s (%(algorithm_text)s), but no RRSIG with algorithm %(algorithm)d covering the RRset was returned in the response.' | |
1631 | description_template = 'The %(source)s RRset for the zone included algorithm %(algorithm)d (%(algorithm_text)s), but no RRSIG with algorithm %(algorithm)d covering the RRset was returned in the response.' | |
1632 | 1632 | references = ['RFC 4035, Sec. 2.2', 'RFC 6840, Sec. 5.11'] |
1633 | 1633 | required_params = ['algorithm'] |
1634 | 1634 | source = None |
1902 | 1902 | |
1903 | 1903 | _abstract = False |
1904 | 1904 | code = 'MISSING_SEP_FOR_ALG' |
1905 | description_template = "The %(source)s RRset for the zone included algorithm %(algorithm)s (%(algorithm_text)s), but no %(source)s RR matched a DNSKEY with algorithm %(algorithm)d that signs the zone's DNSKEY RRset." | |
1905 | description_template = "The %(source)s RRset for the zone included algorithm %(algorithm)d (%(algorithm_text)s), but no %(source)s RR matched a DNSKEY with algorithm %(algorithm)d that signs the zone's DNSKEY RRset." | |
1906 | 1906 | references = ['RFC 4035, Sec. 2.2', 'RFC 6840, Sec. 5.11'] |
1907 | 1907 | required_params = ['algorithm'] |
1908 | 1908 |
1574 | 1574 | rrset_info.rrset.rdtype != dns.rdatatype.DS and \ |
1575 | 1575 | rrsig_status.dnskey is not None: |
1576 | 1576 | if rrset_info.rrset.rdtype == dns.rdatatype.DNSKEY: |
1577 | self.ksks.add(rrsig_status.dnskey) | |
1577 | if self.ksks is not None: | |
1578 | self.ksks.add(rrsig_status.dnskey) | |
1578 | 1579 | else: |
1579 | self.zsks.add(rrsig_status.dnskey) | |
1580 | if self.zsks is not None: | |
1581 | self.zsks.add(rrsig_status.dnskey) | |
1580 | 1582 | |
1581 | 1583 | key = rrsig_status.rrset, rrsig_status.rrsig |
1582 | 1584 | break |
1684 | 1686 | self.response_errors = {} |
1685 | 1687 | self.response_warnings = {} |
1686 | 1688 | |
1687 | if self.is_zone(): | |
1689 | if (self.name, dns.rdatatype.DNSKEY) in self.queries: | |
1688 | 1690 | self.zsks = set() |
1689 | 1691 | self.ksks = set() |
1690 | 1692 | |
1713 | 1715 | self._populate_invalid_response_status(query) |
1714 | 1716 | |
1715 | 1717 | def _finalize_key_roles(self): |
1716 | if self.is_zone(): | |
1718 | if (self.name, dns.rdatatype.DNSKEY) in self.queries: | |
1717 | 1719 | self.published_keys = set(self.get_dnskeys()).difference(self.zsks.union(self.ksks)) |
1718 | 1720 | self.revoked_keys = set([x for x in self.get_dnskeys() if x.rdata.flags & fmt.DNSKEY_FLAGS['revoke']]) |
1719 | 1721 |
1030 | 1030 | for cname in self.cname_targets: |
1031 | 1031 | for target in self.cname_targets[cname]: |
1032 | 1032 | self.cname_targets[cname][target] = self.__class__.deserialize(target, d, cache=cache) |
1033 | # these are optional | |
1033 | 1034 | for signer in self.external_signers: |
1034 | self.external_signers[signer] = self.__class__.deserialize(signer, d, cache=cache) | |
1035 | ||
1036 | # these two are optional | |
1035 | if lb2s(signer.canonicalize().to_text()) in d: | |
1036 | self.external_signers[signer] = self.__class__.deserialize(signer, d, cache=cache) | |
1037 | 1037 | for target in self.ns_dependencies: |
1038 | 1038 | if lb2s(target.canonicalize().to_text()) in d: |
1039 | 1039 | self.ns_dependencies[target] = self.__class__.deserialize(target, d, cache=cache) |
1746 | 1746 | if self.dns_cookies: |
1747 | 1747 | self.logger.debug('Preparing DNS cookie diagnostic query %s/%s...' % (fmt.humanize_name(name_obj.name), dns.rdatatype.to_text(dns.rdatatype.SOA))) |
1748 | 1748 | queries[(name_obj.name, -(dns.rdatatype.SOA+104))] = self.diagnostic_query_bad_server_cookie(name_obj.name, dns.rdatatype.SOA, self.rdclass, servers, bailiwick, self.client_ipv4, self.client_ipv6, odd_ports=odd_ports, cookie_bad=COOKIE_BAD) |
1749 | ||
1750 | # NSEC3PARAM | |
1751 | self.logger.debug('Preparing query %s/%s...' % (fmt.humanize_name(name_obj.name), dns.rdatatype.to_text(dns.rdatatype.NSEC3PARAM))) | |
1752 | queries[(name_obj.name, dns.rdatatype.NSEC3PARAM)] = self.diagnostic_query(name_obj.name, dns.rdatatype.NSEC3PARAM, self.rdclass, servers, bailiwick, self.client_ipv4, self.client_ipv6, odd_ports=odd_ports, cookie_bad=COOKIE_STANDIN) | |
1749 | 1753 | |
1750 | 1754 | # negative queries for all zones |
1751 | 1755 | self._set_negative_queries(name_obj) |
278 | 278 | if self.validation_status == RRSIG_STATUS_VALID: |
279 | 279 | self.validation_status = RRSIG_STATUS_EXPIRED |
280 | 280 | self.errors.append(Errors.ExpirationInPast(expiration=fmt.timestamp_to_datetime(self.rrsig.expiration), reference_time=fmt.timestamp_to_datetime(self.reference_ts))) |
281 | elif self.reference_ts + min_ttl >= self.rrsig.expiration: | |
281 | elif self.reference_ts + min_ttl > self.rrsig.expiration: | |
282 | 282 | self.errors.append(Errors.TTLBeyondExpiration(expiration=fmt.timestamp_to_datetime(self.rrsig.expiration), rrsig_ttl=min_ttl, reference_time=fmt.timestamp_to_datetime(self.reference_ts))) |
283 | 283 | elif self.reference_ts + CLOCK_SKEW_WARNING >= self.rrsig.expiration: |
284 | 284 | self.warnings.append(Errors.ExpirationWithinClockSkew(expiration=fmt.timestamp_to_datetime(self.rrsig.expiration), reference_time=fmt.timestamp_to_datetime(self.reference_ts))) |
118 | 118 | global tm |
119 | 119 | if tm is not None: |
120 | 120 | tm.close() |
121 | tm = None | |
121 | 122 | |
122 | 123 | def _init_stub_resolver(): |
123 | 124 | global resolver |
490 | 491 | _allow_stop_at = None |
491 | 492 | _handle_file_arg = None |
492 | 493 | |
493 | def __init__(self, domain, stop_at, resolver): | |
494 | _resolvers_initialized = False | |
495 | _stub_resolver = None | |
496 | _full_resolver = None | |
497 | ||
498 | def __init__(self, domain, stop_at): | |
494 | 499 | if not (self._allow_file is not None and \ |
495 | 500 | self._allow_name_only is not None and \ |
496 | 501 | self._allow_addr_only is not None and \ |
502 | 507 | raise argparse.ArgumentTypeError('The "+" may not be specified with this option') |
503 | 508 | |
504 | 509 | self.domain = domain |
505 | self._resolver = resolver | |
506 | 510 | self._nsi = 1 |
507 | 511 | |
508 | 512 | self.delegation_mapping = {} |
511 | 515 | self.filename = None |
512 | 516 | |
513 | 517 | self.delegation_mapping[(self.domain, dns.rdatatype.NS)] = dns.rrset.RRset(self.domain, dns.rdataclass.IN, dns.rdatatype.NS) |
518 | ||
519 | @classmethod | |
520 | def init_resolvers(cls): | |
521 | if not NameServerMappingsForDomain._resolvers_initialized: | |
522 | tm = transport.DNSQueryTransportManager() | |
523 | try: | |
524 | NameServerMappingsForDomain._stub_resolver = Resolver.from_file(RESOLV_CONF, StandardRecursiveQueryCD, transport_manager=tm) | |
525 | except ResolvConfError: | |
526 | pass | |
527 | NameServerMappingsForDomain._full_resolver = PrivateFullResolver(transport_manager=tm) | |
528 | NameServerMappingsForDomain._resolvers_initialized = True | |
529 | ||
530 | @classmethod | |
531 | def cleanup_resolvers(cls): | |
532 | NameServerMappingsForDomain._stub_resolver = None | |
533 | NameServerMappingsForDomain._full_resolver = None | |
534 | NameServerMappingsForDomain._resolvers_initialized = False | |
514 | 535 | |
515 | 536 | @classmethod |
516 | 537 | def _strip_port(cls, s): |
539 | 560 | self._handle_name_addr_mapping(name_addr) |
540 | 561 | |
541 | 562 | def _handle_name_no_addr(self, name, port): |
563 | resolver = None | |
564 | self.init_resolvers() | |
565 | if self._stub_resolver is not None: | |
566 | resolver = self._stub_resolver | |
567 | else: | |
568 | resolver = self._full_resolver | |
542 | 569 | query_tuples = ((name, dns.rdatatype.A, dns.rdataclass.IN), (name, dns.rdatatype.AAAA, dns.rdataclass.IN)) |
543 | answer_map = self._resolver.query_multiple_for_answer(*query_tuples) | |
570 | answer_map = resolver.query_multiple_for_answer(*query_tuples) | |
544 | 571 | found_answer = False |
545 | 572 | for (n, rdtype, rdclass) in answer_map: |
546 | 573 | a = answer_map[(n, rdtype, rdclass)] |
761 | 788 | _handle_file_arg = None |
762 | 789 | |
763 | 790 | class DSForDomain: |
764 | def __init__(self, domain, stop_at, resolver): | |
791 | def __init__(self, domain, stop_at): | |
765 | 792 | self.domain = domain |
766 | 793 | |
767 | 794 | if stop_at and not self._allow_stop_at: |
816 | 843 | class DomainListArgHelper: |
817 | 844 | STOP_RE = re.compile(r'^(.*)\+$') |
818 | 845 | |
819 | def __init__(self, resolver): | |
820 | self._resolver = resolver | |
821 | ||
822 | 846 | @classmethod |
823 | 847 | def _strip_stop_marker(cls, s): |
824 | 848 | match = cls.STOP_RE.search(s) |
854 | 878 | if list_arg is not None: |
855 | 879 | list_arg = list_arg.strip() |
856 | 880 | |
857 | obj = cls(domain, stop_at, self._resolver) | |
881 | obj = cls(domain, stop_at) | |
858 | 882 | |
859 | 883 | if list_arg: |
860 | 884 | obj.handle_list_arg(list_arg) |
861 | 885 | return obj |
862 | 886 | |
863 | 887 | def _handle_list_arg(self, cls, list_arg): |
864 | obj = cls(WILDCARD_EXPLICIT_DELEGATION, False, self._resolver) | |
888 | obj = cls(WILDCARD_EXPLICIT_DELEGATION, False) | |
865 | 889 | obj.handle_list_arg(list_arg) |
866 | 890 | return obj |
867 | 891 | |
880 | 904 | class ArgHelper: |
881 | 905 | BRACKETS_RE = re.compile(r'^\[(.*)\]$') |
882 | 906 | |
883 | def __init__(self, resolver, logger): | |
884 | self._resolver = resolver | |
907 | def __init__(self, logger): | |
885 | 908 | self.parser = None |
886 | 909 | |
887 | 910 | self.odd_ports = {} |
905 | 928 | self.args = None |
906 | 929 | self._arg_mapping = None |
907 | 930 | |
908 | self._resolver = resolver | |
909 | 931 | self._logger = logger |
910 | 932 | self._zones_to_serve = [] |
911 | 933 | |
912 | 934 | def build_parser(self, prog): |
913 | 935 | self.parser = argparse.ArgumentParser(description='Issue diagnostic DNS queries', prog=prog) |
914 | helper = DomainListArgHelper(self._resolver) | |
936 | helper = DomainListArgHelper() | |
915 | 937 | |
916 | 938 | # python3/python2 dual compatibility |
917 | 939 | stdout_buffer = io.open(sys.stdout.fileno(), 'wb', closefd=False) |
1236 | 1258 | |
1237 | 1259 | def populate_recursive_servers(self): |
1238 | 1260 | if not self.args.authoritative_analysis and not self.args.recursive_servers: |
1261 | try: | |
1262 | resolver = Resolver.from_file(RESOLV_CONF, StandardRecursiveQueryCD, transport_manager=tm) | |
1263 | except ResolvConfError: | |
1264 | raise argparse.ArgumentTypeError('If servers are not specified with the %s option, then %s must have valid nameserver entries.\n' % \ | |
1265 | (self._arg_mapping['recursive_servers'], RESOLV_CONF)) | |
1239 | 1266 | if (WILDCARD_EXPLICIT_DELEGATION, dns.rdatatype.NS) not in self.explicit_delegations: |
1240 | 1267 | self.explicit_delegations[(WILDCARD_EXPLICIT_DELEGATION, dns.rdatatype.NS)] = dns.rrset.RRset(WILDCARD_EXPLICIT_DELEGATION, dns.rdataclass.IN, dns.rdatatype.NS) |
1241 | for i, server in enumerate(self._resolver._servers): | |
1268 | for i, server in enumerate(resolver._servers): | |
1242 | 1269 | if IPAddr(server).version == 6: |
1243 | 1270 | rdtype = dns.rdatatype.AAAA |
1244 | 1271 | else: |
1446 | 1473 | zone.serve() |
1447 | 1474 | |
1448 | 1475 | def build_helper(logger, cmd, subcmd): |
1449 | try: | |
1450 | resolver = Resolver.from_file(RESOLV_CONF, StandardRecursiveQueryCD, transport_manager=tm) | |
1451 | except ResolvConfError: | |
1452 | sys.stderr.write('File %s not found or contains no nameserver entries.\n' % RESOLV_CONF) | |
1453 | sys.exit(1) | |
1454 | ||
1455 | arghelper = ArgHelper(resolver, logger) | |
1476 | arghelper = ArgHelper(logger) | |
1456 | 1477 | arghelper.build_parser('%s %s' % (cmd, subcmd)) |
1457 | 1478 | return arghelper |
1458 | 1479 | |
1459 | 1480 | def main(argv): |
1460 | global tm | |
1461 | 1481 | global th_factories |
1462 | 1482 | global explicit_delegations |
1463 | 1483 | global odd_ports |
1464 | 1484 | |
1465 | 1485 | try: |
1466 | _init_tm() | |
1467 | 1486 | arghelper = build_helper(logger, sys.argv[0], argv[0]) |
1468 | 1487 | arghelper.parse_args(argv[1:]) |
1469 | 1488 | logger.setLevel(arghelper.get_log_level()) |
1512 | 1531 | kwargs = {} |
1513 | 1532 | dnsviz_meta = { 'version': DNS_RAW_VERSION, 'names': [lb2s(n.to_text()) for n in arghelper.names] } |
1514 | 1533 | |
1534 | NameServerMappingsForDomain.cleanup_resolvers() | |
1535 | ||
1536 | _init_tm() | |
1537 | ||
1515 | 1538 | name_objs = [] |
1516 | 1539 | if arghelper.args.input_file: |
1517 | 1540 | cache = {} |
1532 | 1555 | |
1533 | 1556 | name_objs = a.analyze(arghelper.names) |
1534 | 1557 | |
1558 | _cleanup_tm() | |
1559 | ||
1535 | 1560 | name_objs = [x for x in name_objs if x is not None] |
1536 | 1561 | |
1537 | 1562 | if not name_objs: |
1552 | 1577 | logger.error('Interrupted.') |
1553 | 1578 | sys.exit(4) |
1554 | 1579 | |
1555 | # tm is global (because of possible multiprocessing), so we need to | |
1556 | # explicitly close it here | |
1557 | finally: | |
1558 | _cleanup_tm() | |
1559 | ||
1560 | 1580 | if __name__ == "__main__": |
1561 | 1581 | main(sys.argv) |
1541 | 1541 | # If this was a network error, determine if it was a binding |
1542 | 1542 | # error |
1543 | 1543 | if err == RESPONSE_ERROR_NETWORK_ERROR: |
1544 | if errno1 == errno.EADDRNOTAVAIL: | |
1545 | # Address not unavailable | |
1546 | if qh._client is not None: | |
1547 | raise SourceAddressBindError('Unable to bind to local address %s (%s)' % (qh._client, errno.errorcode[errno1])) | |
1548 | else: | |
1549 | raise SourceAddressBindError('Unable to bind to local address (%s)' % (errno.errorcode[errno1])) | |
1544 | if errno1 == errno.EADDRNOTAVAIL and qh._client is not None: | |
1545 | raise SourceAddressBindError('Unable to bind to local address %s (%s)' % (qh._client, errno.errorcode[errno1])) | |
1550 | 1546 | elif errno1 == errno.EADDRINUSE or \ |
1551 | 1547 | (errno1 == errno.EACCES and qtm.src is None): |
1552 | 1548 | # Address/port in use (EADDRINUSE) or insufficient |
1555 | 1551 | raise PortBindError('Unable to bind to local port %d (%s)' % (qh.params['sport'], errno.errorcode[errno1])) |
1556 | 1552 | else: |
1557 | 1553 | raise PortBindError('Unable to bind to local port (%s)' % (errno.errorcode[errno1])) |
1558 | elif qtm.src is None and errno1 not in (errno.EHOSTUNREACH, errno.ENETUNREACH, errno.EAFNOSUPPORT): | |
1559 | # If source is None it didn't bind properly. If the | |
1560 | # errno1 value after bind() is EHOSTUNREACH or | |
1561 | # ENETUNREACH, it is because there was no proper IPv4 | |
1562 | # or IPv6 connectivity (which is handled elsewhere). | |
1563 | # If socket() failed and resulted in an errno value of | |
1564 | # EAFNOSUPPORT, then likewise there is not IPv6 | |
1565 | # support. In other cases, it was something unknown, so | |
1554 | elif qtm.src is None and errno1 not in (errno.EHOSTUNREACH, errno.ENETUNREACH, errno.EAFNOSUPPORT, errno.EADDRNOTAVAIL): | |
1555 | # If source is None it didn't bind properly. There are several sub-cases: | |
1556 | # 1. If the bind() failed and resulted in an errno | |
1557 | # value of EHOSTUNREACH, it is because there was no | |
1558 | # proper IPv4 or IPv6 connectivity; the error for | |
1559 | # this is handled elsewhere). | |
1560 | # 2. If socket() failed and resulted in an errno value | |
1561 | # of EAFNOSUPPORT, then there is no IPv6 support. | |
1562 | # 3. If connect() failed and resulted in an errno value | |
1563 | # of EADDRNOTAVAIL, then there is no IPv6 support. | |
1564 | # In other cases, it was something unknown, so | |
1566 | 1565 | # raise an error. |
1567 | 1566 | raise BindError('Unable to bind to local address (%s)' % (errno.errorcode.get(errno1, "unknown"))) |
1568 | 1567 |
1402 | 1402 | self._event_map = {} |
1403 | 1403 | |
1404 | 1404 | self._close = threading.Event() |
1405 | t = threading.Thread(target=self._loop) | |
1405 | # python3/python2 dual compatibility | |
1406 | try: | |
1407 | # python 3 | |
1408 | t = threading.Thread(target=self._loop, daemon=True) | |
1409 | except TypeError: | |
1410 | # python 2 | |
1411 | t = threading.Thread(target=self._loop) | |
1412 | t.daemon = True | |
1406 | 1413 | t.start() |
1407 | 1414 | |
1408 | 1415 | def close(self): |
1295 | 1295 | for signed_keys, rrset_info in name_obj.get_dnskey_sets(): |
1296 | 1296 | for rrsig in name_obj.rrsig_status[rrset_info]: |
1297 | 1297 | signer_obj = name_obj.get_name(rrsig.signer) |
1298 | if rrsig.signer != name_obj.name and not is_dlv: | |
1299 | self.graph_zone_auth(signer_obj, False) | |
1298 | if signer_obj is not None: | |
1299 | # if we have the analysis corresponding to the signer, then | |
1300 | # graph it too, if it was different from what we were | |
1301 | # expecting | |
1302 | if rrsig.signer != name_obj.name and not is_dlv: | |
1303 | self.graph_zone_auth(signer_obj, False) | |
1300 | 1304 | for dnskey in name_obj.rrsig_status[rrset_info][rrsig]: |
1301 | 1305 | rrsig_status = name_obj.rrsig_status[rrset_info][rrsig][dnskey] |
1302 | 1306 | if dnskey is None: |
11 | 11 | import dns.name, dns.rdatatype, dns.rrset, dns.zone |
12 | 12 | |
13 | 13 | from dnsviz.commands.probe import ZoneFileToServe, ArgHelper, DomainListArgHelper, StandardRecursiveQueryCD, WILDCARD_EXPLICIT_DELEGATION, AnalysisInputError, CustomQueryMixin |
14 | from dnsviz.ipaddr import IPAddr | |
14 | 15 | from dnsviz import transport |
15 | from dnsviz.resolver import Resolver | |
16 | from dnsviz.ipaddr import IPAddr | |
17 | 16 | |
18 | 17 | DATA_DIR = os.path.dirname(__file__) |
19 | 18 | EXAMPLE_COM_ZONE = os.path.join(DATA_DIR, 'zone', 'example.com.zone') |
22 | 21 | |
23 | 22 | class DNSVizProbeOptionsTestCase(unittest.TestCase): |
24 | 23 | def setUp(self): |
25 | self.tm = transport.DNSQueryTransportManager() | |
26 | self.resolver = Resolver.from_file('/etc/resolv.conf', StandardRecursiveQueryCD, transport_manager=self.tm) | |
27 | self.helper = DomainListArgHelper(self.resolver) | |
24 | self.helper = DomainListArgHelper() | |
28 | 25 | self.logger = logging.getLogger() |
29 | 26 | for handler in self.logger.handlers: |
30 | 27 | self.logger.removeHandler(handler) |
40 | 37 | |
41 | 38 | def tearDown(self): |
42 | 39 | CustomQueryMixin.edns_options = self.custom_query_mixin_edns_options_orig[:] |
43 | if self.tm is not None: | |
44 | self.tm.close() | |
45 | 40 | |
46 | 41 | def test_authoritative_option(self): |
47 | 42 | arg1 = 'example.com+:ns1.example.com=192.0.2.1:1234,ns1.example.com=[2001:db8::1],' + \ |
598 | 593 | |
599 | 594 | ZoneFileToServe._next_free_port = self.first_port |
600 | 595 | |
601 | arghelper1 = ArgHelper(self.resolver, self.logger) | |
596 | arghelper1 = ArgHelper(self.logger) | |
602 | 597 | arghelper1.build_parser('probe') |
603 | 598 | arghelper1.parse_args(args1) |
604 | 599 | arghelper1.aggregate_delegation_info() |
611 | 606 | |
612 | 607 | ZoneFileToServe._next_free_port = self.first_port |
613 | 608 | |
614 | arghelper2 = ArgHelper(self.resolver, self.logger) | |
609 | arghelper2 = ArgHelper(self.logger) | |
615 | 610 | arghelper2.build_parser('probe') |
616 | 611 | arghelper2.parse_args(args2) |
617 | 612 | arghelper2.aggregate_delegation_info() |
624 | 619 | |
625 | 620 | ZoneFileToServe._next_free_port = self.first_port |
626 | 621 | |
627 | arghelper3 = ArgHelper(self.resolver, self.logger) | |
622 | arghelper3 = ArgHelper(self.logger) | |
628 | 623 | arghelper3.build_parser('probe') |
629 | 624 | arghelper3.parse_args(args3) |
630 | 625 | arghelper3.aggregate_delegation_info() |
633 | 628 | |
634 | 629 | ZoneFileToServe._next_free_port = self.first_port |
635 | 630 | |
636 | arghelper4 = ArgHelper(self.resolver, self.logger) | |
631 | arghelper4 = ArgHelper(self.logger) | |
637 | 632 | arghelper4.build_parser('probe') |
638 | 633 | arghelper4.parse_args(args4) |
639 | 634 | arghelper4.aggregate_delegation_info() |
674 | 669 | |
675 | 670 | ZoneFileToServe._next_free_port = self.first_port |
676 | 671 | |
677 | arghelper1 = ArgHelper(self.resolver, self.logger) | |
672 | arghelper1 = ArgHelper(self.logger) | |
678 | 673 | arghelper1.build_parser('probe') |
679 | 674 | arghelper1.parse_args(args1) |
680 | 675 | arghelper1.aggregate_delegation_info() |
685 | 680 | args1 = ['-A', '-N', 'example.com:ns1.example.com=192.0.2.1,ns1.example.com=[2001:db8::1]', |
686 | 681 | '-x', 'com:ns1.foo.com=192.0.2.3'] |
687 | 682 | |
688 | arghelper1 = ArgHelper(self.resolver, self.logger) | |
683 | arghelper1 = ArgHelper(self.logger) | |
689 | 684 | arghelper1.build_parser('probe') |
690 | 685 | arghelper1.parse_args(args1) |
691 | 686 | |
717 | 712 | |
718 | 713 | odd_ports1 = {} |
719 | 714 | |
720 | arghelper1 = ArgHelper(self.resolver, self.logger) | |
715 | arghelper1 = ArgHelper(self.logger) | |
721 | 716 | arghelper1.build_parser('probe') |
722 | 717 | arghelper1.parse_args(args1) |
723 | 718 | arghelper1.aggregate_delegation_info() |
728 | 723 | |
729 | 724 | # Names, input file, or names file required |
730 | 725 | args = [] |
731 | arghelper = ArgHelper(self.resolver, self.logger) | |
726 | arghelper = ArgHelper(self.logger) | |
732 | 727 | arghelper.build_parser('probe') |
733 | 728 | arghelper.parse_args(args) |
734 | 729 | with self.assertRaises(argparse.ArgumentTypeError): |
736 | 731 | |
737 | 732 | # Names file and command-line domain names are mutually exclusive |
738 | 733 | args = ['-f', '/dev/null', 'example.com'] |
739 | arghelper = ArgHelper(self.resolver, self.logger) | |
734 | arghelper = ArgHelper(self.logger) | |
740 | 735 | arghelper.build_parser('probe') |
741 | 736 | arghelper.parse_args(args) |
742 | 737 | with self.assertRaises(argparse.ArgumentTypeError): |
745 | 740 | |
746 | 741 | # Authoritative analysis and recursive servers |
747 | 742 | args = ['-A', '-s', '192.0.2.1', 'example.com'] |
748 | arghelper = ArgHelper(self.resolver, self.logger) | |
743 | arghelper = ArgHelper(self.logger) | |
749 | 744 | arghelper.build_parser('probe') |
750 | 745 | arghelper.parse_args(args) |
751 | 746 | with self.assertRaises(argparse.ArgumentTypeError): |
753 | 748 | |
754 | 749 | # Authoritative servers with recursive analysis |
755 | 750 | args = ['-x', 'example.com:ns1.example.com=192.0.2.1', 'example.com'] |
756 | arghelper = ArgHelper(self.resolver, self.logger) | |
751 | arghelper = ArgHelper(self.logger) | |
757 | 752 | arghelper.build_parser('probe') |
758 | 753 | arghelper.parse_args(args) |
759 | 754 | with self.assertRaises(argparse.ArgumentTypeError): |
761 | 756 | |
762 | 757 | # Delegation information with recursive analysis |
763 | 758 | args = ['-N', 'example.com:ns1.example.com=192.0.2.1', 'example.com'] |
764 | arghelper = ArgHelper(self.resolver, self.logger) | |
759 | arghelper = ArgHelper(self.logger) | |
765 | 760 | arghelper.build_parser('probe') |
766 | 761 | arghelper.parse_args(args) |
767 | 762 | with self.assertRaises(argparse.ArgumentTypeError): |
769 | 764 | |
770 | 765 | # Delegation information with recursive analysis |
771 | 766 | args = [ '-D', 'example.com:34983 10 1 EC358CFAAEC12266EF5ACFC1FEAF2CAFF083C418', 'example.com'] |
772 | arghelper = ArgHelper(self.resolver, self.logger) | |
767 | arghelper = ArgHelper(self.logger) | |
773 | 768 | arghelper.build_parser('probe') |
774 | 769 | arghelper.parse_args(args) |
775 | 770 | with self.assertRaises(argparse.ArgumentTypeError): |
777 | 772 | |
778 | 773 | def test_ceiling(self): |
779 | 774 | args = ['-a', 'com', 'example.com'] |
780 | arghelper = ArgHelper(self.resolver, self.logger) | |
775 | arghelper = ArgHelper(self.logger) | |
781 | 776 | arghelper.build_parser('probe') |
782 | 777 | arghelper.parse_args(args) |
783 | 778 | arghelper.set_kwargs() |
784 | 779 | self.assertEqual(arghelper.ceiling, dns.name.from_text('com')) |
785 | 780 | |
786 | 781 | args = ['example.com'] |
787 | arghelper = ArgHelper(self.resolver, self.logger) | |
782 | arghelper = ArgHelper(self.logger) | |
788 | 783 | arghelper.build_parser('probe') |
789 | 784 | arghelper.parse_args(args) |
790 | 785 | arghelper.set_kwargs() |
791 | 786 | self.assertEqual(arghelper.ceiling, dns.name.root) |
792 | 787 | |
793 | 788 | args = ['-A', 'example.com'] |
794 | arghelper = ArgHelper(self.resolver, self.logger) | |
789 | arghelper = ArgHelper(self.logger) | |
795 | 790 | arghelper.build_parser('probe') |
796 | 791 | arghelper.parse_args(args) |
797 | 792 | arghelper.set_kwargs() |
799 | 794 | |
800 | 795 | def test_ip4_ipv6(self): |
801 | 796 | args = [] |
802 | arghelper = ArgHelper(self.resolver, self.logger) | |
797 | arghelper = ArgHelper(self.logger) | |
803 | 798 | arghelper.build_parser('probe') |
804 | 799 | arghelper.parse_args(args) |
805 | 800 | arghelper.set_kwargs() |
807 | 802 | self.assertEqual(arghelper.try_ipv6, True) |
808 | 803 | |
809 | 804 | args = ['-4', '-6'] |
810 | arghelper = ArgHelper(self.resolver, self.logger) | |
805 | arghelper = ArgHelper(self.logger) | |
811 | 806 | arghelper.build_parser('probe') |
812 | 807 | arghelper.parse_args(args) |
813 | 808 | arghelper.set_kwargs() |
815 | 810 | self.assertEqual(arghelper.try_ipv6, True) |
816 | 811 | |
817 | 812 | args = ['-4'] |
818 | arghelper = ArgHelper(self.resolver, self.logger) | |
813 | arghelper = ArgHelper(self.logger) | |
819 | 814 | arghelper.build_parser('probe') |
820 | 815 | arghelper.parse_args(args) |
821 | 816 | arghelper.set_kwargs() |
823 | 818 | self.assertEqual(arghelper.try_ipv6, False) |
824 | 819 | |
825 | 820 | args = ['-6'] |
826 | arghelper = ArgHelper(self.resolver, self.logger) | |
821 | arghelper = ArgHelper(self.logger) | |
827 | 822 | arghelper.build_parser('probe') |
828 | 823 | arghelper.parse_args(args) |
829 | 824 | arghelper.set_kwargs() |
832 | 827 | |
833 | 828 | def test_client_ip(self): |
834 | 829 | args = [] |
835 | arghelper = ArgHelper(self.resolver, self.logger) | |
830 | arghelper = ArgHelper(self.logger) | |
836 | 831 | arghelper.build_parser('probe') |
837 | 832 | arghelper.parse_args(args) |
838 | 833 | arghelper.set_kwargs() |
842 | 837 | args = ['-b', '127.0.0.1'] |
843 | 838 | if self.use_ipv6: |
844 | 839 | args.extend(['-b', '::1']) |
845 | arghelper = ArgHelper(self.resolver, self.logger) | |
840 | arghelper = ArgHelper(self.logger) | |
846 | 841 | arghelper.build_parser('probe') |
847 | 842 | arghelper.parse_args(args) |
848 | 843 | arghelper.set_kwargs() |
852 | 847 | |
853 | 848 | def test_th_factories(self): |
854 | 849 | args = ['example.com'] |
855 | arghelper = ArgHelper(self.resolver, self.logger) | |
850 | arghelper = ArgHelper(self.logger) | |
856 | 851 | arghelper.build_parser('probe') |
857 | 852 | arghelper.parse_args(args) |
858 | 853 | arghelper.set_kwargs() |
859 | 854 | self.assertIsNone(arghelper.th_factories) |
860 | 855 | |
861 | 856 | args = ['-u', 'http://example.com/', 'example.com'] |
862 | arghelper = ArgHelper(self.resolver, self.logger) | |
857 | arghelper = ArgHelper(self.logger) | |
863 | 858 | arghelper.build_parser('probe') |
864 | 859 | arghelper.parse_args(args) |
865 | 860 | arghelper.set_kwargs() |
866 | 861 | self.assertIsInstance(arghelper.th_factories[0], transport.DNSQueryTransportHandlerHTTPFactory) |
867 | 862 | |
868 | 863 | args = ['-u', 'ws:///dev/null', 'example.com'] |
869 | arghelper = ArgHelper(self.resolver, self.logger) | |
864 | arghelper = ArgHelper(self.logger) | |
870 | 865 | arghelper.build_parser('probe') |
871 | 866 | arghelper.parse_args(args) |
872 | 867 | arghelper.set_kwargs() |
873 | 868 | self.assertIsInstance(arghelper.th_factories[0], transport.DNSQueryTransportHandlerWebSocketServerFactory) |
874 | 869 | |
875 | 870 | args = ['-u', 'ssh://example.com/', 'example.com'] |
876 | arghelper = ArgHelper(self.resolver, self.logger) | |
871 | arghelper = ArgHelper(self.logger) | |
877 | 872 | arghelper.build_parser('probe') |
878 | 873 | arghelper.parse_args(args) |
879 | 874 | arghelper.set_kwargs() |
884 | 879 | |
885 | 880 | # None |
886 | 881 | args = ['-c', '', 'example.com'] |
887 | arghelper = ArgHelper(self.resolver, self.logger) | |
882 | arghelper = ArgHelper(self.logger) | |
888 | 883 | arghelper.build_parser('probe') |
889 | 884 | arghelper.parse_args(args) |
890 | 885 | arghelper.set_kwargs() |
894 | 889 | |
895 | 890 | # Only DNS cookie |
896 | 891 | args = ['example.com'] |
897 | arghelper = ArgHelper(self.resolver, self.logger) | |
892 | arghelper = ArgHelper(self.logger) | |
898 | 893 | arghelper.build_parser('probe') |
899 | 894 | arghelper.parse_args(args) |
900 | 895 | arghelper.set_kwargs() |
904 | 899 | |
905 | 900 | # All EDNS options |
906 | 901 | args = ['-n', '-e', '192.0.2.0/24', 'example.com'] |
907 | arghelper = ArgHelper(self.resolver, self.logger) | |
902 | arghelper = ArgHelper(self.logger) | |
908 | 903 | arghelper.build_parser('probe') |
909 | 904 | arghelper.parse_args(args) |
910 | 905 | arghelper.set_kwargs() |
931 | 926 | |
932 | 927 | try: |
933 | 928 | args = ['-r', example_auth_out.name] |
934 | arghelper = ArgHelper(self.resolver, self.logger) | |
929 | arghelper = ArgHelper(self.logger) | |
935 | 930 | arghelper.build_parser('probe') |
936 | 931 | arghelper.parse_args(args) |
937 | 932 | arghelper.ingest_input() |
938 | 933 | |
939 | 934 | # Bad json |
940 | 935 | args = ['-r', example_bad_json.name] |
941 | arghelper = ArgHelper(self.resolver, self.logger) | |
936 | arghelper = ArgHelper(self.logger) | |
942 | 937 | arghelper.build_parser('probe') |
943 | 938 | arghelper.parse_args(args) |
944 | 939 | with self.assertRaises(AnalysisInputError): |
946 | 941 | |
947 | 942 | # No version |
948 | 943 | args = ['-r', example_no_version.name] |
949 | arghelper = ArgHelper(self.resolver, self.logger) | |
944 | arghelper = ArgHelper(self.logger) | |
950 | 945 | arghelper.build_parser('probe') |
951 | 946 | arghelper.parse_args(args) |
952 | 947 | with self.assertRaises(AnalysisInputError): |
954 | 949 | |
955 | 950 | # Invalid version |
956 | 951 | args = ['-r', example_invalid_version_1.name] |
957 | arghelper = ArgHelper(self.resolver, self.logger) | |
952 | arghelper = ArgHelper(self.logger) | |
958 | 953 | arghelper.build_parser('probe') |
959 | 954 | arghelper.parse_args(args) |
960 | 955 | with self.assertRaises(AnalysisInputError): |
962 | 957 | |
963 | 958 | # Invalid version |
964 | 959 | args = ['-r', example_invalid_version_2.name] |
965 | arghelper = ArgHelper(self.resolver, self.logger) | |
960 | arghelper = ArgHelper(self.logger) | |
966 | 961 | arghelper.build_parser('probe') |
967 | 962 | arghelper.parse_args(args) |
968 | 963 | with self.assertRaises(AnalysisInputError): |
975 | 970 | |
976 | 971 | def test_ingest_names(self): |
977 | 972 | args = ['example.com', 'example.net'] |
978 | arghelper = ArgHelper(self.resolver, self.logger) | |
973 | arghelper = ArgHelper(self.logger) | |
979 | 974 | arghelper.build_parser('probe') |
980 | 975 | arghelper.parse_args(args) |
981 | 976 | arghelper.ingest_names() |
984 | 979 | unicode_name = 'ใในใ' |
985 | 980 | |
986 | 981 | args = [unicode_name] |
987 | arghelper = ArgHelper(self.resolver, self.logger) | |
982 | arghelper = ArgHelper(self.logger) | |
988 | 983 | arghelper.build_parser('probe') |
989 | 984 | arghelper.parse_args(args) |
990 | 985 | arghelper.ingest_names() |
1005 | 1000 | |
1006 | 1001 | try: |
1007 | 1002 | args = ['-f', names_file.name] |
1008 | arghelper = ArgHelper(self.resolver, self.logger) | |
1003 | arghelper = ArgHelper(self.logger) | |
1009 | 1004 | arghelper.build_parser('probe') |
1010 | 1005 | arghelper.parse_args(args) |
1011 | 1006 | arghelper.ingest_names() |
1012 | 1007 | self.assertEqual(list(arghelper.names), [dns.name.from_text('example.com'), dns.name.from_text('example.net')]) |
1013 | 1008 | |
1014 | 1009 | args = ['-f', names_file_unicode.name] |
1015 | arghelper = ArgHelper(self.resolver, self.logger) | |
1010 | arghelper = ArgHelper(self.logger) | |
1016 | 1011 | arghelper.build_parser('probe') |
1017 | 1012 | arghelper.parse_args(args) |
1018 | 1013 | arghelper.ingest_names() |
1019 | 1014 | self.assertEqual(list(arghelper.names), [dns.name.from_text('xn--zckzah.')]) |
1020 | 1015 | |
1021 | 1016 | args = ['-r', example_names_only.name] |
1022 | arghelper = ArgHelper(self.resolver, self.logger) | |
1017 | arghelper = ArgHelper(self.logger) | |
1023 | 1018 | arghelper.build_parser('probe') |
1024 | 1019 | arghelper.parse_args(args) |
1025 | 1020 | arghelper.ingest_input() |
1027 | 1022 | self.assertEqual(list(arghelper.names), [dns.name.from_text('example.com'), dns.name.from_text('example.net'), dns.name.from_text('example.org')]) |
1028 | 1023 | |
1029 | 1024 | args = ['-r', example_names_only.name, 'example.com'] |
1030 | arghelper = ArgHelper(self.resolver, self.logger) | |
1025 | arghelper = ArgHelper(self.logger) | |
1031 | 1026 | arghelper.build_parser('probe') |
1032 | 1027 | arghelper.parse_args(args) |
1033 | 1028 | arghelper.ingest_input() |