pax_global_header00006660000000000000000000000064134030117440014507gustar00rootroot0000000000000052 comment=1d0f64c9bc483ae1bd2c583e1fa096250f0cde0b dnspython-1.16.0/000077500000000000000000000000001340301174400136225ustar00rootroot00000000000000dnspython-1.16.0/.coverage.ini000066400000000000000000000000411340301174400161670ustar00rootroot00000000000000[run] branch = True source = dns dnspython-1.16.0/.gitignore000066400000000000000000000001521340301174400156100ustar00rootroot00000000000000build dist MANIFEST html html.zip html.tar.gz tests/*.out *.pyc .coverage .tox dnspython.egg-info/ .eggs/ dnspython-1.16.0/.travis.yml000066400000000000000000000006131340301174400157330ustar00rootroot00000000000000language: python python: - "2.7" - "3.4" - "3.5" - "3.6" #- "nightly" matrix: include: - python: 3.7 # https://github.com/travis-ci/travis-ci/issues/9815 dist: xenial sudo: true allow_failures: - python: nightly branches: except: - python3 install: - pip install typing pylint pycryptodome ecdsa idna script: - make typecheck - make lint - make test dnspython-1.16.0/LICENSE000066400000000000000000000027661340301174400146420ustar00rootroot00000000000000ISC License Copyright (C) Dnspython Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Copyright (C) 2001-2017 Nominum, Inc. Copyright (C) Google Inc. Permission to use, copy, modify, and distribute this software and its documentation for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. dnspython-1.16.0/MANIFEST.in000066400000000000000000000002051340301174400153550ustar00rootroot00000000000000include LICENSE ChangeLog README.md recursive-include examples *.txt *.py recursive-include tests *.txt *.py Makefile *.good example dnspython-1.16.0/Makefile000066400000000000000000000040071340301174400152630ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # $Id: Makefile,v 1.16 2004/03/19 00:17:27 halley Exp $ PYTHON=python PYTHON3=python3 all: ${PYTHON} ./setup.py build install: ${PYTHON} ./setup.py install clean: ${PYTHON} ./setup.py clean --all find . -name '*.pyc' -exec rm {} \; find . -name '*.pyo' -exec rm {} \; rm -f TAGS distclean: clean docclean rm -rf build dist rm -f MANIFEST doco: epydoc -v -n dnspython -u http://www.dnspython.org \ dns/*.py dns/rdtypes/*.py dns/rdtypes/ANY/*.py \ dns/rdtypes/CH/*.py \ dns/rdtypes/IN/*.py dockits: doco mv html dnspython-html tar czf html.tar.gz dnspython-html zip -r html.zip dnspython-html mv dnspython-html html docclean: rm -rf html.tar.gz html.zip html kits: ${PYTHON3} ./setup.py sdist --formats=gztar,zip bdist_wheel tags: find . -name '*.py' -print | etags - check: test test: cd tests; make PYTHON=${PYTHON} test test2: cd tests; make PYTHON=python test test3: cd tests; make PYTHON=${PYTHON3} test lint: pylint dns tests examples/*.py lint3: pylint3 dns tests examples/*.py typecheck: if [ $(shell python -c "import sys; print(sys.version_info[0])") -ne 2 ]; then pip install mypy; mypy examples tests; else echo Skipping typecheck on Python 2; fi dnspython-1.16.0/README.md000066400000000000000000000504771340301174400151160ustar00rootroot00000000000000# dnspython [![Build Status](https://travis-ci.org/rthalley/dnspython.svg?branch=master)](https://travis-ci.org/rthalley/dnspython) ## INTRODUCTION dnspython is a DNS toolkit for Python. It supports almost all record types. It can be used for queries, zone transfers, and dynamic updates. It supports TSIG authenticated messages and EDNS0. dnspython provides both high and low level access to DNS. The high level classes perform queries for data of a given name, type, and class, and return an answer set. The low level classes allow direct manipulation of DNS zones, messages, names, and records. To see a few of the ways dnspython can be used, look in the `examples/` directory. dnspython is a utility to work with DNS, `/etc/hosts` is thus not used. For simple forward DNS lookups, it's better to use `socket.gethostbyname()`. dnspython originated at Nominum where it was developed to facilitate the testing of DNS software. ## INSTALLATION * Many distributions have dnspython packaged for you, so you should check there first. * If you have pip installed, you can do `pip install dnspython` * If not just download the source file and unzip it, then run `sudo python setup.py install` ## ABOUT THIS RELEASE This is dnspython 1.16.0 ### Notices Python 2.x support ends with the release of 1.16.0, unless there are critical bugs in 1.16.0. Future versions of dnspython will only support Python 3. Version numbering of future dnspython releases will also start at 2.0, as incompatible changes will be permitted. We're not planning huge changes at this time, but we'd like to do a better job at IDNA, and there are other API improvements to be made. The ChangeLog has been discontinued. Please see the git history for detailed change information. ### New since 1.15.0: * Much of the internals of dns.query.udp() and dns.query.tcp() have been factored out into dns.query.send_udp(), dns.query.receive_udp(), dns.query.send_tcp(), and dns.query.receive_tcp(). Applications which want more control over the socket may find the new routines helpful; for example it would be easy to send multiple queries over a single TCP connection. * The OPENPGPKEY RR, and the CHAOS class A RR are now supported. * EDNS0 client-subnet is supported. * dns.resover.query() now has a lifetime timeout optional parameter. * pycryptodome and pycryptodomex are now supported and recommended for use instead of pycrypto. * dns.message.from_wire() now has an ignore_trailing option. * type signatures have been provided. * module dns.hash is now deprecated, use standard Python libraries instead. * setup.py supports Cythonization to improve performance. ### Bugs fixed since 1.15.0: * DNSSEC signature validation didn't check names correctly. [Issue #295] * The NXDOMAIN exception should not use its docstring. [Issue #253] * Fixed regression where trailing zeros in APL RRs were not suppressed, and then fixed the problem where trailing zeros were not added back properly on python 3 when needed. * Masterfile TTL defaulting is now harmonized with BIND practice. * dns.query.xfr() now raises on a non-zero rcode. * Rdata module importing is now locked to avoid races. * Several Python 3 incompatibilities have been fixed. * NSEC3 bitmap parsing now works with mulitple NSEC3 windows. * dns.renderer.Render supports TSIG on DNS envelope sequences. * DNSSEC validation now checks names properly [Issue #295] ### New since 1.14.0: * IDNA 2008 support is now available if the "idna" module has been installed and IDNA 2008 is requested. The default IDNA behavior is still IDNA 2003. The new IDNA codec mechanism is currently only useful for direct calls to dns.name.from_text() or dns.name.from_unicode(), but in future releases it will be deployed throughout dnspython, e.g. so that you can read a masterfile with an IDNA 2008 codec in force. * By default, dns.name.to_unicode() is not strict about which version of IDNA the input complies with. Strictness can be requested by using one of the strict IDNA codecs. * The AVC RR is now supported. ### Bugs fixed since 1.14.0: * Some problems with newlines in various output modes have been addressed. * dns.name.to_text() now returns text and not bytes on Python 3.x * Miscellaneous fixes for the Python 2/3 codeline merge. * Many "lint" fixes after the addition of pylint support. * The random number generator reseeds after a fork(). ## REQUIREMENTS Python 2.7 or 3.4+. ## HOME PAGE For the latest in releases, documentation, and information, visit the dnspython home page at http://www.dnspython.org/ ## BUG REPORTS Bug reports may be opened at https://github.com/rthalley/dnspython/issues or sent to bugs@dnspython.org ## MAILING LISTS A number of mailing lists are available. Visit the dnspython home page to subscribe or unsubscribe. ## PRIOR RELEASE INFORMATION ### New since 1.13.0: * CSYNC RRs are now supported. * `dns/message.py` (`make_query`): Setting any value which implies EDNS will turn on EDNS if `use_edns` has not been specified. ### Bugs fixed since 1.13.0: * TSIG signature algorithm setting was broken by the Python 2 and Python 3 code line merge. * A bug in the LOC RR destroyed N/S and E/W distinctions within a degree of the equator or prime merdian respectively. * Misc. fixes to deal with fallout from the Python 2 & 3 merge. Fixes #156, #157, #158, #159, #160. * Running with python optimization on caused issues when stripped docstrings were referenced. Fixes #154 * `dns.zone.from_text()` erroneously required the zone to be provided. Fixes #153 ### New since 1.12.0: * Dnspython now uses a single source for Python 2 and Python 3, eliminating the painful merging between the Python 2 and Python 3 branches. Thank you so much to Arthur Gautier for taking on this challenge and making it work! It was a big job! * Support for Python older than 2.6 dropped. * Support for Python older than 3.3 dropped. * Zone origin can be specified as a string. * A rich string representation for all DNSExceptions. * setuptools has replaced distutils * Added support for CAA, CDS, CDNSKEY, EUI48, EUI64, and URI RR types. * Names now support the pickle protocol. * Ports can be specified per-nameserver in the stub resolver. ### Bugs fixed since 1.12.0: * A number of Unicode name bugs have been fixed. * `resolv.conf` processing now rejects lines with too few tokens. * NameDicts now keep the max-depth value correct, and update properly. ### New since 1.11.1: * Added `dns.zone.to_text()`. * Added support for "options rotate" in `/etc/resolv.conf`. * `dns.rdtypes.ANY.DNSKEY` now has helpers functions to convert between the numeric form of the flags and a set of human-friendly strings * The reverse name of an IPv6 mapped IPv4 address is now in the IPv4 reverse namespace. * The test system can now run the tests without requiring dnspython to be installed. * Preliminary Elliptic Curve DNSSEC Validation (requires ecdsa module) ### Bugs fixed since 1.11.1: * dnspython raised an exception when reading a masterfile starting with leading whitespace * dnspython was affected by a python slicing API bug present on 64-bit windows. * Unicode escaping was applied at the wrong time. * RRSIG `to_text()` did not respect the relativize setting. * APL RRs with zero rdlength were rejected. * The tokenizer could put back an unescaped token. * Making a response to a message signed with TSIG was broken. * The IXFR state machine didn't handle long IXFR diffs. ### New since 1.11.0: * Nothing ### Bugs fixed since 1.11.0: * `dns.resolver.Resolver` erroneously referred to `retry_servfail` instead of `self.retry_servfail`. * `dns.tsigkeyring.to_text()` would fail trying to convert the keyname to text. * Multi-message TSIGs were broken for algorithms other than HMAC-MD5 because we weren't passing the right digest module to the HMAC code. * `dns.dnssec._find_candidate_keys()` tried to extract the key from the wrong variable name. * $GENERATE tests were not backward compatible with python 2.4. ### New since 1.10.0: * $GENERATE support * TLSA RR support * Added set_flags() method to dns.resolver.Resolver ### Bugs fixed since 1.10.0: * Names with offsets >= 2^14 are no longer added to the compression table. * The "::" syntax is not used to shorten a single 16-bit section of the text form an IPv6 address. * Caches are now locked. * YXDOMAIN is raised if seen by the resolver. * Empty rdatasets are not printed. * DNSKEY key tags are no longer assumed to be unique. ### New since 1.9.4: * Added dns.resolver.LRUCache. In this cache implementation, the cache size is limited to a user-specified number of nodes, and when adding a new node to a full cache the least-recently used node is removed. If you're crawling the web or otherwise doing lots of resolutions and you are using a cache, switching to the LRUCache is recommended. * `dns.resolver.query()` will try TCP if a UDP response is truncated. * The python socket module's DNS methods can be now be overridden with implementations that use dnspython's resolver. * Old DNSSEC types KEY, NXT, and SIG have been removed. * Whitespace is allowed in SSHFP fingerprints. * Origin checking in `dns.zone.from_xfr()` can be disabled. * Trailing junk checking can be disabled. * A source port can be specified when creating a resolver query. * All EDNS values may now be specified to `dns.message.make_query()`. ### Bugs fixed since 1.9.4: * IPv4 and IPv6 address processing is now stricter. * Bounds checking of slices in rdata wire processing is now more strict, and bounds errors (e.g. we got less data than was expected) now raise `dns.exception.FormError` rather than `IndexError`. * Specifying a source port without specifying source used to have no effect, but now uses the wildcard address and the specified port. ### New since 1.9.3: * Nothing. ### Bugs fixed since 1.9.3: * The rdata `_wire_cmp()` routine now handles relative names. * The SIG RR implementation was missing `import struct`. ### New since 1.9.2: * A boolean parameter, `raise_on_no_answer`, has been added to the `query()` methods. In no-error, no-data situations, this parameter determines whether `NoAnswer` should be raised or not. If True, `NoAnswer` is raised. If False, then an `Answer()` object with a None rrset will be returned. * Resolver `Answer()` objects now have a canonical_name field. * Rdata now has a `__hash__` method. ### Bugs fixed since 1.9.2: * Dnspython was erroneously doing case-insensitive comparisons of the names in NSEC and RRSIG RRs. * We now use `is` and not `==` when testing what section an RR is in. * The resolver now disallows metaqueries. ### New since 1.9.1: * Nothing. ### Bugs fixed since 1.9.1: * The `dns.dnssec` module didn't work at all due to missing imports that escaped detection in testing because the test suite also did the imports. The third time is the charm! ### New since 1.9.0: * Nothing. ### Bugs fixed since 1.9.0: * The `dns.dnssec` module didn't work with DSA due to namespace contamination from a "from"-style import. ### New since 1.8.0: * dnspython now uses `poll()` instead of `select()` when available. * Basic DNSSEC validation can be done using `dns.dnsec.validate()` and `dns.dnssec.validate_rrsig()` if you have PyCrypto 2.3 or later installed. Complete secure resolution is not yet available. * Added `key_id()` to the DNSSEC module, which computes the DNSSEC key id of a DNSKEY rdata. * Added `make_ds()` to the DNSSEC module, which returns the DS RR for a given DNSKEY rdata. * dnspython now raises an exception if HMAC-SHA284 or HMAC-SHA512 are used with a Python older than 2.5.2. (Older Pythons do not compute the correct value.) * Symbolic constants are now available for TSIG algorithm names. ### Bugs fixed since 1.8.0 * `dns.resolver.zone_for_name()` didn't handle a query response with a CNAME or DNAME correctly in some cases. * When specifying rdata types and classes as text, Unicode strings may now be used. * Hashlib compatibility issues have been fixed. * `dns.message` now imports `dns.edns`. * The TSIG algorithm value was passed incorrectly to `use_tsig()` in some cases. ### New since 1.7.1: * Support for hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384 and hmac-sha512 has been contributed by Kevin Chen. * The tokenizer's tokens are now Token objects instead of (type, value) tuples. ### Bugs fixed since 1.7.1: * Escapes in masterfiles now work correctly. Previously they were only working correctly when the text involved was part of a domain name. * When constructing a DDNS update, if the `present()` method was used with a single rdata, a zero TTL was not added. * The entropy pool needed locking to be thread safe. * The entropy pool's reading of `/dev/random` could cause dnspython to block. * The entropy pool did buffered reads, potentially consuming more randomness than we needed. * The entropy pool did not seed with high quality randomness on Windows. * SRV records were compared incorrectly. * In the e164 query function, the resolver parameter was not used. ### New since 1.7.0: * Nothing ### Bugs fixed since 1.7.0: * The 1.7.0 kitting process inadvertently omitted the code for the DLV RR. * Negative DDNS prerequisites are now handled correctly. ### New since 1.6.0: * Rdatas now have a `to_digestable()` method, which returns the DNSSEC canonical form of the rdata, suitable for use in signature computations. * The NSEC3, NSEC3PARAM, DLV, and HIP RR types are now supported. * An entropy module has been added and is used to randomize query ids. * EDNS0 options are now supported. * UDP IXFR is now supported. * The wire format parser now has a `one_rr_per_rrset` mode, which suppresses the usual coalescing of all RRs of a given type into a single RRset. * Various helpful DNSSEC-related constants are now defined. * The resolver's `query()` method now has an optional `source` parameter, allowing the source IP address to be specified. ### Bugs fixed since 1.6.0: * On Windows, the resolver set the domain incorrectly. * DS RR parsing only allowed one Base64 chunk. * TSIG validation didn't always use absolute names. * `NSEC.to_text()` only printed the last window. * We did not canonicalize IPv6 addresses before comparing them; we would thus treat equivalent but different textual forms, e.g. "1:00::1" and "1::1" as being non-equivalent. * If the peer set a TSIG error, we didn't raise an exception. * Some EDNS bugs in the message code have been fixed (see the ChangeLog for details). ### New since 1.5.0: * Added dns.inet.is_multicast(). ### Bugs fixed since 1.5.0: * If `select()` raises an exception due to EINTR, we should just `select()` again. * If the queried address is a multicast address, then don't check that the address of the response is the same as the address queried. * NAPTR comparisons didn't compare the preference field due to a typo. * Testing of whether a Windows NIC is enabled now works on Vista thanks to code contributed by Paul Marks. ### New since 1.4.0: * Answer objects now support more of the python sequence protocol, forwarding the requests to the answer rrset. E.g. `for a in answer` is equivalent to `for a in answer.rrset`, `answer[i]` is equivalent to `answer.rrset[i]`, and `answer[i:j]` is equivalent to `answer.rrset[i:j]`. * Making requests using EDNS, including indicating DNSSEC awareness, is now easier. For example, you can now say: `q = dns.message.make_query('www.dnspython.org', 'MX', want_dnssec=True)` * `dns.query.xfr()` can now be used for IXFR. * Support has been added for the DHCID, IPSECKEY, and SPF RR types. * UDP messages from unexpected sources can now be ignored by setting `ignore_unexpected` to True when calling `dns.query.udp`. ### Bugs fixed since 1.4.0: * If `/etc/resolv.conf` didn't exist, we raised an exception instead of simply using the default resolver configuration. * In `dns.resolver.Resolver._config_win32_fromkey()`, we were passing the wrong variable to `self._config_win32_search()`. ### New since 1.3.5: * You can now convert E.164 numbers to/from their ENUM name forms: ```python >>> import dns.e164 >>> n = dns.e164.from_e164("+1 555 1212") >>> n >>> dns.e164.to_e164(n) '+15551212' ``` * You can now convert IPv4 and IPv6 address to/from their corresponding DNS reverse map names: ```python >>> import dns.reversename >>> n = dns.reversename.from_address("127.0.0.1") >>> n >>> dns.reversename.to_address(n) '127.0.0.1' ``` * You can now convert between Unicode strings and their IDN ACE form: ```python >>> n = dns.name.from_text(u'les-\u00e9l\u00e8ves.example.') >>> n >>> n.to_unicode() u'les-\xe9l\xe8ves.example.' ``` * The origin parameter to `dns.zone.from_text()` and `dns.zone.to_text()` is now optional. If not specified, the origin will be taken from the first $ORIGIN statement in the master file. * Sanity checking of a zone can be disabled; this is useful when working with files which are zone fragments. ### Bugs fixed since 1.3.5: * The correct delimiter was not used when retrieving the list of nameservers from the registry in certain versions of windows. * The floating-point version of latitude and longitude in LOC RRs (`float_latitude` and `float_longitude`) had incorrect signs for south latitudes and west longitudes. * BIND 8 TTL syntax is now accepted in all TTL-like places (i.e. SOA fields refresh, retry, expire, and minimum; SIG/RRSIG field original_ttl). * TTLs are now bounds checked when their text form is parsed, and their values must be in the closed interval `[0, 2^31 - 1]`. ### New since 1.3.4: * In the resolver, if time goes backward a little bit, ignore it. * `zone_for_name()` has been added to the resolver module. It returns the zone which is authoritative for the specified name, which is handy for dynamic update. E.g. import dns.resolver print dns.resolver.zone_for_name('www.dnspython.org') will output `"dnspython.org."` and `print dns.resolver.zone_for_name('a.b.c.d.e.f.example.')` will output `"."`. * The default resolver can be fetched with the `get_default_resolver()` method. * You can now get the parent (immediate superdomain) of a name by using the `parent()` method. * `Zone.iterate_rdatasets()` and `Zone.iterate_rdatas()` now have a default rdtype of `dns.rdatatype.ANY` like the documentation says. * A Dynamic DNS example, ddns.py, has been added. ### New since 1.3.3: * The source address and port may now be specified when calling `dns.query.{udp,tcp,xfr}`. * The resolver now does exponential backoff each time it runs through all of the nameservers. * Rcodes which indicate a nameserver is likely to be a "permanent failure" for a query cause the nameserver to be removed from the mix for that query. ### New since 1.3.2: * `dns.message.Message.find_rrset()` now uses an index, vastly improving the `from_wire()` performance of large messages such as zone transfers. * Added `dns.message.make_response()`, which creates a skeletal response for the specified query. * Added `opcode()` and `set_opcode()` convenience methods to the `dns.message.Message` class. Added the `request_payload` attribute to the Message class. * The `file` parameter of `dns.name.Name.to_wire()` is now optional; if omitted, the wire form will be returned as the value of the function. * `dns.zone.from_xfr()` in relativization mode incorrectly set `zone.origin` to the empty name. * The masterfile parser incorrectly rejected TXT records where a value was not quoted. ### New since 1.3.1: * The NSEC format doesn't allow specifying types by number, so we shouldn't either. (Using the unknown type format is still OK though.) * The resolver wasn't catching `dns.exception.Timeout`, so a timeout erroneously caused the whole resolution to fail instead of just going on to the next server. * The renderer module didn't import random, causing an exception to be raised if a query id wasn't provided when a Renderer was created. * The conversion of LOC milliseconds values from text to binary was incorrect if the length of the milliseconds string was not 3. ### New since 1.3.0: * Added support for the SSHFP type. ### New since 1.2.0: * Added support for new DNSSEC types RRSIG, NSEC, and DNSKEY. * This release fixes all known bugs. * See the ChangeLog file for more detailed information on changes since the prior release. dnspython-1.16.0/dns/000077500000000000000000000000001340301174400144065ustar00rootroot00000000000000dnspython-1.16.0/dns/__init__.py000066400000000000000000000025741340301174400165270ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """dnspython DNS toolkit""" __all__ = [ 'dnssec', 'e164', 'edns', 'entropy', 'exception', 'flags', 'hash', 'inet', 'ipv4', 'ipv6', 'message', 'name', 'namedict', 'node', 'opcode', 'query', 'rcode', 'rdata', 'rdataclass', 'rdataset', 'rdatatype', 'renderer', 'resolver', 'reversename', 'rrset', 'set', 'tokenizer', 'tsig', 'tsigkeyring', 'ttl', 'rdtypes', 'update', 'version', 'wiredata', 'zone', ] dnspython-1.16.0/dns/_compat.py000066400000000000000000000027371340301174400164130ustar00rootroot00000000000000import sys import decimal from decimal import Context PY3 = sys.version_info[0] == 3 PY2 = sys.version_info[0] == 2 if PY3: long = int xrange = range else: long = long # pylint: disable=long-builtin xrange = xrange # pylint: disable=xrange-builtin # unicode / binary types if PY3: text_type = str binary_type = bytes string_types = (str,) unichr = chr def maybe_decode(x): return x.decode() def maybe_encode(x): return x.encode() def maybe_chr(x): return x def maybe_ord(x): return x else: text_type = unicode # pylint: disable=unicode-builtin, undefined-variable binary_type = str string_types = ( basestring, # pylint: disable=basestring-builtin, undefined-variable ) unichr = unichr # pylint: disable=unichr-builtin def maybe_decode(x): return x def maybe_encode(x): return x def maybe_chr(x): return chr(x) def maybe_ord(x): return ord(x) def round_py2_compat(what): """ Python 2 and Python 3 use different rounding strategies in round(). This function ensures that results are python2/3 compatible and backward compatible with previous py2 releases :param what: float :return: rounded long """ d = Context( prec=len(str(long(what))), # round to integer with max precision rounding=decimal.ROUND_HALF_UP ).create_decimal(str(what)) # str(): python 2.6 compat return long(d) dnspython-1.16.0/dns/dnssec.py000066400000000000000000000401701340301174400162410ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Common DNSSEC-related functions and constants.""" from io import BytesIO import struct import time import dns.exception import dns.name import dns.node import dns.rdataset import dns.rdata import dns.rdatatype import dns.rdataclass from ._compat import string_types class UnsupportedAlgorithm(dns.exception.DNSException): """The DNSSEC algorithm is not supported.""" class ValidationFailure(dns.exception.DNSException): """The DNSSEC signature is invalid.""" #: RSAMD5 RSAMD5 = 1 #: DH DH = 2 #: DSA DSA = 3 #: ECC ECC = 4 #: RSASHA1 RSASHA1 = 5 #: DSANSEC3SHA1 DSANSEC3SHA1 = 6 #: RSASHA1NSEC3SHA1 RSASHA1NSEC3SHA1 = 7 #: RSASHA256 RSASHA256 = 8 #: RSASHA512 RSASHA512 = 10 #: ECDSAP256SHA256 ECDSAP256SHA256 = 13 #: ECDSAP384SHA384 ECDSAP384SHA384 = 14 #: INDIRECT INDIRECT = 252 #: PRIVATEDNS PRIVATEDNS = 253 #: PRIVATEOID PRIVATEOID = 254 _algorithm_by_text = { 'RSAMD5': RSAMD5, 'DH': DH, 'DSA': DSA, 'ECC': ECC, 'RSASHA1': RSASHA1, 'DSANSEC3SHA1': DSANSEC3SHA1, 'RSASHA1NSEC3SHA1': RSASHA1NSEC3SHA1, 'RSASHA256': RSASHA256, 'RSASHA512': RSASHA512, 'INDIRECT': INDIRECT, 'ECDSAP256SHA256': ECDSAP256SHA256, 'ECDSAP384SHA384': ECDSAP384SHA384, 'PRIVATEDNS': PRIVATEDNS, 'PRIVATEOID': PRIVATEOID, } # We construct the inverse mapping programmatically to ensure that we # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that # would cause the mapping not to be true inverse. _algorithm_by_value = {y: x for x, y in _algorithm_by_text.items()} def algorithm_from_text(text): """Convert text into a DNSSEC algorithm value. Returns an ``int``. """ value = _algorithm_by_text.get(text.upper()) if value is None: value = int(text) return value def algorithm_to_text(value): """Convert a DNSSEC algorithm value to text Returns a ``str``. """ text = _algorithm_by_value.get(value) if text is None: text = str(value) return text def _to_rdata(record, origin): s = BytesIO() record.to_wire(s, origin=origin) return s.getvalue() def key_id(key, origin=None): """Return the key id (a 16-bit number) for the specified key. Note the *origin* parameter of this function is historical and is not needed. Returns an ``int`` between 0 and 65535. """ rdata = _to_rdata(key, origin) rdata = bytearray(rdata) if key.algorithm == RSAMD5: return (rdata[-3] << 8) + rdata[-2] else: total = 0 for i in range(len(rdata) // 2): total += (rdata[2 * i] << 8) + \ rdata[2 * i + 1] if len(rdata) % 2 != 0: total += rdata[len(rdata) - 1] << 8 total += ((total >> 16) & 0xffff) return total & 0xffff def make_ds(name, key, algorithm, origin=None): """Create a DS record for a DNSSEC key. *name* is the owner name of the DS record. *key* is a ``dns.rdtypes.ANY.DNSKEY``. *algorithm* is a string describing which hash algorithm to use. The currently supported hashes are "SHA1" and "SHA256". Case does not matter for these strings. *origin* is a ``dns.name.Name`` and will be used as the origin if *key* is a relative name. Returns a ``dns.rdtypes.ANY.DS``. """ if algorithm.upper() == 'SHA1': dsalg = 1 hash = SHA1.new() elif algorithm.upper() == 'SHA256': dsalg = 2 hash = SHA256.new() else: raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm) if isinstance(name, string_types): name = dns.name.from_text(name, origin) hash.update(name.canonicalize().to_wire()) hash.update(_to_rdata(key, origin)) digest = hash.digest() dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, dsalg) + digest return dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, len(dsrdata)) def _find_candidate_keys(keys, rrsig): candidate_keys = [] value = keys.get(rrsig.signer) if value is None: return None if isinstance(value, dns.node.Node): try: rdataset = value.find_rdataset(dns.rdataclass.IN, dns.rdatatype.DNSKEY) except KeyError: return None else: rdataset = value for rdata in rdataset: if rdata.algorithm == rrsig.algorithm and \ key_id(rdata) == rrsig.key_tag: candidate_keys.append(rdata) return candidate_keys def _is_rsa(algorithm): return algorithm in (RSAMD5, RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512) def _is_dsa(algorithm): return algorithm in (DSA, DSANSEC3SHA1) def _is_ecdsa(algorithm): return _have_ecdsa and (algorithm in (ECDSAP256SHA256, ECDSAP384SHA384)) def _is_md5(algorithm): return algorithm == RSAMD5 def _is_sha1(algorithm): return algorithm in (DSA, RSASHA1, DSANSEC3SHA1, RSASHA1NSEC3SHA1) def _is_sha256(algorithm): return algorithm in (RSASHA256, ECDSAP256SHA256) def _is_sha384(algorithm): return algorithm == ECDSAP384SHA384 def _is_sha512(algorithm): return algorithm == RSASHA512 def _make_hash(algorithm): if _is_md5(algorithm): return MD5.new() if _is_sha1(algorithm): return SHA1.new() if _is_sha256(algorithm): return SHA256.new() if _is_sha384(algorithm): return SHA384.new() if _is_sha512(algorithm): return SHA512.new() raise ValidationFailure('unknown hash for algorithm %u' % algorithm) def _make_algorithm_id(algorithm): if _is_md5(algorithm): oid = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05] elif _is_sha1(algorithm): oid = [0x2b, 0x0e, 0x03, 0x02, 0x1a] elif _is_sha256(algorithm): oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01] elif _is_sha512(algorithm): oid = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03] else: raise ValidationFailure('unknown algorithm %u' % algorithm) olen = len(oid) dlen = _make_hash(algorithm).digest_size idbytes = [0x30] + [8 + olen + dlen] + \ [0x30, olen + 4] + [0x06, olen] + oid + \ [0x05, 0x00] + [0x04, dlen] return struct.pack('!%dB' % len(idbytes), *idbytes) def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None): """Validate an RRset against a single signature rdata The owner name of *rrsig* is assumed to be the same as the owner name of *rrset*. *rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple. *rrsig* is a ``dns.rdata.Rdata``, the signature to validate. *keys* is the key dictionary, used to find the DNSKEY associated with a given name. The dictionary is keyed by a ``dns.name.Name``, and has ``dns.node.Node`` or ``dns.rdataset.Rdataset`` values. *origin* is a ``dns.name.Name``, the origin to use for relative names. *now* is an ``int``, the time to use when validating the signatures, in seconds since the UNIX epoch. The default is the current time. """ if isinstance(origin, string_types): origin = dns.name.from_text(origin, dns.name.root) candidate_keys = _find_candidate_keys(keys, rrsig) if candidate_keys is None: raise ValidationFailure('unknown key') for candidate_key in candidate_keys: # For convenience, allow the rrset to be specified as a (name, # rdataset) tuple as well as a proper rrset if isinstance(rrset, tuple): rrname = rrset[0] rdataset = rrset[1] else: rrname = rrset.name rdataset = rrset if now is None: now = time.time() if rrsig.expiration < now: raise ValidationFailure('expired') if rrsig.inception > now: raise ValidationFailure('not yet valid') hash = _make_hash(rrsig.algorithm) if _is_rsa(rrsig.algorithm): keyptr = candidate_key.key (bytes_,) = struct.unpack('!B', keyptr[0:1]) keyptr = keyptr[1:] if bytes_ == 0: (bytes_,) = struct.unpack('!H', keyptr[0:2]) keyptr = keyptr[2:] rsa_e = keyptr[0:bytes_] rsa_n = keyptr[bytes_:] try: pubkey = CryptoRSA.construct( (number.bytes_to_long(rsa_n), number.bytes_to_long(rsa_e))) except ValueError: raise ValidationFailure('invalid public key') sig = rrsig.signature elif _is_dsa(rrsig.algorithm): keyptr = candidate_key.key (t,) = struct.unpack('!B', keyptr[0:1]) keyptr = keyptr[1:] octets = 64 + t * 8 dsa_q = keyptr[0:20] keyptr = keyptr[20:] dsa_p = keyptr[0:octets] keyptr = keyptr[octets:] dsa_g = keyptr[0:octets] keyptr = keyptr[octets:] dsa_y = keyptr[0:octets] pubkey = CryptoDSA.construct( (number.bytes_to_long(dsa_y), number.bytes_to_long(dsa_g), number.bytes_to_long(dsa_p), number.bytes_to_long(dsa_q))) sig = rrsig.signature[1:] elif _is_ecdsa(rrsig.algorithm): # use ecdsa for NIST-384p -- not currently supported by pycryptodome keyptr = candidate_key.key if rrsig.algorithm == ECDSAP256SHA256: curve = ecdsa.curves.NIST256p key_len = 32 elif rrsig.algorithm == ECDSAP384SHA384: curve = ecdsa.curves.NIST384p key_len = 48 x = number.bytes_to_long(keyptr[0:key_len]) y = number.bytes_to_long(keyptr[key_len:key_len * 2]) if not ecdsa.ecdsa.point_is_valid(curve.generator, x, y): raise ValidationFailure('invalid ECDSA key') point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order) verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point, curve) pubkey = ECKeyWrapper(verifying_key, key_len) r = rrsig.signature[:key_len] s = rrsig.signature[key_len:] sig = ecdsa.ecdsa.Signature(number.bytes_to_long(r), number.bytes_to_long(s)) else: raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) hash.update(_to_rdata(rrsig, origin)[:18]) hash.update(rrsig.signer.to_digestable(origin)) if rrsig.labels < len(rrname) - 1: suffix = rrname.split(rrsig.labels + 1)[1] rrname = dns.name.from_text('*', suffix) rrnamebuf = rrname.to_digestable(origin) rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass, rrsig.original_ttl) rrlist = sorted(rdataset) for rr in rrlist: hash.update(rrnamebuf) hash.update(rrfixed) rrdata = rr.to_digestable(origin) rrlen = struct.pack('!H', len(rrdata)) hash.update(rrlen) hash.update(rrdata) try: if _is_rsa(rrsig.algorithm): verifier = pkcs1_15.new(pubkey) # will raise ValueError if verify fails: verifier.verify(hash, sig) elif _is_dsa(rrsig.algorithm): verifier = DSS.new(pubkey, 'fips-186-3') verifier.verify(hash, sig) elif _is_ecdsa(rrsig.algorithm): digest = hash.digest() if not pubkey.verify(digest, sig): raise ValueError else: # Raise here for code clarity; this won't actually ever happen # since if the algorithm is really unknown we'd already have # raised an exception above raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm) # If we got here, we successfully verified so we can return without error return except ValueError: # this happens on an individual validation failure continue # nothing verified -- raise failure: raise ValidationFailure('verify failure') def _validate(rrset, rrsigset, keys, origin=None, now=None): """Validate an RRset. *rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple. *rrsigset* is the signature RRset to be validated. It can be a ``dns.rrset.RRset`` or a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple. *keys* is the key dictionary, used to find the DNSKEY associated with a given name. The dictionary is keyed by a ``dns.name.Name``, and has ``dns.node.Node`` or ``dns.rdataset.Rdataset`` values. *origin* is a ``dns.name.Name``, the origin to use for relative names. *now* is an ``int``, the time to use when validating the signatures, in seconds since the UNIX epoch. The default is the current time. """ if isinstance(origin, string_types): origin = dns.name.from_text(origin, dns.name.root) if isinstance(rrset, tuple): rrname = rrset[0] else: rrname = rrset.name if isinstance(rrsigset, tuple): rrsigname = rrsigset[0] rrsigrdataset = rrsigset[1] else: rrsigname = rrsigset.name rrsigrdataset = rrsigset rrname = rrname.choose_relativity(origin) rrsigname = rrsigname.choose_relativity(origin) if rrname != rrsigname: raise ValidationFailure("owner names do not match") for rrsig in rrsigrdataset: try: _validate_rrsig(rrset, rrsig, keys, origin, now) return except ValidationFailure: pass raise ValidationFailure("no RRSIGs validated") def _need_pycrypto(*args, **kwargs): raise NotImplementedError("DNSSEC validation requires pycryptodome/pycryptodomex") try: try: # test we're using pycryptodome, not pycrypto (which misses SHA1 for example) from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512 from Crypto.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA from Crypto.Signature import pkcs1_15, DSS from Crypto.Util import number except ImportError: from Cryptodome.Hash import MD5, SHA1, SHA256, SHA384, SHA512 from Cryptodome.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA from Cryptodome.Signature import pkcs1_15, DSS from Cryptodome.Util import number except ImportError: validate = _need_pycrypto validate_rrsig = _need_pycrypto _have_pycrypto = False _have_ecdsa = False else: validate = _validate validate_rrsig = _validate_rrsig _have_pycrypto = True try: import ecdsa import ecdsa.ecdsa import ecdsa.ellipticcurve import ecdsa.keys except ImportError: _have_ecdsa = False else: _have_ecdsa = True class ECKeyWrapper(object): def __init__(self, key, key_len): self.key = key self.key_len = key_len def verify(self, digest, sig): diglong = number.bytes_to_long(digest) return self.key.pubkey.verifies(diglong, sig) dnspython-1.16.0/dns/dnssec.pyi000066400000000000000000000016151340301174400164130ustar00rootroot00000000000000from typing import Union, Dict, Tuple, Optional from . import rdataset, rrset, exception, name, rdtypes, rdata, node import dns.rdtypes.ANY.DS as DS import dns.rdtypes.ANY.DNSKEY as DNSKEY _have_ecdsa : bool _have_pycrypto : bool def validate_rrsig(rrset : Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], rrsig : rdata.Rdata, keys : Dict[name.Name, Union[node.Node, rdataset.Rdataset]], origin : Optional[name.Name] = None, now : Optional[int] = None) -> None: ... def validate(rrset: Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], rrsigset : Union[Tuple[name.Name, rdataset.Rdataset], rrset.RRset], keys : Dict[name.Name, Union[node.Node, rdataset.Rdataset]], origin=None, now=None) -> None: ... class ValidationFailure(exception.DNSException): ... def make_ds(name : name.Name, key : DNSKEY.DNSKEY, algorithm : str, origin : Optional[name.Name] = None) -> DS.DS: ... dnspython-1.16.0/dns/e164.py000066400000000000000000000072511340301174400154440ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS E.164 helpers.""" import dns.exception import dns.name import dns.resolver from ._compat import string_types, maybe_decode #: The public E.164 domain. public_enum_domain = dns.name.from_text('e164.arpa.') def from_e164(text, origin=public_enum_domain): """Convert an E.164 number in textual form into a Name object whose value is the ENUM domain name for that number. Non-digits in the text are ignored, i.e. "16505551212", "+1.650.555.1212" and "1 (650) 555-1212" are all the same. *text*, a ``text``, is an E.164 number in textual form. *origin*, a ``dns.name.Name``, the domain in which the number should be constructed. The default is ``e164.arpa.``. Returns a ``dns.name.Name``. """ parts = [d for d in text if d.isdigit()] parts.reverse() return dns.name.from_text('.'.join(parts), origin=origin) def to_e164(name, origin=public_enum_domain, want_plus_prefix=True): """Convert an ENUM domain name into an E.164 number. Note that dnspython does not have any information about preferred number formats within national numbering plans, so all numbers are emitted as a simple string of digits, prefixed by a '+' (unless *want_plus_prefix* is ``False``). *name* is a ``dns.name.Name``, the ENUM domain name. *origin* is a ``dns.name.Name``, a domain containing the ENUM domain name. The name is relativized to this domain before being converted to text. If ``None``, no relativization is done. *want_plus_prefix* is a ``bool``. If True, add a '+' to the beginning of the returned number. Returns a ``text``. """ if origin is not None: name = name.relativize(origin) dlabels = [d for d in name.labels if d.isdigit() and len(d) == 1] if len(dlabels) != len(name.labels): raise dns.exception.SyntaxError('non-digit labels in ENUM domain name') dlabels.reverse() text = b''.join(dlabels) if want_plus_prefix: text = b'+' + text return maybe_decode(text) def query(number, domains, resolver=None): """Look for NAPTR RRs for the specified number in the specified domains. e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) *number*, a ``text`` is the number to look for. *domains* is an iterable containing ``dns.name.Name`` values. *resolver*, a ``dns.resolver.Resolver``, is the resolver to use. If ``None``, the default resolver is used. """ if resolver is None: resolver = dns.resolver.get_default_resolver() e_nx = dns.resolver.NXDOMAIN() for domain in domains: if isinstance(domain, string_types): domain = dns.name.from_text(domain) qname = dns.e164.from_e164(number, domain) try: return resolver.query(qname, 'NAPTR') except dns.resolver.NXDOMAIN as e: e_nx += e raise e_nx dnspython-1.16.0/dns/e164.pyi000066400000000000000000000005621340301174400156130ustar00rootroot00000000000000from typing import Optional, Iterable from . import name, resolver def from_e164(text : str, origin=name.Name(".")) -> name.Name: ... def to_e164(name : name.Name, origin : Optional[name.Name] = None, want_plus_prefix=True) -> str: ... def query(number : str, domains : Iterable[str], resolver : Optional[resolver.Resolver] = None) -> resolver.Answer: ... dnspython-1.16.0/dns/edns.py000066400000000000000000000164001340301174400157120ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2009-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """EDNS Options""" from __future__ import absolute_import import math import struct import dns.inet #: NSID NSID = 3 #: DAU DAU = 5 #: DHU DHU = 6 #: N3U N3U = 7 #: ECS (client-subnet) ECS = 8 #: EXPIRE EXPIRE = 9 #: COOKIE COOKIE = 10 #: KEEPALIVE KEEPALIVE = 11 #: PADDING PADDING = 12 #: CHAIN CHAIN = 13 class Option(object): """Base class for all EDNS option types.""" def __init__(self, otype): """Initialize an option. *otype*, an ``int``, is the option type. """ self.otype = otype def to_wire(self, file): """Convert an option to wire format. """ raise NotImplementedError @classmethod def from_wire(cls, otype, wire, current, olen): """Build an EDNS option object from wire format. *otype*, an ``int``, is the option type. *wire*, a ``binary``, is the wire-format message. *current*, an ``int``, is the offset in *wire* of the beginning of the rdata. *olen*, an ``int``, is the length of the wire-format option data Returns a ``dns.edns.Option``. """ raise NotImplementedError def _cmp(self, other): """Compare an EDNS option with another option of the same type. Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*. """ raise NotImplementedError def __eq__(self, other): if not isinstance(other, Option): return False if self.otype != other.otype: return False return self._cmp(other) == 0 def __ne__(self, other): if not isinstance(other, Option): return False if self.otype != other.otype: return False return self._cmp(other) != 0 def __lt__(self, other): if not isinstance(other, Option) or \ self.otype != other.otype: return NotImplemented return self._cmp(other) < 0 def __le__(self, other): if not isinstance(other, Option) or \ self.otype != other.otype: return NotImplemented return self._cmp(other) <= 0 def __ge__(self, other): if not isinstance(other, Option) or \ self.otype != other.otype: return NotImplemented return self._cmp(other) >= 0 def __gt__(self, other): if not isinstance(other, Option) or \ self.otype != other.otype: return NotImplemented return self._cmp(other) > 0 class GenericOption(Option): """Generic Option Class This class is used for EDNS option types for which we have no better implementation. """ def __init__(self, otype, data): super(GenericOption, self).__init__(otype) self.data = data def to_wire(self, file): file.write(self.data) def to_text(self): return "Generic %d" % self.otype @classmethod def from_wire(cls, otype, wire, current, olen): return cls(otype, wire[current: current + olen]) def _cmp(self, other): if self.data == other.data: return 0 if self.data > other.data: return 1 return -1 class ECSOption(Option): """EDNS Client Subnet (ECS, RFC7871)""" def __init__(self, address, srclen=None, scopelen=0): """*address*, a ``text``, is the client address information. *srclen*, an ``int``, the source prefix length, which is the leftmost number of bits of the address to be used for the lookup. The default is 24 for IPv4 and 56 for IPv6. *scopelen*, an ``int``, the scope prefix length. This value must be 0 in queries, and should be set in responses. """ super(ECSOption, self).__init__(ECS) af = dns.inet.af_for_address(address) if af == dns.inet.AF_INET6: self.family = 2 if srclen is None: srclen = 56 elif af == dns.inet.AF_INET: self.family = 1 if srclen is None: srclen = 24 else: raise ValueError('Bad ip family') self.address = address self.srclen = srclen self.scopelen = scopelen addrdata = dns.inet.inet_pton(af, address) nbytes = int(math.ceil(srclen/8.0)) # Truncate to srclen and pad to the end of the last octet needed # See RFC section 6 self.addrdata = addrdata[:nbytes] nbits = srclen % 8 if nbits != 0: last = struct.pack('B', ord(self.addrdata[-1:]) & (0xff << nbits)) self.addrdata = self.addrdata[:-1] + last def to_text(self): return "ECS {}/{} scope/{}".format(self.address, self.srclen, self.scopelen) def to_wire(self, file): file.write(struct.pack('!H', self.family)) file.write(struct.pack('!BB', self.srclen, self.scopelen)) file.write(self.addrdata) @classmethod def from_wire(cls, otype, wire, cur, olen): family, src, scope = struct.unpack('!HBB', wire[cur:cur+4]) cur += 4 addrlen = int(math.ceil(src/8.0)) if family == 1: af = dns.inet.AF_INET pad = 4 - addrlen elif family == 2: af = dns.inet.AF_INET6 pad = 16 - addrlen else: raise ValueError('unsupported family') addr = dns.inet.inet_ntop(af, wire[cur:cur+addrlen] + b'\x00' * pad) return cls(addr, src, scope) def _cmp(self, other): if self.addrdata == other.addrdata: return 0 if self.addrdata > other.addrdata: return 1 return -1 _type_to_class = { ECS: ECSOption } def get_option_class(otype): """Return the class for the specified option type. The GenericOption class is used if a more specific class is not known. """ cls = _type_to_class.get(otype) if cls is None: cls = GenericOption return cls def option_from_wire(otype, wire, current, olen): """Build an EDNS option object from wire format. *otype*, an ``int``, is the option type. *wire*, a ``binary``, is the wire-format message. *current*, an ``int``, is the offset in *wire* of the beginning of the rdata. *olen*, an ``int``, is the length of the wire-format option data Returns an instance of a subclass of ``dns.edns.Option``. """ cls = get_option_class(otype) return cls.from_wire(otype, wire, current, olen) dnspython-1.16.0/dns/entropy.py000066400000000000000000000112431340301174400164610ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2009-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import os import random import time from ._compat import long, binary_type try: import threading as _threading except ImportError: import dummy_threading as _threading class EntropyPool(object): # This is an entropy pool for Python implementations that do not # have a working SystemRandom. I'm not sure there are any, but # leaving this code doesn't hurt anything as the library code # is used if present. def __init__(self, seed=None): self.pool_index = 0 self.digest = None self.next_byte = 0 self.lock = _threading.Lock() try: import hashlib self.hash = hashlib.sha1() self.hash_len = 20 except ImportError: try: import sha self.hash = sha.new() self.hash_len = 20 except ImportError: import md5 # pylint: disable=import-error self.hash = md5.new() self.hash_len = 16 self.pool = bytearray(b'\0' * self.hash_len) if seed is not None: self.stir(bytearray(seed)) self.seeded = True self.seed_pid = os.getpid() else: self.seeded = False self.seed_pid = 0 def stir(self, entropy, already_locked=False): if not already_locked: self.lock.acquire() try: for c in entropy: if self.pool_index == self.hash_len: self.pool_index = 0 b = c & 0xff self.pool[self.pool_index] ^= b self.pool_index += 1 finally: if not already_locked: self.lock.release() def _maybe_seed(self): if not self.seeded or self.seed_pid != os.getpid(): try: seed = os.urandom(16) except Exception: try: r = open('/dev/urandom', 'rb', 0) try: seed = r.read(16) finally: r.close() except Exception: seed = str(time.time()) self.seeded = True self.seed_pid = os.getpid() self.digest = None seed = bytearray(seed) self.stir(seed, True) def random_8(self): self.lock.acquire() try: self._maybe_seed() if self.digest is None or self.next_byte == self.hash_len: self.hash.update(binary_type(self.pool)) self.digest = bytearray(self.hash.digest()) self.stir(self.digest, True) self.next_byte = 0 value = self.digest[self.next_byte] self.next_byte += 1 finally: self.lock.release() return value def random_16(self): return self.random_8() * 256 + self.random_8() def random_32(self): return self.random_16() * 65536 + self.random_16() def random_between(self, first, last): size = last - first + 1 if size > long(4294967296): raise ValueError('too big') if size > 65536: rand = self.random_32 max = long(4294967295) elif size > 256: rand = self.random_16 max = 65535 else: rand = self.random_8 max = 255 return first + size * rand() // (max + 1) pool = EntropyPool() try: system_random = random.SystemRandom() except Exception: system_random = None def random_16(): if system_random is not None: return system_random.randrange(0, 65536) else: return pool.random_16() def between(first, last): if system_random is not None: return system_random.randrange(first, last + 1) else: return pool.random_between(first, last) dnspython-1.16.0/dns/entropy.pyi000066400000000000000000000002721340301174400166320ustar00rootroot00000000000000from typing import Optional from random import SystemRandom system_random : Optional[SystemRandom] def random_16() -> int: pass def between(first: int, last: int) -> int: pass dnspython-1.16.0/dns/exception.py000066400000000000000000000114141340301174400167570ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Common DNS Exceptions. Dnspython modules may also define their own exceptions, which will always be subclasses of ``DNSException``. """ class DNSException(Exception): """Abstract base class shared by all dnspython exceptions. It supports two basic modes of operation: a) Old/compatible mode is used if ``__init__`` was called with empty *kwargs*. In compatible mode all *args* are passed to the standard Python Exception class as before and all *args* are printed by the standard ``__str__`` implementation. Class variable ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()`` if *args* is empty. b) New/parametrized mode is used if ``__init__`` was called with non-empty *kwargs*. In the new mode *args* must be empty and all kwargs must match those set in class variable ``supp_kwargs``. All kwargs are stored inside ``self.kwargs`` and used in a new ``__str__`` implementation to construct a formatted message based on the ``fmt`` class variable, a ``string``. In the simplest case it is enough to override the ``supp_kwargs`` and ``fmt`` class variables to get nice parametrized messages. """ msg = None # non-parametrized message supp_kwargs = set() # accepted parameters for _fmt_kwargs (sanity check) fmt = None # message parametrized with results from _fmt_kwargs def __init__(self, *args, **kwargs): self._check_params(*args, **kwargs) if kwargs: self.kwargs = self._check_kwargs(**kwargs) self.msg = str(self) else: self.kwargs = dict() # defined but empty for old mode exceptions if self.msg is None: # doc string is better implicit message than empty string self.msg = self.__doc__ if args: super(DNSException, self).__init__(*args) else: super(DNSException, self).__init__(self.msg) def _check_params(self, *args, **kwargs): """Old exceptions supported only args and not kwargs. For sanity we do not allow to mix old and new behavior.""" if args or kwargs: assert bool(args) != bool(kwargs), \ 'keyword arguments are mutually exclusive with positional args' def _check_kwargs(self, **kwargs): if kwargs: assert set(kwargs.keys()) == self.supp_kwargs, \ 'following set of keyword args is required: %s' % ( self.supp_kwargs) return kwargs def _fmt_kwargs(self, **kwargs): """Format kwargs before printing them. Resulting dictionary has to have keys necessary for str.format call on fmt class variable. """ fmtargs = {} for kw, data in kwargs.items(): if isinstance(data, (list, set)): # convert list of to list of str() fmtargs[kw] = list(map(str, data)) if len(fmtargs[kw]) == 1: # remove list brackets [] from single-item lists fmtargs[kw] = fmtargs[kw].pop() else: fmtargs[kw] = data return fmtargs def __str__(self): if self.kwargs and self.fmt: # provide custom message constructed from keyword arguments fmtargs = self._fmt_kwargs(**self.kwargs) return self.fmt.format(**fmtargs) else: # print *args directly in the same way as old DNSException return super(DNSException, self).__str__() class FormError(DNSException): """DNS message is malformed.""" class SyntaxError(DNSException): """Text input is malformed.""" class UnexpectedEnd(SyntaxError): """Text input ended unexpectedly.""" class TooBig(DNSException): """The DNS message is too big.""" class Timeout(DNSException): """The DNS operation timed out.""" supp_kwargs = {'timeout'} fmt = "The DNS operation timed out after {timeout} seconds" dnspython-1.16.0/dns/exception.pyi000066400000000000000000000003501340301174400171250ustar00rootroot00000000000000from typing import Set, Optional, Dict class DNSException(Exception): supp_kwargs : Set[str] kwargs : Optional[Dict] class SyntaxError(DNSException): ... class FormError(DNSException): ... class Timeout(DNSException): ... dnspython-1.16.0/dns/flags.py000066400000000000000000000055651340301174400160670ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Message Flags.""" # Standard DNS flags #: Query Response QR = 0x8000 #: Authoritative Answer AA = 0x0400 #: Truncated Response TC = 0x0200 #: Recursion Desired RD = 0x0100 #: Recursion Available RA = 0x0080 #: Authentic Data AD = 0x0020 #: Checking Disabled CD = 0x0010 # EDNS flags #: DNSSEC answer OK DO = 0x8000 _by_text = { 'QR': QR, 'AA': AA, 'TC': TC, 'RD': RD, 'RA': RA, 'AD': AD, 'CD': CD } _edns_by_text = { 'DO': DO } # We construct the inverse mappings programmatically to ensure that we # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that # would cause the mappings not to be true inverses. _by_value = {y: x for x, y in _by_text.items()} _edns_by_value = {y: x for x, y in _edns_by_text.items()} def _order_flags(table): order = list(table.items()) order.sort() order.reverse() return order _flags_order = _order_flags(_by_value) _edns_flags_order = _order_flags(_edns_by_value) def _from_text(text, table): flags = 0 tokens = text.split() for t in tokens: flags = flags | table[t.upper()] return flags def _to_text(flags, table, order): text_flags = [] for k, v in order: if flags & k != 0: text_flags.append(v) return ' '.join(text_flags) def from_text(text): """Convert a space-separated list of flag text values into a flags value. Returns an ``int`` """ return _from_text(text, _by_text) def to_text(flags): """Convert a flags value into a space-separated list of flag text values. Returns a ``text``. """ return _to_text(flags, _by_value, _flags_order) def edns_from_text(text): """Convert a space-separated list of EDNS flag text values into a EDNS flags value. Returns an ``int`` """ return _from_text(text, _edns_by_text) def edns_to_text(flags): """Convert an EDNS flags value into a space-separated list of EDNS flag text values. Returns a ``text``. """ return _to_text(flags, _edns_by_value, _edns_flags_order) dnspython-1.16.0/dns/grange.py000066400000000000000000000040071340301174400162240ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2012-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS GENERATE range conversion.""" import dns def from_text(text): """Convert the text form of a range in a ``$GENERATE`` statement to an integer. *text*, a ``str``, the textual range in ``$GENERATE`` form. Returns a tuple of three ``int`` values ``(start, stop, step)``. """ # TODO, figure out the bounds on start, stop and step. step = 1 cur = '' state = 0 # state 0 1 2 3 4 # x - y / z if text and text[0] == '-': raise dns.exception.SyntaxError("Start cannot be a negative number") for c in text: if c == '-' and state == 0: start = int(cur) cur = '' state = 2 elif c == '/': stop = int(cur) cur = '' state = 4 elif c.isdigit(): cur += c else: raise dns.exception.SyntaxError("Could not parse %s" % (c)) if state in (1, 3): raise dns.exception.SyntaxError() if state == 2: stop = int(cur) if state == 4: step = int(cur) assert step >= 1 assert start >= 0 assert start <= stop # TODO, can start == stop? return (start, stop, step) dnspython-1.16.0/dns/hash.py000066400000000000000000000024471340301174400157120ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Hashing backwards compatibility wrapper""" import hashlib import warnings warnings.warn( "dns.hash module will be removed in future versions. Please use hashlib instead.", DeprecationWarning) hashes = {} hashes['MD5'] = hashlib.md5 hashes['SHA1'] = hashlib.sha1 hashes['SHA224'] = hashlib.sha224 hashes['SHA256'] = hashlib.sha256 hashes['SHA384'] = hashlib.sha384 hashes['SHA512'] = hashlib.sha512 def get(algorithm): return hashes[algorithm.upper()] dnspython-1.16.0/dns/inet.py000066400000000000000000000064611340301174400157260ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Generic Internet address helper functions.""" import socket import dns.ipv4 import dns.ipv6 from ._compat import maybe_ord # We assume that AF_INET is always defined. AF_INET = socket.AF_INET # AF_INET6 might not be defined in the socket module, but we need it. # We'll try to use the socket module's value, and if it doesn't work, # we'll use our own value. try: AF_INET6 = socket.AF_INET6 except AttributeError: AF_INET6 = 9999 def inet_pton(family, text): """Convert the textual form of a network address into its binary form. *family* is an ``int``, the address family. *text* is a ``text``, the textual address. Raises ``NotImplementedError`` if the address family specified is not implemented. Returns a ``binary``. """ if family == AF_INET: return dns.ipv4.inet_aton(text) elif family == AF_INET6: return dns.ipv6.inet_aton(text) else: raise NotImplementedError def inet_ntop(family, address): """Convert the binary form of a network address into its textual form. *family* is an ``int``, the address family. *address* is a ``binary``, the network address in binary form. Raises ``NotImplementedError`` if the address family specified is not implemented. Returns a ``text``. """ if family == AF_INET: return dns.ipv4.inet_ntoa(address) elif family == AF_INET6: return dns.ipv6.inet_ntoa(address) else: raise NotImplementedError def af_for_address(text): """Determine the address family of a textual-form network address. *text*, a ``text``, the textual address. Raises ``ValueError`` if the address family cannot be determined from the input. Returns an ``int``. """ try: dns.ipv4.inet_aton(text) return AF_INET except Exception: try: dns.ipv6.inet_aton(text) return AF_INET6 except: raise ValueError def is_multicast(text): """Is the textual-form network address a multicast address? *text*, a ``text``, the textual address. Raises ``ValueError`` if the address family cannot be determined from the input. Returns a ``bool``. """ try: first = maybe_ord(dns.ipv4.inet_aton(text)[0]) return first >= 224 and first <= 239 except Exception: try: first = maybe_ord(dns.ipv6.inet_aton(text)[0]) return first == 255 except Exception: raise ValueError dnspython-1.16.0/dns/inet.pyi000066400000000000000000000001401340301174400160630ustar00rootroot00000000000000from typing import Union from socket import AddressFamily AF_INET6 : Union[int, AddressFamily] dnspython-1.16.0/dns/ipv4.py000066400000000000000000000040601340301174400156420ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """IPv4 helper functions.""" import struct import dns.exception from ._compat import binary_type def inet_ntoa(address): """Convert an IPv4 address in binary form to text form. *address*, a ``binary``, the IPv4 address in binary form. Returns a ``text``. """ if len(address) != 4: raise dns.exception.SyntaxError if not isinstance(address, bytearray): address = bytearray(address) return ('%u.%u.%u.%u' % (address[0], address[1], address[2], address[3])) def inet_aton(text): """Convert an IPv4 address in text form to binary form. *text*, a ``text``, the IPv4 address in textual form. Returns a ``binary``. """ if not isinstance(text, binary_type): text = text.encode() parts = text.split(b'.') if len(parts) != 4: raise dns.exception.SyntaxError for part in parts: if not part.isdigit(): raise dns.exception.SyntaxError if len(part) > 1 and part[0] == '0': # No leading zeros raise dns.exception.SyntaxError try: bytes = [int(part) for part in parts] return struct.pack('BBBB', *bytes) except: raise dns.exception.SyntaxError dnspython-1.16.0/dns/ipv6.py000066400000000000000000000126271340301174400156540ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """IPv6 helper functions.""" import re import binascii import dns.exception import dns.ipv4 from ._compat import xrange, binary_type, maybe_decode _leading_zero = re.compile(r'0+([0-9a-f]+)') def inet_ntoa(address): """Convert an IPv6 address in binary form to text form. *address*, a ``binary``, the IPv6 address in binary form. Raises ``ValueError`` if the address isn't 16 bytes long. Returns a ``text``. """ if len(address) != 16: raise ValueError("IPv6 addresses are 16 bytes long") hex = binascii.hexlify(address) chunks = [] i = 0 l = len(hex) while i < l: chunk = maybe_decode(hex[i : i + 4]) # strip leading zeros. we do this with an re instead of # with lstrip() because lstrip() didn't support chars until # python 2.2.2 m = _leading_zero.match(chunk) if not m is None: chunk = m.group(1) chunks.append(chunk) i += 4 # # Compress the longest subsequence of 0-value chunks to :: # best_start = 0 best_len = 0 start = -1 last_was_zero = False for i in xrange(8): if chunks[i] != '0': if last_was_zero: end = i current_len = end - start if current_len > best_len: best_start = start best_len = current_len last_was_zero = False elif not last_was_zero: start = i last_was_zero = True if last_was_zero: end = 8 current_len = end - start if current_len > best_len: best_start = start best_len = current_len if best_len > 1: if best_start == 0 and \ (best_len == 6 or best_len == 5 and chunks[5] == 'ffff'): # We have an embedded IPv4 address if best_len == 6: prefix = '::' else: prefix = '::ffff:' hex = prefix + dns.ipv4.inet_ntoa(address[12:]) else: hex = ':'.join(chunks[:best_start]) + '::' + \ ':'.join(chunks[best_start + best_len:]) else: hex = ':'.join(chunks) return hex _v4_ending = re.compile(br'(.*):(\d+\.\d+\.\d+\.\d+)$') _colon_colon_start = re.compile(br'::.*') _colon_colon_end = re.compile(br'.*::$') def inet_aton(text): """Convert an IPv6 address in text form to binary form. *text*, a ``text``, the IPv6 address in textual form. Returns a ``binary``. """ # # Our aim here is not something fast; we just want something that works. # if not isinstance(text, binary_type): text = text.encode() if text == b'::': text = b'0::' # # Get rid of the icky dot-quad syntax if we have it. # m = _v4_ending.match(text) if not m is None: b = bytearray(dns.ipv4.inet_aton(m.group(2))) text = (u"{}:{:02x}{:02x}:{:02x}{:02x}".format(m.group(1).decode(), b[0], b[1], b[2], b[3])).encode() # # Try to turn '::' into ':'; if no match try to # turn '::' into ':' # m = _colon_colon_start.match(text) if not m is None: text = text[1:] else: m = _colon_colon_end.match(text) if not m is None: text = text[:-1] # # Now canonicalize into 8 chunks of 4 hex digits each # chunks = text.split(b':') l = len(chunks) if l > 8: raise dns.exception.SyntaxError seen_empty = False canonical = [] for c in chunks: if c == b'': if seen_empty: raise dns.exception.SyntaxError seen_empty = True for i in xrange(0, 8 - l + 1): canonical.append(b'0000') else: lc = len(c) if lc > 4: raise dns.exception.SyntaxError if lc != 4: c = (b'0' * (4 - lc)) + c canonical.append(c) if l < 8 and not seen_empty: raise dns.exception.SyntaxError text = b''.join(canonical) # # Finally we can go to binary. # try: return binascii.unhexlify(text) except (binascii.Error, TypeError): raise dns.exception.SyntaxError _mapped_prefix = b'\x00' * 10 + b'\xff\xff' def is_mapped(address): """Is the specified address a mapped IPv4 address? *address*, a ``binary`` is an IPv6 address in binary form. Returns a ``bool``. """ return address.startswith(_mapped_prefix) dnspython-1.16.0/dns/message.py000066400000000000000000001207621340301174400164140ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Messages""" from __future__ import absolute_import from io import StringIO import struct import time import dns.edns import dns.exception import dns.flags import dns.name import dns.opcode import dns.entropy import dns.rcode import dns.rdata import dns.rdataclass import dns.rdatatype import dns.rrset import dns.renderer import dns.tsig import dns.wiredata from ._compat import long, xrange, string_types class ShortHeader(dns.exception.FormError): """The DNS packet passed to from_wire() is too short.""" class TrailingJunk(dns.exception.FormError): """The DNS packet passed to from_wire() has extra junk at the end of it.""" class UnknownHeaderField(dns.exception.DNSException): """The header field name was not recognized when converting from text into a message.""" class BadEDNS(dns.exception.FormError): """An OPT record occurred somewhere other than the start of the additional data section.""" class BadTSIG(dns.exception.FormError): """A TSIG record occurred somewhere other than the end of the additional data section.""" class UnknownTSIGKey(dns.exception.DNSException): """A TSIG with an unknown key was received.""" #: The question section number QUESTION = 0 #: The answer section number ANSWER = 1 #: The authority section number AUTHORITY = 2 #: The additional section number ADDITIONAL = 3 class Message(object): """A DNS message.""" def __init__(self, id=None): if id is None: self.id = dns.entropy.random_16() else: self.id = id self.flags = 0 self.question = [] self.answer = [] self.authority = [] self.additional = [] self.edns = -1 self.ednsflags = 0 self.payload = 0 self.options = [] self.request_payload = 0 self.keyring = None self.keyname = None self.keyalgorithm = dns.tsig.default_algorithm self.request_mac = b'' self.other_data = b'' self.tsig_error = 0 self.fudge = 300 self.original_id = self.id self.mac = b'' self.xfr = False self.origin = None self.tsig_ctx = None self.had_tsig = False self.multi = False self.first = True self.index = {} def __repr__(self): return '' def __str__(self): return self.to_text() def to_text(self, origin=None, relativize=True, **kw): """Convert the message to text. The *origin*, *relativize*, and any other keyword arguments are passed to the RRset ``to_wire()`` method. Returns a ``text``. """ s = StringIO() s.write(u'id %d\n' % self.id) s.write(u'opcode %s\n' % dns.opcode.to_text(dns.opcode.from_flags(self.flags))) rc = dns.rcode.from_flags(self.flags, self.ednsflags) s.write(u'rcode %s\n' % dns.rcode.to_text(rc)) s.write(u'flags %s\n' % dns.flags.to_text(self.flags)) if self.edns >= 0: s.write(u'edns %s\n' % self.edns) if self.ednsflags != 0: s.write(u'eflags %s\n' % dns.flags.edns_to_text(self.ednsflags)) s.write(u'payload %d\n' % self.payload) for opt in self.options: s.write(u'option %s\n' % opt.to_text()) is_update = dns.opcode.is_update(self.flags) if is_update: s.write(u';ZONE\n') else: s.write(u';QUESTION\n') for rrset in self.question: s.write(rrset.to_text(origin, relativize, **kw)) s.write(u'\n') if is_update: s.write(u';PREREQ\n') else: s.write(u';ANSWER\n') for rrset in self.answer: s.write(rrset.to_text(origin, relativize, **kw)) s.write(u'\n') if is_update: s.write(u';UPDATE\n') else: s.write(u';AUTHORITY\n') for rrset in self.authority: s.write(rrset.to_text(origin, relativize, **kw)) s.write(u'\n') s.write(u';ADDITIONAL\n') for rrset in self.additional: s.write(rrset.to_text(origin, relativize, **kw)) s.write(u'\n') # # We strip off the final \n so the caller can print the result without # doing weird things to get around eccentricities in Python print # formatting # return s.getvalue()[:-1] def __eq__(self, other): """Two messages are equal if they have the same content in the header, question, answer, and authority sections. Returns a ``bool``. """ if not isinstance(other, Message): return False if self.id != other.id: return False if self.flags != other.flags: return False for n in self.question: if n not in other.question: return False for n in other.question: if n not in self.question: return False for n in self.answer: if n not in other.answer: return False for n in other.answer: if n not in self.answer: return False for n in self.authority: if n not in other.authority: return False for n in other.authority: if n not in self.authority: return False return True def __ne__(self, other): return not self.__eq__(other) def is_response(self, other): """Is this message a response to *other*? Returns a ``bool``. """ if other.flags & dns.flags.QR == 0 or \ self.id != other.id or \ dns.opcode.from_flags(self.flags) != \ dns.opcode.from_flags(other.flags): return False if dns.rcode.from_flags(other.flags, other.ednsflags) != \ dns.rcode.NOERROR: return True if dns.opcode.is_update(self.flags): return True for n in self.question: if n not in other.question: return False for n in other.question: if n not in self.question: return False return True def section_number(self, section): """Return the "section number" of the specified section for use in indexing. The question section is 0, the answer section is 1, the authority section is 2, and the additional section is 3. *section* is one of the section attributes of this message. Raises ``ValueError`` if the section isn't known. Returns an ``int``. """ if section is self.question: return QUESTION elif section is self.answer: return ANSWER elif section is self.authority: return AUTHORITY elif section is self.additional: return ADDITIONAL else: raise ValueError('unknown section') def section_from_number(self, number): """Return the "section number" of the specified section for use in indexing. The question section is 0, the answer section is 1, the authority section is 2, and the additional section is 3. *section* is one of the section attributes of this message. Raises ``ValueError`` if the section isn't known. Returns an ``int``. """ if number == QUESTION: return self.question elif number == ANSWER: return self.answer elif number == AUTHORITY: return self.authority elif number == ADDITIONAL: return self.additional else: raise ValueError('unknown section') def find_rrset(self, section, name, rdclass, rdtype, covers=dns.rdatatype.NONE, deleting=None, create=False, force_unique=False): """Find the RRset with the given attributes in the specified section. *section*, an ``int`` section number, or one of the section attributes of this message. This specifies the the section of the message to search. For example:: my_message.find_rrset(my_message.answer, name, rdclass, rdtype) my_message.find_rrset(dns.message.ANSWER, name, rdclass, rdtype) *name*, a ``dns.name.Name``, the name of the RRset. *rdclass*, an ``int``, the class of the RRset. *rdtype*, an ``int``, the type of the RRset. *covers*, an ``int`` or ``None``, the covers value of the RRset. The default is ``None``. *deleting*, an ``int`` or ``None``, the deleting value of the RRset. The default is ``None``. *create*, a ``bool``. If ``True``, create the RRset if it is not found. The created RRset is appended to *section*. *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, create a new RRset regardless of whether a matching RRset exists already. The default is ``False``. This is useful when creating DDNS Update messages, as order matters for them. Raises ``KeyError`` if the RRset was not found and create was ``False``. Returns a ``dns.rrset.RRset object``. """ if isinstance(section, int): section_number = section section = self.section_from_number(section_number) else: section_number = self.section_number(section) key = (section_number, name, rdclass, rdtype, covers, deleting) if not force_unique: if self.index is not None: rrset = self.index.get(key) if rrset is not None: return rrset else: for rrset in section: if rrset.match(name, rdclass, rdtype, covers, deleting): return rrset if not create: raise KeyError rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting) section.append(rrset) if self.index is not None: self.index[key] = rrset return rrset def get_rrset(self, section, name, rdclass, rdtype, covers=dns.rdatatype.NONE, deleting=None, create=False, force_unique=False): """Get the RRset with the given attributes in the specified section. If the RRset is not found, None is returned. *section*, an ``int`` section number, or one of the section attributes of this message. This specifies the the section of the message to search. For example:: my_message.get_rrset(my_message.answer, name, rdclass, rdtype) my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype) *name*, a ``dns.name.Name``, the name of the RRset. *rdclass*, an ``int``, the class of the RRset. *rdtype*, an ``int``, the type of the RRset. *covers*, an ``int`` or ``None``, the covers value of the RRset. The default is ``None``. *deleting*, an ``int`` or ``None``, the deleting value of the RRset. The default is ``None``. *create*, a ``bool``. If ``True``, create the RRset if it is not found. The created RRset is appended to *section*. *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, create a new RRset regardless of whether a matching RRset exists already. The default is ``False``. This is useful when creating DDNS Update messages, as order matters for them. Returns a ``dns.rrset.RRset object`` or ``None``. """ try: rrset = self.find_rrset(section, name, rdclass, rdtype, covers, deleting, create, force_unique) except KeyError: rrset = None return rrset def to_wire(self, origin=None, max_size=0, **kw): """Return a string containing the message in DNS compressed wire format. Additional keyword arguments are passed to the RRset ``to_wire()`` method. *origin*, a ``dns.name.Name`` or ``None``, the origin to be appended to any relative names. *max_size*, an ``int``, the maximum size of the wire format output; default is 0, which means "the message's request payload, if nonzero, or 65535". Raises ``dns.exception.TooBig`` if *max_size* was exceeded. Returns a ``binary``. """ if max_size == 0: if self.request_payload != 0: max_size = self.request_payload else: max_size = 65535 if max_size < 512: max_size = 512 elif max_size > 65535: max_size = 65535 r = dns.renderer.Renderer(self.id, self.flags, max_size, origin) for rrset in self.question: r.add_question(rrset.name, rrset.rdtype, rrset.rdclass) for rrset in self.answer: r.add_rrset(dns.renderer.ANSWER, rrset, **kw) for rrset in self.authority: r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw) if self.edns >= 0: r.add_edns(self.edns, self.ednsflags, self.payload, self.options) for rrset in self.additional: r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw) r.write_header() if self.keyname is not None: r.add_tsig(self.keyname, self.keyring[self.keyname], self.fudge, self.original_id, self.tsig_error, self.other_data, self.request_mac, self.keyalgorithm) self.mac = r.mac return r.get_wire() def use_tsig(self, keyring, keyname=None, fudge=300, original_id=None, tsig_error=0, other_data=b'', algorithm=dns.tsig.default_algorithm): """When sending, a TSIG signature using the specified keyring and keyname should be added. See the documentation of the Message class for a complete description of the keyring dictionary. *keyring*, a ``dict``, the TSIG keyring to use. If a *keyring* is specified but a *keyname* is not, then the key used will be the first key in the *keyring*. Note that the order of keys in a dictionary is not defined, so applications should supply a keyname when a keyring is used, unless they know the keyring contains only one key. *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key to use; defaults to ``None``. The key must be defined in the keyring. *fudge*, an ``int``, the TSIG time fudge. *original_id*, an ``int``, the TSIG original id. If ``None``, the message's id is used. *tsig_error*, an ``int``, the TSIG error code. *other_data*, a ``binary``, the TSIG other data. *algorithm*, a ``dns.name.Name``, the TSIG algorithm to use. """ self.keyring = keyring if keyname is None: self.keyname = list(self.keyring.keys())[0] else: if isinstance(keyname, string_types): keyname = dns.name.from_text(keyname) self.keyname = keyname self.keyalgorithm = algorithm self.fudge = fudge if original_id is None: self.original_id = self.id else: self.original_id = original_id self.tsig_error = tsig_error self.other_data = other_data def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None, options=None): """Configure EDNS behavior. *edns*, an ``int``, is the EDNS level to use. Specifying ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case the other parameters are ignored. Specifying ``True`` is equivalent to specifying 0, i.e. "use EDNS0". *ednsflags*, an ``int``, the EDNS flag values. *payload*, an ``int``, is the EDNS sender's payload field, which is the maximum size of UDP datagram the sender can handle. I.e. how big a response to this message can be. *request_payload*, an ``int``, is the EDNS payload size to use when sending this message. If not specified, defaults to the value of *payload*. *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS options. """ if edns is None or edns is False: edns = -1 if edns is True: edns = 0 if request_payload is None: request_payload = payload if edns < 0: ednsflags = 0 payload = 0 request_payload = 0 options = [] else: # make sure the EDNS version in ednsflags agrees with edns ednsflags &= long(0xFF00FFFF) ednsflags |= (edns << 16) if options is None: options = [] self.edns = edns self.ednsflags = ednsflags self.payload = payload self.options = options self.request_payload = request_payload def want_dnssec(self, wanted=True): """Enable or disable 'DNSSEC desired' flag in requests. *wanted*, a ``bool``. If ``True``, then DNSSEC data is desired in the response, EDNS is enabled if required, and then the DO bit is set. If ``False``, the DO bit is cleared if EDNS is enabled. """ if wanted: if self.edns < 0: self.use_edns() self.ednsflags |= dns.flags.DO elif self.edns >= 0: self.ednsflags &= ~dns.flags.DO def rcode(self): """Return the rcode. Returns an ``int``. """ return dns.rcode.from_flags(self.flags, self.ednsflags) def set_rcode(self, rcode): """Set the rcode. *rcode*, an ``int``, is the rcode to set. """ (value, evalue) = dns.rcode.to_flags(rcode) self.flags &= 0xFFF0 self.flags |= value self.ednsflags &= long(0x00FFFFFF) self.ednsflags |= evalue if self.ednsflags != 0 and self.edns < 0: self.edns = 0 def opcode(self): """Return the opcode. Returns an ``int``. """ return dns.opcode.from_flags(self.flags) def set_opcode(self, opcode): """Set the opcode. *opcode*, an ``int``, is the opcode to set. """ self.flags &= 0x87FF self.flags |= dns.opcode.to_flags(opcode) class _WireReader(object): """Wire format reader. wire: a binary, is the wire-format message. message: The message object being built current: When building a message object from wire format, this variable contains the offset from the beginning of wire of the next octet to be read. updating: Is the message a dynamic update? one_rr_per_rrset: Put each RR into its own RRset? ignore_trailing: Ignore trailing junk at end of request? zone_rdclass: The class of the zone in messages which are DNS dynamic updates. """ def __init__(self, wire, message, question_only=False, one_rr_per_rrset=False, ignore_trailing=False): self.wire = dns.wiredata.maybe_wrap(wire) self.message = message self.current = 0 self.updating = False self.zone_rdclass = dns.rdataclass.IN self.question_only = question_only self.one_rr_per_rrset = one_rr_per_rrset self.ignore_trailing = ignore_trailing def _get_question(self, qcount): """Read the next *qcount* records from the wire data and add them to the question section. """ if self.updating and qcount > 1: raise dns.exception.FormError for i in xrange(0, qcount): (qname, used) = dns.name.from_wire(self.wire, self.current) if self.message.origin is not None: qname = qname.relativize(self.message.origin) self.current = self.current + used (rdtype, rdclass) = \ struct.unpack('!HH', self.wire[self.current:self.current + 4]) self.current = self.current + 4 self.message.find_rrset(self.message.question, qname, rdclass, rdtype, create=True, force_unique=True) if self.updating: self.zone_rdclass = rdclass def _get_section(self, section, count): """Read the next I{count} records from the wire data and add them to the specified section. section: the section of the message to which to add records count: the number of records to read """ if self.updating or self.one_rr_per_rrset: force_unique = True else: force_unique = False seen_opt = False for i in xrange(0, count): rr_start = self.current (name, used) = dns.name.from_wire(self.wire, self.current) absolute_name = name if self.message.origin is not None: name = name.relativize(self.message.origin) self.current = self.current + used (rdtype, rdclass, ttl, rdlen) = \ struct.unpack('!HHIH', self.wire[self.current:self.current + 10]) self.current = self.current + 10 if rdtype == dns.rdatatype.OPT: if section is not self.message.additional or seen_opt: raise BadEDNS self.message.payload = rdclass self.message.ednsflags = ttl self.message.edns = (ttl & 0xff0000) >> 16 self.message.options = [] current = self.current optslen = rdlen while optslen > 0: (otype, olen) = \ struct.unpack('!HH', self.wire[current:current + 4]) current = current + 4 opt = dns.edns.option_from_wire( otype, self.wire, current, olen) self.message.options.append(opt) current = current + olen optslen = optslen - 4 - olen seen_opt = True elif rdtype == dns.rdatatype.TSIG: if not (section is self.message.additional and i == (count - 1)): raise BadTSIG if self.message.keyring is None: raise UnknownTSIGKey('got signed message without keyring') secret = self.message.keyring.get(absolute_name) if secret is None: raise UnknownTSIGKey("key '%s' unknown" % name) self.message.keyname = absolute_name (self.message.keyalgorithm, self.message.mac) = \ dns.tsig.get_algorithm_and_mac(self.wire, self.current, rdlen) self.message.tsig_ctx = \ dns.tsig.validate(self.wire, absolute_name, secret, int(time.time()), self.message.request_mac, rr_start, self.current, rdlen, self.message.tsig_ctx, self.message.multi, self.message.first) self.message.had_tsig = True else: if ttl < 0: ttl = 0 if self.updating and \ (rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE): deleting = rdclass rdclass = self.zone_rdclass else: deleting = None if deleting == dns.rdataclass.ANY or \ (deleting == dns.rdataclass.NONE and section is self.message.answer): covers = dns.rdatatype.NONE rd = None else: rd = dns.rdata.from_wire(rdclass, rdtype, self.wire, self.current, rdlen, self.message.origin) covers = rd.covers() if self.message.xfr and rdtype == dns.rdatatype.SOA: force_unique = True rrset = self.message.find_rrset(section, name, rdclass, rdtype, covers, deleting, True, force_unique) if rd is not None: rrset.add(rd, ttl) self.current = self.current + rdlen def read(self): """Read a wire format DNS message and build a dns.message.Message object.""" l = len(self.wire) if l < 12: raise ShortHeader (self.message.id, self.message.flags, qcount, ancount, aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12]) self.current = 12 if dns.opcode.is_update(self.message.flags): self.updating = True self._get_question(qcount) if self.question_only: return self._get_section(self.message.answer, ancount) self._get_section(self.message.authority, aucount) self._get_section(self.message.additional, adcount) if not self.ignore_trailing and self.current != l: raise TrailingJunk if self.message.multi and self.message.tsig_ctx and \ not self.message.had_tsig: self.message.tsig_ctx.update(self.wire) def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None, tsig_ctx=None, multi=False, first=True, question_only=False, one_rr_per_rrset=False, ignore_trailing=False): """Convert a DNS wire format message into a message object. *keyring*, a ``dict``, the keyring to use if the message is signed. *request_mac*, a ``binary``. If the message is a response to a TSIG-signed request, *request_mac* should be set to the MAC of that request. *xfr*, a ``bool``, should be set to ``True`` if this message is part of a zone transfer. *origin*, a ``dns.name.Name`` or ``None``. If the message is part of a zone transfer, *origin* should be the origin name of the zone. *tsig_ctx*, a ``hmac.HMAC`` objext, the ongoing TSIG context, used when validating zone transfers. *multi*, a ``bool``, should be set to ``True`` if this message part of a multiple message sequence. *first*, a ``bool``, should be set to ``True`` if this message is stand-alone, or the first message in a multi-message sequence. *question_only*, a ``bool``. If ``True``, read only up to the end of the question section. *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the message. Raises ``dns.message.ShortHeader`` if the message is less than 12 octets long. Raises ``dns.messaage.TrailingJunk`` if there were octets in the message past the end of the proper DNS message, and *ignore_trailing* is ``False``. Raises ``dns.message.BadEDNS`` if an OPT record was in the wrong section, or occurred more than once. Raises ``dns.message.BadTSIG`` if a TSIG record was not the last record of the additional data section. Returns a ``dns.message.Message``. """ m = Message(id=0) m.keyring = keyring m.request_mac = request_mac m.xfr = xfr m.origin = origin m.tsig_ctx = tsig_ctx m.multi = multi m.first = first reader = _WireReader(wire, m, question_only, one_rr_per_rrset, ignore_trailing) reader.read() return m class _TextReader(object): """Text format reader. tok: the tokenizer. message: The message object being built. updating: Is the message a dynamic update? zone_rdclass: The class of the zone in messages which are DNS dynamic updates. last_name: The most recently read name when building a message object. """ def __init__(self, text, message): self.message = message self.tok = dns.tokenizer.Tokenizer(text) self.last_name = None self.zone_rdclass = dns.rdataclass.IN self.updating = False def _header_line(self, section): """Process one line from the text format header section.""" token = self.tok.get() what = token.value if what == 'id': self.message.id = self.tok.get_int() elif what == 'flags': while True: token = self.tok.get() if not token.is_identifier(): self.tok.unget(token) break self.message.flags = self.message.flags | \ dns.flags.from_text(token.value) if dns.opcode.is_update(self.message.flags): self.updating = True elif what == 'edns': self.message.edns = self.tok.get_int() self.message.ednsflags = self.message.ednsflags | \ (self.message.edns << 16) elif what == 'eflags': if self.message.edns < 0: self.message.edns = 0 while True: token = self.tok.get() if not token.is_identifier(): self.tok.unget(token) break self.message.ednsflags = self.message.ednsflags | \ dns.flags.edns_from_text(token.value) elif what == 'payload': self.message.payload = self.tok.get_int() if self.message.edns < 0: self.message.edns = 0 elif what == 'opcode': text = self.tok.get_string() self.message.flags = self.message.flags | \ dns.opcode.to_flags(dns.opcode.from_text(text)) elif what == 'rcode': text = self.tok.get_string() self.message.set_rcode(dns.rcode.from_text(text)) else: raise UnknownHeaderField self.tok.get_eol() def _question_line(self, section): """Process one line from the text format question section.""" token = self.tok.get(want_leading=True) if not token.is_whitespace(): self.last_name = dns.name.from_text(token.value, None) name = self.last_name token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError # Class try: rdclass = dns.rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except dns.exception.SyntaxError: raise dns.exception.SyntaxError except Exception: rdclass = dns.rdataclass.IN # Type rdtype = dns.rdatatype.from_text(token.value) self.message.find_rrset(self.message.question, name, rdclass, rdtype, create=True, force_unique=True) if self.updating: self.zone_rdclass = rdclass self.tok.get_eol() def _rr_line(self, section): """Process one line from the text format answer, authority, or additional data sections. """ deleting = None # Name token = self.tok.get(want_leading=True) if not token.is_whitespace(): self.last_name = dns.name.from_text(token.value, None) name = self.last_name token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError # TTL try: ttl = int(token.value, 0) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except dns.exception.SyntaxError: raise dns.exception.SyntaxError except Exception: ttl = 0 # Class try: rdclass = dns.rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE: deleting = rdclass rdclass = self.zone_rdclass except dns.exception.SyntaxError: raise dns.exception.SyntaxError except Exception: rdclass = dns.rdataclass.IN # Type rdtype = dns.rdatatype.from_text(token.value) token = self.tok.get() if not token.is_eol_or_eof(): self.tok.unget(token) rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None) covers = rd.covers() else: rd = None covers = dns.rdatatype.NONE rrset = self.message.find_rrset(section, name, rdclass, rdtype, covers, deleting, True, self.updating) if rd is not None: rrset.add(rd, ttl) def read(self): """Read a text format DNS message and build a dns.message.Message object.""" line_method = self._header_line section = None while 1: token = self.tok.get(True, True) if token.is_eol_or_eof(): break if token.is_comment(): u = token.value.upper() if u == 'HEADER': line_method = self._header_line elif u == 'QUESTION' or u == 'ZONE': line_method = self._question_line section = self.message.question elif u == 'ANSWER' or u == 'PREREQ': line_method = self._rr_line section = self.message.answer elif u == 'AUTHORITY' or u == 'UPDATE': line_method = self._rr_line section = self.message.authority elif u == 'ADDITIONAL': line_method = self._rr_line section = self.message.additional self.tok.get_eol() continue self.tok.unget(token) line_method(section) def from_text(text): """Convert the text format message into a message object. *text*, a ``text``, the text format message. Raises ``dns.message.UnknownHeaderField`` if a header is unknown. Raises ``dns.exception.SyntaxError`` if the text is badly formed. Returns a ``dns.message.Message object`` """ # 'text' can also be a file, but we don't publish that fact # since it's an implementation detail. The official file # interface is from_file(). m = Message() reader = _TextReader(text, m) reader.read() return m def from_file(f): """Read the next text format message from the specified file. *f*, a ``file`` or ``text``. If *f* is text, it is treated as the pathname of a file to open. Raises ``dns.message.UnknownHeaderField`` if a header is unknown. Raises ``dns.exception.SyntaxError`` if the text is badly formed. Returns a ``dns.message.Message object`` """ str_type = string_types opts = 'rU' if isinstance(f, str_type): f = open(f, opts) want_close = True else: want_close = False try: m = from_text(f) finally: if want_close: f.close() return m def make_query(qname, rdtype, rdclass=dns.rdataclass.IN, use_edns=None, want_dnssec=False, ednsflags=None, payload=None, request_payload=None, options=None): """Make a query message. The query name, type, and class may all be specified either as objects of the appropriate type, or as strings. The query will have a randomly chosen query id, and its DNS flags will be set to dns.flags.RD. qname, a ``dns.name.Name`` or ``text``, the query name. *rdtype*, an ``int`` or ``text``, the desired rdata type. *rdclass*, an ``int`` or ``text``, the desired rdata class; the default is class IN. *use_edns*, an ``int``, ``bool`` or ``None``. The EDNS level to use; the default is None (no EDNS). See the description of dns.message.Message.use_edns() for the possible values for use_edns and their meanings. *want_dnssec*, a ``bool``. If ``True``, DNSSEC data is desired. *ednsflags*, an ``int``, the EDNS flag values. *payload*, an ``int``, is the EDNS sender's payload field, which is the maximum size of UDP datagram the sender can handle. I.e. how big a response to this message can be. *request_payload*, an ``int``, is the EDNS payload size to use when sending this message. If not specified, defaults to the value of *payload*. *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS options. Returns a ``dns.message.Message`` """ if isinstance(qname, string_types): qname = dns.name.from_text(qname) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if isinstance(rdclass, string_types): rdclass = dns.rdataclass.from_text(rdclass) m = Message() m.flags |= dns.flags.RD m.find_rrset(m.question, qname, rdclass, rdtype, create=True, force_unique=True) # only pass keywords on to use_edns if they have been set to a # non-None value. Setting a field will turn EDNS on if it hasn't # been configured. kwargs = {} if ednsflags is not None: kwargs['ednsflags'] = ednsflags if use_edns is None: use_edns = 0 if payload is not None: kwargs['payload'] = payload if use_edns is None: use_edns = 0 if request_payload is not None: kwargs['request_payload'] = request_payload if use_edns is None: use_edns = 0 if options is not None: kwargs['options'] = options if use_edns is None: use_edns = 0 kwargs['edns'] = use_edns m.use_edns(**kwargs) m.want_dnssec(want_dnssec) return m def make_response(query, recursion_available=False, our_payload=8192, fudge=300): """Make a message which is a response for the specified query. The message returned is really a response skeleton; it has all of the infrastructure required of a response, but none of the content. The response's question section is a shallow copy of the query's question section, so the query's question RRsets should not be changed. *query*, a ``dns.message.Message``, the query to respond to. *recursion_available*, a ``bool``, should RA be set in the response? *our_payload*, an ``int``, the payload size to advertise in EDNS responses. *fudge*, an ``int``, the TSIG time fudge. Returns a ``dns.message.Message`` object. """ if query.flags & dns.flags.QR: raise dns.exception.FormError('specified query message is not a query') response = dns.message.Message(query.id) response.flags = dns.flags.QR | (query.flags & dns.flags.RD) if recursion_available: response.flags |= dns.flags.RA response.set_opcode(query.opcode()) response.question = list(query.question) if query.edns >= 0: response.use_edns(0, 0, our_payload, query.payload) if query.had_tsig: response.use_tsig(query.keyring, query.keyname, fudge, None, 0, b'', query.keyalgorithm) response.request_mac = query.mac return response dnspython-1.16.0/dns/message.pyi000066400000000000000000000044521340301174400165620ustar00rootroot00000000000000from typing import Optional, Dict, List, Tuple, Union from . import name, rrset, tsig, rdatatype, entropy, edns, rdataclass import hmac class Message: def to_wire(self, origin : Optional[name.Name]=None, max_size=0, **kw) -> bytes: ... def find_rrset(self, section : List[rrset.RRset], name : name.Name, rdclass : int, rdtype : int, covers=rdatatype.NONE, deleting : Optional[int]=None, create=False, force_unique=False) -> rrset.RRset: ... def __init__(self, id : Optional[int] =None) -> None: self.id : int self.flags = 0 self.question : List[rrset.RRset] = [] self.answer : List[rrset.RRset] = [] self.authority : List[rrset.RRset] = [] self.additional : List[rrset.RRset] = [] self.edns = -1 self.ednsflags = 0 self.payload = 0 self.options : List[edns.Option] = [] self.request_payload = 0 self.keyring = None self.keyname = None self.keyalgorithm = tsig.default_algorithm self.request_mac = b'' self.other_data = b'' self.tsig_error = 0 self.fudge = 300 self.original_id = self.id self.mac = b'' self.xfr = False self.origin = None self.tsig_ctx = None self.had_tsig = False self.multi = False self.first = True self.index : Dict[Tuple[rrset.RRset, name.Name, int, int, Union[int,str], int], rrset.RRset] = {} def from_text(a : str) -> Message: ... def from_wire(wire, keyring : Optional[Dict[name.Name,bytes]] = None, request_mac = b'', xfr=False, origin=None, tsig_ctx : Optional[hmac.HMAC] = None, multi=False, first=True, question_only=False, one_rr_per_rrset=False, ignore_trailing=False) -> Message: ... def make_response(query : Message, recursion_available=False, our_payload=8192, fudge=300) -> Message: ... def make_query(qname : Union[name.Name,str], rdtype : Union[str,int], rdclass : Union[int,str] =rdataclass.IN, use_edns : Optional[bool] = None, want_dnssec=False, ednsflags : Optional[int] = None, payload : Optional[int] = None, request_payload : Optional[int] = None, options : Optional[List[edns.Option]] = None) -> Message: ... dnspython-1.16.0/dns/name.py000066400000000000000000000750331340301174400157100ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Names. """ from io import BytesIO import struct import sys import copy import encodings.idna try: import idna have_idna_2008 = True except ImportError: have_idna_2008 = False import dns.exception import dns.wiredata from ._compat import long, binary_type, text_type, unichr, maybe_decode try: maxint = sys.maxint # pylint: disable=sys-max-int except AttributeError: maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1 # fullcompare() result values #: The compared names have no relationship to each other. NAMERELN_NONE = 0 #: the first name is a superdomain of the second. NAMERELN_SUPERDOMAIN = 1 #: The first name is a subdomain of the second. NAMERELN_SUBDOMAIN = 2 #: The compared names are equal. NAMERELN_EQUAL = 3 #: The compared names have a common ancestor. NAMERELN_COMMONANCESTOR = 4 class EmptyLabel(dns.exception.SyntaxError): """A DNS label is empty.""" class BadEscape(dns.exception.SyntaxError): """An escaped code in a text format of DNS name is invalid.""" class BadPointer(dns.exception.FormError): """A DNS compression pointer points forward instead of backward.""" class BadLabelType(dns.exception.FormError): """The label type in DNS name wire format is unknown.""" class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): """An attempt was made to convert a non-absolute name to wire when there was also a non-absolute (or missing) origin.""" class NameTooLong(dns.exception.FormError): """A DNS name is > 255 octets long.""" class LabelTooLong(dns.exception.SyntaxError): """A DNS label is > 63 octets long.""" class AbsoluteConcatenation(dns.exception.DNSException): """An attempt was made to append anything other than the empty name to an absolute DNS name.""" class NoParent(dns.exception.DNSException): """An attempt was made to get the parent of the root name or the empty name.""" class NoIDNA2008(dns.exception.DNSException): """IDNA 2008 processing was requested but the idna module is not available.""" class IDNAException(dns.exception.DNSException): """IDNA processing raised an exception.""" supp_kwargs = {'idna_exception'} fmt = "IDNA processing exception: {idna_exception}" class IDNACodec(object): """Abstract base class for IDNA encoder/decoders.""" def __init__(self): pass def encode(self, label): raise NotImplementedError def decode(self, label): # We do not apply any IDNA policy on decode; we just downcased = label.lower() if downcased.startswith(b'xn--'): try: label = downcased[4:].decode('punycode') except Exception as e: raise IDNAException(idna_exception=e) else: label = maybe_decode(label) return _escapify(label, True) class IDNA2003Codec(IDNACodec): """IDNA 2003 encoder/decoder.""" def __init__(self, strict_decode=False): """Initialize the IDNA 2003 encoder/decoder. *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking is done when decoding. This can cause failures if the name was encoded with IDNA2008. The default is `False`. """ super(IDNA2003Codec, self).__init__() self.strict_decode = strict_decode def encode(self, label): """Encode *label*.""" if label == '': return b'' try: return encodings.idna.ToASCII(label) except UnicodeError: raise LabelTooLong def decode(self, label): """Decode *label*.""" if not self.strict_decode: return super(IDNA2003Codec, self).decode(label) if label == b'': return u'' try: return _escapify(encodings.idna.ToUnicode(label), True) except Exception as e: raise IDNAException(idna_exception=e) class IDNA2008Codec(IDNACodec): """IDNA 2008 encoder/decoder. *uts_46* is a ``bool``. If True, apply Unicode IDNA compatibility processing as described in Unicode Technical Standard #46 (http://unicode.org/reports/tr46/). If False, do not apply the mapping. The default is False. *transitional* is a ``bool``: If True, use the "transitional" mode described in Unicode Technical Standard #46. The default is False. *allow_pure_ascii* is a ``bool``. If True, then a label which consists of only ASCII characters is allowed. This is less strict than regular IDNA 2008, but is also necessary for mixed names, e.g. a name with starting with "_sip._tcp." and ending in an IDN suffix which would otherwise be disallowed. The default is False. *strict_decode* is a ``bool``: If True, then IDNA2008 checking is done when decoding. This can cause failures if the name was encoded with IDNA2003. The default is False. """ def __init__(self, uts_46=False, transitional=False, allow_pure_ascii=False, strict_decode=False): """Initialize the IDNA 2008 encoder/decoder.""" super(IDNA2008Codec, self).__init__() self.uts_46 = uts_46 self.transitional = transitional self.allow_pure_ascii = allow_pure_ascii self.strict_decode = strict_decode def is_all_ascii(self, label): for c in label: if ord(c) > 0x7f: return False return True def encode(self, label): if label == '': return b'' if self.allow_pure_ascii and self.is_all_ascii(label): return label.encode('ascii') if not have_idna_2008: raise NoIDNA2008 try: if self.uts_46: label = idna.uts46_remap(label, False, self.transitional) return idna.alabel(label) except idna.IDNAError as e: raise IDNAException(idna_exception=e) def decode(self, label): if not self.strict_decode: return super(IDNA2008Codec, self).decode(label) if label == b'': return u'' if not have_idna_2008: raise NoIDNA2008 try: if self.uts_46: label = idna.uts46_remap(label, False, False) return _escapify(idna.ulabel(label), True) except idna.IDNAError as e: raise IDNAException(idna_exception=e) _escaped = bytearray(b'"().;\\@$') IDNA_2003_Practical = IDNA2003Codec(False) IDNA_2003_Strict = IDNA2003Codec(True) IDNA_2003 = IDNA_2003_Practical IDNA_2008_Practical = IDNA2008Codec(True, False, True, False) IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False) IDNA_2008_Strict = IDNA2008Codec(False, False, False, True) IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False) IDNA_2008 = IDNA_2008_Practical def _escapify(label, unicode_mode=False): """Escape the characters in label which need it. @param unicode_mode: escapify only special and whitespace (<= 0x20) characters @returns: the escaped string @rtype: string""" if not unicode_mode: text = '' if isinstance(label, text_type): label = label.encode() for c in bytearray(label): if c in _escaped: text += '\\' + chr(c) elif c > 0x20 and c < 0x7F: text += chr(c) else: text += '\\%03d' % c return text.encode() text = u'' if isinstance(label, binary_type): label = label.decode() for c in label: if c > u'\x20' and c < u'\x7f': text += c else: if c >= u'\x7f': text += c else: text += u'\\%03d' % ord(c) return text def _validate_labels(labels): """Check for empty labels in the middle of a label sequence, labels that are too long, and for too many labels. Raises ``dns.name.NameTooLong`` if the name as a whole is too long. Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root label) and appears in a position other than the end of the label sequence """ l = len(labels) total = 0 i = -1 j = 0 for label in labels: ll = len(label) total += ll + 1 if ll > 63: raise LabelTooLong if i < 0 and label == b'': i = j j += 1 if total > 255: raise NameTooLong if i >= 0 and i != l - 1: raise EmptyLabel def _maybe_convert_to_binary(label): """If label is ``text``, convert it to ``binary``. If it is already ``binary`` just return it. """ if isinstance(label, binary_type): return label if isinstance(label, text_type): return label.encode() raise ValueError class Name(object): """A DNS name. The dns.name.Name class represents a DNS name as a tuple of labels. Each label is a `binary` in DNS wire format. Instances of the class are immutable. """ __slots__ = ['labels'] def __init__(self, labels): """*labels* is any iterable whose values are ``text`` or ``binary``. """ labels = [_maybe_convert_to_binary(x) for x in labels] super(Name, self).__setattr__('labels', tuple(labels)) _validate_labels(self.labels) def __setattr__(self, name, value): # Names are immutable raise TypeError("object doesn't support attribute assignment") def __copy__(self): return Name(self.labels) def __deepcopy__(self, memo): return Name(copy.deepcopy(self.labels, memo)) def __getstate__(self): # Names can be pickled return {'labels': self.labels} def __setstate__(self, state): super(Name, self).__setattr__('labels', state['labels']) _validate_labels(self.labels) def is_absolute(self): """Is the most significant label of this name the root label? Returns a ``bool``. """ return len(self.labels) > 0 and self.labels[-1] == b'' def is_wild(self): """Is this name wild? (I.e. Is the least significant label '*'?) Returns a ``bool``. """ return len(self.labels) > 0 and self.labels[0] == b'*' def __hash__(self): """Return a case-insensitive hash of the name. Returns an ``int``. """ h = long(0) for label in self.labels: for c in bytearray(label.lower()): h += (h << 3) + c return int(h % maxint) def fullcompare(self, other): """Compare two names, returning a 3-tuple ``(relation, order, nlabels)``. *relation* describes the relation ship between the names, and is one of: ``dns.name.NAMERELN_NONE``, ``dns.name.NAMERELN_SUPERDOMAIN``, ``dns.name.NAMERELN_SUBDOMAIN``, ``dns.name.NAMERELN_EQUAL``, or ``dns.name.NAMERELN_COMMONANCESTOR``. *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and == 0 if *self* == *other*. A relative name is always less than an absolute name. If both names have the same relativity, then the DNSSEC order relation is used to order them. *nlabels* is the number of significant labels that the two names have in common. Here are some examples. Names ending in "." are absolute names, those not ending in "." are relative names. ============= ============= =========== ===== ======= self other relation order nlabels ============= ============= =========== ===== ======= www.example. www.example. equal 0 3 www.example. example. subdomain > 0 2 example. www.example. superdomain < 0 2 example1.com. example2.com. common anc. < 0 2 example1 example2. none < 0 0 example1. example2 none > 0 0 ============= ============= =========== ===== ======= """ sabs = self.is_absolute() oabs = other.is_absolute() if sabs != oabs: if sabs: return (NAMERELN_NONE, 1, 0) else: return (NAMERELN_NONE, -1, 0) l1 = len(self.labels) l2 = len(other.labels) ldiff = l1 - l2 if ldiff < 0: l = l1 else: l = l2 order = 0 nlabels = 0 namereln = NAMERELN_NONE while l > 0: l -= 1 l1 -= 1 l2 -= 1 label1 = self.labels[l1].lower() label2 = other.labels[l2].lower() if label1 < label2: order = -1 if nlabels > 0: namereln = NAMERELN_COMMONANCESTOR return (namereln, order, nlabels) elif label1 > label2: order = 1 if nlabels > 0: namereln = NAMERELN_COMMONANCESTOR return (namereln, order, nlabels) nlabels += 1 order = ldiff if ldiff < 0: namereln = NAMERELN_SUPERDOMAIN elif ldiff > 0: namereln = NAMERELN_SUBDOMAIN else: namereln = NAMERELN_EQUAL return (namereln, order, nlabels) def is_subdomain(self, other): """Is self a subdomain of other? Note that the notion of subdomain includes equality, e.g. "dnpython.org" is a subdomain of itself. Returns a ``bool``. """ (nr, o, nl) = self.fullcompare(other) if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL: return True return False def is_superdomain(self, other): """Is self a superdomain of other? Note that the notion of superdomain includes equality, e.g. "dnpython.org" is a superdomain of itself. Returns a ``bool``. """ (nr, o, nl) = self.fullcompare(other) if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL: return True return False def canonicalize(self): """Return a name which is equal to the current name, but is in DNSSEC canonical form. """ return Name([x.lower() for x in self.labels]) def __eq__(self, other): if isinstance(other, Name): return self.fullcompare(other)[1] == 0 else: return False def __ne__(self, other): if isinstance(other, Name): return self.fullcompare(other)[1] != 0 else: return True def __lt__(self, other): if isinstance(other, Name): return self.fullcompare(other)[1] < 0 else: return NotImplemented def __le__(self, other): if isinstance(other, Name): return self.fullcompare(other)[1] <= 0 else: return NotImplemented def __ge__(self, other): if isinstance(other, Name): return self.fullcompare(other)[1] >= 0 else: return NotImplemented def __gt__(self, other): if isinstance(other, Name): return self.fullcompare(other)[1] > 0 else: return NotImplemented def __repr__(self): return '' def __str__(self): return self.to_text(False) def to_text(self, omit_final_dot=False): """Convert name to DNS text format. *omit_final_dot* is a ``bool``. If True, don't emit the final dot (denoting the root label) for absolute names. The default is False. Returns a ``text``. """ if len(self.labels) == 0: return maybe_decode(b'@') if len(self.labels) == 1 and self.labels[0] == b'': return maybe_decode(b'.') if omit_final_dot and self.is_absolute(): l = self.labels[:-1] else: l = self.labels s = b'.'.join(map(_escapify, l)) return maybe_decode(s) def to_unicode(self, omit_final_dot=False, idna_codec=None): """Convert name to Unicode text format. IDN ACE labels are converted to Unicode. *omit_final_dot* is a ``bool``. If True, don't emit the final dot (denoting the root label) for absolute names. The default is False. *idna_codec* specifies the IDNA encoder/decoder. If None, the dns.name.IDNA_2003_Practical encoder/decoder is used. The IDNA_2003_Practical decoder does not impose any policy, it just decodes punycode, so if you don't want checking for compliance, you can use this decoder for IDNA2008 as well. Returns a ``text``. """ if len(self.labels) == 0: return u'@' if len(self.labels) == 1 and self.labels[0] == b'': return u'.' if omit_final_dot and self.is_absolute(): l = self.labels[:-1] else: l = self.labels if idna_codec is None: idna_codec = IDNA_2003_Practical return u'.'.join([idna_codec.decode(x) for x in l]) def to_digestable(self, origin=None): """Convert name to a format suitable for digesting in hashes. The name is canonicalized and converted to uncompressed wire format. All names in wire format are absolute. If the name is a relative name, then an origin must be supplied. *origin* is a ``dns.name.Name`` or ``None``. If the name is relative and origin is not ``None``, then origin will be appended to the name. Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is relative and no origin was provided. Returns a ``binary``. """ if not self.is_absolute(): if origin is None or not origin.is_absolute(): raise NeedAbsoluteNameOrOrigin labels = list(self.labels) labels.extend(list(origin.labels)) else: labels = self.labels dlabels = [struct.pack('!B%ds' % len(x), len(x), x.lower()) for x in labels] return b''.join(dlabels) def to_wire(self, file=None, compress=None, origin=None): """Convert name to wire format, possibly compressing it. *file* is the file where the name is emitted (typically a BytesIO file). If ``None`` (the default), a ``binary`` containing the wire name will be returned. *compress*, a ``dict``, is the compression table to use. If ``None`` (the default), names will not be compressed. *origin* is a ``dns.name.Name`` or ``None``. If the name is relative and origin is not ``None``, then *origin* will be appended to it. Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is relative and no origin was provided. Returns a ``binary`` or ``None``. """ if file is None: file = BytesIO() want_return = True else: want_return = False if not self.is_absolute(): if origin is None or not origin.is_absolute(): raise NeedAbsoluteNameOrOrigin labels = list(self.labels) labels.extend(list(origin.labels)) else: labels = self.labels i = 0 for label in labels: n = Name(labels[i:]) i += 1 if compress is not None: pos = compress.get(n) else: pos = None if pos is not None: value = 0xc000 + pos s = struct.pack('!H', value) file.write(s) break else: if compress is not None and len(n) > 1: pos = file.tell() if pos <= 0x3fff: compress[n] = pos l = len(label) file.write(struct.pack('!B', l)) if l > 0: file.write(label) if want_return: return file.getvalue() def __len__(self): """The length of the name (in labels). Returns an ``int``. """ return len(self.labels) def __getitem__(self, index): return self.labels[index] def __add__(self, other): return self.concatenate(other) def __sub__(self, other): return self.relativize(other) def split(self, depth): """Split a name into a prefix and suffix names at the specified depth. *depth* is an ``int`` specifying the number of labels in the suffix Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the name. Returns the tuple ``(prefix, suffix)``. """ l = len(self.labels) if depth == 0: return (self, dns.name.empty) elif depth == l: return (dns.name.empty, self) elif depth < 0 or depth > l: raise ValueError( 'depth must be >= 0 and <= the length of the name') return (Name(self[: -depth]), Name(self[-depth:])) def concatenate(self, other): """Return a new name which is the concatenation of self and other. Raises ``dns.name.AbsoluteConcatenation`` if the name is absolute and *other* is not the empty name. Returns a ``dns.name.Name``. """ if self.is_absolute() and len(other) > 0: raise AbsoluteConcatenation labels = list(self.labels) labels.extend(list(other.labels)) return Name(labels) def relativize(self, origin): """If the name is a subdomain of *origin*, return a new name which is the name relative to origin. Otherwise return the name. For example, relativizing ``www.dnspython.org.`` to origin ``dnspython.org.`` returns the name ``www``. Relativizing ``example.`` to origin ``dnspython.org.`` returns ``example.``. Returns a ``dns.name.Name``. """ if origin is not None and self.is_subdomain(origin): return Name(self[: -len(origin)]) else: return self def derelativize(self, origin): """If the name is a relative name, return a new name which is the concatenation of the name and origin. Otherwise return the name. For example, derelativizing ``www`` to origin ``dnspython.org.`` returns the name ``www.dnspython.org.``. Derelativizing ``example.`` to origin ``dnspython.org.`` returns ``example.``. Returns a ``dns.name.Name``. """ if not self.is_absolute(): return self.concatenate(origin) else: return self def choose_relativity(self, origin=None, relativize=True): """Return a name with the relativity desired by the caller. If *origin* is ``None``, then the name is returned. Otherwise, if *relativize* is ``True`` the name is relativized, and if *relativize* is ``False`` the name is derelativized. Returns a ``dns.name.Name``. """ if origin: if relativize: return self.relativize(origin) else: return self.derelativize(origin) else: return self def parent(self): """Return the parent of the name. For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``. Raises ``dns.name.NoParent`` if the name is either the root name or the empty name, and thus has no parent. Returns a ``dns.name.Name``. """ if self == root or self == empty: raise NoParent return Name(self.labels[1:]) #: The root name, '.' root = Name([b'']) #: The empty name. empty = Name([]) def from_unicode(text, origin=root, idna_codec=None): """Convert unicode text into a Name object. Labels are encoded in IDN ACE form according to rules specified by the IDNA codec. *text*, a ``text``, is the text to convert into a name. *origin*, a ``dns.name.Name``, specifies the origin to append to non-absolute names. The default is the root name. *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder is used. Returns a ``dns.name.Name``. """ if not isinstance(text, text_type): raise ValueError("input to from_unicode() must be a unicode string") if not (origin is None or isinstance(origin, Name)): raise ValueError("origin must be a Name or None") labels = [] label = u'' escaping = False edigits = 0 total = 0 if idna_codec is None: idna_codec = IDNA_2003 if text == u'@': text = u'' if text: if text == u'.': return Name([b'']) # no Unicode "u" on this constant! for c in text: if escaping: if edigits == 0: if c.isdigit(): total = int(c) edigits += 1 else: label += c escaping = False else: if not c.isdigit(): raise BadEscape total *= 10 total += int(c) edigits += 1 if edigits == 3: escaping = False label += unichr(total) elif c in [u'.', u'\u3002', u'\uff0e', u'\uff61']: if len(label) == 0: raise EmptyLabel labels.append(idna_codec.encode(label)) label = u'' elif c == u'\\': escaping = True edigits = 0 total = 0 else: label += c if escaping: raise BadEscape if len(label) > 0: labels.append(idna_codec.encode(label)) else: labels.append(b'') if (len(labels) == 0 or labels[-1] != b'') and origin is not None: labels.extend(list(origin.labels)) return Name(labels) def from_text(text, origin=root, idna_codec=None): """Convert text into a Name object. *text*, a ``text``, is the text to convert into a name. *origin*, a ``dns.name.Name``, specifies the origin to append to non-absolute names. The default is the root name. *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder is used. Returns a ``dns.name.Name``. """ if isinstance(text, text_type): return from_unicode(text, origin, idna_codec) if not isinstance(text, binary_type): raise ValueError("input to from_text() must be a string") if not (origin is None or isinstance(origin, Name)): raise ValueError("origin must be a Name or None") labels = [] label = b'' escaping = False edigits = 0 total = 0 if text == b'@': text = b'' if text: if text == b'.': return Name([b'']) for c in bytearray(text): byte_ = struct.pack('!B', c) if escaping: if edigits == 0: if byte_.isdigit(): total = int(byte_) edigits += 1 else: label += byte_ escaping = False else: if not byte_.isdigit(): raise BadEscape total *= 10 total += int(byte_) edigits += 1 if edigits == 3: escaping = False label += struct.pack('!B', total) elif byte_ == b'.': if len(label) == 0: raise EmptyLabel labels.append(label) label = b'' elif byte_ == b'\\': escaping = True edigits = 0 total = 0 else: label += byte_ if escaping: raise BadEscape if len(label) > 0: labels.append(label) else: labels.append(b'') if (len(labels) == 0 or labels[-1] != b'') and origin is not None: labels.extend(list(origin.labels)) return Name(labels) def from_wire(message, current): """Convert possibly compressed wire format into a Name. *message* is a ``binary`` containing an entire DNS message in DNS wire form. *current*, an ``int``, is the offset of the beginning of the name from the start of the message Raises ``dns.name.BadPointer`` if a compression pointer did not point backwards in the message. Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. Returns a ``(dns.name.Name, int)`` tuple consisting of the name that was read and the number of bytes of the wire format message which were consumed reading it. """ if not isinstance(message, binary_type): raise ValueError("input to from_wire() must be a byte string") message = dns.wiredata.maybe_wrap(message) labels = [] biggest_pointer = current hops = 0 count = message[current] current += 1 cused = 1 while count != 0: if count < 64: labels.append(message[current: current + count].unwrap()) current += count if hops == 0: cused += count elif count >= 192: current = (count & 0x3f) * 256 + message[current] if hops == 0: cused += 1 if current >= biggest_pointer: raise BadPointer biggest_pointer = current hops += 1 else: raise BadLabelType count = message[current] current += 1 if hops == 0: cused += 1 labels.append('') return (Name(labels), cused) dnspython-1.16.0/dns/name.pyi000066400000000000000000000026241340301174400160550ustar00rootroot00000000000000from typing import Optional, Union, Tuple, Iterable, List class Name: def is_subdomain(self, o : Name) -> bool: ... def is_superdomain(self, o : Name) -> bool: ... def __init__(self, labels : Iterable[Union[bytes,str]]) -> None: self.labels : List[bytes] def is_absolute(self) -> bool: ... def is_wild(self) -> bool: ... def fullcompare(self, other) -> Tuple[int,int,int]: ... def canonicalize(self) -> Name: ... def __lt__(self, other : Name): ... def __le__(self, other : Name): ... def __ge__(self, other : Name): ... def __gt__(self, other : Name): ... def to_text(self, omit_final_dot=False) -> str: ... def to_unicode(self, omit_final_dot=False, idna_codec=None) -> str: ... def to_digestable(self, origin=None) -> bytes: ... def to_wire(self, file=None, compress=None, origin=None) -> Optional[bytes]: ... def __add__(self, other : Name): ... def __sub__(self, other : Name): ... def split(self, depth) -> List[Tuple[str,str]]: ... def concatenate(self, other : Name) -> Name: ... def relativize(self, origin): ... def derelativize(self, origin): ... def choose_relativity(self, origin : Optional[Name] = None, relativize=True): ... def parent(self) -> Name: ... class IDNACodec: pass def from_text(text, origin : Optional[Name] = Name('.'), idna_codec : Optional[IDNACodec] = None) -> Name: ... empty : Name dnspython-1.16.0/dns/namedict.py000066400000000000000000000075661340301174400165620ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # Copyright (C) 2016 Coresec Systems AB # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC # SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS name dictionary""" import collections import dns.name from ._compat import xrange class NameDict(collections.MutableMapping): """A dictionary whose keys are dns.name.Name objects. In addition to being like a regular Python dictionary, this dictionary can also get the deepest match for a given key. """ __slots__ = ["max_depth", "max_depth_items", "__store"] def __init__(self, *args, **kwargs): super(NameDict, self).__init__() self.__store = dict() #: the maximum depth of the keys that have ever been added self.max_depth = 0 #: the number of items of maximum depth self.max_depth_items = 0 self.update(dict(*args, **kwargs)) def __update_max_depth(self, key): if len(key) == self.max_depth: self.max_depth_items = self.max_depth_items + 1 elif len(key) > self.max_depth: self.max_depth = len(key) self.max_depth_items = 1 def __getitem__(self, key): return self.__store[key] def __setitem__(self, key, value): if not isinstance(key, dns.name.Name): raise ValueError('NameDict key must be a name') self.__store[key] = value self.__update_max_depth(key) def __delitem__(self, key): value = self.__store.pop(key) if len(value) == self.max_depth: self.max_depth_items = self.max_depth_items - 1 if self.max_depth_items == 0: self.max_depth = 0 for k in self.__store: self.__update_max_depth(k) def __iter__(self): return iter(self.__store) def __len__(self): return len(self.__store) def has_key(self, key): return key in self.__store def get_deepest_match(self, name): """Find the deepest match to *fname* in the dictionary. The deepest match is the longest name in the dictionary which is a superdomain of *name*. Note that *superdomain* includes matching *name* itself. *name*, a ``dns.name.Name``, the name to find. Returns a ``(key, value)`` where *key* is the deepest ``dns.name.Name``, and *value* is the value associated with *key*. """ depth = len(name) if depth > self.max_depth: depth = self.max_depth for i in xrange(-depth, 0): n = dns.name.Name(name[i:]) if n in self: return (n, self[n]) v = self[dns.name.empty] return (dns.name.empty, v) dnspython-1.16.0/dns/node.py000066400000000000000000000143201340301174400157050ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS nodes. A node is a set of rdatasets.""" from io import StringIO import dns.rdataset import dns.rdatatype import dns.renderer class Node(object): """A Node is a set of rdatasets.""" __slots__ = ['rdatasets'] def __init__(self): #: the set of rdatsets, represented as a list. self.rdatasets = [] def to_text(self, name, **kw): """Convert a node to text format. Each rdataset at the node is printed. Any keyword arguments to this method are passed on to the rdataset's to_text() method. *name*, a ``dns.name.Name`` or ``text``, the owner name of the rdatasets. Returns a ``text``. """ s = StringIO() for rds in self.rdatasets: if len(rds) > 0: s.write(rds.to_text(name, **kw)) s.write(u'\n') return s.getvalue()[:-1] def __repr__(self): return '' def __eq__(self, other): # # This is inefficient. Good thing we don't need to do it much. # for rd in self.rdatasets: if rd not in other.rdatasets: return False for rd in other.rdatasets: if rd not in self.rdatasets: return False return True def __ne__(self, other): return not self.__eq__(other) def __len__(self): return len(self.rdatasets) def __iter__(self): return iter(self.rdatasets) def find_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE, create=False): """Find an rdataset matching the specified properties in the current node. *rdclass*, an ``int``, the class of the rdataset. *rdtype*, an ``int``, the type of the rdataset. *covers*, an ``int``, the covered type. Usually this value is dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or dns.rdatatype.RRSIG, then the covers value will be the rdata type the SIG/RRSIG covers. The library treats the SIG and RRSIG types as if they were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much easier to work with than if RRSIGs covering different rdata types were aggregated into a single RRSIG rdataset. *create*, a ``bool``. If True, create the rdataset if it is not found. Raises ``KeyError`` if an rdataset of the desired type and class does not exist and *create* is not ``True``. Returns a ``dns.rdataset.Rdataset``. """ for rds in self.rdatasets: if rds.match(rdclass, rdtype, covers): return rds if not create: raise KeyError rds = dns.rdataset.Rdataset(rdclass, rdtype) self.rdatasets.append(rds) return rds def get_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE, create=False): """Get an rdataset matching the specified properties in the current node. None is returned if an rdataset of the specified type and class does not exist and *create* is not ``True``. *rdclass*, an ``int``, the class of the rdataset. *rdtype*, an ``int``, the type of the rdataset. *covers*, an ``int``, the covered type. Usually this value is dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or dns.rdatatype.RRSIG, then the covers value will be the rdata type the SIG/RRSIG covers. The library treats the SIG and RRSIG types as if they were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much easier to work with than if RRSIGs covering different rdata types were aggregated into a single RRSIG rdataset. *create*, a ``bool``. If True, create the rdataset if it is not found. Returns a ``dns.rdataset.Rdataset`` or ``None``. """ try: rds = self.find_rdataset(rdclass, rdtype, covers, create) except KeyError: rds = None return rds def delete_rdataset(self, rdclass, rdtype, covers=dns.rdatatype.NONE): """Delete the rdataset matching the specified properties in the current node. If a matching rdataset does not exist, it is not an error. *rdclass*, an ``int``, the class of the rdataset. *rdtype*, an ``int``, the type of the rdataset. *covers*, an ``int``, the covered type. """ rds = self.get_rdataset(rdclass, rdtype, covers) if rds is not None: self.rdatasets.remove(rds) def replace_rdataset(self, replacement): """Replace an rdataset. It is not an error if there is no rdataset matching *replacement*. Ownership of the *replacement* object is transferred to the node; in other words, this method does not store a copy of *replacement* at the node, it stores *replacement* itself. *replacement*, a ``dns.rdataset.Rdataset``. Raises ``ValueError`` if *replacement* is not a ``dns.rdataset.Rdataset``. """ if not isinstance(replacement, dns.rdataset.Rdataset): raise ValueError('replacement is not an rdataset') self.delete_rdataset(replacement.rdclass, replacement.rdtype, replacement.covers) self.rdatasets.append(replacement) dnspython-1.16.0/dns/node.pyi000066400000000000000000000013331340301174400160560ustar00rootroot00000000000000from typing import List, Optional, Union from . import rdataset, rdatatype, name class Node: def __init__(self): self.rdatasets : List[rdataset.Rdataset] def to_text(self, name : Union[str,name.Name], **kw) -> str: ... def find_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE, create=False) -> rdataset.Rdataset: ... def get_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE, create=False) -> Optional[rdataset.Rdataset]: ... def delete_rdataset(self, rdclass : int, rdtype : int, covers=rdatatype.NONE): ... def replace_rdataset(self, replacement : rdataset.Rdataset) -> None: ... dnspython-1.16.0/dns/opcode.py000066400000000000000000000054201340301174400162320ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Opcodes.""" import dns.exception #: Query QUERY = 0 #: Inverse Query (historical) IQUERY = 1 #: Server Status (unspecified and unimplemented anywhere) STATUS = 2 #: Notify NOTIFY = 4 #: Dynamic Update UPDATE = 5 _by_text = { 'QUERY': QUERY, 'IQUERY': IQUERY, 'STATUS': STATUS, 'NOTIFY': NOTIFY, 'UPDATE': UPDATE } # We construct the inverse mapping programmatically to ensure that we # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that # would cause the mapping not to be true inverse. _by_value = {y: x for x, y in _by_text.items()} class UnknownOpcode(dns.exception.DNSException): """An DNS opcode is unknown.""" def from_text(text): """Convert text into an opcode. *text*, a ``text``, the textual opcode Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. Returns an ``int``. """ if text.isdigit(): value = int(text) if value >= 0 and value <= 15: return value value = _by_text.get(text.upper()) if value is None: raise UnknownOpcode return value def from_flags(flags): """Extract an opcode from DNS message flags. *flags*, an ``int``, the DNS flags. Returns an ``int``. """ return (flags & 0x7800) >> 11 def to_flags(value): """Convert an opcode to a value suitable for ORing into DNS message flags. *value*, an ``int``, the DNS opcode value. Returns an ``int``. """ return (value << 11) & 0x7800 def to_text(value): """Convert an opcode to text. *value*, an ``int`` the opcode value, Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. Returns a ``text``. """ text = _by_value.get(value) if text is None: text = str(value) return text def is_update(flags): """Is the opcode in flags UPDATE? *flags*, an ``int``, the DNS message flags. Returns a ``bool``. """ return from_flags(flags) == UPDATE dnspython-1.16.0/dns/py.typed000066400000000000000000000000001340301174400160730ustar00rootroot00000000000000dnspython-1.16.0/dns/query.py000066400000000000000000000562131340301174400161340ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Talk to a DNS server.""" from __future__ import generators import errno import select import socket import struct import sys import time import dns.exception import dns.inet import dns.name import dns.message import dns.rcode import dns.rdataclass import dns.rdatatype from ._compat import long, string_types, PY3 if PY3: select_error = OSError else: select_error = select.error # Function used to create a socket. Can be overridden if needed in special # situations. socket_factory = socket.socket class UnexpectedSource(dns.exception.DNSException): """A DNS query response came from an unexpected address or port.""" class BadResponse(dns.exception.FormError): """A DNS query response does not respond to the question asked.""" class TransferError(dns.exception.DNSException): """A zone transfer response got a non-zero rcode.""" def __init__(self, rcode): message = 'Zone transfer error: %s' % dns.rcode.to_text(rcode) super(TransferError, self).__init__(message) self.rcode = rcode def _compute_expiration(timeout): if timeout is None: return None else: return time.time() + timeout # This module can use either poll() or select() as the "polling backend". # # A backend function takes an fd, bools for readability, writablity, and # error detection, and a timeout. def _poll_for(fd, readable, writable, error, timeout): """Poll polling backend.""" event_mask = 0 if readable: event_mask |= select.POLLIN if writable: event_mask |= select.POLLOUT if error: event_mask |= select.POLLERR pollable = select.poll() pollable.register(fd, event_mask) if timeout: event_list = pollable.poll(long(timeout * 1000)) else: event_list = pollable.poll() return bool(event_list) def _select_for(fd, readable, writable, error, timeout): """Select polling backend.""" rset, wset, xset = [], [], [] if readable: rset = [fd] if writable: wset = [fd] if error: xset = [fd] if timeout is None: (rcount, wcount, xcount) = select.select(rset, wset, xset) else: (rcount, wcount, xcount) = select.select(rset, wset, xset, timeout) return bool((rcount or wcount or xcount)) def _wait_for(fd, readable, writable, error, expiration): # Use the selected polling backend to wait for any of the specified # events. An "expiration" absolute time is converted into a relative # timeout. done = False while not done: if expiration is None: timeout = None else: timeout = expiration - time.time() if timeout <= 0.0: raise dns.exception.Timeout try: if not _polling_backend(fd, readable, writable, error, timeout): raise dns.exception.Timeout except select_error as e: if e.args[0] != errno.EINTR: raise e done = True def _set_polling_backend(fn): # Internal API. Do not use. global _polling_backend _polling_backend = fn if hasattr(select, 'poll'): # Prefer poll() on platforms that support it because it has no # limits on the maximum value of a file descriptor (plus it will # be more efficient for high values). _polling_backend = _poll_for else: _polling_backend = _select_for def _wait_for_readable(s, expiration): _wait_for(s, True, False, True, expiration) def _wait_for_writable(s, expiration): _wait_for(s, False, True, True, expiration) def _addresses_equal(af, a1, a2): # Convert the first value of the tuple, which is a textual format # address into binary form, so that we are not confused by different # textual representations of the same address try: n1 = dns.inet.inet_pton(af, a1[0]) n2 = dns.inet.inet_pton(af, a2[0]) except dns.exception.SyntaxError: return False return n1 == n2 and a1[1:] == a2[1:] def _destination_and_source(af, where, port, source, source_port): # Apply defaults and compute destination and source tuples # suitable for use in connect(), sendto(), or bind(). if af is None: try: af = dns.inet.af_for_address(where) except Exception: af = dns.inet.AF_INET if af == dns.inet.AF_INET: destination = (where, port) if source is not None or source_port != 0: if source is None: source = '0.0.0.0' source = (source, source_port) elif af == dns.inet.AF_INET6: destination = (where, port, 0, 0) if source is not None or source_port != 0: if source is None: source = '::' source = (source, source_port, 0, 0) return (af, destination, source) def send_udp(sock, what, destination, expiration=None): """Send a DNS message to the specified UDP socket. *sock*, a ``socket``. *what*, a ``binary`` or ``dns.message.Message``, the message to send. *destination*, a destination tuple appropriate for the address family of the socket, specifying where to send the query. *expiration*, a ``float`` or ``None``, the absolute time at which a timeout exception should be raised. If ``None``, no timeout will occur. Returns an ``(int, float)`` tuple of bytes sent and the sent time. """ if isinstance(what, dns.message.Message): what = what.to_wire() _wait_for_writable(sock, expiration) sent_time = time.time() n = sock.sendto(what, destination) return (n, sent_time) def receive_udp(sock, destination, expiration=None, ignore_unexpected=False, one_rr_per_rrset=False, keyring=None, request_mac=b'', ignore_trailing=False): """Read a DNS message from a UDP socket. *sock*, a ``socket``. *destination*, a destination tuple appropriate for the address family of the socket, specifying where the associated query was sent. *expiration*, a ``float`` or ``None``, the absolute time at which a timeout exception should be raised. If ``None``, no timeout will occur. *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from unexpected sources. *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. *keyring*, a ``dict``, the keyring to use for TSIG. *request_mac*, a ``binary``, the MAC of the request (for TSIG). *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the received message. Raises if the message is malformed, if network errors occur, of if there is a timeout. Returns a ``dns.message.Message`` object. """ wire = b'' while 1: _wait_for_readable(sock, expiration) (wire, from_address) = sock.recvfrom(65535) if _addresses_equal(sock.family, from_address, destination) or \ (dns.inet.is_multicast(destination[0]) and from_address[1:] == destination[1:]): break if not ignore_unexpected: raise UnexpectedSource('got a response from ' '%s instead of %s' % (from_address, destination)) received_time = time.time() r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac, one_rr_per_rrset=one_rr_per_rrset, ignore_trailing=ignore_trailing) return (r, received_time) def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, ignore_unexpected=False, one_rr_per_rrset=False, ignore_trailing=False): """Return the response obtained after sending a query via UDP. *q*, a ``dns.message.Message``, the query to send *where*, a ``text`` containing an IPv4 or IPv6 address, where to send the message. *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query times out. If ``None``, the default, wait forever. *port*, an ``int``, the port send the message to. The default is 53. *af*, an ``int``, the address family to use. The default is ``None``, which causes the address family to use to be inferred from the form of *where*. If the inference attempt fails, AF_INET is used. This parameter is historical; you need never set it. *source*, a ``text`` containing an IPv4 or IPv6 address, specifying the source address. The default is the wildcard address. *source_port*, an ``int``, the port from which to send the message. The default is 0. *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from unexpected sources. *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the received message. Returns a ``dns.message.Message``. """ wire = q.to_wire() (af, destination, source) = _destination_and_source(af, where, port, source, source_port) s = socket_factory(af, socket.SOCK_DGRAM, 0) received_time = None sent_time = None try: expiration = _compute_expiration(timeout) s.setblocking(0) if source is not None: s.bind(source) (_, sent_time) = send_udp(s, wire, destination, expiration) (r, received_time) = receive_udp(s, destination, expiration, ignore_unexpected, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing) finally: if sent_time is None or received_time is None: response_time = 0 else: response_time = received_time - sent_time s.close() r.time = response_time if not q.is_response(r): raise BadResponse return r def _net_read(sock, count, expiration): """Read the specified number of bytes from sock. Keep trying until we either get the desired amount, or we hit EOF. A Timeout exception will be raised if the operation is not completed by the expiration time. """ s = b'' while count > 0: _wait_for_readable(sock, expiration) n = sock.recv(count) if n == b'': raise EOFError count = count - len(n) s = s + n return s def _net_write(sock, data, expiration): """Write the specified data to the socket. A Timeout exception will be raised if the operation is not completed by the expiration time. """ current = 0 l = len(data) while current < l: _wait_for_writable(sock, expiration) current += sock.send(data[current:]) def send_tcp(sock, what, expiration=None): """Send a DNS message to the specified TCP socket. *sock*, a ``socket``. *what*, a ``binary`` or ``dns.message.Message``, the message to send. *expiration*, a ``float`` or ``None``, the absolute time at which a timeout exception should be raised. If ``None``, no timeout will occur. Returns an ``(int, float)`` tuple of bytes sent and the sent time. """ if isinstance(what, dns.message.Message): what = what.to_wire() l = len(what) # copying the wire into tcpmsg is inefficient, but lets us # avoid writev() or doing a short write that would get pushed # onto the net tcpmsg = struct.pack("!H", l) + what _wait_for_writable(sock, expiration) sent_time = time.time() _net_write(sock, tcpmsg, expiration) return (len(tcpmsg), sent_time) def receive_tcp(sock, expiration=None, one_rr_per_rrset=False, keyring=None, request_mac=b'', ignore_trailing=False): """Read a DNS message from a TCP socket. *sock*, a ``socket``. *expiration*, a ``float`` or ``None``, the absolute time at which a timeout exception should be raised. If ``None``, no timeout will occur. *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. *keyring*, a ``dict``, the keyring to use for TSIG. *request_mac*, a ``binary``, the MAC of the request (for TSIG). *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the received message. Raises if the message is malformed, if network errors occur, of if there is a timeout. Returns a ``dns.message.Message`` object. """ ldata = _net_read(sock, 2, expiration) (l,) = struct.unpack("!H", ldata) wire = _net_read(sock, l, expiration) received_time = time.time() r = dns.message.from_wire(wire, keyring=keyring, request_mac=request_mac, one_rr_per_rrset=one_rr_per_rrset, ignore_trailing=ignore_trailing) return (r, received_time) def _connect(s, address): try: s.connect(address) except socket.error: (ty, v) = sys.exc_info()[:2] if hasattr(v, 'errno'): v_err = v.errno else: v_err = v[0] if v_err not in [errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EALREADY]: raise v def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, one_rr_per_rrset=False, ignore_trailing=False): """Return the response obtained after sending a query via TCP. *q*, a ``dns.message.Message``, the query to send *where*, a ``text`` containing an IPv4 or IPv6 address, where to send the message. *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query times out. If ``None``, the default, wait forever. *port*, an ``int``, the port send the message to. The default is 53. *af*, an ``int``, the address family to use. The default is ``None``, which causes the address family to use to be inferred from the form of *where*. If the inference attempt fails, AF_INET is used. This parameter is historical; you need never set it. *source*, a ``text`` containing an IPv4 or IPv6 address, specifying the source address. The default is the wildcard address. *source_port*, an ``int``, the port from which to send the message. The default is 0. *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the received message. Returns a ``dns.message.Message``. """ wire = q.to_wire() (af, destination, source) = _destination_and_source(af, where, port, source, source_port) s = socket_factory(af, socket.SOCK_STREAM, 0) begin_time = None received_time = None try: expiration = _compute_expiration(timeout) s.setblocking(0) begin_time = time.time() if source is not None: s.bind(source) _connect(s, destination) send_tcp(s, wire, expiration) (r, received_time) = receive_tcp(s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing) finally: if begin_time is None or received_time is None: response_time = 0 else: response_time = received_time - begin_time s.close() r.time = response_time if not q.is_response(r): raise BadResponse return r def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, timeout=None, port=53, keyring=None, keyname=None, relativize=True, af=None, lifetime=None, source=None, source_port=0, serial=0, use_udp=False, keyalgorithm=dns.tsig.default_algorithm): """Return a generator for the responses to a zone transfer. *where*. If the inference attempt fails, AF_INET is used. This parameter is historical; you need never set it. *zone*, a ``dns.name.Name`` or ``text``, the name of the zone to transfer. *rdtype*, an ``int`` or ``text``, the type of zone transfer. The default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be used to do an incremental transfer instead. *rdclass*, an ``int`` or ``text``, the class of the zone transfer. The default is ``dns.rdataclass.IN``. *timeout*, a ``float``, the number of seconds to wait for each response message. If None, the default, wait forever. *port*, an ``int``, the port send the message to. The default is 53. *keyring*, a ``dict``, the keyring to use for TSIG. *keyname*, a ``dns.name.Name`` or ``text``, the name of the TSIG key to use. *relativize*, a ``bool``. If ``True``, all names in the zone will be relativized to the zone origin. It is essential that the relativize setting matches the one specified to ``dns.zone.from_xfr()`` if using this generator to make a zone. *af*, an ``int``, the address family to use. The default is ``None``, which causes the address family to use to be inferred from the form of *where*. If the inference attempt fails, AF_INET is used. This parameter is historical; you need never set it. *lifetime*, a ``float``, the total number of seconds to spend doing the transfer. If ``None``, the default, then there is no limit on the time the transfer may take. *source*, a ``text`` containing an IPv4 or IPv6 address, specifying the source address. The default is the wildcard address. *source_port*, an ``int``, the port from which to send the message. The default is 0. *serial*, an ``int``, the SOA serial number to use as the base for an IXFR diff sequence (only meaningful if *rdtype* is ``dns.rdatatype.IXFR``). *use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR). *keyalgorithm*, a ``dns.name.Name`` or ``text``, the TSIG algorithm to use. Raises on errors, and so does the generator. Returns a generator of ``dns.message.Message`` objects. """ if isinstance(zone, string_types): zone = dns.name.from_text(zone) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) q = dns.message.make_query(zone, rdtype, rdclass) if rdtype == dns.rdatatype.IXFR: rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA', '. . %u 0 0 0 0' % serial) q.authority.append(rrset) if keyring is not None: q.use_tsig(keyring, keyname, algorithm=keyalgorithm) wire = q.to_wire() (af, destination, source) = _destination_and_source(af, where, port, source, source_port) if use_udp: if rdtype != dns.rdatatype.IXFR: raise ValueError('cannot do a UDP AXFR') s = socket_factory(af, socket.SOCK_DGRAM, 0) else: s = socket_factory(af, socket.SOCK_STREAM, 0) s.setblocking(0) if source is not None: s.bind(source) expiration = _compute_expiration(lifetime) _connect(s, destination) l = len(wire) if use_udp: _wait_for_writable(s, expiration) s.send(wire) else: tcpmsg = struct.pack("!H", l) + wire _net_write(s, tcpmsg, expiration) done = False delete_mode = True expecting_SOA = False soa_rrset = None if relativize: origin = zone oname = dns.name.empty else: origin = None oname = zone tsig_ctx = None first = True while not done: mexpiration = _compute_expiration(timeout) if mexpiration is None or mexpiration > expiration: mexpiration = expiration if use_udp: _wait_for_readable(s, expiration) (wire, from_address) = s.recvfrom(65535) else: ldata = _net_read(s, 2, mexpiration) (l,) = struct.unpack("!H", ldata) wire = _net_read(s, l, mexpiration) is_ixfr = (rdtype == dns.rdatatype.IXFR) r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, xfr=True, origin=origin, tsig_ctx=tsig_ctx, multi=True, first=first, one_rr_per_rrset=is_ixfr) rcode = r.rcode() if rcode != dns.rcode.NOERROR: raise TransferError(rcode) tsig_ctx = r.tsig_ctx first = False answer_index = 0 if soa_rrset is None: if not r.answer or r.answer[0].name != oname: raise dns.exception.FormError( "No answer or RRset not for qname") rrset = r.answer[0] if rrset.rdtype != dns.rdatatype.SOA: raise dns.exception.FormError("first RRset is not an SOA") answer_index = 1 soa_rrset = rrset.copy() if rdtype == dns.rdatatype.IXFR: if soa_rrset[0].serial <= serial: # # We're already up-to-date. # done = True else: expecting_SOA = True # # Process SOAs in the answer section (other than the initial # SOA in the first message). # for rrset in r.answer[answer_index:]: if done: raise dns.exception.FormError("answers after final SOA") if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname: if expecting_SOA: if rrset[0].serial != serial: raise dns.exception.FormError( "IXFR base serial mismatch") expecting_SOA = False elif rdtype == dns.rdatatype.IXFR: delete_mode = not delete_mode # # If this SOA RRset is equal to the first we saw then we're # finished. If this is an IXFR we also check that we're seeing # the record in the expected part of the response. # if rrset == soa_rrset and \ (rdtype == dns.rdatatype.AXFR or (rdtype == dns.rdatatype.IXFR and delete_mode)): done = True elif expecting_SOA: # # We made an IXFR request and are expecting another # SOA RR, but saw something else, so this must be an # AXFR response. # rdtype = dns.rdatatype.AXFR expecting_SOA = False if done and q.keyring and not r.had_tsig: raise dns.exception.FormError("missing TSIG") yield r s.close() dnspython-1.16.0/dns/query.pyi000066400000000000000000000020521340301174400162750ustar00rootroot00000000000000from typing import Optional, Union, Dict, Generator, Any from . import message, tsig, rdatatype, rdataclass, name, message def tcp(q : message.Message, where : str, timeout : float = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port : int = 0, one_rr_per_rrset=False) -> message.Message: pass def xfr(where : None, zone : Union[name.Name,str], rdtype=rdatatype.AXFR, rdclass=rdataclass.IN, timeout : Optional[float] =None, port=53, keyring : Optional[Dict[name.Name, bytes]] =None, keyname : Union[str,name.Name]=None, relativize=True, af : Optional[int] =None, lifetime : Optional[float]=None, source : Optional[str] =None, source_port=0, serial=0, use_udp=False, keyalgorithm=tsig.default_algorithm) -> Generator[Any,Any,message.Message]: pass def udp(q : message.Message, where : str, timeout : Optional[float] = None, port=53, af : Optional[int] = None, source : Optional[str] = None, source_port=0, ignore_unexpected=False, one_rr_per_rrset=False) -> message.Message: ... dnspython-1.16.0/dns/rcode.py000066400000000000000000000070351340301174400160610ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Result Codes.""" import dns.exception from ._compat import long #: No error NOERROR = 0 #: Form error FORMERR = 1 #: Server failure SERVFAIL = 2 #: Name does not exist ("Name Error" in RFC 1025 terminology). NXDOMAIN = 3 #: Not implemented NOTIMP = 4 #: Refused REFUSED = 5 #: Name exists. YXDOMAIN = 6 #: RRset exists. YXRRSET = 7 #: RRset does not exist. NXRRSET = 8 #: Not authoritative. NOTAUTH = 9 #: Name not in zone. NOTZONE = 10 #: Bad EDNS version. BADVERS = 16 _by_text = { 'NOERROR': NOERROR, 'FORMERR': FORMERR, 'SERVFAIL': SERVFAIL, 'NXDOMAIN': NXDOMAIN, 'NOTIMP': NOTIMP, 'REFUSED': REFUSED, 'YXDOMAIN': YXDOMAIN, 'YXRRSET': YXRRSET, 'NXRRSET': NXRRSET, 'NOTAUTH': NOTAUTH, 'NOTZONE': NOTZONE, 'BADVERS': BADVERS } # We construct the inverse mapping programmatically to ensure that we # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that # would cause the mapping not to be a true inverse. _by_value = {y: x for x, y in _by_text.items()} class UnknownRcode(dns.exception.DNSException): """A DNS rcode is unknown.""" def from_text(text): """Convert text into an rcode. *text*, a ``text``, the textual rcode or an integer in textual form. Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown. Returns an ``int``. """ if text.isdigit(): v = int(text) if v >= 0 and v <= 4095: return v v = _by_text.get(text.upper()) if v is None: raise UnknownRcode return v def from_flags(flags, ednsflags): """Return the rcode value encoded by flags and ednsflags. *flags*, an ``int``, the DNS flags field. *ednsflags*, an ``int``, the EDNS flags field. Raises ``ValueError`` if rcode is < 0 or > 4095 Returns an ``int``. """ value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0) if value < 0 or value > 4095: raise ValueError('rcode must be >= 0 and <= 4095') return value def to_flags(value): """Return a (flags, ednsflags) tuple which encodes the rcode. *value*, an ``int``, the rcode. Raises ``ValueError`` if rcode is < 0 or > 4095. Returns an ``(int, int)`` tuple. """ if value < 0 or value > 4095: raise ValueError('rcode must be >= 0 and <= 4095') v = value & 0xf ev = long(value & 0xff0) << 20 return (v, ev) def to_text(value): """Convert rcode into text. *value*, and ``int``, the rcode. Raises ``ValueError`` if rcode is < 0 or > 4095. Returns a ``text``. """ if value < 0 or value > 4095: raise ValueError('rcode must be >= 0 and <= 4095') text = _by_value.get(value) if text is None: text = str(value) return text dnspython-1.16.0/dns/rdata.py000066400000000000000000000341631340301174400160620ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS rdata.""" from io import BytesIO import base64 import binascii import dns.exception import dns.name import dns.rdataclass import dns.rdatatype import dns.tokenizer import dns.wiredata from ._compat import xrange, string_types, text_type try: import threading as _threading except ImportError: import dummy_threading as _threading _hex_chunksize = 32 def _hexify(data, chunksize=_hex_chunksize): """Convert a binary string into its hex encoding, broken up into chunks of chunksize characters separated by a space. """ line = binascii.hexlify(data) return b' '.join([line[i:i + chunksize] for i in range(0, len(line), chunksize)]).decode() _base64_chunksize = 32 def _base64ify(data, chunksize=_base64_chunksize): """Convert a binary string into its base64 encoding, broken up into chunks of chunksize characters separated by a space. """ line = base64.b64encode(data) return b' '.join([line[i:i + chunksize] for i in range(0, len(line), chunksize)]).decode() __escaped = bytearray(b'"\\') def _escapify(qstring): """Escape the characters in a quoted string which need it.""" if isinstance(qstring, text_type): qstring = qstring.encode() if not isinstance(qstring, bytearray): qstring = bytearray(qstring) text = '' for c in qstring: if c in __escaped: text += '\\' + chr(c) elif c >= 0x20 and c < 0x7F: text += chr(c) else: text += '\\%03d' % c return text def _truncate_bitmap(what): """Determine the index of greatest byte that isn't all zeros, and return the bitmap that contains all the bytes less than that index. """ for i in xrange(len(what) - 1, -1, -1): if what[i] != 0: return what[0: i + 1] return what[0:1] class Rdata(object): """Base class for all DNS rdata types.""" __slots__ = ['rdclass', 'rdtype'] def __init__(self, rdclass, rdtype): """Initialize an rdata. *rdclass*, an ``int`` is the rdataclass of the Rdata. *rdtype*, an ``int`` is the rdatatype of the Rdata. """ self.rdclass = rdclass self.rdtype = rdtype def covers(self): """Return the type a Rdata covers. DNS SIG/RRSIG rdatas apply to a specific type; this type is returned by the covers() function. If the rdata type is not SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when creating rdatasets, allowing the rdataset to contain only RRSIGs of a particular type, e.g. RRSIG(NS). Returns an ``int``. """ return dns.rdatatype.NONE def extended_rdatatype(self): """Return a 32-bit type value, the least significant 16 bits of which are the ordinary DNS type, and the upper 16 bits of which are the "covered" type, if any. Returns an ``int``. """ return self.covers() << 16 | self.rdtype def to_text(self, origin=None, relativize=True, **kw): """Convert an rdata to text format. Returns a ``text``. """ raise NotImplementedError def to_wire(self, file, compress=None, origin=None): """Convert an rdata to wire format. Returns a ``binary``. """ raise NotImplementedError def to_digestable(self, origin=None): """Convert rdata to a format suitable for digesting in hashes. This is also the DNSSEC canonical form. Returns a ``binary``. """ f = BytesIO() self.to_wire(f, None, origin) return f.getvalue() def validate(self): """Check that the current contents of the rdata's fields are valid. If you change an rdata by assigning to its fields, it is a good idea to call validate() when you are done making changes. Raises various exceptions if there are problems. Returns ``None``. """ dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text()) def __repr__(self): covers = self.covers() if covers == dns.rdatatype.NONE: ctext = '' else: ctext = '(' + dns.rdatatype.to_text(covers) + ')' return '' def __str__(self): return self.to_text() def _cmp(self, other): """Compare an rdata with another rdata of the same rdtype and rdclass. Return < 0 if self < other in the DNSSEC ordering, 0 if self == other, and > 0 if self > other. """ our = self.to_digestable(dns.name.root) their = other.to_digestable(dns.name.root) if our == their: return 0 elif our > their: return 1 else: return -1 def __eq__(self, other): if not isinstance(other, Rdata): return False if self.rdclass != other.rdclass or self.rdtype != other.rdtype: return False return self._cmp(other) == 0 def __ne__(self, other): if not isinstance(other, Rdata): return True if self.rdclass != other.rdclass or self.rdtype != other.rdtype: return True return self._cmp(other) != 0 def __lt__(self, other): if not isinstance(other, Rdata) or \ self.rdclass != other.rdclass or self.rdtype != other.rdtype: return NotImplemented return self._cmp(other) < 0 def __le__(self, other): if not isinstance(other, Rdata) or \ self.rdclass != other.rdclass or self.rdtype != other.rdtype: return NotImplemented return self._cmp(other) <= 0 def __ge__(self, other): if not isinstance(other, Rdata) or \ self.rdclass != other.rdclass or self.rdtype != other.rdtype: return NotImplemented return self._cmp(other) >= 0 def __gt__(self, other): if not isinstance(other, Rdata) or \ self.rdclass != other.rdclass or self.rdtype != other.rdtype: return NotImplemented return self._cmp(other) > 0 def __hash__(self): return hash(self.to_digestable(dns.name.root)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): raise NotImplementedError @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): raise NotImplementedError def choose_relativity(self, origin=None, relativize=True): """Convert any domain names in the rdata to the specified relativization. """ class GenericRdata(Rdata): """Generic Rdata Class This class is used for rdata types for which we have no better implementation. It implements the DNS "unknown RRs" scheme. """ __slots__ = ['data'] def __init__(self, rdclass, rdtype, data): super(GenericRdata, self).__init__(rdclass, rdtype) self.data = data def to_text(self, origin=None, relativize=True, **kw): return r'\# %d ' % len(self.data) + _hexify(self.data) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): token = tok.get() if not token.is_identifier() or token.value != r'\#': raise dns.exception.SyntaxError( r'generic rdata does not start with \#') length = tok.get_int() chunks = [] while 1: token = tok.get() if token.is_eol_or_eof(): break chunks.append(token.value.encode()) hex = b''.join(chunks) data = binascii.unhexlify(hex) if len(data) != length: raise dns.exception.SyntaxError( 'generic rdata hex data has wrong length') return cls(rdclass, rdtype, data) def to_wire(self, file, compress=None, origin=None): file.write(self.data) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): return cls(rdclass, rdtype, wire[current: current + rdlen]) _rdata_modules = {} _module_prefix = 'dns.rdtypes' _import_lock = _threading.Lock() def get_rdata_class(rdclass, rdtype): def import_module(name): with _import_lock: mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod mod = _rdata_modules.get((rdclass, rdtype)) rdclass_text = dns.rdataclass.to_text(rdclass) rdtype_text = dns.rdatatype.to_text(rdtype) rdtype_text = rdtype_text.replace('-', '_') if not mod: mod = _rdata_modules.get((dns.rdatatype.ANY, rdtype)) if not mod: try: mod = import_module('.'.join([_module_prefix, rdclass_text, rdtype_text])) _rdata_modules[(rdclass, rdtype)] = mod except ImportError: try: mod = import_module('.'.join([_module_prefix, 'ANY', rdtype_text])) _rdata_modules[(dns.rdataclass.ANY, rdtype)] = mod except ImportError: mod = None if mod: cls = getattr(mod, rdtype_text) else: cls = GenericRdata return cls def from_text(rdclass, rdtype, tok, origin=None, relativize=True): """Build an rdata object from text format. This function attempts to dynamically load a class which implements the specified rdata class and type. If there is no class-and-type-specific implementation, the GenericRdata class is used. Once a class is chosen, its from_text() class method is called with the parameters to this function. If *tok* is a ``text``, then a tokenizer is created and the string is used as its input. *rdclass*, an ``int``, the rdataclass. *rdtype*, an ``int``, the rdatatype. *tok*, a ``dns.tokenizer.Tokenizer`` or a ``text``. *origin*, a ``dns.name.Name`` (or ``None``), the origin to use for relative names. *relativize*, a ``bool``. If true, name will be relativized to the specified origin. Returns an instance of the chosen Rdata subclass. """ if isinstance(tok, string_types): tok = dns.tokenizer.Tokenizer(tok) cls = get_rdata_class(rdclass, rdtype) if cls != GenericRdata: # peek at first token token = tok.get() tok.unget(token) if token.is_identifier() and \ token.value == r'\#': # # Known type using the generic syntax. Extract the # wire form from the generic syntax, and then run # from_wire on it. # rdata = GenericRdata.from_text(rdclass, rdtype, tok, origin, relativize) return from_wire(rdclass, rdtype, rdata.data, 0, len(rdata.data), origin) return cls.from_text(rdclass, rdtype, tok, origin, relativize) def from_wire(rdclass, rdtype, wire, current, rdlen, origin=None): """Build an rdata object from wire format This function attempts to dynamically load a class which implements the specified rdata class and type. If there is no class-and-type-specific implementation, the GenericRdata class is used. Once a class is chosen, its from_wire() class method is called with the parameters to this function. *rdclass*, an ``int``, the rdataclass. *rdtype*, an ``int``, the rdatatype. *wire*, a ``binary``, the wire-format message. *current*, an ``int``, the offset in wire of the beginning of the rdata. *rdlen*, an ``int``, the length of the wire-format rdata *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, then names will be relativized to this origin. Returns an instance of the chosen Rdata subclass. """ wire = dns.wiredata.maybe_wrap(wire) cls = get_rdata_class(rdclass, rdtype) return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin) class RdatatypeExists(dns.exception.DNSException): """DNS rdatatype already exists.""" supp_kwargs = {'rdclass', 'rdtype'} fmt = "The rdata type with class {rdclass} and rdtype {rdtype} " + \ "already exists." def register_type(implementation, rdtype, rdtype_text, is_singleton=False, rdclass=dns.rdataclass.IN): """Dynamically register a module to handle an rdatatype. *implementation*, a module implementing the type in the usual dnspython way. *rdtype*, an ``int``, the rdatatype to register. *rdtype_text*, a ``text``, the textual form of the rdatatype. *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. RRsets of the type can have only one member.) *rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if it applies to all classes. """ existing_cls = get_rdata_class(rdclass, rdtype) if existing_cls != GenericRdata: raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype) _rdata_modules[(rdclass, rdtype)] = implementation dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton) dnspython-1.16.0/dns/rdata.pyi000066400000000000000000000012311340301174400162210ustar00rootroot00000000000000from typing import Dict, Tuple, Any, Optional from .name import Name class Rdata: def __init__(self): self.address : str def to_wire(self, file, compress : Optional[Dict[Name,int]], origin : Optional[Name]) -> bytes: ... @classmethod def from_text(cls, rdclass : int, rdtype : int, tok, origin=None, relativize=True): ... _rdata_modules : Dict[Tuple[Any,Rdata],Any] def from_text(rdclass : int, rdtype : int, tok : Optional[str], origin : Optional[Name] = None, relativize : bool = True): ... def from_wire(rdclass : int, rdtype : int, wire : bytes, current : int, rdlen : int, origin : Optional[Name] = None): ... dnspython-1.16.0/dns/rdataclass.py000066400000000000000000000062231340301174400171040ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Rdata Classes.""" import re import dns.exception RESERVED0 = 0 IN = 1 CH = 3 HS = 4 NONE = 254 ANY = 255 _by_text = { 'RESERVED0': RESERVED0, 'IN': IN, 'CH': CH, 'HS': HS, 'NONE': NONE, 'ANY': ANY } # We construct the inverse mapping programmatically to ensure that we # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that # would cause the mapping not to be true inverse. _by_value = {y: x for x, y in _by_text.items()} # Now that we've built the inverse map, we can add class aliases to # the _by_text mapping. _by_text.update({ 'INTERNET': IN, 'CHAOS': CH, 'HESIOD': HS }) _metaclasses = { NONE: True, ANY: True } _unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I) class UnknownRdataclass(dns.exception.DNSException): """A DNS class is unknown.""" def from_text(text): """Convert text into a DNS rdata class value. The input text can be a defined DNS RR class mnemonic or instance of the DNS generic class syntax. For example, "IN" and "CLASS1" will both result in a value of 1. Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown. Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. Returns an ``int``. """ value = _by_text.get(text.upper()) if value is None: match = _unknown_class_pattern.match(text) if match is None: raise UnknownRdataclass value = int(match.group(1)) if value < 0 or value > 65535: raise ValueError("class must be between >= 0 and <= 65535") return value def to_text(value): """Convert a DNS rdata type value to text. If the value has a known mnemonic, it will be used, otherwise the DNS generic class syntax will be used. Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. Returns a ``str``. """ if value < 0 or value > 65535: raise ValueError("class must be between >= 0 and <= 65535") text = _by_value.get(value) if text is None: text = 'CLASS' + repr(value) return text def is_metaclass(rdclass): """True if the specified class is a metaclass. The currently defined metaclasses are ANY and NONE. *rdclass* is an ``int``. """ if rdclass in _metaclasses: return True return False dnspython-1.16.0/dns/rdataset.py000066400000000000000000000263071340301174400165770ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS rdatasets (an rdataset is a set of rdatas of a given type and class)""" import random from io import StringIO import struct import dns.exception import dns.rdatatype import dns.rdataclass import dns.rdata import dns.set from ._compat import string_types # define SimpleSet here for backwards compatibility SimpleSet = dns.set.Set class DifferingCovers(dns.exception.DNSException): """An attempt was made to add a DNS SIG/RRSIG whose covered type is not the same as that of the other rdatas in the rdataset.""" class IncompatibleTypes(dns.exception.DNSException): """An attempt was made to add DNS RR data of an incompatible type.""" class Rdataset(dns.set.Set): """A DNS rdataset.""" __slots__ = ['rdclass', 'rdtype', 'covers', 'ttl'] def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE, ttl=0): """Create a new rdataset of the specified class and type. *rdclass*, an ``int``, the rdataclass. *rdtype*, an ``int``, the rdatatype. *covers*, an ``int``, the covered rdatatype. *ttl*, an ``int``, the TTL. """ super(Rdataset, self).__init__() self.rdclass = rdclass self.rdtype = rdtype self.covers = covers self.ttl = ttl def _clone(self): obj = super(Rdataset, self)._clone() obj.rdclass = self.rdclass obj.rdtype = self.rdtype obj.covers = self.covers obj.ttl = self.ttl return obj def update_ttl(self, ttl): """Perform TTL minimization. Set the TTL of the rdataset to be the lesser of the set's current TTL or the specified TTL. If the set contains no rdatas, set the TTL to the specified TTL. *ttl*, an ``int``. """ if len(self) == 0: self.ttl = ttl elif ttl < self.ttl: self.ttl = ttl def add(self, rd, ttl=None): """Add the specified rdata to the rdataset. If the optional *ttl* parameter is supplied, then ``self.update_ttl(ttl)`` will be called prior to adding the rdata. *rd*, a ``dns.rdata.Rdata``, the rdata *ttl*, an ``int``, the TTL. Raises ``dns.rdataset.IncompatibleTypes`` if the type and class do not match the type and class of the rdataset. Raises ``dns.rdataset.DifferingCovers`` if the type is a signature type and the covered type does not match that of the rdataset. """ # # If we're adding a signature, do some special handling to # check that the signature covers the same type as the # other rdatas in this rdataset. If this is the first rdata # in the set, initialize the covers field. # if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: raise IncompatibleTypes if ttl is not None: self.update_ttl(ttl) if self.rdtype == dns.rdatatype.RRSIG or \ self.rdtype == dns.rdatatype.SIG: covers = rd.covers() if len(self) == 0 and self.covers == dns.rdatatype.NONE: self.covers = covers elif self.covers != covers: raise DifferingCovers if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: self.clear() super(Rdataset, self).add(rd) def union_update(self, other): self.update_ttl(other.ttl) super(Rdataset, self).union_update(other) def intersection_update(self, other): self.update_ttl(other.ttl) super(Rdataset, self).intersection_update(other) def update(self, other): """Add all rdatas in other to self. *other*, a ``dns.rdataset.Rdataset``, the rdataset from which to update. """ self.update_ttl(other.ttl) super(Rdataset, self).update(other) def __repr__(self): if self.covers == 0: ctext = '' else: ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' return '' def __str__(self): return self.to_text() def __eq__(self, other): if not isinstance(other, Rdataset): return False if self.rdclass != other.rdclass or \ self.rdtype != other.rdtype or \ self.covers != other.covers: return False return super(Rdataset, self).__eq__(other) def __ne__(self, other): return not self.__eq__(other) def to_text(self, name=None, origin=None, relativize=True, override_rdclass=None, **kw): """Convert the rdataset into DNS master file format. See ``dns.name.Name.choose_relativity`` for more information on how *origin* and *relativize* determine the way names are emitted. Any additional keyword arguments are passed on to the rdata ``to_text()`` method. *name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with *name* as the owner name. *origin*, a ``dns.name.Name`` or ``None``, the origin for relative names. *relativize*, a ``bool``. If ``True``, names will be relativized to *origin*. """ if name is not None: name = name.choose_relativity(origin, relativize) ntext = str(name) pad = ' ' else: ntext = '' pad = '' s = StringIO() if override_rdclass is not None: rdclass = override_rdclass else: rdclass = self.rdclass if len(self) == 0: # # Empty rdatasets are used for the question section, and in # some dynamic updates, so we don't need to print out the TTL # (which is meaningless anyway). # s.write(u'{}{}{} {}\n'.format(ntext, pad, dns.rdataclass.to_text(rdclass), dns.rdatatype.to_text(self.rdtype))) else: for rd in self: s.write(u'%s%s%d %s %s %s\n' % (ntext, pad, self.ttl, dns.rdataclass.to_text(rdclass), dns.rdatatype.to_text(self.rdtype), rd.to_text(origin=origin, relativize=relativize, **kw))) # # We strip off the final \n for the caller's convenience in printing # return s.getvalue()[:-1] def to_wire(self, name, file, compress=None, origin=None, override_rdclass=None, want_shuffle=True): """Convert the rdataset to wire format. *name*, a ``dns.name.Name`` is the owner name to use. *file* is the file where the name is emitted (typically a BytesIO file). *compress*, a ``dict``, is the compression table to use. If ``None`` (the default), names will not be compressed. *origin* is a ``dns.name.Name`` or ``None``. If the name is relative and origin is not ``None``, then *origin* will be appended to it. *override_rdclass*, an ``int``, is used as the class instead of the class of the rdataset. This is useful when rendering rdatasets associated with dynamic updates. *want_shuffle*, a ``bool``. If ``True``, then the order of the Rdatas within the Rdataset will be shuffled before rendering. Returns an ``int``, the number of records emitted. """ if override_rdclass is not None: rdclass = override_rdclass want_shuffle = False else: rdclass = self.rdclass file.seek(0, 2) if len(self) == 0: name.to_wire(file, compress, origin) stuff = struct.pack("!HHIH", self.rdtype, rdclass, 0, 0) file.write(stuff) return 1 else: if want_shuffle: l = list(self) random.shuffle(l) else: l = self for rd in l: name.to_wire(file, compress, origin) stuff = struct.pack("!HHIH", self.rdtype, rdclass, self.ttl, 0) file.write(stuff) start = file.tell() rd.to_wire(file, compress, origin) end = file.tell() assert end - start < 65536 file.seek(start - 2) stuff = struct.pack("!H", end - start) file.write(stuff) file.seek(0, 2) return len(self) def match(self, rdclass, rdtype, covers): """Returns ``True`` if this rdataset matches the specified class, type, and covers. """ if self.rdclass == rdclass and \ self.rdtype == rdtype and \ self.covers == covers: return True return False def from_text_list(rdclass, rdtype, ttl, text_rdatas): """Create an rdataset with the specified class, type, and TTL, and with the specified list of rdatas in text format. Returns a ``dns.rdataset.Rdataset`` object. """ if isinstance(rdclass, string_types): rdclass = dns.rdataclass.from_text(rdclass) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) r = Rdataset(rdclass, rdtype) r.update_ttl(ttl) for t in text_rdatas: rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) r.add(rd) return r def from_text(rdclass, rdtype, ttl, *text_rdatas): """Create an rdataset with the specified class, type, and TTL, and with the specified rdatas in text format. Returns a ``dns.rdataset.Rdataset`` object. """ return from_text_list(rdclass, rdtype, ttl, text_rdatas) def from_rdata_list(ttl, rdatas): """Create an rdataset with the specified TTL, and with the specified list of rdata objects. Returns a ``dns.rdataset.Rdataset`` object. """ if len(rdatas) == 0: raise ValueError("rdata list must not be empty") r = None for rd in rdatas: if r is None: r = Rdataset(rd.rdclass, rd.rdtype) r.update_ttl(ttl) r.add(rd) return r def from_rdata(ttl, *rdatas): """Create an rdataset with the specified TTL, and with the specified rdata objects. Returns a ``dns.rdataset.Rdataset`` object. """ return from_rdata_list(ttl, rdatas) dnspython-1.16.0/dns/rdataset.pyi000066400000000000000000000036421340301174400167450ustar00rootroot00000000000000from typing import Optional, Dict, List, Union from io import BytesIO from . import exception, name, set, rdatatype, rdata, rdataset class DifferingCovers(exception.DNSException): """An attempt was made to add a DNS SIG/RRSIG whose covered type is not the same as that of the other rdatas in the rdataset.""" class IncompatibleTypes(exception.DNSException): """An attempt was made to add DNS RR data of an incompatible type.""" class Rdataset(set.Set): def __init__(self, rdclass, rdtype, covers=rdatatype.NONE, ttl=0): self.rdclass : int = rdclass self.rdtype : int = rdtype self.covers : int = covers self.ttl : int = ttl def update_ttl(self, ttl : int) -> None: ... def add(self, rd : rdata.Rdata, ttl : Optional[int] =None): ... def union_update(self, other : Rdataset): ... def intersection_update(self, other : Rdataset): ... def update(self, other : Rdataset): ... def to_text(self, name : Optional[name.Name] =None, origin : Optional[name.Name] =None, relativize=True, override_rdclass : Optional[int] =None, **kw) -> bytes: ... def to_wire(self, name : Optional[name.Name], file : BytesIO, compress : Optional[Dict[name.Name, int]] = None, origin : Optional[name.Name] = None, override_rdclass : Optional[int] = None, want_shuffle=True) -> int: ... def match(self, rdclass : int, rdtype : int, covers : int) -> bool: ... def from_text_list(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, text_rdatas : str) -> rdataset.Rdataset: ... def from_text(rdclass : Union[int,str], rdtype : Union[int,str], ttl : int, *text_rdatas : str) -> rdataset.Rdataset: ... def from_rdata_list(ttl : int, rdatas : List[rdata.Rdata]) -> rdataset.Rdataset: ... def from_rdata(ttl : int, *rdatas : List[rdata.Rdata]) -> rdataset.Rdataset: ... dnspython-1.16.0/dns/rdatatype.py000066400000000000000000000134351340301174400167630ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Rdata Types.""" import re import dns.exception NONE = 0 A = 1 NS = 2 MD = 3 MF = 4 CNAME = 5 SOA = 6 MB = 7 MG = 8 MR = 9 NULL = 10 WKS = 11 PTR = 12 HINFO = 13 MINFO = 14 MX = 15 TXT = 16 RP = 17 AFSDB = 18 X25 = 19 ISDN = 20 RT = 21 NSAP = 22 NSAP_PTR = 23 SIG = 24 KEY = 25 PX = 26 GPOS = 27 AAAA = 28 LOC = 29 NXT = 30 SRV = 33 NAPTR = 35 KX = 36 CERT = 37 A6 = 38 DNAME = 39 OPT = 41 APL = 42 DS = 43 SSHFP = 44 IPSECKEY = 45 RRSIG = 46 NSEC = 47 DNSKEY = 48 DHCID = 49 NSEC3 = 50 NSEC3PARAM = 51 TLSA = 52 HIP = 55 CDS = 59 CDNSKEY = 60 OPENPGPKEY = 61 CSYNC = 62 SPF = 99 UNSPEC = 103 EUI48 = 108 EUI64 = 109 TKEY = 249 TSIG = 250 IXFR = 251 AXFR = 252 MAILB = 253 MAILA = 254 ANY = 255 URI = 256 CAA = 257 AVC = 258 TA = 32768 DLV = 32769 _by_text = { 'NONE': NONE, 'A': A, 'NS': NS, 'MD': MD, 'MF': MF, 'CNAME': CNAME, 'SOA': SOA, 'MB': MB, 'MG': MG, 'MR': MR, 'NULL': NULL, 'WKS': WKS, 'PTR': PTR, 'HINFO': HINFO, 'MINFO': MINFO, 'MX': MX, 'TXT': TXT, 'RP': RP, 'AFSDB': AFSDB, 'X25': X25, 'ISDN': ISDN, 'RT': RT, 'NSAP': NSAP, 'NSAP-PTR': NSAP_PTR, 'SIG': SIG, 'KEY': KEY, 'PX': PX, 'GPOS': GPOS, 'AAAA': AAAA, 'LOC': LOC, 'NXT': NXT, 'SRV': SRV, 'NAPTR': NAPTR, 'KX': KX, 'CERT': CERT, 'A6': A6, 'DNAME': DNAME, 'OPT': OPT, 'APL': APL, 'DS': DS, 'SSHFP': SSHFP, 'IPSECKEY': IPSECKEY, 'RRSIG': RRSIG, 'NSEC': NSEC, 'DNSKEY': DNSKEY, 'DHCID': DHCID, 'NSEC3': NSEC3, 'NSEC3PARAM': NSEC3PARAM, 'TLSA': TLSA, 'HIP': HIP, 'CDS': CDS, 'CDNSKEY': CDNSKEY, 'OPENPGPKEY': OPENPGPKEY, 'CSYNC': CSYNC, 'SPF': SPF, 'UNSPEC': UNSPEC, 'EUI48': EUI48, 'EUI64': EUI64, 'TKEY': TKEY, 'TSIG': TSIG, 'IXFR': IXFR, 'AXFR': AXFR, 'MAILB': MAILB, 'MAILA': MAILA, 'ANY': ANY, 'URI': URI, 'CAA': CAA, 'AVC': AVC, 'TA': TA, 'DLV': DLV, } # We construct the inverse mapping programmatically to ensure that we # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that # would cause the mapping not to be true inverse. _by_value = {y: x for x, y in _by_text.items()} _metatypes = { OPT: True } _singletons = { SOA: True, NXT: True, DNAME: True, NSEC: True, CNAME: True, } _unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I) class UnknownRdatatype(dns.exception.DNSException): """DNS resource record type is unknown.""" def from_text(text): """Convert text into a DNS rdata type value. The input text can be a defined DNS RR type mnemonic or instance of the DNS generic type syntax. For example, "NS" and "TYPE2" will both result in a value of 2. Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown. Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. Returns an ``int``. """ value = _by_text.get(text.upper()) if value is None: match = _unknown_type_pattern.match(text) if match is None: raise UnknownRdatatype value = int(match.group(1)) if value < 0 or value > 65535: raise ValueError("type must be between >= 0 and <= 65535") return value def to_text(value): """Convert a DNS rdata type value to text. If the value has a known mnemonic, it will be used, otherwise the DNS generic type syntax will be used. Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. Returns a ``str``. """ if value < 0 or value > 65535: raise ValueError("type must be between >= 0 and <= 65535") text = _by_value.get(value) if text is None: text = 'TYPE' + repr(value) return text def is_metatype(rdtype): """True if the specified type is a metatype. *rdtype* is an ``int``. The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA, MAILB, ANY, and OPT. Returns a ``bool``. """ if rdtype >= TKEY and rdtype <= ANY or rdtype in _metatypes: return True return False def is_singleton(rdtype): """Is the specified type a singleton type? Singleton types can only have a single rdata in an rdataset, or a single RR in an RRset. The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and SOA. *rdtype* is an ``int``. Returns a ``bool``. """ if rdtype in _singletons: return True return False def register_type(rdtype, rdtype_text, is_singleton=False): # pylint: disable=redefined-outer-name """Dynamically register an rdatatype. *rdtype*, an ``int``, the rdatatype to register. *rdtype_text*, a ``text``, the textual form of the rdatatype. *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. RRsets of the type can have only one member.) """ _by_text[rdtype_text] = rdtype _by_value[rdtype] = rdtype_text if is_singleton: _singletons[rdtype] = True dnspython-1.16.0/dns/rdtypes/000077500000000000000000000000001340301174400161005ustar00rootroot00000000000000dnspython-1.16.0/dns/rdtypes/ANY/000077500000000000000000000000001340301174400165275ustar00rootroot00000000000000dnspython-1.16.0/dns/rdtypes/ANY/AFSDB.py000066400000000000000000000036051340301174400177240ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.mxbase class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX): """AFSDB record @ivar subtype: the subtype value @type subtype: int @ivar hostname: the hostname name @type hostname: dns.name.Name object""" # Use the property mechanism to make "subtype" an alias for the # "preference" attribute, and "hostname" an alias for the "exchange" # attribute. # # This lets us inherit the UncompressedMX implementation but lets # the caller use appropriate attribute names for the rdata type. # # We probably lose some performance vs. a cut-and-paste # implementation, but this way we don't copy code, and that's # good. def get_subtype(self): return self.preference def set_subtype(self, subtype): self.preference = subtype subtype = property(get_subtype, set_subtype) def get_hostname(self): return self.exchange def set_hostname(self, hostname): self.exchange = hostname hostname = property(get_hostname, set_hostname) dnspython-1.16.0/dns/rdtypes/ANY/AVC.py000066400000000000000000000020031340301174400175050ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2016 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.txtbase class AVC(dns.rdtypes.txtbase.TXTBase): """AVC record @see: U{http://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template}""" dnspython-1.16.0/dns/rdtypes/ANY/CAA.py000066400000000000000000000052131340301174400174660ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.tokenizer class CAA(dns.rdata.Rdata): """CAA (Certification Authority Authorization) record @ivar flags: the flags @type flags: int @ivar tag: the tag @type tag: string @ivar value: the value @type value: string @see: RFC 6844""" __slots__ = ['flags', 'tag', 'value'] def __init__(self, rdclass, rdtype, flags, tag, value): super(CAA, self).__init__(rdclass, rdtype) self.flags = flags self.tag = tag self.value = value def to_text(self, origin=None, relativize=True, **kw): return '%u %s "%s"' % (self.flags, dns.rdata._escapify(self.tag), dns.rdata._escapify(self.value)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): flags = tok.get_uint8() tag = tok.get_string().encode() if len(tag) > 255: raise dns.exception.SyntaxError("tag too long") if not tag.isalnum(): raise dns.exception.SyntaxError("tag is not alphanumeric") value = tok.get_string().encode() return cls(rdclass, rdtype, flags, tag, value) def to_wire(self, file, compress=None, origin=None): file.write(struct.pack('!B', self.flags)) l = len(self.tag) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.tag) file.write(self.value) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (flags, l) = struct.unpack('!BB', wire[current: current + 2]) current += 2 tag = wire[current: current + l] value = wire[current + l:current + rdlen - 2] return cls(rdclass, rdtype, flags, tag, value) dnspython-1.16.0/dns/rdtypes/ANY/CDNSKEY.py000066400000000000000000000021171340301174400202020ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.dnskeybase from dns.rdtypes.dnskeybase import flags_to_text_set, flags_from_text_set __all__ = ['flags_to_text_set', 'flags_from_text_set'] class CDNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): """CDNSKEY record""" dnspython-1.16.0/dns/rdtypes/ANY/CDS.py000066400000000000000000000016701340301174400175160ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.dsbase class CDS(dns.rdtypes.dsbase.DSBase): """CDS record""" dnspython-1.16.0/dns/rdtypes/ANY/CERT.py000066400000000000000000000076741340301174400176540ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import base64 import dns.exception import dns.dnssec import dns.rdata import dns.tokenizer _ctype_by_value = { 1: 'PKIX', 2: 'SPKI', 3: 'PGP', 253: 'URI', 254: 'OID', } _ctype_by_name = { 'PKIX': 1, 'SPKI': 2, 'PGP': 3, 'URI': 253, 'OID': 254, } def _ctype_from_text(what): v = _ctype_by_name.get(what) if v is not None: return v return int(what) def _ctype_to_text(what): v = _ctype_by_value.get(what) if v is not None: return v return str(what) class CERT(dns.rdata.Rdata): """CERT record @ivar certificate_type: certificate type @type certificate_type: int @ivar key_tag: key tag @type key_tag: int @ivar algorithm: algorithm @type algorithm: int @ivar certificate: the certificate or CRL @type certificate: string @see: RFC 2538""" __slots__ = ['certificate_type', 'key_tag', 'algorithm', 'certificate'] def __init__(self, rdclass, rdtype, certificate_type, key_tag, algorithm, certificate): super(CERT, self).__init__(rdclass, rdtype) self.certificate_type = certificate_type self.key_tag = key_tag self.algorithm = algorithm self.certificate = certificate def to_text(self, origin=None, relativize=True, **kw): certificate_type = _ctype_to_text(self.certificate_type) return "%s %d %s %s" % (certificate_type, self.key_tag, dns.dnssec.algorithm_to_text(self.algorithm), dns.rdata._base64ify(self.certificate)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): certificate_type = _ctype_from_text(tok.get_string()) key_tag = tok.get_uint16() algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) if algorithm < 0 or algorithm > 255: raise dns.exception.SyntaxError("bad algorithm type") chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) b64 = b''.join(chunks) certificate = base64.b64decode(b64) return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) def to_wire(self, file, compress=None, origin=None): prefix = struct.pack("!HHB", self.certificate_type, self.key_tag, self.algorithm) file.write(prefix) file.write(self.certificate) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): prefix = wire[current: current + 5].unwrap() current += 5 rdlen -= 5 if rdlen < 0: raise dns.exception.FormError (certificate_type, key_tag, algorithm) = struct.unpack("!HHB", prefix) certificate = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) dnspython-1.16.0/dns/rdtypes/ANY/CNAME.py000066400000000000000000000022111340301174400177200ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.nsbase class CNAME(dns.rdtypes.nsbase.NSBase): """CNAME record Note: although CNAME is officially a singleton type, dnspython allows non-singleton CNAME rdatasets because such sets have been commonly used by BIND and other nameservers for load balancing.""" dnspython-1.16.0/dns/rdtypes/ANY/CSYNC.py000066400000000000000000000111611340301174400177600ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.rdatatype import dns.name from dns._compat import xrange class CSYNC(dns.rdata.Rdata): """CSYNC record @ivar serial: the SOA serial number @type serial: int @ivar flags: the CSYNC flags @type flags: int @ivar windows: the windowed bitmap list @type windows: list of (window number, string) tuples""" __slots__ = ['serial', 'flags', 'windows'] def __init__(self, rdclass, rdtype, serial, flags, windows): super(CSYNC, self).__init__(rdclass, rdtype) self.serial = serial self.flags = flags self.windows = windows def to_text(self, origin=None, relativize=True, **kw): text = '' for (window, bitmap) in self.windows: bits = [] for i in xrange(0, len(bitmap)): byte = bitmap[i] for j in xrange(0, 8): if byte & (0x80 >> j): bits.append(dns.rdatatype.to_text(window * 256 + i * 8 + j)) text += (' ' + ' '.join(bits)) return '%d %d%s' % (self.serial, self.flags, text) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): serial = tok.get_uint32() flags = tok.get_uint16() rdtypes = [] while 1: token = tok.get().unescape() if token.is_eol_or_eof(): break nrdtype = dns.rdatatype.from_text(token.value) if nrdtype == 0: raise dns.exception.SyntaxError("CSYNC with bit 0") if nrdtype > 65535: raise dns.exception.SyntaxError("CSYNC with bit > 65535") rdtypes.append(nrdtype) rdtypes.sort() window = 0 octets = 0 prior_rdtype = 0 bitmap = bytearray(b'\0' * 32) windows = [] for nrdtype in rdtypes: if nrdtype == prior_rdtype: continue prior_rdtype = nrdtype new_window = nrdtype // 256 if new_window != window: windows.append((window, bitmap[0:octets])) bitmap = bytearray(b'\0' * 32) window = new_window offset = nrdtype % 256 byte = offset // 8 bit = offset % 8 octets = byte + 1 bitmap[byte] = bitmap[byte] | (0x80 >> bit) windows.append((window, bitmap[0:octets])) return cls(rdclass, rdtype, serial, flags, windows) def to_wire(self, file, compress=None, origin=None): file.write(struct.pack('!IH', self.serial, self.flags)) for (window, bitmap) in self.windows: file.write(struct.pack('!BB', window, len(bitmap))) file.write(bitmap) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): if rdlen < 6: raise dns.exception.FormError("CSYNC too short") (serial, flags) = struct.unpack("!IH", wire[current: current + 6]) current += 6 rdlen -= 6 windows = [] while rdlen > 0: if rdlen < 3: raise dns.exception.FormError("CSYNC too short") window = wire[current] octets = wire[current + 1] if octets == 0 or octets > 32: raise dns.exception.FormError("bad CSYNC octets") current += 2 rdlen -= 2 if rdlen < octets: raise dns.exception.FormError("bad CSYNC bitmap length") bitmap = bytearray(wire[current: current + octets].unwrap()) current += octets rdlen -= octets windows.append((window, bitmap)) return cls(rdclass, rdtype, serial, flags, windows) dnspython-1.16.0/dns/rdtypes/ANY/DLV.py000066400000000000000000000016551340301174400175350ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.dsbase class DLV(dns.rdtypes.dsbase.DSBase): """DLV record""" dnspython-1.16.0/dns/rdtypes/ANY/DNAME.py000066400000000000000000000020401340301174400177210ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.nsbase class DNAME(dns.rdtypes.nsbase.UncompressedNS): """DNAME record""" def to_digestable(self, origin=None): return self.target.to_digestable(origin) dnspython-1.16.0/dns/rdtypes/ANY/DNSKEY.py000066400000000000000000000021151340301174400200750ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.dnskeybase from dns.rdtypes.dnskeybase import flags_to_text_set, flags_from_text_set __all__ = ['flags_to_text_set', 'flags_from_text_set'] class DNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): """DNSKEY record""" dnspython-1.16.0/dns/rdtypes/ANY/DS.py000066400000000000000000000016661340301174400174200ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.dsbase class DS(dns.rdtypes.dsbase.DSBase): """DS record""" dnspython-1.16.0/dns/rdtypes/ANY/EUI48.py000066400000000000000000000021441340301174400177000ustar00rootroot00000000000000# Copyright (C) 2015 Red Hat, Inc. # Author: Petr Spacek # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.euibase class EUI48(dns.rdtypes.euibase.EUIBase): """EUI48 record @ivar fingerprint: 48-bit Extended Unique Identifier (EUI-48) @type fingerprint: string @see: rfc7043.txt""" byte_len = 6 # 0123456789ab (in hex) text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab dnspython-1.16.0/dns/rdtypes/ANY/EUI64.py000066400000000000000000000021561340301174400177010ustar00rootroot00000000000000# Copyright (C) 2015 Red Hat, Inc. # Author: Petr Spacek # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.euibase class EUI64(dns.rdtypes.euibase.EUIBase): """EUI64 record @ivar fingerprint: 64-bit Extended Unique Identifier (EUI-64) @type fingerprint: string @see: rfc7043.txt""" byte_len = 8 # 0123456789abcdef (in hex) text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab-cd-ef dnspython-1.16.0/dns/rdtypes/ANY/GPOS.py000066400000000000000000000125121340301174400176520ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.tokenizer from dns._compat import long, text_type def _validate_float_string(what): if what[0] == b'-'[0] or what[0] == b'+'[0]: what = what[1:] if what.isdigit(): return (left, right) = what.split(b'.') if left == b'' and right == b'': raise dns.exception.FormError if not left == b'' and not left.decode().isdigit(): raise dns.exception.FormError if not right == b'' and not right.decode().isdigit(): raise dns.exception.FormError def _sanitize(value): if isinstance(value, text_type): return value.encode() return value class GPOS(dns.rdata.Rdata): """GPOS record @ivar latitude: latitude @type latitude: string @ivar longitude: longitude @type longitude: string @ivar altitude: altitude @type altitude: string @see: RFC 1712""" __slots__ = ['latitude', 'longitude', 'altitude'] def __init__(self, rdclass, rdtype, latitude, longitude, altitude): super(GPOS, self).__init__(rdclass, rdtype) if isinstance(latitude, float) or \ isinstance(latitude, int) or \ isinstance(latitude, long): latitude = str(latitude) if isinstance(longitude, float) or \ isinstance(longitude, int) or \ isinstance(longitude, long): longitude = str(longitude) if isinstance(altitude, float) or \ isinstance(altitude, int) or \ isinstance(altitude, long): altitude = str(altitude) latitude = _sanitize(latitude) longitude = _sanitize(longitude) altitude = _sanitize(altitude) _validate_float_string(latitude) _validate_float_string(longitude) _validate_float_string(altitude) self.latitude = latitude self.longitude = longitude self.altitude = altitude def to_text(self, origin=None, relativize=True, **kw): return '{} {} {}'.format(self.latitude.decode(), self.longitude.decode(), self.altitude.decode()) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): latitude = tok.get_string() longitude = tok.get_string() altitude = tok.get_string() tok.get_eol() return cls(rdclass, rdtype, latitude, longitude, altitude) def to_wire(self, file, compress=None, origin=None): l = len(self.latitude) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.latitude) l = len(self.longitude) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.longitude) l = len(self.altitude) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.altitude) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): l = wire[current] current += 1 rdlen -= 1 if l > rdlen: raise dns.exception.FormError latitude = wire[current: current + l].unwrap() current += l rdlen -= l l = wire[current] current += 1 rdlen -= 1 if l > rdlen: raise dns.exception.FormError longitude = wire[current: current + l].unwrap() current += l rdlen -= l l = wire[current] current += 1 rdlen -= 1 if l != rdlen: raise dns.exception.FormError altitude = wire[current: current + l].unwrap() return cls(rdclass, rdtype, latitude, longitude, altitude) def _get_float_latitude(self): return float(self.latitude) def _set_float_latitude(self, value): self.latitude = str(value) float_latitude = property(_get_float_latitude, _set_float_latitude, doc="latitude as a floating point value") def _get_float_longitude(self): return float(self.longitude) def _set_float_longitude(self, value): self.longitude = str(value) float_longitude = property(_get_float_longitude, _set_float_longitude, doc="longitude as a floating point value") def _get_float_altitude(self): return float(self.altitude) def _set_float_altitude(self, value): self.altitude = str(value) float_altitude = property(_get_float_altitude, _set_float_altitude, doc="altitude as a floating point value") dnspython-1.16.0/dns/rdtypes/ANY/HINFO.py000066400000000000000000000052761340301174400177560ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.tokenizer from dns._compat import text_type class HINFO(dns.rdata.Rdata): """HINFO record @ivar cpu: the CPU type @type cpu: string @ivar os: the OS type @type os: string @see: RFC 1035""" __slots__ = ['cpu', 'os'] def __init__(self, rdclass, rdtype, cpu, os): super(HINFO, self).__init__(rdclass, rdtype) if isinstance(cpu, text_type): self.cpu = cpu.encode() else: self.cpu = cpu if isinstance(os, text_type): self.os = os.encode() else: self.os = os def to_text(self, origin=None, relativize=True, **kw): return '"{}" "{}"'.format(dns.rdata._escapify(self.cpu), dns.rdata._escapify(self.os)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): cpu = tok.get_string() os = tok.get_string() tok.get_eol() return cls(rdclass, rdtype, cpu, os) def to_wire(self, file, compress=None, origin=None): l = len(self.cpu) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.cpu) l = len(self.os) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.os) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): l = wire[current] current += 1 rdlen -= 1 if l > rdlen: raise dns.exception.FormError cpu = wire[current:current + l].unwrap() current += l rdlen -= l l = wire[current] current += 1 rdlen -= 1 if l != rdlen: raise dns.exception.FormError os = wire[current: current + l].unwrap() return cls(rdclass, rdtype, cpu, os) dnspython-1.16.0/dns/rdtypes/ANY/HIP.py000066400000000000000000000101751340301174400175250ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2010, 2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import base64 import binascii import dns.exception import dns.rdata import dns.rdatatype class HIP(dns.rdata.Rdata): """HIP record @ivar hit: the host identity tag @type hit: string @ivar algorithm: the public key cryptographic algorithm @type algorithm: int @ivar key: the public key @type key: string @ivar servers: the rendezvous servers @type servers: list of dns.name.Name objects @see: RFC 5205""" __slots__ = ['hit', 'algorithm', 'key', 'servers'] def __init__(self, rdclass, rdtype, hit, algorithm, key, servers): super(HIP, self).__init__(rdclass, rdtype) self.hit = hit self.algorithm = algorithm self.key = key self.servers = servers def to_text(self, origin=None, relativize=True, **kw): hit = binascii.hexlify(self.hit).decode() key = base64.b64encode(self.key).replace(b'\n', b'').decode() text = u'' servers = [] for server in self.servers: servers.append(server.choose_relativity(origin, relativize)) if len(servers) > 0: text += (u' ' + u' '.join((x.to_unicode() for x in servers))) return u'%u %s %s%s' % (self.algorithm, hit, key, text) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): algorithm = tok.get_uint8() hit = binascii.unhexlify(tok.get_string().encode()) if len(hit) > 255: raise dns.exception.SyntaxError("HIT too long") key = base64.b64decode(tok.get_string().encode()) servers = [] while 1: token = tok.get() if token.is_eol_or_eof(): break server = dns.name.from_text(token.value, origin) server.choose_relativity(origin, relativize) servers.append(server) return cls(rdclass, rdtype, hit, algorithm, key, servers) def to_wire(self, file, compress=None, origin=None): lh = len(self.hit) lk = len(self.key) file.write(struct.pack("!BBH", lh, self.algorithm, lk)) file.write(self.hit) file.write(self.key) for server in self.servers: server.to_wire(file, None, origin) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (lh, algorithm, lk) = struct.unpack('!BBH', wire[current: current + 4]) current += 4 rdlen -= 4 hit = wire[current: current + lh].unwrap() current += lh rdlen -= lh key = wire[current: current + lk].unwrap() current += lk rdlen -= lk servers = [] while rdlen > 0: (server, cused) = dns.name.from_wire(wire[: current + rdlen], current) current += cused rdlen -= cused if origin is not None: server = server.relativize(origin) servers.append(server) return cls(rdclass, rdtype, hit, algorithm, key, servers) def choose_relativity(self, origin=None, relativize=True): servers = [] for server in self.servers: server = server.choose_relativity(origin, relativize) servers.append(server) self.servers = servers dnspython-1.16.0/dns/rdtypes/ANY/ISDN.py000066400000000000000000000064261340301174400176460ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.tokenizer from dns._compat import text_type class ISDN(dns.rdata.Rdata): """ISDN record @ivar address: the ISDN address @type address: string @ivar subaddress: the ISDN subaddress (or '' if not present) @type subaddress: string @see: RFC 1183""" __slots__ = ['address', 'subaddress'] def __init__(self, rdclass, rdtype, address, subaddress): super(ISDN, self).__init__(rdclass, rdtype) if isinstance(address, text_type): self.address = address.encode() else: self.address = address if isinstance(address, text_type): self.subaddress = subaddress.encode() else: self.subaddress = subaddress def to_text(self, origin=None, relativize=True, **kw): if self.subaddress: return '"{}" "{}"'.format(dns.rdata._escapify(self.address), dns.rdata._escapify(self.subaddress)) else: return '"%s"' % dns.rdata._escapify(self.address) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): address = tok.get_string() t = tok.get() if not t.is_eol_or_eof(): tok.unget(t) subaddress = tok.get_string() else: tok.unget(t) subaddress = '' tok.get_eol() return cls(rdclass, rdtype, address, subaddress) def to_wire(self, file, compress=None, origin=None): l = len(self.address) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.address) l = len(self.subaddress) if l > 0: assert l < 256 file.write(struct.pack('!B', l)) file.write(self.subaddress) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): l = wire[current] current += 1 rdlen -= 1 if l > rdlen: raise dns.exception.FormError address = wire[current: current + l].unwrap() current += l rdlen -= l if rdlen > 0: l = wire[current] current += 1 rdlen -= 1 if l != rdlen: raise dns.exception.FormError subaddress = wire[current: current + l].unwrap() else: subaddress = '' return cls(rdclass, rdtype, address, subaddress) dnspython-1.16.0/dns/rdtypes/ANY/LOC.py000066400000000000000000000300771340301174400175250ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import division import struct import dns.exception import dns.rdata from dns._compat import long, xrange, round_py2_compat _pows = tuple(long(10**i) for i in range(0, 11)) # default values are in centimeters _default_size = 100.0 _default_hprec = 1000000.0 _default_vprec = 1000.0 def _exponent_of(what, desc): if what == 0: return 0 exp = None for i in xrange(len(_pows)): if what // _pows[i] == long(0): exp = i - 1 break if exp is None or exp < 0: raise dns.exception.SyntaxError("%s value out of bounds" % desc) return exp def _float_to_tuple(what): if what < 0: sign = -1 what *= -1 else: sign = 1 what = round_py2_compat(what * 3600000) degrees = int(what // 3600000) what -= degrees * 3600000 minutes = int(what // 60000) what -= minutes * 60000 seconds = int(what // 1000) what -= int(seconds * 1000) what = int(what) return (degrees, minutes, seconds, what, sign) def _tuple_to_float(what): value = float(what[0]) value += float(what[1]) / 60.0 value += float(what[2]) / 3600.0 value += float(what[3]) / 3600000.0 return float(what[4]) * value def _encode_size(what, desc): what = long(what) exponent = _exponent_of(what, desc) & 0xF base = what // pow(10, exponent) & 0xF return base * 16 + exponent def _decode_size(what, desc): exponent = what & 0x0F if exponent > 9: raise dns.exception.SyntaxError("bad %s exponent" % desc) base = (what & 0xF0) >> 4 if base > 9: raise dns.exception.SyntaxError("bad %s base" % desc) return long(base) * pow(10, exponent) class LOC(dns.rdata.Rdata): """LOC record @ivar latitude: latitude @type latitude: (int, int, int, int, sign) tuple specifying the degrees, minutes, seconds, milliseconds, and sign of the coordinate. @ivar longitude: longitude @type longitude: (int, int, int, int, sign) tuple specifying the degrees, minutes, seconds, milliseconds, and sign of the coordinate. @ivar altitude: altitude @type altitude: float @ivar size: size of the sphere @type size: float @ivar horizontal_precision: horizontal precision @type horizontal_precision: float @ivar vertical_precision: vertical precision @type vertical_precision: float @see: RFC 1876""" __slots__ = ['latitude', 'longitude', 'altitude', 'size', 'horizontal_precision', 'vertical_precision'] def __init__(self, rdclass, rdtype, latitude, longitude, altitude, size=_default_size, hprec=_default_hprec, vprec=_default_vprec): """Initialize a LOC record instance. The parameters I{latitude} and I{longitude} may be either a 4-tuple of integers specifying (degrees, minutes, seconds, milliseconds), or they may be floating point values specifying the number of degrees. The other parameters are floats. Size, horizontal precision, and vertical precision are specified in centimeters.""" super(LOC, self).__init__(rdclass, rdtype) if isinstance(latitude, int) or isinstance(latitude, long): latitude = float(latitude) if isinstance(latitude, float): latitude = _float_to_tuple(latitude) self.latitude = latitude if isinstance(longitude, int) or isinstance(longitude, long): longitude = float(longitude) if isinstance(longitude, float): longitude = _float_to_tuple(longitude) self.longitude = longitude self.altitude = float(altitude) self.size = float(size) self.horizontal_precision = float(hprec) self.vertical_precision = float(vprec) def to_text(self, origin=None, relativize=True, **kw): if self.latitude[4] > 0: lat_hemisphere = 'N' else: lat_hemisphere = 'S' if self.longitude[4] > 0: long_hemisphere = 'E' else: long_hemisphere = 'W' text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % ( self.latitude[0], self.latitude[1], self.latitude[2], self.latitude[3], lat_hemisphere, self.longitude[0], self.longitude[1], self.longitude[2], self.longitude[3], long_hemisphere, self.altitude / 100.0 ) # do not print default values if self.size != _default_size or \ self.horizontal_precision != _default_hprec or \ self.vertical_precision != _default_vprec: text += " {:0.2f}m {:0.2f}m {:0.2f}m".format( self.size / 100.0, self.horizontal_precision / 100.0, self.vertical_precision / 100.0 ) return text @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): latitude = [0, 0, 0, 0, 1] longitude = [0, 0, 0, 0, 1] size = _default_size hprec = _default_hprec vprec = _default_vprec latitude[0] = tok.get_int() t = tok.get_string() if t.isdigit(): latitude[1] = int(t) t = tok.get_string() if '.' in t: (seconds, milliseconds) = t.split('.') if not seconds.isdigit(): raise dns.exception.SyntaxError( 'bad latitude seconds value') latitude[2] = int(seconds) if latitude[2] >= 60: raise dns.exception.SyntaxError('latitude seconds >= 60') l = len(milliseconds) if l == 0 or l > 3 or not milliseconds.isdigit(): raise dns.exception.SyntaxError( 'bad latitude milliseconds value') if l == 1: m = 100 elif l == 2: m = 10 else: m = 1 latitude[3] = m * int(milliseconds) t = tok.get_string() elif t.isdigit(): latitude[2] = int(t) t = tok.get_string() if t == 'S': latitude[4] = -1 elif t != 'N': raise dns.exception.SyntaxError('bad latitude hemisphere value') longitude[0] = tok.get_int() t = tok.get_string() if t.isdigit(): longitude[1] = int(t) t = tok.get_string() if '.' in t: (seconds, milliseconds) = t.split('.') if not seconds.isdigit(): raise dns.exception.SyntaxError( 'bad longitude seconds value') longitude[2] = int(seconds) if longitude[2] >= 60: raise dns.exception.SyntaxError('longitude seconds >= 60') l = len(milliseconds) if l == 0 or l > 3 or not milliseconds.isdigit(): raise dns.exception.SyntaxError( 'bad longitude milliseconds value') if l == 1: m = 100 elif l == 2: m = 10 else: m = 1 longitude[3] = m * int(milliseconds) t = tok.get_string() elif t.isdigit(): longitude[2] = int(t) t = tok.get_string() if t == 'W': longitude[4] = -1 elif t != 'E': raise dns.exception.SyntaxError('bad longitude hemisphere value') t = tok.get_string() if t[-1] == 'm': t = t[0: -1] altitude = float(t) * 100.0 # m -> cm token = tok.get().unescape() if not token.is_eol_or_eof(): value = token.value if value[-1] == 'm': value = value[0: -1] size = float(value) * 100.0 # m -> cm token = tok.get().unescape() if not token.is_eol_or_eof(): value = token.value if value[-1] == 'm': value = value[0: -1] hprec = float(value) * 100.0 # m -> cm token = tok.get().unescape() if not token.is_eol_or_eof(): value = token.value if value[-1] == 'm': value = value[0: -1] vprec = float(value) * 100.0 # m -> cm tok.get_eol() return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) def to_wire(self, file, compress=None, origin=None): milliseconds = (self.latitude[0] * 3600000 + self.latitude[1] * 60000 + self.latitude[2] * 1000 + self.latitude[3]) * self.latitude[4] latitude = long(0x80000000) + milliseconds milliseconds = (self.longitude[0] * 3600000 + self.longitude[1] * 60000 + self.longitude[2] * 1000 + self.longitude[3]) * self.longitude[4] longitude = long(0x80000000) + milliseconds altitude = long(self.altitude) + long(10000000) size = _encode_size(self.size, "size") hprec = _encode_size(self.horizontal_precision, "horizontal precision") vprec = _encode_size(self.vertical_precision, "vertical precision") wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude, longitude, altitude) file.write(wire) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (version, size, hprec, vprec, latitude, longitude, altitude) = \ struct.unpack("!BBBBIII", wire[current: current + rdlen]) if latitude > long(0x80000000): latitude = float(latitude - long(0x80000000)) / 3600000 else: latitude = -1 * float(long(0x80000000) - latitude) / 3600000 if latitude < -90.0 or latitude > 90.0: raise dns.exception.FormError("bad latitude") if longitude > long(0x80000000): longitude = float(longitude - long(0x80000000)) / 3600000 else: longitude = -1 * float(long(0x80000000) - longitude) / 3600000 if longitude < -180.0 or longitude > 180.0: raise dns.exception.FormError("bad longitude") altitude = float(altitude) - 10000000.0 size = _decode_size(size, "size") hprec = _decode_size(hprec, "horizontal precision") vprec = _decode_size(vprec, "vertical precision") return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) def _get_float_latitude(self): return _tuple_to_float(self.latitude) def _set_float_latitude(self, value): self.latitude = _float_to_tuple(value) float_latitude = property(_get_float_latitude, _set_float_latitude, doc="latitude as a floating point value") def _get_float_longitude(self): return _tuple_to_float(self.longitude) def _set_float_longitude(self, value): self.longitude = _float_to_tuple(value) float_longitude = property(_get_float_longitude, _set_float_longitude, doc="longitude as a floating point value") dnspython-1.16.0/dns/rdtypes/ANY/MX.py000066400000000000000000000016661340301174400174360ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.mxbase class MX(dns.rdtypes.mxbase.MXBase): """MX record""" dnspython-1.16.0/dns/rdtypes/ANY/NS.py000066400000000000000000000016661340301174400174320ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.nsbase class NS(dns.rdtypes.nsbase.NSBase): """NS record""" dnspython-1.16.0/dns/rdtypes/ANY/NSEC.py000066400000000000000000000112431340301174400176320ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.rdatatype import dns.name from dns._compat import xrange class NSEC(dns.rdata.Rdata): """NSEC record @ivar next: the next name @type next: dns.name.Name object @ivar windows: the windowed bitmap list @type windows: list of (window number, string) tuples""" __slots__ = ['next', 'windows'] def __init__(self, rdclass, rdtype, next, windows): super(NSEC, self).__init__(rdclass, rdtype) self.next = next self.windows = windows def to_text(self, origin=None, relativize=True, **kw): next = self.next.choose_relativity(origin, relativize) text = '' for (window, bitmap) in self.windows: bits = [] for i in xrange(0, len(bitmap)): byte = bitmap[i] for j in xrange(0, 8): if byte & (0x80 >> j): bits.append(dns.rdatatype.to_text(window * 256 + i * 8 + j)) text += (' ' + ' '.join(bits)) return '{}{}'.format(next, text) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): next = tok.get_name() next = next.choose_relativity(origin, relativize) rdtypes = [] while 1: token = tok.get().unescape() if token.is_eol_or_eof(): break nrdtype = dns.rdatatype.from_text(token.value) if nrdtype == 0: raise dns.exception.SyntaxError("NSEC with bit 0") if nrdtype > 65535: raise dns.exception.SyntaxError("NSEC with bit > 65535") rdtypes.append(nrdtype) rdtypes.sort() window = 0 octets = 0 prior_rdtype = 0 bitmap = bytearray(b'\0' * 32) windows = [] for nrdtype in rdtypes: if nrdtype == prior_rdtype: continue prior_rdtype = nrdtype new_window = nrdtype // 256 if new_window != window: windows.append((window, bitmap[0:octets])) bitmap = bytearray(b'\0' * 32) window = new_window offset = nrdtype % 256 byte = offset // 8 bit = offset % 8 octets = byte + 1 bitmap[byte] = bitmap[byte] | (0x80 >> bit) windows.append((window, bitmap[0:octets])) return cls(rdclass, rdtype, next, windows) def to_wire(self, file, compress=None, origin=None): self.next.to_wire(file, None, origin) for (window, bitmap) in self.windows: file.write(struct.pack('!BB', window, len(bitmap))) file.write(bitmap) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (next, cused) = dns.name.from_wire(wire[: current + rdlen], current) current += cused rdlen -= cused windows = [] while rdlen > 0: if rdlen < 3: raise dns.exception.FormError("NSEC too short") window = wire[current] octets = wire[current + 1] if octets == 0 or octets > 32: raise dns.exception.FormError("bad NSEC octets") current += 2 rdlen -= 2 if rdlen < octets: raise dns.exception.FormError("bad NSEC bitmap length") bitmap = bytearray(wire[current: current + octets].unwrap()) current += octets rdlen -= octets windows.append((window, bitmap)) if origin is not None: next = next.relativize(origin) return cls(rdclass, rdtype, next, windows) def choose_relativity(self, origin=None, relativize=True): self.next = self.next.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/ANY/NSEC3.py000066400000000000000000000160531340301174400177210ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import base64 import binascii import string import struct import dns.exception import dns.rdata import dns.rdatatype from dns._compat import xrange, text_type, PY3 # pylint: disable=deprecated-string-function if PY3: b32_hex_to_normal = bytes.maketrans(b'0123456789ABCDEFGHIJKLMNOPQRSTUV', b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') b32_normal_to_hex = bytes.maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', b'0123456789ABCDEFGHIJKLMNOPQRSTUV') else: b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', '0123456789ABCDEFGHIJKLMNOPQRSTUV') # pylint: enable=deprecated-string-function # hash algorithm constants SHA1 = 1 # flag constants OPTOUT = 1 class NSEC3(dns.rdata.Rdata): """NSEC3 record @ivar algorithm: the hash algorithm number @type algorithm: int @ivar flags: the flags @type flags: int @ivar iterations: the number of iterations @type iterations: int @ivar salt: the salt @type salt: string @ivar next: the next name hash @type next: string @ivar windows: the windowed bitmap list @type windows: list of (window number, string) tuples""" __slots__ = ['algorithm', 'flags', 'iterations', 'salt', 'next', 'windows'] def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt, next, windows): super(NSEC3, self).__init__(rdclass, rdtype) self.algorithm = algorithm self.flags = flags self.iterations = iterations if isinstance(salt, text_type): self.salt = salt.encode() else: self.salt = salt self.next = next self.windows = windows def to_text(self, origin=None, relativize=True, **kw): next = base64.b32encode(self.next).translate( b32_normal_to_hex).lower().decode() if self.salt == b'': salt = '-' else: salt = binascii.hexlify(self.salt).decode() text = u'' for (window, bitmap) in self.windows: bits = [] for i in xrange(0, len(bitmap)): byte = bitmap[i] for j in xrange(0, 8): if byte & (0x80 >> j): bits.append(dns.rdatatype.to_text(window * 256 + i * 8 + j)) text += (u' ' + u' '.join(bits)) return u'%u %u %u %s %s%s' % (self.algorithm, self.flags, self.iterations, salt, next, text) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): algorithm = tok.get_uint8() flags = tok.get_uint8() iterations = tok.get_uint16() salt = tok.get_string() if salt == u'-': salt = b'' else: salt = binascii.unhexlify(salt.encode('ascii')) next = tok.get_string().encode( 'ascii').upper().translate(b32_hex_to_normal) next = base64.b32decode(next) rdtypes = [] while 1: token = tok.get().unescape() if token.is_eol_or_eof(): break nrdtype = dns.rdatatype.from_text(token.value) if nrdtype == 0: raise dns.exception.SyntaxError("NSEC3 with bit 0") if nrdtype > 65535: raise dns.exception.SyntaxError("NSEC3 with bit > 65535") rdtypes.append(nrdtype) rdtypes.sort() window = 0 octets = 0 prior_rdtype = 0 bitmap = bytearray(b'\0' * 32) windows = [] for nrdtype in rdtypes: if nrdtype == prior_rdtype: continue prior_rdtype = nrdtype new_window = nrdtype // 256 if new_window != window: if octets != 0: windows.append((window, bitmap[0:octets])) bitmap = bytearray(b'\0' * 32) window = new_window offset = nrdtype % 256 byte = offset // 8 bit = offset % 8 octets = byte + 1 bitmap[byte] = bitmap[byte] | (0x80 >> bit) if octets != 0: windows.append((window, bitmap[0:octets])) return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) def to_wire(self, file, compress=None, origin=None): l = len(self.salt) file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) file.write(self.salt) l = len(self.next) file.write(struct.pack("!B", l)) file.write(self.next) for (window, bitmap) in self.windows: file.write(struct.pack("!BB", window, len(bitmap))) file.write(bitmap) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (algorithm, flags, iterations, slen) = \ struct.unpack('!BBHB', wire[current: current + 5]) current += 5 rdlen -= 5 salt = wire[current: current + slen].unwrap() current += slen rdlen -= slen nlen = wire[current] current += 1 rdlen -= 1 next = wire[current: current + nlen].unwrap() current += nlen rdlen -= nlen windows = [] while rdlen > 0: if rdlen < 3: raise dns.exception.FormError("NSEC3 too short") window = wire[current] octets = wire[current + 1] if octets == 0 or octets > 32: raise dns.exception.FormError("bad NSEC3 octets") current += 2 rdlen -= 2 if rdlen < octets: raise dns.exception.FormError("bad NSEC3 bitmap length") bitmap = bytearray(wire[current: current + octets].unwrap()) current += octets rdlen -= octets windows.append((window, bitmap)) return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, windows) dnspython-1.16.0/dns/rdtypes/ANY/NSEC3PARAM.py000066400000000000000000000061471340301174400205050ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import binascii import dns.exception import dns.rdata from dns._compat import text_type class NSEC3PARAM(dns.rdata.Rdata): """NSEC3PARAM record @ivar algorithm: the hash algorithm number @type algorithm: int @ivar flags: the flags @type flags: int @ivar iterations: the number of iterations @type iterations: int @ivar salt: the salt @type salt: string""" __slots__ = ['algorithm', 'flags', 'iterations', 'salt'] def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt): super(NSEC3PARAM, self).__init__(rdclass, rdtype) self.algorithm = algorithm self.flags = flags self.iterations = iterations if isinstance(salt, text_type): self.salt = salt.encode() else: self.salt = salt def to_text(self, origin=None, relativize=True, **kw): if self.salt == b'': salt = '-' else: salt = binascii.hexlify(self.salt).decode() return '%u %u %u %s' % (self.algorithm, self.flags, self.iterations, salt) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): algorithm = tok.get_uint8() flags = tok.get_uint8() iterations = tok.get_uint16() salt = tok.get_string() if salt == '-': salt = '' else: salt = binascii.unhexlify(salt.encode()) tok.get_eol() return cls(rdclass, rdtype, algorithm, flags, iterations, salt) def to_wire(self, file, compress=None, origin=None): l = len(self.salt) file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) file.write(self.salt) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (algorithm, flags, iterations, slen) = \ struct.unpack('!BBHB', wire[current: current + 5]) current += 5 rdlen -= 5 salt = wire[current: current + slen].unwrap() current += slen rdlen -= slen if rdlen != 0: raise dns.exception.FormError return cls(rdclass, rdtype, algorithm, flags, iterations, salt) dnspython-1.16.0/dns/rdtypes/ANY/OPENPGPKEY.py000066400000000000000000000037541340301174400205730ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2016 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import base64 import dns.exception import dns.rdata import dns.tokenizer class OPENPGPKEY(dns.rdata.Rdata): """OPENPGPKEY record @ivar key: the key @type key: bytes @see: RFC 7929 """ def __init__(self, rdclass, rdtype, key): super(OPENPGPKEY, self).__init__(rdclass, rdtype) self.key = key def to_text(self, origin=None, relativize=True, **kw): return dns.rdata._base64ify(self.key) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) b64 = b''.join(chunks) key = base64.b64decode(b64) return cls(rdclass, rdtype, key) def to_wire(self, file, compress=None, origin=None): file.write(self.key) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): key = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, key) dnspython-1.16.0/dns/rdtypes/ANY/PTR.py000066400000000000000000000016701340301174400175520ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.nsbase class PTR(dns.rdtypes.nsbase.NSBase): """PTR record""" dnspython-1.16.0/dns/rdtypes/ANY/RP.py000066400000000000000000000061171340301174400174270ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.exception import dns.rdata import dns.name class RP(dns.rdata.Rdata): """RP record @ivar mbox: The responsible person's mailbox @type mbox: dns.name.Name object @ivar txt: The owner name of a node with TXT records, or the root name if no TXT records are associated with this RP. @type txt: dns.name.Name object @see: RFC 1183""" __slots__ = ['mbox', 'txt'] def __init__(self, rdclass, rdtype, mbox, txt): super(RP, self).__init__(rdclass, rdtype) self.mbox = mbox self.txt = txt def to_text(self, origin=None, relativize=True, **kw): mbox = self.mbox.choose_relativity(origin, relativize) txt = self.txt.choose_relativity(origin, relativize) return "{} {}".format(str(mbox), str(txt)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): mbox = tok.get_name() txt = tok.get_name() mbox = mbox.choose_relativity(origin, relativize) txt = txt.choose_relativity(origin, relativize) tok.get_eol() return cls(rdclass, rdtype, mbox, txt) def to_wire(self, file, compress=None, origin=None): self.mbox.to_wire(file, None, origin) self.txt.to_wire(file, None, origin) def to_digestable(self, origin=None): return self.mbox.to_digestable(origin) + \ self.txt.to_digestable(origin) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (mbox, cused) = dns.name.from_wire(wire[: current + rdlen], current) current += cused rdlen -= cused if rdlen <= 0: raise dns.exception.FormError (txt, cused) = dns.name.from_wire(wire[: current + rdlen], current) if cused != rdlen: raise dns.exception.FormError if origin is not None: mbox = mbox.relativize(origin) txt = txt.relativize(origin) return cls(rdclass, rdtype, mbox, txt) def choose_relativity(self, origin=None, relativize=True): self.mbox = self.mbox.choose_relativity(origin, relativize) self.txt = self.txt.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/ANY/RRSIG.py000066400000000000000000000131541340301174400177730ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import base64 import calendar import struct import time import dns.dnssec import dns.exception import dns.rdata import dns.rdatatype class BadSigTime(dns.exception.DNSException): """Time in DNS SIG or RRSIG resource record cannot be parsed.""" def sigtime_to_posixtime(what): if len(what) != 14: raise BadSigTime year = int(what[0:4]) month = int(what[4:6]) day = int(what[6:8]) hour = int(what[8:10]) minute = int(what[10:12]) second = int(what[12:14]) return calendar.timegm((year, month, day, hour, minute, second, 0, 0, 0)) def posixtime_to_sigtime(what): return time.strftime('%Y%m%d%H%M%S', time.gmtime(what)) class RRSIG(dns.rdata.Rdata): """RRSIG record @ivar type_covered: the rdata type this signature covers @type type_covered: int @ivar algorithm: the algorithm used for the sig @type algorithm: int @ivar labels: number of labels @type labels: int @ivar original_ttl: the original TTL @type original_ttl: long @ivar expiration: signature expiration time @type expiration: long @ivar inception: signature inception time @type inception: long @ivar key_tag: the key tag @type key_tag: int @ivar signer: the signer @type signer: dns.name.Name object @ivar signature: the signature @type signature: string""" __slots__ = ['type_covered', 'algorithm', 'labels', 'original_ttl', 'expiration', 'inception', 'key_tag', 'signer', 'signature'] def __init__(self, rdclass, rdtype, type_covered, algorithm, labels, original_ttl, expiration, inception, key_tag, signer, signature): super(RRSIG, self).__init__(rdclass, rdtype) self.type_covered = type_covered self.algorithm = algorithm self.labels = labels self.original_ttl = original_ttl self.expiration = expiration self.inception = inception self.key_tag = key_tag self.signer = signer self.signature = signature def covers(self): return self.type_covered def to_text(self, origin=None, relativize=True, **kw): return '%s %d %d %d %s %s %d %s %s' % ( dns.rdatatype.to_text(self.type_covered), self.algorithm, self.labels, self.original_ttl, posixtime_to_sigtime(self.expiration), posixtime_to_sigtime(self.inception), self.key_tag, self.signer.choose_relativity(origin, relativize), dns.rdata._base64ify(self.signature) ) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): type_covered = dns.rdatatype.from_text(tok.get_string()) algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) labels = tok.get_int() original_ttl = tok.get_ttl() expiration = sigtime_to_posixtime(tok.get_string()) inception = sigtime_to_posixtime(tok.get_string()) key_tag = tok.get_int() signer = tok.get_name() signer = signer.choose_relativity(origin, relativize) chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) b64 = b''.join(chunks) signature = base64.b64decode(b64) return cls(rdclass, rdtype, type_covered, algorithm, labels, original_ttl, expiration, inception, key_tag, signer, signature) def to_wire(self, file, compress=None, origin=None): header = struct.pack('!HBBIIIH', self.type_covered, self.algorithm, self.labels, self.original_ttl, self.expiration, self.inception, self.key_tag) file.write(header) self.signer.to_wire(file, None, origin) file.write(self.signature) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): header = struct.unpack('!HBBIIIH', wire[current: current + 18]) current += 18 rdlen -= 18 (signer, cused) = dns.name.from_wire(wire[: current + rdlen], current) current += cused rdlen -= cused if origin is not None: signer = signer.relativize(origin) signature = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, header[0], header[1], header[2], header[3], header[4], header[5], header[6], signer, signature) def choose_relativity(self, origin=None, relativize=True): self.signer = self.signer.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/ANY/RT.py000066400000000000000000000017101340301174400174250ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.mxbase class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX): """RT record""" dnspython-1.16.0/dns/rdtypes/ANY/SOA.py000066400000000000000000000107651340301174400175340ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.name class SOA(dns.rdata.Rdata): """SOA record @ivar mname: the SOA MNAME (master name) field @type mname: dns.name.Name object @ivar rname: the SOA RNAME (responsible name) field @type rname: dns.name.Name object @ivar serial: The zone's serial number @type serial: int @ivar refresh: The zone's refresh value (in seconds) @type refresh: int @ivar retry: The zone's retry value (in seconds) @type retry: int @ivar expire: The zone's expiration value (in seconds) @type expire: int @ivar minimum: The zone's negative caching time (in seconds, called "minimum" for historical reasons) @type minimum: int @see: RFC 1035""" __slots__ = ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire', 'minimum'] def __init__(self, rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum): super(SOA, self).__init__(rdclass, rdtype) self.mname = mname self.rname = rname self.serial = serial self.refresh = refresh self.retry = retry self.expire = expire self.minimum = minimum def to_text(self, origin=None, relativize=True, **kw): mname = self.mname.choose_relativity(origin, relativize) rname = self.rname.choose_relativity(origin, relativize) return '%s %s %d %d %d %d %d' % ( mname, rname, self.serial, self.refresh, self.retry, self.expire, self.minimum) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): mname = tok.get_name() rname = tok.get_name() mname = mname.choose_relativity(origin, relativize) rname = rname.choose_relativity(origin, relativize) serial = tok.get_uint32() refresh = tok.get_ttl() retry = tok.get_ttl() expire = tok.get_ttl() minimum = tok.get_ttl() tok.get_eol() return cls(rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum) def to_wire(self, file, compress=None, origin=None): self.mname.to_wire(file, compress, origin) self.rname.to_wire(file, compress, origin) five_ints = struct.pack('!IIIII', self.serial, self.refresh, self.retry, self.expire, self.minimum) file.write(five_ints) def to_digestable(self, origin=None): return self.mname.to_digestable(origin) + \ self.rname.to_digestable(origin) + \ struct.pack('!IIIII', self.serial, self.refresh, self.retry, self.expire, self.minimum) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (mname, cused) = dns.name.from_wire(wire[: current + rdlen], current) current += cused rdlen -= cused (rname, cused) = dns.name.from_wire(wire[: current + rdlen], current) current += cused rdlen -= cused if rdlen != 20: raise dns.exception.FormError five_ints = struct.unpack('!IIIII', wire[current: current + rdlen]) if origin is not None: mname = mname.relativize(origin) rname = rname.relativize(origin) return cls(rdclass, rdtype, mname, rname, five_ints[0], five_ints[1], five_ints[2], five_ints[3], five_ints[4]) def choose_relativity(self, origin=None, relativize=True): self.mname = self.mname.choose_relativity(origin, relativize) self.rname = self.rname.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/ANY/SPF.py000066400000000000000000000017201340301174400175310ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.txtbase class SPF(dns.rdtypes.txtbase.TXTBase): """SPF record @see: RFC 4408""" dnspython-1.16.0/dns/rdtypes/ANY/SSHFP.py000066400000000000000000000055311340301174400177700ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import binascii import dns.rdata import dns.rdatatype class SSHFP(dns.rdata.Rdata): """SSHFP record @ivar algorithm: the algorithm @type algorithm: int @ivar fp_type: the digest type @type fp_type: int @ivar fingerprint: the fingerprint @type fingerprint: string @see: draft-ietf-secsh-dns-05.txt""" __slots__ = ['algorithm', 'fp_type', 'fingerprint'] def __init__(self, rdclass, rdtype, algorithm, fp_type, fingerprint): super(SSHFP, self).__init__(rdclass, rdtype) self.algorithm = algorithm self.fp_type = fp_type self.fingerprint = fingerprint def to_text(self, origin=None, relativize=True, **kw): return '%d %d %s' % (self.algorithm, self.fp_type, dns.rdata._hexify(self.fingerprint, chunksize=128)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): algorithm = tok.get_uint8() fp_type = tok.get_uint8() chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) fingerprint = b''.join(chunks) fingerprint = binascii.unhexlify(fingerprint) return cls(rdclass, rdtype, algorithm, fp_type, fingerprint) def to_wire(self, file, compress=None, origin=None): header = struct.pack("!BB", self.algorithm, self.fp_type) file.write(header) file.write(self.fingerprint) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): header = struct.unpack("!BB", wire[current: current + 2]) current += 2 rdlen -= 2 fingerprint = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, header[0], header[1], fingerprint) dnspython-1.16.0/dns/rdtypes/ANY/TLSA.py000066400000000000000000000057311340301174400176520ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import binascii import dns.rdata import dns.rdatatype class TLSA(dns.rdata.Rdata): """TLSA record @ivar usage: The certificate usage @type usage: int @ivar selector: The selector field @type selector: int @ivar mtype: The 'matching type' field @type mtype: int @ivar cert: The 'Certificate Association Data' field @type cert: string @see: RFC 6698""" __slots__ = ['usage', 'selector', 'mtype', 'cert'] def __init__(self, rdclass, rdtype, usage, selector, mtype, cert): super(TLSA, self).__init__(rdclass, rdtype) self.usage = usage self.selector = selector self.mtype = mtype self.cert = cert def to_text(self, origin=None, relativize=True, **kw): return '%d %d %d %s' % (self.usage, self.selector, self.mtype, dns.rdata._hexify(self.cert, chunksize=128)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): usage = tok.get_uint8() selector = tok.get_uint8() mtype = tok.get_uint8() cert_chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError cert_chunks.append(t.value.encode()) cert = b''.join(cert_chunks) cert = binascii.unhexlify(cert) return cls(rdclass, rdtype, usage, selector, mtype, cert) def to_wire(self, file, compress=None, origin=None): header = struct.pack("!BBB", self.usage, self.selector, self.mtype) file.write(header) file.write(self.cert) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): header = struct.unpack("!BBB", wire[current: current + 3]) current += 3 rdlen -= 3 cert = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, header[0], header[1], header[2], cert) dnspython-1.16.0/dns/rdtypes/ANY/TXT.py000066400000000000000000000016731340301174400175670ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.txtbase class TXT(dns.rdtypes.txtbase.TXTBase): """TXT record""" dnspython-1.16.0/dns/rdtypes/ANY/URI.py000066400000000000000000000056371340301174400175530ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # Copyright (C) 2015 Red Hat, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.name from dns._compat import text_type class URI(dns.rdata.Rdata): """URI record @ivar priority: the priority @type priority: int @ivar weight: the weight @type weight: int @ivar target: the target host @type target: dns.name.Name object @see: draft-faltstrom-uri-13""" __slots__ = ['priority', 'weight', 'target'] def __init__(self, rdclass, rdtype, priority, weight, target): super(URI, self).__init__(rdclass, rdtype) self.priority = priority self.weight = weight if len(target) < 1: raise dns.exception.SyntaxError("URI target cannot be empty") if isinstance(target, text_type): self.target = target.encode() else: self.target = target def to_text(self, origin=None, relativize=True, **kw): return '%d %d "%s"' % (self.priority, self.weight, self.target.decode()) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): priority = tok.get_uint16() weight = tok.get_uint16() target = tok.get().unescape() if not (target.is_quoted_string() or target.is_identifier()): raise dns.exception.SyntaxError("URI target must be a string") tok.get_eol() return cls(rdclass, rdtype, priority, weight, target.value) def to_wire(self, file, compress=None, origin=None): two_ints = struct.pack("!HH", self.priority, self.weight) file.write(two_ints) file.write(self.target) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): if rdlen < 5: raise dns.exception.FormError('URI RR is shorter than 5 octets') (priority, weight) = struct.unpack('!HH', wire[current: current + 4]) current += 4 rdlen -= 4 target = wire[current: current + rdlen] current += rdlen return cls(rdclass, rdtype, priority, weight, target) dnspython-1.16.0/dns/rdtypes/ANY/X25.py000066400000000000000000000042241340301174400174610ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.tokenizer from dns._compat import text_type class X25(dns.rdata.Rdata): """X25 record @ivar address: the PSDN address @type address: string @see: RFC 1183""" __slots__ = ['address'] def __init__(self, rdclass, rdtype, address): super(X25, self).__init__(rdclass, rdtype) if isinstance(address, text_type): self.address = address.encode() else: self.address = address def to_text(self, origin=None, relativize=True, **kw): return '"%s"' % dns.rdata._escapify(self.address) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): address = tok.get_string() tok.get_eol() return cls(rdclass, rdtype, address) def to_wire(self, file, compress=None, origin=None): l = len(self.address) assert l < 256 file.write(struct.pack('!B', l)) file.write(self.address) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): l = wire[current] current += 1 rdlen -= 1 if l != rdlen: raise dns.exception.FormError address = wire[current: current + l].unwrap() return cls(rdclass, rdtype, address) dnspython-1.16.0/dns/rdtypes/ANY/__init__.py000066400000000000000000000025221340301174400206410ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Class ANY (generic) rdata type classes.""" __all__ = [ 'AFSDB', 'AVC', 'CAA', 'CDNSKEY', 'CDS', 'CERT', 'CNAME', 'CSYNC', 'DLV', 'DNAME', 'DNSKEY', 'DS', 'EUI48', 'EUI64', 'GPOS', 'HINFO', 'HIP', 'ISDN', 'LOC', 'MX', 'NS', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'OPENPGPKEY', 'PTR', 'RP', 'RRSIG', 'RT', 'SOA', 'SPF', 'SSHFP', 'TLSA', 'TXT', 'URI', 'X25', ] dnspython-1.16.0/dns/rdtypes/CH/000077500000000000000000000000001340301174400163725ustar00rootroot00000000000000dnspython-1.16.0/dns/rdtypes/CH/A.py000066400000000000000000000053551340301174400171340ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.mxbase import struct class A(dns.rdtypes.mxbase.MXBase): """A record for Chaosnet @ivar domain: the domain of the address @type domain: dns.name.Name object @ivar address: the 16-bit address @type address: int""" __slots__ = ['domain', 'address'] def __init__(self, rdclass, rdtype, address, domain): super(A, self).__init__(rdclass, rdtype, address, domain) self.domain = domain self.address = address def to_text(self, origin=None, relativize=True, **kw): domain = self.domain.choose_relativity(origin, relativize) return '%s %o' % (domain, self.address) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): domain = tok.get_name() address = tok.get_uint16(base=8) domain = domain.choose_relativity(origin, relativize) tok.get_eol() return cls(rdclass, rdtype, address, domain) def to_wire(self, file, compress=None, origin=None): self.domain.to_wire(file, compress, origin) pref = struct.pack("!H", self.address) file.write(pref) def to_digestable(self, origin=None): return self.domain.to_digestable(origin) + \ struct.pack("!H", self.address) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (domain, cused) = dns.name.from_wire(wire[: current + rdlen-2], current) current += cused (address,) = struct.unpack('!H', wire[current: current + 2]) if cused+2 != rdlen: raise dns.exception.FormError if origin is not None: domain = domain.relativize(origin) return cls(rdclass, rdtype, address, domain) def choose_relativity(self, origin=None, relativize=True): self.domain = self.domain.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/CH/__init__.py000066400000000000000000000016331340301174400205060ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Class CH rdata type classes.""" __all__ = [ 'A', ] dnspython-1.16.0/dns/rdtypes/IN/000077500000000000000000000000001340301174400164065ustar00rootroot00000000000000dnspython-1.16.0/dns/rdtypes/IN/A.py000066400000000000000000000036011340301174400171400ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.exception import dns.ipv4 import dns.rdata import dns.tokenizer class A(dns.rdata.Rdata): """A record. @ivar address: an IPv4 address @type address: string (in the standard "dotted quad" format)""" __slots__ = ['address'] def __init__(self, rdclass, rdtype, address): super(A, self).__init__(rdclass, rdtype) # check that it's OK dns.ipv4.inet_aton(address) self.address = address def to_text(self, origin=None, relativize=True, **kw): return self.address @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): address = tok.get_identifier() tok.get_eol() return cls(rdclass, rdtype, address) def to_wire(self, file, compress=None, origin=None): file.write(dns.ipv4.inet_aton(self.address)) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): address = dns.ipv4.inet_ntoa(wire[current: current + rdlen]) return cls(rdclass, rdtype, address) dnspython-1.16.0/dns/rdtypes/IN/AAAA.py000066400000000000000000000037371340301174400174550ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.exception import dns.inet import dns.rdata import dns.tokenizer class AAAA(dns.rdata.Rdata): """AAAA record. @ivar address: an IPv6 address @type address: string (in the standard IPv6 format)""" __slots__ = ['address'] def __init__(self, rdclass, rdtype, address): super(AAAA, self).__init__(rdclass, rdtype) # check that it's OK dns.inet.inet_pton(dns.inet.AF_INET6, address) self.address = address def to_text(self, origin=None, relativize=True, **kw): return self.address @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): address = tok.get_identifier() tok.get_eol() return cls(rdclass, rdtype, address) def to_wire(self, file, compress=None, origin=None): file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.address)) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): address = dns.inet.inet_ntop(dns.inet.AF_INET6, wire[current: current + rdlen]) return cls(rdclass, rdtype, address) dnspython-1.16.0/dns/rdtypes/IN/APL.py000066400000000000000000000123401340301174400173740ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import binascii import codecs import struct import dns.exception import dns.inet import dns.rdata import dns.tokenizer from dns._compat import xrange, maybe_chr class APLItem(object): """An APL list item. @ivar family: the address family (IANA address family registry) @type family: int @ivar negation: is this item negated? @type negation: bool @ivar address: the address @type address: string @ivar prefix: the prefix length @type prefix: int """ __slots__ = ['family', 'negation', 'address', 'prefix'] def __init__(self, family, negation, address, prefix): self.family = family self.negation = negation self.address = address self.prefix = prefix def __str__(self): if self.negation: return "!%d:%s/%s" % (self.family, self.address, self.prefix) else: return "%d:%s/%s" % (self.family, self.address, self.prefix) def to_wire(self, file): if self.family == 1: address = dns.inet.inet_pton(dns.inet.AF_INET, self.address) elif self.family == 2: address = dns.inet.inet_pton(dns.inet.AF_INET6, self.address) else: address = binascii.unhexlify(self.address) # # Truncate least significant zero bytes. # last = 0 for i in xrange(len(address) - 1, -1, -1): if address[i] != maybe_chr(0): last = i + 1 break address = address[0: last] l = len(address) assert l < 128 if self.negation: l |= 0x80 header = struct.pack('!HBB', self.family, self.prefix, l) file.write(header) file.write(address) class APL(dns.rdata.Rdata): """APL record. @ivar items: a list of APL items @type items: list of APL_Item @see: RFC 3123""" __slots__ = ['items'] def __init__(self, rdclass, rdtype, items): super(APL, self).__init__(rdclass, rdtype) self.items = items def to_text(self, origin=None, relativize=True, **kw): return ' '.join(map(str, self.items)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): items = [] while 1: token = tok.get().unescape() if token.is_eol_or_eof(): break item = token.value if item[0] == '!': negation = True item = item[1:] else: negation = False (family, rest) = item.split(':', 1) family = int(family) (address, prefix) = rest.split('/', 1) prefix = int(prefix) item = APLItem(family, negation, address, prefix) items.append(item) return cls(rdclass, rdtype, items) def to_wire(self, file, compress=None, origin=None): for item in self.items: item.to_wire(file) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): items = [] while 1: if rdlen == 0: break if rdlen < 4: raise dns.exception.FormError header = struct.unpack('!HBB', wire[current: current + 4]) afdlen = header[2] if afdlen > 127: negation = True afdlen -= 128 else: negation = False current += 4 rdlen -= 4 if rdlen < afdlen: raise dns.exception.FormError address = wire[current: current + afdlen].unwrap() l = len(address) if header[0] == 1: if l < 4: address += b'\x00' * (4 - l) address = dns.inet.inet_ntop(dns.inet.AF_INET, address) elif header[0] == 2: if l < 16: address += b'\x00' * (16 - l) address = dns.inet.inet_ntop(dns.inet.AF_INET6, address) else: # # This isn't really right according to the RFC, but it # seems better than throwing an exception # address = codecs.encode(address, 'hex_codec') current += afdlen rdlen -= afdlen item = APLItem(header[0], negation, address, header[1]) items.append(item) return cls(rdclass, rdtype, items) dnspython-1.16.0/dns/rdtypes/IN/DHCID.py000066400000000000000000000040601340301174400175730ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import base64 import dns.exception class DHCID(dns.rdata.Rdata): """DHCID record @ivar data: the data (the content of the RR is opaque as far as the DNS is concerned) @type data: string @see: RFC 4701""" __slots__ = ['data'] def __init__(self, rdclass, rdtype, data): super(DHCID, self).__init__(rdclass, rdtype) self.data = data def to_text(self, origin=None, relativize=True, **kw): return dns.rdata._base64ify(self.data) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) b64 = b''.join(chunks) data = base64.b64decode(b64) return cls(rdclass, rdtype, data) def to_wire(self, file, compress=None, origin=None): file.write(self.data) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): data = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, data) dnspython-1.16.0/dns/rdtypes/IN/IPSECKEY.py000066400000000000000000000131761340301174400202040ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import base64 import dns.exception import dns.inet import dns.name class IPSECKEY(dns.rdata.Rdata): """IPSECKEY record @ivar precedence: the precedence for this key data @type precedence: int @ivar gateway_type: the gateway type @type gateway_type: int @ivar algorithm: the algorithm to use @type algorithm: int @ivar gateway: the public key @type gateway: None, IPv4 address, IPV6 address, or domain name @ivar key: the public key @type key: string @see: RFC 4025""" __slots__ = ['precedence', 'gateway_type', 'algorithm', 'gateway', 'key'] def __init__(self, rdclass, rdtype, precedence, gateway_type, algorithm, gateway, key): super(IPSECKEY, self).__init__(rdclass, rdtype) if gateway_type == 0: if gateway != '.' and gateway is not None: raise SyntaxError('invalid gateway for gateway type 0') gateway = None elif gateway_type == 1: # check that it's OK dns.inet.inet_pton(dns.inet.AF_INET, gateway) elif gateway_type == 2: # check that it's OK dns.inet.inet_pton(dns.inet.AF_INET6, gateway) elif gateway_type == 3: pass else: raise SyntaxError( 'invalid IPSECKEY gateway type: %d' % gateway_type) self.precedence = precedence self.gateway_type = gateway_type self.algorithm = algorithm self.gateway = gateway self.key = key def to_text(self, origin=None, relativize=True, **kw): if self.gateway_type == 0: gateway = '.' elif self.gateway_type == 1: gateway = self.gateway elif self.gateway_type == 2: gateway = self.gateway elif self.gateway_type == 3: gateway = str(self.gateway.choose_relativity(origin, relativize)) else: raise ValueError('invalid gateway type') return '%d %d %d %s %s' % (self.precedence, self.gateway_type, self.algorithm, gateway, dns.rdata._base64ify(self.key)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): precedence = tok.get_uint8() gateway_type = tok.get_uint8() algorithm = tok.get_uint8() if gateway_type == 3: gateway = tok.get_name().choose_relativity(origin, relativize) else: gateway = tok.get_string() chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) b64 = b''.join(chunks) key = base64.b64decode(b64) return cls(rdclass, rdtype, precedence, gateway_type, algorithm, gateway, key) def to_wire(self, file, compress=None, origin=None): header = struct.pack("!BBB", self.precedence, self.gateway_type, self.algorithm) file.write(header) if self.gateway_type == 0: pass elif self.gateway_type == 1: file.write(dns.inet.inet_pton(dns.inet.AF_INET, self.gateway)) elif self.gateway_type == 2: file.write(dns.inet.inet_pton(dns.inet.AF_INET6, self.gateway)) elif self.gateway_type == 3: self.gateway.to_wire(file, None, origin) else: raise ValueError('invalid gateway type') file.write(self.key) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): if rdlen < 3: raise dns.exception.FormError header = struct.unpack('!BBB', wire[current: current + 3]) gateway_type = header[1] current += 3 rdlen -= 3 if gateway_type == 0: gateway = None elif gateway_type == 1: gateway = dns.inet.inet_ntop(dns.inet.AF_INET, wire[current: current + 4]) current += 4 rdlen -= 4 elif gateway_type == 2: gateway = dns.inet.inet_ntop(dns.inet.AF_INET6, wire[current: current + 16]) current += 16 rdlen -= 16 elif gateway_type == 3: (gateway, cused) = dns.name.from_wire(wire[: current + rdlen], current) current += cused rdlen -= cused else: raise dns.exception.FormError('invalid IPSECKEY gateway type') key = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, header[0], gateway_type, header[2], gateway, key) dnspython-1.16.0/dns/rdtypes/IN/KX.py000066400000000000000000000016761340301174400173140ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.mxbase class KX(dns.rdtypes.mxbase.UncompressedMX): """KX record""" dnspython-1.16.0/dns/rdtypes/IN/NAPTR.py000066400000000000000000000107031340301174400176450ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.name import dns.rdata from dns._compat import xrange, text_type def _write_string(file, s): l = len(s) assert l < 256 file.write(struct.pack('!B', l)) file.write(s) def _sanitize(value): if isinstance(value, text_type): return value.encode() return value class NAPTR(dns.rdata.Rdata): """NAPTR record @ivar order: order @type order: int @ivar preference: preference @type preference: int @ivar flags: flags @type flags: string @ivar service: service @type service: string @ivar regexp: regular expression @type regexp: string @ivar replacement: replacement name @type replacement: dns.name.Name object @see: RFC 3403""" __slots__ = ['order', 'preference', 'flags', 'service', 'regexp', 'replacement'] def __init__(self, rdclass, rdtype, order, preference, flags, service, regexp, replacement): super(NAPTR, self).__init__(rdclass, rdtype) self.flags = _sanitize(flags) self.service = _sanitize(service) self.regexp = _sanitize(regexp) self.order = order self.preference = preference self.replacement = replacement def to_text(self, origin=None, relativize=True, **kw): replacement = self.replacement.choose_relativity(origin, relativize) return '%d %d "%s" "%s" "%s" %s' % \ (self.order, self.preference, dns.rdata._escapify(self.flags), dns.rdata._escapify(self.service), dns.rdata._escapify(self.regexp), replacement) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): order = tok.get_uint16() preference = tok.get_uint16() flags = tok.get_string() service = tok.get_string() regexp = tok.get_string() replacement = tok.get_name() replacement = replacement.choose_relativity(origin, relativize) tok.get_eol() return cls(rdclass, rdtype, order, preference, flags, service, regexp, replacement) def to_wire(self, file, compress=None, origin=None): two_ints = struct.pack("!HH", self.order, self.preference) file.write(two_ints) _write_string(file, self.flags) _write_string(file, self.service) _write_string(file, self.regexp) self.replacement.to_wire(file, compress, origin) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (order, preference) = struct.unpack('!HH', wire[current: current + 4]) current += 4 rdlen -= 4 strings = [] for i in xrange(3): l = wire[current] current += 1 rdlen -= 1 if l > rdlen or rdlen < 0: raise dns.exception.FormError s = wire[current: current + l].unwrap() current += l rdlen -= l strings.append(s) (replacement, cused) = dns.name.from_wire(wire[: current + rdlen], current) if cused != rdlen: raise dns.exception.FormError if origin is not None: replacement = replacement.relativize(origin) return cls(rdclass, rdtype, order, preference, strings[0], strings[1], strings[2], replacement) def choose_relativity(self, origin=None, relativize=True): self.replacement = self.replacement.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/IN/NSAP.py000066400000000000000000000041541340301174400175250ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import binascii import dns.exception import dns.rdata import dns.tokenizer class NSAP(dns.rdata.Rdata): """NSAP record. @ivar address: a NASP @type address: string @see: RFC 1706""" __slots__ = ['address'] def __init__(self, rdclass, rdtype, address): super(NSAP, self).__init__(rdclass, rdtype) self.address = address def to_text(self, origin=None, relativize=True, **kw): return "0x%s" % binascii.hexlify(self.address).decode() @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): address = tok.get_string() tok.get_eol() if address[0:2] != '0x': raise dns.exception.SyntaxError('string does not start with 0x') address = address[2:].replace('.', '') if len(address) % 2 != 0: raise dns.exception.SyntaxError('hexstring has odd length') address = binascii.unhexlify(address.encode()) return cls(rdclass, rdtype, address) def to_wire(self, file, compress=None, origin=None): file.write(self.address) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): address = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, address) dnspython-1.16.0/dns/rdtypes/IN/NSAP_PTR.py000066400000000000000000000017121340301174400202470ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import dns.rdtypes.nsbase class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS): """NSAP-PTR record""" dnspython-1.16.0/dns/rdtypes/IN/PX.py000066400000000000000000000066171340301174400173210ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.name class PX(dns.rdata.Rdata): """PX record. @ivar preference: the preference value @type preference: int @ivar map822: the map822 name @type map822: dns.name.Name object @ivar mapx400: the mapx400 name @type mapx400: dns.name.Name object @see: RFC 2163""" __slots__ = ['preference', 'map822', 'mapx400'] def __init__(self, rdclass, rdtype, preference, map822, mapx400): super(PX, self).__init__(rdclass, rdtype) self.preference = preference self.map822 = map822 self.mapx400 = mapx400 def to_text(self, origin=None, relativize=True, **kw): map822 = self.map822.choose_relativity(origin, relativize) mapx400 = self.mapx400.choose_relativity(origin, relativize) return '%d %s %s' % (self.preference, map822, mapx400) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): preference = tok.get_uint16() map822 = tok.get_name() map822 = map822.choose_relativity(origin, relativize) mapx400 = tok.get_name(None) mapx400 = mapx400.choose_relativity(origin, relativize) tok.get_eol() return cls(rdclass, rdtype, preference, map822, mapx400) def to_wire(self, file, compress=None, origin=None): pref = struct.pack("!H", self.preference) file.write(pref) self.map822.to_wire(file, None, origin) self.mapx400.to_wire(file, None, origin) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (preference, ) = struct.unpack('!H', wire[current: current + 2]) current += 2 rdlen -= 2 (map822, cused) = dns.name.from_wire(wire[: current + rdlen], current) if cused > rdlen: raise dns.exception.FormError current += cused rdlen -= cused if origin is not None: map822 = map822.relativize(origin) (mapx400, cused) = dns.name.from_wire(wire[: current + rdlen], current) if cused != rdlen: raise dns.exception.FormError if origin is not None: mapx400 = mapx400.relativize(origin) return cls(rdclass, rdtype, preference, map822, mapx400) def choose_relativity(self, origin=None, relativize=True): self.map822 = self.map822.choose_relativity(origin, relativize) self.mapx400 = self.mapx400.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/IN/SRV.py000066400000000000000000000060731340301174400174400ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import dns.exception import dns.rdata import dns.name class SRV(dns.rdata.Rdata): """SRV record @ivar priority: the priority @type priority: int @ivar weight: the weight @type weight: int @ivar port: the port of the service @type port: int @ivar target: the target host @type target: dns.name.Name object @see: RFC 2782""" __slots__ = ['priority', 'weight', 'port', 'target'] def __init__(self, rdclass, rdtype, priority, weight, port, target): super(SRV, self).__init__(rdclass, rdtype) self.priority = priority self.weight = weight self.port = port self.target = target def to_text(self, origin=None, relativize=True, **kw): target = self.target.choose_relativity(origin, relativize) return '%d %d %d %s' % (self.priority, self.weight, self.port, target) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): priority = tok.get_uint16() weight = tok.get_uint16() port = tok.get_uint16() target = tok.get_name(None) target = target.choose_relativity(origin, relativize) tok.get_eol() return cls(rdclass, rdtype, priority, weight, port, target) def to_wire(self, file, compress=None, origin=None): three_ints = struct.pack("!HHH", self.priority, self.weight, self.port) file.write(three_ints) self.target.to_wire(file, compress, origin) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (priority, weight, port) = struct.unpack('!HHH', wire[current: current + 6]) current += 6 rdlen -= 6 (target, cused) = dns.name.from_wire(wire[: current + rdlen], current) if cused != rdlen: raise dns.exception.FormError if origin is not None: target = target.relativize(origin) return cls(rdclass, rdtype, priority, weight, port, target) def choose_relativity(self, origin=None, relativize=True): self.target = self.target.choose_relativity(origin, relativize) dnspython-1.16.0/dns/rdtypes/IN/WKS.py000066400000000000000000000074601340301174400174330ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import socket import struct import dns.ipv4 import dns.rdata from dns._compat import xrange _proto_tcp = socket.getprotobyname('tcp') _proto_udp = socket.getprotobyname('udp') class WKS(dns.rdata.Rdata): """WKS record @ivar address: the address @type address: string @ivar protocol: the protocol @type protocol: int @ivar bitmap: the bitmap @type bitmap: string @see: RFC 1035""" __slots__ = ['address', 'protocol', 'bitmap'] def __init__(self, rdclass, rdtype, address, protocol, bitmap): super(WKS, self).__init__(rdclass, rdtype) self.address = address self.protocol = protocol if not isinstance(bitmap, bytearray): self.bitmap = bytearray(bitmap) else: self.bitmap = bitmap def to_text(self, origin=None, relativize=True, **kw): bits = [] for i in xrange(0, len(self.bitmap)): byte = self.bitmap[i] for j in xrange(0, 8): if byte & (0x80 >> j): bits.append(str(i * 8 + j)) text = ' '.join(bits) return '%s %d %s' % (self.address, self.protocol, text) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): address = tok.get_string() protocol = tok.get_string() if protocol.isdigit(): protocol = int(protocol) else: protocol = socket.getprotobyname(protocol) bitmap = bytearray() while 1: token = tok.get().unescape() if token.is_eol_or_eof(): break if token.value.isdigit(): serv = int(token.value) else: if protocol != _proto_udp and protocol != _proto_tcp: raise NotImplementedError("protocol must be TCP or UDP") if protocol == _proto_udp: protocol_text = "udp" else: protocol_text = "tcp" serv = socket.getservbyname(token.value, protocol_text) i = serv // 8 l = len(bitmap) if l < i + 1: for j in xrange(l, i + 1): bitmap.append(0) bitmap[i] = bitmap[i] | (0x80 >> (serv % 8)) bitmap = dns.rdata._truncate_bitmap(bitmap) return cls(rdclass, rdtype, address, protocol, bitmap) def to_wire(self, file, compress=None, origin=None): file.write(dns.ipv4.inet_aton(self.address)) protocol = struct.pack('!B', self.protocol) file.write(protocol) file.write(self.bitmap) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): address = dns.ipv4.inet_ntoa(wire[current: current + 4]) protocol, = struct.unpack('!B', wire[current + 4: current + 5]) current += 5 rdlen -= 5 bitmap = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, address, protocol, bitmap) dnspython-1.16.0/dns/rdtypes/IN/__init__.py000066400000000000000000000020421340301174400205150ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Class IN rdata type classes.""" __all__ = [ 'A', 'AAAA', 'APL', 'DHCID', 'IPSECKEY', 'KX', 'NAPTR', 'NSAP', 'NSAP_PTR', 'PX', 'SRV', 'WKS', ] dnspython-1.16.0/dns/rdtypes/__init__.py000066400000000000000000000017261340301174400202170ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS rdata type classes""" __all__ = [ 'ANY', 'IN', 'CH', 'euibase', 'mxbase', 'nsbase', ] dnspython-1.16.0/dns/rdtypes/dnskeybase.py000066400000000000000000000105521340301174400206050ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import base64 import struct import dns.exception import dns.dnssec import dns.rdata # wildcard import __all__ = ["SEP", "REVOKE", "ZONE", "flags_to_text_set", "flags_from_text_set"] # flag constants SEP = 0x0001 REVOKE = 0x0080 ZONE = 0x0100 _flag_by_text = { 'SEP': SEP, 'REVOKE': REVOKE, 'ZONE': ZONE } # We construct the inverse mapping programmatically to ensure that we # cannot make any mistakes (e.g. omissions, cut-and-paste errors) that # would cause the mapping not to be true inverse. _flag_by_value = {y: x for x, y in _flag_by_text.items()} def flags_to_text_set(flags): """Convert a DNSKEY flags value to set texts @rtype: set([string])""" flags_set = set() mask = 0x1 while mask <= 0x8000: if flags & mask: text = _flag_by_value.get(mask) if not text: text = hex(mask) flags_set.add(text) mask <<= 1 return flags_set def flags_from_text_set(texts_set): """Convert set of DNSKEY flag mnemonic texts to DNSKEY flag value @rtype: int""" flags = 0 for text in texts_set: try: flags += _flag_by_text[text] except KeyError: raise NotImplementedError( "DNSKEY flag '%s' is not supported" % text) return flags class DNSKEYBase(dns.rdata.Rdata): """Base class for rdata that is like a DNSKEY record @ivar flags: the key flags @type flags: int @ivar protocol: the protocol for which this key may be used @type protocol: int @ivar algorithm: the algorithm used for the key @type algorithm: int @ivar key: the public key @type key: string""" __slots__ = ['flags', 'protocol', 'algorithm', 'key'] def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): super(DNSKEYBase, self).__init__(rdclass, rdtype) self.flags = flags self.protocol = protocol self.algorithm = algorithm self.key = key def to_text(self, origin=None, relativize=True, **kw): return '%d %d %d %s' % (self.flags, self.protocol, self.algorithm, dns.rdata._base64ify(self.key)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): flags = tok.get_uint16() protocol = tok.get_uint8() algorithm = dns.dnssec.algorithm_from_text(tok.get_string()) chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) b64 = b''.join(chunks) key = base64.b64decode(b64) return cls(rdclass, rdtype, flags, protocol, algorithm, key) def to_wire(self, file, compress=None, origin=None): header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) file.write(header) file.write(self.key) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): if rdlen < 4: raise dns.exception.FormError header = struct.unpack('!HBB', wire[current: current + 4]) current += 4 rdlen -= 4 key = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, header[0], header[1], header[2], key) def flags_to_text_set(self): """Convert a DNSKEY flags value to set texts @rtype: set([string])""" return flags_to_text_set(self.flags) dnspython-1.16.0/dns/rdtypes/dnskeybase.pyi000066400000000000000000000014771340301174400207640ustar00rootroot00000000000000from typing import Set, Any SEP : int REVOKE : int ZONE : int def flags_to_text_set(flags : int) -> Set[str]: ... def flags_from_text_set(texts_set) -> int: ... from .. import rdata class DNSKEYBase(rdata.Rdata): def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): self.flags : int self.protocol : int self.key : str self.algorithm : int def to_text(self, origin : Any = None, relativize=True, **kw : Any): ... @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): ... def to_wire(self, file, compress=None, origin=None): ... @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): ... def flags_to_text_set(self) -> Set[str]: ... dnspython-1.16.0/dns/rdtypes/dsbase.py000066400000000000000000000061251340301174400177170ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2010, 2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import struct import binascii import dns.rdata import dns.rdatatype class DSBase(dns.rdata.Rdata): """Base class for rdata that is like a DS record @ivar key_tag: the key tag @type key_tag: int @ivar algorithm: the algorithm @type algorithm: int @ivar digest_type: the digest type @type digest_type: int @ivar digest: the digest @type digest: int @see: draft-ietf-dnsext-delegation-signer-14.txt""" __slots__ = ['key_tag', 'algorithm', 'digest_type', 'digest'] def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type, digest): super(DSBase, self).__init__(rdclass, rdtype) self.key_tag = key_tag self.algorithm = algorithm self.digest_type = digest_type self.digest = digest def to_text(self, origin=None, relativize=True, **kw): return '%d %d %d %s' % (self.key_tag, self.algorithm, self.digest_type, dns.rdata._hexify(self.digest, chunksize=128)) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): key_tag = tok.get_uint16() algorithm = tok.get_uint8() digest_type = tok.get_uint8() chunks = [] while 1: t = tok.get().unescape() if t.is_eol_or_eof(): break if not t.is_identifier(): raise dns.exception.SyntaxError chunks.append(t.value.encode()) digest = b''.join(chunks) digest = binascii.unhexlify(digest) return cls(rdclass, rdtype, key_tag, algorithm, digest_type, digest) def to_wire(self, file, compress=None, origin=None): header = struct.pack("!HBB", self.key_tag, self.algorithm, self.digest_type) file.write(header) file.write(self.digest) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): header = struct.unpack("!HBB", wire[current: current + 4]) current += 4 rdlen -= 4 digest = wire[current: current + rdlen].unwrap() return cls(rdclass, rdtype, header[0], header[1], header[2], digest) dnspython-1.16.0/dns/rdtypes/euibase.py000066400000000000000000000053311340301174400200710ustar00rootroot00000000000000# Copyright (C) 2015 Red Hat, Inc. # Author: Petr Spacek # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import binascii import dns.rdata from dns._compat import xrange class EUIBase(dns.rdata.Rdata): """EUIxx record @ivar fingerprint: xx-bit Extended Unique Identifier (EUI-xx) @type fingerprint: string @see: rfc7043.txt""" __slots__ = ['eui'] # define these in subclasses # byte_len = 6 # 0123456789ab (in hex) # text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab def __init__(self, rdclass, rdtype, eui): super(EUIBase, self).__init__(rdclass, rdtype) if len(eui) != self.byte_len: raise dns.exception.FormError('EUI%s rdata has to have %s bytes' % (self.byte_len * 8, self.byte_len)) self.eui = eui def to_text(self, origin=None, relativize=True, **kw): return dns.rdata._hexify(self.eui, chunksize=2).replace(' ', '-') @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): text = tok.get_string() tok.get_eol() if len(text) != cls.text_len: raise dns.exception.SyntaxError( 'Input text must have %s characters' % cls.text_len) expected_dash_idxs = xrange(2, cls.byte_len * 3 - 1, 3) for i in expected_dash_idxs: if text[i] != '-': raise dns.exception.SyntaxError('Dash expected at position %s' % i) text = text.replace('-', '') try: data = binascii.unhexlify(text.encode()) except (ValueError, TypeError) as ex: raise dns.exception.SyntaxError('Hex decoding error: %s' % str(ex)) return cls(rdclass, rdtype, data) def to_wire(self, file, compress=None, origin=None): file.write(self.eui) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): eui = wire[current:current + rdlen].unwrap() return cls(rdclass, rdtype, eui) dnspython-1.16.0/dns/rdtypes/mxbase.py000066400000000000000000000072311340301174400177340ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """MX-like base classes.""" from io import BytesIO import struct import dns.exception import dns.rdata import dns.name class MXBase(dns.rdata.Rdata): """Base class for rdata that is like an MX record. @ivar preference: the preference value @type preference: int @ivar exchange: the exchange name @type exchange: dns.name.Name object""" __slots__ = ['preference', 'exchange'] def __init__(self, rdclass, rdtype, preference, exchange): super(MXBase, self).__init__(rdclass, rdtype) self.preference = preference self.exchange = exchange def to_text(self, origin=None, relativize=True, **kw): exchange = self.exchange.choose_relativity(origin, relativize) return '%d %s' % (self.preference, exchange) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): preference = tok.get_uint16() exchange = tok.get_name() exchange = exchange.choose_relativity(origin, relativize) tok.get_eol() return cls(rdclass, rdtype, preference, exchange) def to_wire(self, file, compress=None, origin=None): pref = struct.pack("!H", self.preference) file.write(pref) self.exchange.to_wire(file, compress, origin) def to_digestable(self, origin=None): return struct.pack("!H", self.preference) + \ self.exchange.to_digestable(origin) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (preference, ) = struct.unpack('!H', wire[current: current + 2]) current += 2 rdlen -= 2 (exchange, cused) = dns.name.from_wire(wire[: current + rdlen], current) if cused != rdlen: raise dns.exception.FormError if origin is not None: exchange = exchange.relativize(origin) return cls(rdclass, rdtype, preference, exchange) def choose_relativity(self, origin=None, relativize=True): self.exchange = self.exchange.choose_relativity(origin, relativize) class UncompressedMX(MXBase): """Base class for rdata that is like an MX record, but whose name is not compressed when converted to DNS wire format, and whose digestable form is not downcased.""" def to_wire(self, file, compress=None, origin=None): super(UncompressedMX, self).to_wire(file, None, origin) def to_digestable(self, origin=None): f = BytesIO() self.to_wire(f, None, origin) return f.getvalue() class UncompressedDowncasingMX(MXBase): """Base class for rdata that is like an MX record, but whose name is not compressed when convert to DNS wire format.""" def to_wire(self, file, compress=None, origin=None): super(UncompressedDowncasingMX, self).to_wire(file, None, origin) dnspython-1.16.0/dns/rdtypes/nsbase.py000066400000000000000000000055601340301174400177330ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """NS-like base classes.""" from io import BytesIO import dns.exception import dns.rdata import dns.name class NSBase(dns.rdata.Rdata): """Base class for rdata that is like an NS record. @ivar target: the target name of the rdata @type target: dns.name.Name object""" __slots__ = ['target'] def __init__(self, rdclass, rdtype, target): super(NSBase, self).__init__(rdclass, rdtype) self.target = target def to_text(self, origin=None, relativize=True, **kw): target = self.target.choose_relativity(origin, relativize) return str(target) @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): target = tok.get_name() target = target.choose_relativity(origin, relativize) tok.get_eol() return cls(rdclass, rdtype, target) def to_wire(self, file, compress=None, origin=None): self.target.to_wire(file, compress, origin) def to_digestable(self, origin=None): return self.target.to_digestable(origin) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (target, cused) = dns.name.from_wire(wire[: current + rdlen], current) if cused != rdlen: raise dns.exception.FormError if origin is not None: target = target.relativize(origin) return cls(rdclass, rdtype, target) def choose_relativity(self, origin=None, relativize=True): self.target = self.target.choose_relativity(origin, relativize) class UncompressedNS(NSBase): """Base class for rdata that is like an NS record, but whose name is not compressed when convert to DNS wire format, and whose digestable form is not downcased.""" def to_wire(self, file, compress=None, origin=None): super(UncompressedNS, self).to_wire(file, None, origin) def to_digestable(self, origin=None): f = BytesIO() self.to_wire(f, None, origin) return f.getvalue() dnspython-1.16.0/dns/rdtypes/txtbase.py000066400000000000000000000064001340301174400201240ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """TXT-like base class.""" import struct import dns.exception import dns.rdata import dns.tokenizer from dns._compat import binary_type, string_types class TXTBase(dns.rdata.Rdata): """Base class for rdata that is like a TXT record @ivar strings: the strings @type strings: list of binary @see: RFC 1035""" __slots__ = ['strings'] def __init__(self, rdclass, rdtype, strings): super(TXTBase, self).__init__(rdclass, rdtype) if isinstance(strings, binary_type) or \ isinstance(strings, string_types): strings = [strings] self.strings = [] for string in strings: if isinstance(string, string_types): string = string.encode() self.strings.append(string) def to_text(self, origin=None, relativize=True, **kw): txt = '' prefix = '' for s in self.strings: txt += '{}"{}"'.format(prefix, dns.rdata._escapify(s)) prefix = ' ' return txt @classmethod def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True): strings = [] while 1: token = tok.get().unescape() if token.is_eol_or_eof(): break if not (token.is_quoted_string() or token.is_identifier()): raise dns.exception.SyntaxError("expected a string") if len(token.value) > 255: raise dns.exception.SyntaxError("string too long") value = token.value if isinstance(value, binary_type): strings.append(value) else: strings.append(value.encode()) if len(strings) == 0: raise dns.exception.UnexpectedEnd return cls(rdclass, rdtype, strings) def to_wire(self, file, compress=None, origin=None): for s in self.strings: l = len(s) assert l < 256 file.write(struct.pack('!B', l)) file.write(s) @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): strings = [] while rdlen > 0: l = wire[current] current += 1 rdlen -= 1 if l > rdlen: raise dns.exception.FormError s = wire[current: current + l].unwrap() current += l rdlen -= l strings.append(s) return cls(rdclass, rdtype, strings) dnspython-1.16.0/dns/rdtypes/txtbase.pyi000066400000000000000000000001261340301174400202740ustar00rootroot00000000000000from .. import rdata class TXTBase(rdata.Rdata): ... class TXT(TXTBase): ... dnspython-1.16.0/dns/renderer.py000066400000000000000000000251121340301174400165670ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Help for building DNS wire format messages""" from io import BytesIO import struct import random import time import dns.exception import dns.tsig from ._compat import long QUESTION = 0 ANSWER = 1 AUTHORITY = 2 ADDITIONAL = 3 class Renderer(object): """Helper class for building DNS wire-format messages. Most applications can use the higher-level L{dns.message.Message} class and its to_wire() method to generate wire-format messages. This class is for those applications which need finer control over the generation of messages. Typical use:: r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512) r.add_question(qname, qtype, qclass) r.add_rrset(dns.renderer.ANSWER, rrset_1) r.add_rrset(dns.renderer.ANSWER, rrset_2) r.add_rrset(dns.renderer.AUTHORITY, ns_rrset) r.add_edns(0, 0, 4096) r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_1) r.add_rrset(dns.renderer.ADDTIONAL, ad_rrset_2) r.write_header() r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac) wire = r.get_wire() output, a BytesIO, where rendering is written id: the message id flags: the message flags max_size: the maximum size of the message origin: the origin to use when rendering relative names compress: the compression table section: an int, the section currently being rendered counts: list of the number of RRs in each section mac: the MAC of the rendered message (if TSIG was used) """ def __init__(self, id=None, flags=0, max_size=65535, origin=None): """Initialize a new renderer.""" self.output = BytesIO() if id is None: self.id = random.randint(0, 65535) else: self.id = id self.flags = flags self.max_size = max_size self.origin = origin self.compress = {} self.section = QUESTION self.counts = [0, 0, 0, 0] self.output.write(b'\x00' * 12) self.mac = '' def _rollback(self, where): """Truncate the output buffer at offset *where*, and remove any compression table entries that pointed beyond the truncation point. """ self.output.seek(where) self.output.truncate() keys_to_delete = [] for k, v in self.compress.items(): if v >= where: keys_to_delete.append(k) for k in keys_to_delete: del self.compress[k] def _set_section(self, section): """Set the renderer's current section. Sections must be rendered order: QUESTION, ANSWER, AUTHORITY, ADDITIONAL. Sections may be empty. Raises dns.exception.FormError if an attempt was made to set a section value less than the current section. """ if self.section != section: if self.section > section: raise dns.exception.FormError self.section = section def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN): """Add a question to the message.""" self._set_section(QUESTION) before = self.output.tell() qname.to_wire(self.output, self.compress, self.origin) self.output.write(struct.pack("!HH", rdtype, rdclass)) after = self.output.tell() if after >= self.max_size: self._rollback(before) raise dns.exception.TooBig self.counts[QUESTION] += 1 def add_rrset(self, section, rrset, **kw): """Add the rrset to the specified section. Any keyword arguments are passed on to the rdataset's to_wire() routine. """ self._set_section(section) before = self.output.tell() n = rrset.to_wire(self.output, self.compress, self.origin, **kw) after = self.output.tell() if after >= self.max_size: self._rollback(before) raise dns.exception.TooBig self.counts[section] += n def add_rdataset(self, section, name, rdataset, **kw): """Add the rdataset to the specified section, using the specified name as the owner name. Any keyword arguments are passed on to the rdataset's to_wire() routine. """ self._set_section(section) before = self.output.tell() n = rdataset.to_wire(name, self.output, self.compress, self.origin, **kw) after = self.output.tell() if after >= self.max_size: self._rollback(before) raise dns.exception.TooBig self.counts[section] += n def add_edns(self, edns, ednsflags, payload, options=None): """Add an EDNS OPT record to the message.""" # make sure the EDNS version in ednsflags agrees with edns ednsflags &= long(0xFF00FFFF) ednsflags |= (edns << 16) self._set_section(ADDITIONAL) before = self.output.tell() self.output.write(struct.pack('!BHHIH', 0, dns.rdatatype.OPT, payload, ednsflags, 0)) if options is not None: lstart = self.output.tell() for opt in options: stuff = struct.pack("!HH", opt.otype, 0) self.output.write(stuff) start = self.output.tell() opt.to_wire(self.output) end = self.output.tell() assert end - start < 65536 self.output.seek(start - 2) stuff = struct.pack("!H", end - start) self.output.write(stuff) self.output.seek(0, 2) lend = self.output.tell() assert lend - lstart < 65536 self.output.seek(lstart - 2) stuff = struct.pack("!H", lend - lstart) self.output.write(stuff) self.output.seek(0, 2) after = self.output.tell() if after >= self.max_size: self._rollback(before) raise dns.exception.TooBig self.counts[ADDITIONAL] += 1 def add_tsig(self, keyname, secret, fudge, id, tsig_error, other_data, request_mac, algorithm=dns.tsig.default_algorithm): """Add a TSIG signature to the message.""" s = self.output.getvalue() (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s, keyname, secret, int(time.time()), fudge, id, tsig_error, other_data, request_mac, algorithm=algorithm) self._write_tsig(tsig_rdata, keyname) def add_multi_tsig(self, ctx, keyname, secret, fudge, id, tsig_error, other_data, request_mac, algorithm=dns.tsig.default_algorithm): """Add a TSIG signature to the message. Unlike add_tsig(), this can be used for a series of consecutive DNS envelopes, e.g. for a zone transfer over TCP [RFC2845, 4.4]. For the first message in the sequence, give ctx=None. For each subsequent message, give the ctx that was returned from the add_multi_tsig() call for the previous message.""" s = self.output.getvalue() (tsig_rdata, self.mac, ctx) = dns.tsig.sign(s, keyname, secret, int(time.time()), fudge, id, tsig_error, other_data, request_mac, ctx=ctx, first=ctx is None, multi=True, algorithm=algorithm) self._write_tsig(tsig_rdata, keyname) return ctx def _write_tsig(self, tsig_rdata, keyname): self._set_section(ADDITIONAL) before = self.output.tell() keyname.to_wire(self.output, self.compress, self.origin) self.output.write(struct.pack('!HHIH', dns.rdatatype.TSIG, dns.rdataclass.ANY, 0, 0)) rdata_start = self.output.tell() self.output.write(tsig_rdata) after = self.output.tell() assert after - rdata_start < 65536 if after >= self.max_size: self._rollback(before) raise dns.exception.TooBig self.output.seek(rdata_start - 2) self.output.write(struct.pack('!H', after - rdata_start)) self.counts[ADDITIONAL] += 1 self.output.seek(10) self.output.write(struct.pack('!H', self.counts[ADDITIONAL])) self.output.seek(0, 2) def write_header(self): """Write the DNS message header. Writing the DNS message header is done after all sections have been rendered, but before the optional TSIG signature is added. """ self.output.seek(0) self.output.write(struct.pack('!HHHHHH', self.id, self.flags, self.counts[0], self.counts[1], self.counts[2], self.counts[3])) self.output.seek(0, 2) def get_wire(self): """Return the wire format message.""" return self.output.getvalue() dnspython-1.16.0/dns/resolver.py000066400000000000000000001420421340301174400166240ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS stub resolver.""" import socket import sys import time import random try: import threading as _threading except ImportError: import dummy_threading as _threading import dns.exception import dns.flags import dns.ipv4 import dns.ipv6 import dns.message import dns.name import dns.query import dns.rcode import dns.rdataclass import dns.rdatatype import dns.reversename import dns.tsig from ._compat import xrange, string_types if sys.platform == 'win32': try: import winreg as _winreg except ImportError: import _winreg # pylint: disable=import-error class NXDOMAIN(dns.exception.DNSException): """The DNS query name does not exist.""" supp_kwargs = {'qnames', 'responses'} fmt = None # we have our own __str__ implementation def _check_kwargs(self, qnames, responses=None): if not isinstance(qnames, (list, tuple, set)): raise AttributeError("qnames must be a list, tuple or set") if len(qnames) == 0: raise AttributeError("qnames must contain at least one element") if responses is None: responses = {} elif not isinstance(responses, dict): raise AttributeError("responses must be a dict(qname=response)") kwargs = dict(qnames=qnames, responses=responses) return kwargs def __str__(self): if 'qnames' not in self.kwargs: return super(NXDOMAIN, self).__str__() qnames = self.kwargs['qnames'] if len(qnames) > 1: msg = 'None of DNS query names exist' else: msg = 'The DNS query name does not exist' qnames = ', '.join(map(str, qnames)) return "{}: {}".format(msg, qnames) def canonical_name(self): if not 'qnames' in self.kwargs: raise TypeError("parametrized exception required") IN = dns.rdataclass.IN CNAME = dns.rdatatype.CNAME cname = None for qname in self.kwargs['qnames']: response = self.kwargs['responses'][qname] for answer in response.answer: if answer.rdtype != CNAME or answer.rdclass != IN: continue cname = answer.items[0].target.to_text() if cname is not None: return dns.name.from_text(cname) return self.kwargs['qnames'][0] canonical_name = property(canonical_name, doc=( "Return the unresolved canonical name.")) def __add__(self, e_nx): """Augment by results from another NXDOMAIN exception.""" qnames0 = list(self.kwargs.get('qnames', [])) responses0 = dict(self.kwargs.get('responses', {})) responses1 = e_nx.kwargs.get('responses', {}) for qname1 in e_nx.kwargs.get('qnames', []): if qname1 not in qnames0: qnames0.append(qname1) if qname1 in responses1: responses0[qname1] = responses1[qname1] return NXDOMAIN(qnames=qnames0, responses=responses0) def qnames(self): """All of the names that were tried. Returns a list of ``dns.name.Name``. """ return self.kwargs['qnames'] def responses(self): """A map from queried names to their NXDOMAIN responses. Returns a dict mapping a ``dns.name.Name`` to a ``dns.message.Message``. """ return self.kwargs['responses'] def response(self, qname): """The response for query *qname*. Returns a ``dns.message.Message``. """ return self.kwargs['responses'][qname] class YXDOMAIN(dns.exception.DNSException): """The DNS query name is too long after DNAME substitution.""" # The definition of the Timeout exception has moved from here to the # dns.exception module. We keep dns.resolver.Timeout defined for # backwards compatibility. Timeout = dns.exception.Timeout class NoAnswer(dns.exception.DNSException): """The DNS response does not contain an answer to the question.""" fmt = 'The DNS response does not contain an answer ' + \ 'to the question: {query}' supp_kwargs = {'response'} def _fmt_kwargs(self, **kwargs): return super(NoAnswer, self)._fmt_kwargs( query=kwargs['response'].question) class NoNameservers(dns.exception.DNSException): """All nameservers failed to answer the query. errors: list of servers and respective errors The type of errors is [(server IP address, any object convertible to string)]. Non-empty errors list will add explanatory message () """ msg = "All nameservers failed to answer the query." fmt = "%s {query}: {errors}" % msg[:-1] supp_kwargs = {'request', 'errors'} def _fmt_kwargs(self, **kwargs): srv_msgs = [] for err in kwargs['errors']: srv_msgs.append('Server {} {} port {} answered {}'.format(err[0], 'TCP' if err[1] else 'UDP', err[2], err[3])) return super(NoNameservers, self)._fmt_kwargs( query=kwargs['request'].question, errors='; '.join(srv_msgs)) class NotAbsolute(dns.exception.DNSException): """An absolute domain name is required but a relative name was provided.""" class NoRootSOA(dns.exception.DNSException): """There is no SOA RR at the DNS root name. This should never happen!""" class NoMetaqueries(dns.exception.DNSException): """DNS metaqueries are not allowed.""" class Answer(object): """DNS stub resolver answer. Instances of this class bundle up the result of a successful DNS resolution. For convenience, the answer object implements much of the sequence protocol, forwarding to its ``rrset`` attribute. E.g. ``for a in answer`` is equivalent to ``for a in answer.rrset``. ``answer[i]`` is equivalent to ``answer.rrset[i]``, and ``answer[i:j]`` is equivalent to ``answer.rrset[i:j]``. Note that CNAMEs or DNAMEs in the response may mean that answer RRset's name might not be the query name. """ def __init__(self, qname, rdtype, rdclass, response, raise_on_no_answer=True): self.qname = qname self.rdtype = rdtype self.rdclass = rdclass self.response = response min_ttl = -1 rrset = None for count in xrange(0, 15): try: rrset = response.find_rrset(response.answer, qname, rdclass, rdtype) if min_ttl == -1 or rrset.ttl < min_ttl: min_ttl = rrset.ttl break except KeyError: if rdtype != dns.rdatatype.CNAME: try: crrset = response.find_rrset(response.answer, qname, rdclass, dns.rdatatype.CNAME) if min_ttl == -1 or crrset.ttl < min_ttl: min_ttl = crrset.ttl for rd in crrset: qname = rd.target break continue except KeyError: if raise_on_no_answer: raise NoAnswer(response=response) if raise_on_no_answer: raise NoAnswer(response=response) if rrset is None and raise_on_no_answer: raise NoAnswer(response=response) self.canonical_name = qname self.rrset = rrset if rrset is None: while 1: # Look for a SOA RR whose owner name is a superdomain # of qname. try: srrset = response.find_rrset(response.authority, qname, rdclass, dns.rdatatype.SOA) if min_ttl == -1 or srrset.ttl < min_ttl: min_ttl = srrset.ttl if srrset[0].minimum < min_ttl: min_ttl = srrset[0].minimum break except KeyError: try: qname = qname.parent() except dns.name.NoParent: break self.expiration = time.time() + min_ttl def __getattr__(self, attr): if attr == 'name': return self.rrset.name elif attr == 'ttl': return self.rrset.ttl elif attr == 'covers': return self.rrset.covers elif attr == 'rdclass': return self.rrset.rdclass elif attr == 'rdtype': return self.rrset.rdtype else: raise AttributeError(attr) def __len__(self): return self.rrset and len(self.rrset) or 0 def __iter__(self): return self.rrset and iter(self.rrset) or iter(tuple()) def __getitem__(self, i): if self.rrset is None: raise IndexError return self.rrset[i] def __delitem__(self, i): if self.rrset is None: raise IndexError del self.rrset[i] class Cache(object): """Simple thread-safe DNS answer cache.""" def __init__(self, cleaning_interval=300.0): """*cleaning_interval*, a ``float`` is the number of seconds between periodic cleanings. """ self.data = {} self.cleaning_interval = cleaning_interval self.next_cleaning = time.time() + self.cleaning_interval self.lock = _threading.Lock() def _maybe_clean(self): """Clean the cache if it's time to do so.""" now = time.time() if self.next_cleaning <= now: keys_to_delete = [] for (k, v) in self.data.items(): if v.expiration <= now: keys_to_delete.append(k) for k in keys_to_delete: del self.data[k] now = time.time() self.next_cleaning = now + self.cleaning_interval def get(self, key): """Get the answer associated with *key*. Returns None if no answer is cached for the key. *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the query name, rdtype, and rdclass respectively. Returns a ``dns.resolver.Answer`` or ``None``. """ try: self.lock.acquire() self._maybe_clean() v = self.data.get(key) if v is None or v.expiration <= time.time(): return None return v finally: self.lock.release() def put(self, key, value): """Associate key and value in the cache. *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the query name, rdtype, and rdclass respectively. *value*, a ``dns.resolver.Answer``, the answer. """ try: self.lock.acquire() self._maybe_clean() self.data[key] = value finally: self.lock.release() def flush(self, key=None): """Flush the cache. If *key* is not ``None``, only that item is flushed. Otherwise the entire cache is flushed. *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the query name, rdtype, and rdclass respectively. """ try: self.lock.acquire() if key is not None: if key in self.data: del self.data[key] else: self.data = {} self.next_cleaning = time.time() + self.cleaning_interval finally: self.lock.release() class LRUCacheNode(object): """LRUCache node.""" def __init__(self, key, value): self.key = key self.value = value self.prev = self self.next = self def link_before(self, node): self.prev = node.prev self.next = node node.prev.next = self node.prev = self def link_after(self, node): self.prev = node self.next = node.next node.next.prev = self node.next = self def unlink(self): self.next.prev = self.prev self.prev.next = self.next class LRUCache(object): """Thread-safe, bounded, least-recently-used DNS answer cache. This cache is better than the simple cache (above) if you're running a web crawler or other process that does a lot of resolutions. The LRUCache has a maximum number of nodes, and when it is full, the least-recently used node is removed to make space for a new one. """ def __init__(self, max_size=100000): """*max_size*, an ``int``, is the maximum number of nodes to cache; it must be greater than 0. """ self.data = {} self.set_max_size(max_size) self.sentinel = LRUCacheNode(None, None) self.lock = _threading.Lock() def set_max_size(self, max_size): if max_size < 1: max_size = 1 self.max_size = max_size def get(self, key): """Get the answer associated with *key*. Returns None if no answer is cached for the key. *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the query name, rdtype, and rdclass respectively. Returns a ``dns.resolver.Answer`` or ``None``. """ try: self.lock.acquire() node = self.data.get(key) if node is None: return None # Unlink because we're either going to move the node to the front # of the LRU list or we're going to free it. node.unlink() if node.value.expiration <= time.time(): del self.data[node.key] return None node.link_after(self.sentinel) return node.value finally: self.lock.release() def put(self, key, value): """Associate key and value in the cache. *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the query name, rdtype, and rdclass respectively. *value*, a ``dns.resolver.Answer``, the answer. """ try: self.lock.acquire() node = self.data.get(key) if node is not None: node.unlink() del self.data[node.key] while len(self.data) >= self.max_size: node = self.sentinel.prev node.unlink() del self.data[node.key] node = LRUCacheNode(key, value) node.link_after(self.sentinel) self.data[key] = node finally: self.lock.release() def flush(self, key=None): """Flush the cache. If *key* is not ``None``, only that item is flushed. Otherwise the entire cache is flushed. *key*, a ``(dns.name.Name, int, int)`` tuple whose values are the query name, rdtype, and rdclass respectively. """ try: self.lock.acquire() if key is not None: node = self.data.get(key) if node is not None: node.unlink() del self.data[node.key] else: node = self.sentinel.next while node != self.sentinel: next = node.next node.prev = None node.next = None node = next self.data = {} finally: self.lock.release() class Resolver(object): """DNS stub resolver.""" def __init__(self, filename='/etc/resolv.conf', configure=True): """*filename*, a ``text`` or file object, specifying a file in standard /etc/resolv.conf format. This parameter is meaningful only when *configure* is true and the platform is POSIX. *configure*, a ``bool``. If True (the default), the resolver instance is configured in the normal fashion for the operating system the resolver is running on. (I.e. by reading a /etc/resolv.conf file on POSIX systems and from the registry on Windows systems.) """ self.domain = None self.nameservers = None self.nameserver_ports = None self.port = None self.search = None self.timeout = None self.lifetime = None self.keyring = None self.keyname = None self.keyalgorithm = None self.edns = None self.ednsflags = None self.payload = None self.cache = None self.flags = None self.retry_servfail = False self.rotate = False self.reset() if configure: if sys.platform == 'win32': self.read_registry() elif filename: self.read_resolv_conf(filename) def reset(self): """Reset all resolver configuration to the defaults.""" self.domain = \ dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) if len(self.domain) == 0: self.domain = dns.name.root self.nameservers = [] self.nameserver_ports = {} self.port = 53 self.search = [] self.timeout = 2.0 self.lifetime = 30.0 self.keyring = None self.keyname = None self.keyalgorithm = dns.tsig.default_algorithm self.edns = -1 self.ednsflags = 0 self.payload = 0 self.cache = None self.flags = None self.retry_servfail = False self.rotate = False def read_resolv_conf(self, f): """Process *f* as a file in the /etc/resolv.conf format. If f is a ``text``, it is used as the name of the file to open; otherwise it is treated as the file itself.""" if isinstance(f, string_types): try: f = open(f, 'r') except IOError: # /etc/resolv.conf doesn't exist, can't be read, etc. # We'll just use the default resolver configuration. self.nameservers = ['127.0.0.1'] return want_close = True else: want_close = False try: for l in f: if len(l) == 0 or l[0] == '#' or l[0] == ';': continue tokens = l.split() # Any line containing less than 2 tokens is malformed if len(tokens) < 2: continue if tokens[0] == 'nameserver': self.nameservers.append(tokens[1]) elif tokens[0] == 'domain': self.domain = dns.name.from_text(tokens[1]) elif tokens[0] == 'search': for suffix in tokens[1:]: self.search.append(dns.name.from_text(suffix)) elif tokens[0] == 'options': if 'rotate' in tokens[1:]: self.rotate = True finally: if want_close: f.close() if len(self.nameservers) == 0: self.nameservers.append('127.0.0.1') def _determine_split_char(self, entry): # # The windows registry irritatingly changes the list element # delimiter in between ' ' and ',' (and vice-versa) in various # versions of windows. # if entry.find(' ') >= 0: split_char = ' ' elif entry.find(',') >= 0: split_char = ',' else: # probably a singleton; treat as a space-separated list. split_char = ' ' return split_char def _config_win32_nameservers(self, nameservers): # we call str() on nameservers to convert it from unicode to ascii nameservers = str(nameservers) split_char = self._determine_split_char(nameservers) ns_list = nameservers.split(split_char) for ns in ns_list: if ns not in self.nameservers: self.nameservers.append(ns) def _config_win32_domain(self, domain): # we call str() on domain to convert it from unicode to ascii self.domain = dns.name.from_text(str(domain)) def _config_win32_search(self, search): # we call str() on search to convert it from unicode to ascii search = str(search) split_char = self._determine_split_char(search) search_list = search.split(split_char) for s in search_list: if s not in self.search: self.search.append(dns.name.from_text(s)) def _config_win32_fromkey(self, key, always_try_domain): try: servers, rtype = _winreg.QueryValueEx(key, 'NameServer') except WindowsError: # pylint: disable=undefined-variable servers = None if servers: self._config_win32_nameservers(servers) if servers or always_try_domain: try: dom, rtype = _winreg.QueryValueEx(key, 'Domain') if dom: self._config_win32_domain(dom) except WindowsError: # pylint: disable=undefined-variable pass else: try: servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer') except WindowsError: # pylint: disable=undefined-variable servers = None if servers: self._config_win32_nameservers(servers) try: dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain') if dom: self._config_win32_domain(dom) except WindowsError: # pylint: disable=undefined-variable pass try: search, rtype = _winreg.QueryValueEx(key, 'SearchList') except WindowsError: # pylint: disable=undefined-variable search = None if search: self._config_win32_search(search) def read_registry(self): """Extract resolver configuration from the Windows registry.""" lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) want_scan = False try: try: # XP, 2000 tcp_params = _winreg.OpenKey(lm, r'SYSTEM\CurrentControlSet' r'\Services\Tcpip\Parameters') want_scan = True except EnvironmentError: # ME tcp_params = _winreg.OpenKey(lm, r'SYSTEM\CurrentControlSet' r'\Services\VxD\MSTCP') try: self._config_win32_fromkey(tcp_params, True) finally: tcp_params.Close() if want_scan: interfaces = _winreg.OpenKey(lm, r'SYSTEM\CurrentControlSet' r'\Services\Tcpip\Parameters' r'\Interfaces') try: i = 0 while True: try: guid = _winreg.EnumKey(interfaces, i) i += 1 key = _winreg.OpenKey(interfaces, guid) if not self._win32_is_nic_enabled(lm, guid, key): continue try: self._config_win32_fromkey(key, False) finally: key.Close() except EnvironmentError: break finally: interfaces.Close() finally: lm.Close() def _win32_is_nic_enabled(self, lm, guid, interface_key): # Look in the Windows Registry to determine whether the network # interface corresponding to the given guid is enabled. # # (Code contributed by Paul Marks, thanks!) # try: # This hard-coded location seems to be consistent, at least # from Windows 2000 through Vista. connection_key = _winreg.OpenKey( lm, r'SYSTEM\CurrentControlSet\Control\Network' r'\{4D36E972-E325-11CE-BFC1-08002BE10318}' r'\%s\Connection' % guid) try: # The PnpInstanceID points to a key inside Enum (pnp_id, ttype) = _winreg.QueryValueEx( connection_key, 'PnpInstanceID') if ttype != _winreg.REG_SZ: raise ValueError device_key = _winreg.OpenKey( lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id) try: # Get ConfigFlags for this device (flags, ttype) = _winreg.QueryValueEx( device_key, 'ConfigFlags') if ttype != _winreg.REG_DWORD: raise ValueError # Based on experimentation, bit 0x1 indicates that the # device is disabled. return not flags & 0x1 finally: device_key.Close() finally: connection_key.Close() except (EnvironmentError, ValueError): # Pre-vista, enabled interfaces seem to have a non-empty # NTEContextList; this was how dnspython detected enabled # nics before the code above was contributed. We've retained # the old method since we don't know if the code above works # on Windows 95/98/ME. try: (nte, ttype) = _winreg.QueryValueEx(interface_key, 'NTEContextList') return nte is not None except WindowsError: # pylint: disable=undefined-variable return False def _compute_timeout(self, start, lifetime=None): lifetime = self.lifetime if lifetime is None else lifetime now = time.time() duration = now - start if duration < 0: if duration < -1: # Time going backwards is bad. Just give up. raise Timeout(timeout=duration) else: # Time went backwards, but only a little. This can # happen, e.g. under vmware with older linux kernels. # Pretend it didn't happen. now = start if duration >= lifetime: raise Timeout(timeout=duration) return min(lifetime - duration, self.timeout) def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, tcp=False, source=None, raise_on_no_answer=True, source_port=0, lifetime=None): """Query nameservers to find the answer to the question. The *qname*, *rdtype*, and *rdclass* parameters may be objects of the appropriate type, or strings that can be converted into objects of the appropriate type. *qname*, a ``dns.name.Name`` or ``text``, the query name. *rdtype*, an ``int`` or ``text``, the query type. *rdclass*, an ``int`` or ``text``, the query class. *tcp*, a ``bool``. If ``True``, use TCP to make the query. *source*, a ``text`` or ``None``. If not ``None``, bind to this IP address when making queries. *raise_on_no_answer*, a ``bool``. If ``True``, raise ``dns.resolver.NoAnswer`` if there's no answer to the question. *source_port*, an ``int``, the port from which to send the message. *lifetime*, a ``float``, how long query should run before timing out. Raises ``dns.exception.Timeout`` if no answers could be found in the specified lifetime. Raises ``dns.resolver.NXDOMAIN`` if the query name does not exist. Raises ``dns.resolver.YXDOMAIN`` if the query name is too long after DNAME substitution. Raises ``dns.resolver.NoAnswer`` if *raise_on_no_answer* is ``True`` and the query name exists but has no RRset of the desired type and class. Raises ``dns.resolver.NoNameservers`` if no non-broken nameservers are available to answer the question. Returns a ``dns.resolver.Answer`` instance. """ if isinstance(qname, string_types): qname = dns.name.from_text(qname, None) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if dns.rdatatype.is_metatype(rdtype): raise NoMetaqueries if isinstance(rdclass, string_types): rdclass = dns.rdataclass.from_text(rdclass) if dns.rdataclass.is_metaclass(rdclass): raise NoMetaqueries qnames_to_try = [] if qname.is_absolute(): qnames_to_try.append(qname) else: if len(qname) > 1: qnames_to_try.append(qname.concatenate(dns.name.root)) if self.search: for suffix in self.search: qnames_to_try.append(qname.concatenate(suffix)) else: qnames_to_try.append(qname.concatenate(self.domain)) all_nxdomain = True nxdomain_responses = {} start = time.time() _qname = None # make pylint happy for _qname in qnames_to_try: if self.cache: answer = self.cache.get((_qname, rdtype, rdclass)) if answer is not None: if answer.rrset is None and raise_on_no_answer: raise NoAnswer(response=answer.response) else: return answer request = dns.message.make_query(_qname, rdtype, rdclass) if self.keyname is not None: request.use_tsig(self.keyring, self.keyname, algorithm=self.keyalgorithm) request.use_edns(self.edns, self.ednsflags, self.payload) if self.flags is not None: request.flags = self.flags response = None # # make a copy of the servers list so we can alter it later. # nameservers = self.nameservers[:] errors = [] if self.rotate: random.shuffle(nameservers) backoff = 0.10 while response is None: if len(nameservers) == 0: raise NoNameservers(request=request, errors=errors) for nameserver in nameservers[:]: timeout = self._compute_timeout(start, lifetime) port = self.nameserver_ports.get(nameserver, self.port) try: tcp_attempt = tcp if tcp: response = dns.query.tcp(request, nameserver, timeout, port, source=source, source_port=source_port) else: response = dns.query.udp(request, nameserver, timeout, port, source=source, source_port=source_port) if response.flags & dns.flags.TC: # Response truncated; retry with TCP. tcp_attempt = True timeout = self._compute_timeout(start, lifetime) response = \ dns.query.tcp(request, nameserver, timeout, port, source=source, source_port=source_port) except (socket.error, dns.exception.Timeout) as ex: # # Communication failure or timeout. Go to the # next server # errors.append((nameserver, tcp_attempt, port, ex, response)) response = None continue except dns.query.UnexpectedSource as ex: # # Who knows? Keep going. # errors.append((nameserver, tcp_attempt, port, ex, response)) response = None continue except dns.exception.FormError as ex: # # We don't understand what this server is # saying. Take it out of the mix and # continue. # nameservers.remove(nameserver) errors.append((nameserver, tcp_attempt, port, ex, response)) response = None continue except EOFError as ex: # # We're using TCP and they hung up on us. # Probably they don't support TCP (though # they're supposed to!). Take it out of the # mix and continue. # nameservers.remove(nameserver) errors.append((nameserver, tcp_attempt, port, ex, response)) response = None continue rcode = response.rcode() if rcode == dns.rcode.YXDOMAIN: ex = YXDOMAIN() errors.append((nameserver, tcp_attempt, port, ex, response)) raise ex if rcode == dns.rcode.NOERROR or \ rcode == dns.rcode.NXDOMAIN: break # # We got a response, but we're not happy with the # rcode in it. Remove the server from the mix if # the rcode isn't SERVFAIL. # if rcode != dns.rcode.SERVFAIL or not self.retry_servfail: nameservers.remove(nameserver) errors.append((nameserver, tcp_attempt, port, dns.rcode.to_text(rcode), response)) response = None if response is not None: break # # All nameservers failed! # if len(nameservers) > 0: # # But we still have servers to try. Sleep a bit # so we don't pound them! # timeout = self._compute_timeout(start, lifetime) sleep_time = min(timeout, backoff) backoff *= 2 time.sleep(sleep_time) if response.rcode() == dns.rcode.NXDOMAIN: nxdomain_responses[_qname] = response continue all_nxdomain = False break if all_nxdomain: raise NXDOMAIN(qnames=qnames_to_try, responses=nxdomain_responses) answer = Answer(_qname, rdtype, rdclass, response, raise_on_no_answer) if self.cache: self.cache.put((_qname, rdtype, rdclass), answer) return answer def use_tsig(self, keyring, keyname=None, algorithm=dns.tsig.default_algorithm): """Add a TSIG signature to the query. See the documentation of the Message class for a complete description of the keyring dictionary. *keyring*, a ``dict``, the TSIG keyring to use. If a *keyring* is specified but a *keyname* is not, then the key used will be the first key in the *keyring*. Note that the order of keys in a dictionary is not defined, so applications should supply a keyname when a keyring is used, unless they know the keyring contains only one key. *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key to use; defaults to ``None``. The key must be defined in the keyring. *algorithm*, a ``dns.name.Name``, the TSIG algorithm to use. """ self.keyring = keyring if keyname is None: self.keyname = list(self.keyring.keys())[0] else: self.keyname = keyname self.keyalgorithm = algorithm def use_edns(self, edns, ednsflags, payload): """Configure EDNS behavior. *edns*, an ``int``, is the EDNS level to use. Specifying ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case the other parameters are ignored. Specifying ``True`` is equivalent to specifying 0, i.e. "use EDNS0". *ednsflags*, an ``int``, the EDNS flag values. *payload*, an ``int``, is the EDNS sender's payload field, which is the maximum size of UDP datagram the sender can handle. I.e. how big a response to this message can be. """ if edns is None: edns = -1 self.edns = edns self.ednsflags = ednsflags self.payload = payload def set_flags(self, flags): """Overrides the default flags with your own. *flags*, an ``int``, the message flags to use. """ self.flags = flags #: The default resolver. default_resolver = None def get_default_resolver(): """Get the default resolver, initializing it if necessary.""" if default_resolver is None: reset_default_resolver() return default_resolver def reset_default_resolver(): """Re-initialize default resolver. Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX systems) will be re-read immediately. """ global default_resolver default_resolver = Resolver() def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, tcp=False, source=None, raise_on_no_answer=True, source_port=0, lifetime=None): """Query nameservers to find the answer to the question. This is a convenience function that uses the default resolver object to make the query. See ``dns.resolver.Resolver.query`` for more information on the parameters. """ return get_default_resolver().query(qname, rdtype, rdclass, tcp, source, raise_on_no_answer, source_port, lifetime) def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None): """Find the name of the zone which contains the specified name. *name*, an absolute ``dns.name.Name`` or ``text``, the query name. *rdclass*, an ``int``, the query class. *tcp*, a ``bool``. If ``True``, use TCP to make the query. *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. If ``None``, the default resolver is used. Raises ``dns.resolver.NoRootSOA`` if there is no SOA RR at the DNS root. (This is only likely to happen if you're using non-default root servers in your network and they are misconfigured.) Returns a ``dns.name.Name``. """ if isinstance(name, string_types): name = dns.name.from_text(name, dns.name.root) if resolver is None: resolver = get_default_resolver() if not name.is_absolute(): raise NotAbsolute(name) while 1: try: answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp) if answer.rrset.name == name: return name # otherwise we were CNAMEd or DNAMEd and need to look higher except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): pass try: name = name.parent() except dns.name.NoParent: raise NoRootSOA # # Support for overriding the system resolver for all python code in the # running process. # _protocols_for_socktype = { socket.SOCK_DGRAM: [socket.SOL_UDP], socket.SOCK_STREAM: [socket.SOL_TCP], } _resolver = None _original_getaddrinfo = socket.getaddrinfo _original_getnameinfo = socket.getnameinfo _original_getfqdn = socket.getfqdn _original_gethostbyname = socket.gethostbyname _original_gethostbyname_ex = socket.gethostbyname_ex _original_gethostbyaddr = socket.gethostbyaddr def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0, proto=0, flags=0): if flags & (socket.AI_ADDRCONFIG | socket.AI_V4MAPPED) != 0: raise NotImplementedError if host is None and service is None: raise socket.gaierror(socket.EAI_NONAME) v6addrs = [] v4addrs = [] canonical_name = None try: # Is host None or a V6 address literal? if host is None: canonical_name = 'localhost' if flags & socket.AI_PASSIVE != 0: v6addrs.append('::') v4addrs.append('0.0.0.0') else: v6addrs.append('::1') v4addrs.append('127.0.0.1') else: parts = host.split('%') if len(parts) == 2: ahost = parts[0] else: ahost = host addr = dns.ipv6.inet_aton(ahost) v6addrs.append(host) canonical_name = host except Exception: try: # Is it a V4 address literal? addr = dns.ipv4.inet_aton(host) v4addrs.append(host) canonical_name = host except Exception: if flags & socket.AI_NUMERICHOST == 0: try: if family == socket.AF_INET6 or family == socket.AF_UNSPEC: v6 = _resolver.query(host, dns.rdatatype.AAAA, raise_on_no_answer=False) # Note that setting host ensures we query the same name # for A as we did for AAAA. host = v6.qname canonical_name = v6.canonical_name.to_text(True) if v6.rrset is not None: for rdata in v6.rrset: v6addrs.append(rdata.address) if family == socket.AF_INET or family == socket.AF_UNSPEC: v4 = _resolver.query(host, dns.rdatatype.A, raise_on_no_answer=False) host = v4.qname canonical_name = v4.canonical_name.to_text(True) if v4.rrset is not None: for rdata in v4.rrset: v4addrs.append(rdata.address) except dns.resolver.NXDOMAIN: raise socket.gaierror(socket.EAI_NONAME) except Exception: raise socket.gaierror(socket.EAI_SYSTEM) port = None try: # Is it a port literal? if service is None: port = 0 else: port = int(service) except Exception: if flags & socket.AI_NUMERICSERV == 0: try: port = socket.getservbyname(service) except Exception: pass if port is None: raise socket.gaierror(socket.EAI_NONAME) tuples = [] if socktype == 0: socktypes = [socket.SOCK_DGRAM, socket.SOCK_STREAM] else: socktypes = [socktype] if flags & socket.AI_CANONNAME != 0: cname = canonical_name else: cname = '' if family == socket.AF_INET6 or family == socket.AF_UNSPEC: for addr in v6addrs: for socktype in socktypes: for proto in _protocols_for_socktype[socktype]: tuples.append((socket.AF_INET6, socktype, proto, cname, (addr, port, 0, 0))) if family == socket.AF_INET or family == socket.AF_UNSPEC: for addr in v4addrs: for socktype in socktypes: for proto in _protocols_for_socktype[socktype]: tuples.append((socket.AF_INET, socktype, proto, cname, (addr, port))) if len(tuples) == 0: raise socket.gaierror(socket.EAI_NONAME) return tuples def _getnameinfo(sockaddr, flags=0): host = sockaddr[0] port = sockaddr[1] if len(sockaddr) == 4: scope = sockaddr[3] family = socket.AF_INET6 else: scope = None family = socket.AF_INET tuples = _getaddrinfo(host, port, family, socket.SOCK_STREAM, socket.SOL_TCP, 0) if len(tuples) > 1: raise socket.error('sockaddr resolved to multiple addresses') addr = tuples[0][4][0] if flags & socket.NI_DGRAM: pname = 'udp' else: pname = 'tcp' qname = dns.reversename.from_address(addr) if flags & socket.NI_NUMERICHOST == 0: try: answer = _resolver.query(qname, 'PTR') hostname = answer.rrset[0].target.to_text(True) except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): if flags & socket.NI_NAMEREQD: raise socket.gaierror(socket.EAI_NONAME) hostname = addr if scope is not None: hostname += '%' + str(scope) else: hostname = addr if scope is not None: hostname += '%' + str(scope) if flags & socket.NI_NUMERICSERV: service = str(port) else: service = socket.getservbyport(port, pname) return (hostname, service) def _getfqdn(name=None): if name is None: name = socket.gethostname() try: return _getnameinfo(_getaddrinfo(name, 80)[0][4])[0] except Exception: return name def _gethostbyname(name): return _gethostbyname_ex(name)[2][0] def _gethostbyname_ex(name): aliases = [] addresses = [] tuples = _getaddrinfo(name, 0, socket.AF_INET, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME) canonical = tuples[0][3] for item in tuples: addresses.append(item[4][0]) # XXX we just ignore aliases return (canonical, aliases, addresses) def _gethostbyaddr(ip): try: dns.ipv6.inet_aton(ip) sockaddr = (ip, 80, 0, 0) family = socket.AF_INET6 except Exception: sockaddr = (ip, 80) family = socket.AF_INET (name, port) = _getnameinfo(sockaddr, socket.NI_NAMEREQD) aliases = [] addresses = [] tuples = _getaddrinfo(name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME) canonical = tuples[0][3] for item in tuples: addresses.append(item[4][0]) # XXX we just ignore aliases return (canonical, aliases, addresses) def override_system_resolver(resolver=None): """Override the system resolver routines in the socket module with versions which use dnspython's resolver. This can be useful in testing situations where you want to control the resolution behavior of python code without having to change the system's resolver settings (e.g. /etc/resolv.conf). The resolver to use may be specified; if it's not, the default resolver will be used. resolver, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. """ if resolver is None: resolver = get_default_resolver() global _resolver _resolver = resolver socket.getaddrinfo = _getaddrinfo socket.getnameinfo = _getnameinfo socket.getfqdn = _getfqdn socket.gethostbyname = _gethostbyname socket.gethostbyname_ex = _gethostbyname_ex socket.gethostbyaddr = _gethostbyaddr def restore_system_resolver(): """Undo the effects of prior override_system_resolver().""" global _resolver _resolver = None socket.getaddrinfo = _original_getaddrinfo socket.getnameinfo = _original_getnameinfo socket.getfqdn = _original_getfqdn socket.gethostbyname = _original_gethostbyname socket.gethostbyname_ex = _original_gethostbyname_ex socket.gethostbyaddr = _original_gethostbyaddr dnspython-1.16.0/dns/resolver.pyi000066400000000000000000000020721340301174400167730ustar00rootroot00000000000000from typing import Union, Optional, List from . import exception, rdataclass, name, rdatatype import socket _gethostbyname = socket.gethostbyname class NXDOMAIN(exception.DNSException): ... def query(qname : str, rdtype : Union[int,str] = 0, rdclass : Union[int,str] = 0, tcp=False, source=None, raise_on_no_answer=True, source_port=0): ... class LRUCache: def __init__(self, max_size=1000): ... def get(self, key): ... def put(self, key, val): ... class Answer: def __init__(self, qname, rdtype, rdclass, response, raise_on_no_answer=True): ... def zone_for_name(name, rdclass : int = rdataclass.IN, tcp=False, resolver : Optional[Resolver] = None): ... class Resolver: def __init__(self, configure): self.nameservers : List[str] def query(self, qname : str, rdtype : Union[int,str] = rdatatype.A, rdclass : Union[int,str] = rdataclass.IN, tcp : bool = False, source : Optional[str] = None, raise_on_no_answer=True, source_port : int = 0): ... dnspython-1.16.0/dns/reversename.py000066400000000000000000000063331340301174400173010ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Reverse Map Names.""" import binascii import dns.name import dns.ipv6 import dns.ipv4 from dns._compat import PY3 ipv4_reverse_domain = dns.name.from_text('in-addr.arpa.') ipv6_reverse_domain = dns.name.from_text('ip6.arpa.') def from_address(text): """Convert an IPv4 or IPv6 address in textual form into a Name object whose value is the reverse-map domain name of the address. *text*, a ``text``, is an IPv4 or IPv6 address in textual form (e.g. '127.0.0.1', '::1') Raises ``dns.exception.SyntaxError`` if the address is badly formed. Returns a ``dns.name.Name``. """ try: v6 = dns.ipv6.inet_aton(text) if dns.ipv6.is_mapped(v6): if PY3: parts = ['%d' % byte for byte in v6[12:]] else: parts = ['%d' % ord(byte) for byte in v6[12:]] origin = ipv4_reverse_domain else: parts = [x for x in str(binascii.hexlify(v6).decode())] origin = ipv6_reverse_domain except Exception: parts = ['%d' % byte for byte in bytearray(dns.ipv4.inet_aton(text))] origin = ipv4_reverse_domain parts.reverse() return dns.name.from_text('.'.join(parts), origin=origin) def to_address(name): """Convert a reverse map domain name into textual address form. *name*, a ``dns.name.Name``, an IPv4 or IPv6 address in reverse-map name form. Raises ``dns.exception.SyntaxError`` if the name does not have a reverse-map form. Returns a ``text``. """ if name.is_subdomain(ipv4_reverse_domain): name = name.relativize(ipv4_reverse_domain) labels = list(name.labels) labels.reverse() text = b'.'.join(labels) # run through inet_aton() to check syntax and make pretty. return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) elif name.is_subdomain(ipv6_reverse_domain): name = name.relativize(ipv6_reverse_domain) labels = list(name.labels) labels.reverse() parts = [] i = 0 l = len(labels) while i < l: parts.append(b''.join(labels[i:i + 4])) i += 4 text = b':'.join(parts) # run through inet_aton() to check syntax and make pretty. return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) else: raise dns.exception.SyntaxError('unknown reverse-map address family') dnspython-1.16.0/dns/reversename.pyi000066400000000000000000000001701340301174400174430ustar00rootroot00000000000000from . import name def from_address(text : str) -> name.Name: ... def to_address(name : name.Name) -> str: ... dnspython-1.16.0/dns/rrset.py000066400000000000000000000140171340301174400161220ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS RRsets (an RRset is a named rdataset)""" import dns.name import dns.rdataset import dns.rdataclass import dns.renderer from ._compat import string_types class RRset(dns.rdataset.Rdataset): """A DNS RRset (named rdataset). RRset inherits from Rdataset, and RRsets can be treated as Rdatasets in most cases. There are, however, a few notable exceptions. RRsets have different to_wire() and to_text() method arguments, reflecting the fact that RRsets always have an owner name. """ __slots__ = ['name', 'deleting'] def __init__(self, name, rdclass, rdtype, covers=dns.rdatatype.NONE, deleting=None): """Create a new RRset.""" super(RRset, self).__init__(rdclass, rdtype, covers) self.name = name self.deleting = deleting def _clone(self): obj = super(RRset, self)._clone() obj.name = self.name obj.deleting = self.deleting return obj def __repr__(self): if self.covers == 0: ctext = '' else: ctext = '(' + dns.rdatatype.to_text(self.covers) + ')' if self.deleting is not None: dtext = ' delete=' + dns.rdataclass.to_text(self.deleting) else: dtext = '' return '' def __str__(self): return self.to_text() def __eq__(self, other): if not isinstance(other, RRset): return False if self.name != other.name: return False return super(RRset, self).__eq__(other) def match(self, name, rdclass, rdtype, covers, deleting=None): """Returns ``True`` if this rrset matches the specified class, type, covers, and deletion state. """ if not super(RRset, self).match(rdclass, rdtype, covers): return False if self.name != name or self.deleting != deleting: return False return True def to_text(self, origin=None, relativize=True, **kw): """Convert the RRset into DNS master file format. See ``dns.name.Name.choose_relativity`` for more information on how *origin* and *relativize* determine the way names are emitted. Any additional keyword arguments are passed on to the rdata ``to_text()`` method. *origin*, a ``dns.name.Name`` or ``None``, the origin for relative names. *relativize*, a ``bool``. If ``True``, names will be relativized to *origin*. """ return super(RRset, self).to_text(self.name, origin, relativize, self.deleting, **kw) def to_wire(self, file, compress=None, origin=None, **kw): """Convert the RRset to wire format. All keyword arguments are passed to ``dns.rdataset.to_wire()``; see that function for details. Returns an ``int``, the number of records emitted. """ return super(RRset, self).to_wire(self.name, file, compress, origin, self.deleting, **kw) def to_rdataset(self): """Convert an RRset into an Rdataset. Returns a ``dns.rdataset.Rdataset``. """ return dns.rdataset.from_rdata_list(self.ttl, list(self)) def from_text_list(name, ttl, rdclass, rdtype, text_rdatas, idna_codec=None): """Create an RRset with the specified name, TTL, class, and type, and with the specified list of rdatas in text format. Returns a ``dns.rrset.RRset`` object. """ if isinstance(name, string_types): name = dns.name.from_text(name, None, idna_codec=idna_codec) if isinstance(rdclass, string_types): rdclass = dns.rdataclass.from_text(rdclass) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) r = RRset(name, rdclass, rdtype) r.update_ttl(ttl) for t in text_rdatas: rd = dns.rdata.from_text(r.rdclass, r.rdtype, t) r.add(rd) return r def from_text(name, ttl, rdclass, rdtype, *text_rdatas): """Create an RRset with the specified name, TTL, class, and type and with the specified rdatas in text format. Returns a ``dns.rrset.RRset`` object. """ return from_text_list(name, ttl, rdclass, rdtype, text_rdatas) def from_rdata_list(name, ttl, rdatas, idna_codec=None): """Create an RRset with the specified name and TTL, and with the specified list of rdata objects. Returns a ``dns.rrset.RRset`` object. """ if isinstance(name, string_types): name = dns.name.from_text(name, None, idna_codec=idna_codec) if len(rdatas) == 0: raise ValueError("rdata list must not be empty") r = None for rd in rdatas: if r is None: r = RRset(name, rd.rdclass, rd.rdtype) r.update_ttl(ttl) r.add(rd) return r def from_rdata(name, ttl, *rdatas): """Create an RRset with the specified name and TTL, and with the specified rdata objects. Returns a ``dns.rrset.RRset`` object. """ return from_rdata_list(name, ttl, rdatas) dnspython-1.16.0/dns/rrset.pyi000066400000000000000000000006121340301174400162670ustar00rootroot00000000000000from typing import List, Optional from . import rdataset, rdatatype class RRset(rdataset.Rdataset): def __init__(self, name, rdclass : int , rdtype : int, covers=rdatatype.NONE, deleting : Optional[int] =None) -> None: self.name = name self.deleting = deleting def from_text(name : str, ttl : int, rdclass : str, rdtype : str, *text_rdatas : str): ... dnspython-1.16.0/dns/set.py000066400000000000000000000162161340301174400155610ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. class Set(object): """A simple set class. This class was originally used to deal with sets being missing in ancient versions of python, but dnspython will continue to use it as these sets are based on lists and are thus indexable, and this ability is widely used in dnspython applications. """ __slots__ = ['items'] def __init__(self, items=None): """Initialize the set. *items*, an iterable or ``None``, the initial set of items. """ self.items = [] if items is not None: for item in items: self.add(item) def __repr__(self): return "dns.simpleset.Set(%s)" % repr(self.items) def add(self, item): """Add an item to the set. """ if item not in self.items: self.items.append(item) def remove(self, item): """Remove an item from the set. """ self.items.remove(item) def discard(self, item): """Remove an item from the set if present. """ try: self.items.remove(item) except ValueError: pass def _clone(self): """Make a (shallow) copy of the set. There is a 'clone protocol' that subclasses of this class should use. To make a copy, first call your super's _clone() method, and use the object returned as the new instance. Then make shallow copies of the attributes defined in the subclass. This protocol allows us to write the set algorithms that return new instances (e.g. union) once, and keep using them in subclasses. """ cls = self.__class__ obj = cls.__new__(cls) obj.items = list(self.items) return obj def __copy__(self): """Make a (shallow) copy of the set. """ return self._clone() def copy(self): """Make a (shallow) copy of the set. """ return self._clone() def union_update(self, other): """Update the set, adding any elements from other which are not already in the set. """ if not isinstance(other, Set): raise ValueError('other must be a Set instance') if self is other: return for item in other.items: self.add(item) def intersection_update(self, other): """Update the set, removing any elements from other which are not in both sets. """ if not isinstance(other, Set): raise ValueError('other must be a Set instance') if self is other: return # we make a copy of the list so that we can remove items from # the list without breaking the iterator. for item in list(self.items): if item not in other.items: self.items.remove(item) def difference_update(self, other): """Update the set, removing any elements from other which are in the set. """ if not isinstance(other, Set): raise ValueError('other must be a Set instance') if self is other: self.items = [] else: for item in other.items: self.discard(item) def union(self, other): """Return a new set which is the union of ``self`` and ``other``. Returns the same Set type as this set. """ obj = self._clone() obj.union_update(other) return obj def intersection(self, other): """Return a new set which is the intersection of ``self`` and ``other``. Returns the same Set type as this set. """ obj = self._clone() obj.intersection_update(other) return obj def difference(self, other): """Return a new set which ``self`` - ``other``, i.e. the items in ``self`` which are not also in ``other``. Returns the same Set type as this set. """ obj = self._clone() obj.difference_update(other) return obj def __or__(self, other): return self.union(other) def __and__(self, other): return self.intersection(other) def __add__(self, other): return self.union(other) def __sub__(self, other): return self.difference(other) def __ior__(self, other): self.union_update(other) return self def __iand__(self, other): self.intersection_update(other) return self def __iadd__(self, other): self.union_update(other) return self def __isub__(self, other): self.difference_update(other) return self def update(self, other): """Update the set, adding any elements from other which are not already in the set. *other*, the collection of items with which to update the set, which may be any iterable type. """ for item in other: self.add(item) def clear(self): """Make the set empty.""" self.items = [] def __eq__(self, other): # Yes, this is inefficient but the sets we're dealing with are # usually quite small, so it shouldn't hurt too much. for item in self.items: if item not in other.items: return False for item in other.items: if item not in self.items: return False return True def __ne__(self, other): return not self.__eq__(other) def __len__(self): return len(self.items) def __iter__(self): return iter(self.items) def __getitem__(self, i): return self.items[i] def __delitem__(self, i): del self.items[i] def issubset(self, other): """Is this set a subset of *other*? Returns a ``bool``. """ if not isinstance(other, Set): raise ValueError('other must be a Set instance') for item in self.items: if item not in other.items: return False return True def issuperset(self, other): """Is this set a superset of *other*? Returns a ``bool``. """ if not isinstance(other, Set): raise ValueError('other must be a Set instance') for item in other.items: if item not in self.items: return False return True dnspython-1.16.0/dns/tokenizer.py000066400000000000000000000431701340301174400167770ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """Tokenize DNS master file format""" from io import StringIO import sys import dns.exception import dns.name import dns.ttl from ._compat import long, text_type, binary_type _DELIMITERS = { ' ': True, '\t': True, '\n': True, ';': True, '(': True, ')': True, '"': True} _QUOTING_DELIMITERS = {'"': True} EOF = 0 EOL = 1 WHITESPACE = 2 IDENTIFIER = 3 QUOTED_STRING = 4 COMMENT = 5 DELIMITER = 6 class UngetBufferFull(dns.exception.DNSException): """An attempt was made to unget a token when the unget buffer was full.""" class Token(object): """A DNS master file format token. ttype: The token type value: The token value has_escape: Does the token value contain escapes? """ def __init__(self, ttype, value='', has_escape=False): """Initialize a token instance.""" self.ttype = ttype self.value = value self.has_escape = has_escape def is_eof(self): return self.ttype == EOF def is_eol(self): return self.ttype == EOL def is_whitespace(self): return self.ttype == WHITESPACE def is_identifier(self): return self.ttype == IDENTIFIER def is_quoted_string(self): return self.ttype == QUOTED_STRING def is_comment(self): return self.ttype == COMMENT def is_delimiter(self): return self.ttype == DELIMITER def is_eol_or_eof(self): return self.ttype == EOL or self.ttype == EOF def __eq__(self, other): if not isinstance(other, Token): return False return (self.ttype == other.ttype and self.value == other.value) def __ne__(self, other): if not isinstance(other, Token): return True return (self.ttype != other.ttype or self.value != other.value) def __str__(self): return '%d "%s"' % (self.ttype, self.value) def unescape(self): if not self.has_escape: return self unescaped = '' l = len(self.value) i = 0 while i < l: c = self.value[i] i += 1 if c == '\\': if i >= l: raise dns.exception.UnexpectedEnd c = self.value[i] i += 1 if c.isdigit(): if i >= l: raise dns.exception.UnexpectedEnd c2 = self.value[i] i += 1 if i >= l: raise dns.exception.UnexpectedEnd c3 = self.value[i] i += 1 if not (c2.isdigit() and c3.isdigit()): raise dns.exception.SyntaxError c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) unescaped += c return Token(self.ttype, unescaped) # compatibility for old-style tuple tokens def __len__(self): return 2 def __iter__(self): return iter((self.ttype, self.value)) def __getitem__(self, i): if i == 0: return self.ttype elif i == 1: return self.value else: raise IndexError class Tokenizer(object): """A DNS master file format tokenizer. A token object is basically a (type, value) tuple. The valid types are EOF, EOL, WHITESPACE, IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER. file: The file to tokenize ungotten_char: The most recently ungotten character, or None. ungotten_token: The most recently ungotten token, or None. multiline: The current multiline level. This value is increased by one every time a '(' delimiter is read, and decreased by one every time a ')' delimiter is read. quoting: This variable is true if the tokenizer is currently reading a quoted string. eof: This variable is true if the tokenizer has encountered EOF. delimiters: The current delimiter dictionary. line_number: The current line number filename: A filename that will be returned by the where() method. """ def __init__(self, f=sys.stdin, filename=None): """Initialize a tokenizer instance. f: The file to tokenize. The default is sys.stdin. This parameter may also be a string, in which case the tokenizer will take its input from the contents of the string. filename: the name of the filename that the where() method will return. """ if isinstance(f, text_type): f = StringIO(f) if filename is None: filename = '' elif isinstance(f, binary_type): f = StringIO(f.decode()) if filename is None: filename = '' else: if filename is None: if f is sys.stdin: filename = '' else: filename = '' self.file = f self.ungotten_char = None self.ungotten_token = None self.multiline = 0 self.quoting = False self.eof = False self.delimiters = _DELIMITERS self.line_number = 1 self.filename = filename def _get_char(self): """Read a character from input. """ if self.ungotten_char is None: if self.eof: c = '' else: c = self.file.read(1) if c == '': self.eof = True elif c == '\n': self.line_number += 1 else: c = self.ungotten_char self.ungotten_char = None return c def where(self): """Return the current location in the input. Returns a (string, int) tuple. The first item is the filename of the input, the second is the current line number. """ return (self.filename, self.line_number) def _unget_char(self, c): """Unget a character. The unget buffer for characters is only one character large; it is an error to try to unget a character when the unget buffer is not empty. c: the character to unget raises UngetBufferFull: there is already an ungotten char """ if self.ungotten_char is not None: raise UngetBufferFull self.ungotten_char = c def skip_whitespace(self): """Consume input until a non-whitespace character is encountered. The non-whitespace character is then ungotten, and the number of whitespace characters consumed is returned. If the tokenizer is in multiline mode, then newlines are whitespace. Returns the number of characters skipped. """ skipped = 0 while True: c = self._get_char() if c != ' ' and c != '\t': if (c != '\n') or not self.multiline: self._unget_char(c) return skipped skipped += 1 def get(self, want_leading=False, want_comment=False): """Get the next token. want_leading: If True, return a WHITESPACE token if the first character read is whitespace. The default is False. want_comment: If True, return a COMMENT token if the first token read is a comment. The default is False. Raises dns.exception.UnexpectedEnd: input ended prematurely Raises dns.exception.SyntaxError: input was badly formed Returns a Token. """ if self.ungotten_token is not None: token = self.ungotten_token self.ungotten_token = None if token.is_whitespace(): if want_leading: return token elif token.is_comment(): if want_comment: return token else: return token skipped = self.skip_whitespace() if want_leading and skipped > 0: return Token(WHITESPACE, ' ') token = '' ttype = IDENTIFIER has_escape = False while True: c = self._get_char() if c == '' or c in self.delimiters: if c == '' and self.quoting: raise dns.exception.UnexpectedEnd if token == '' and ttype != QUOTED_STRING: if c == '(': self.multiline += 1 self.skip_whitespace() continue elif c == ')': if self.multiline <= 0: raise dns.exception.SyntaxError self.multiline -= 1 self.skip_whitespace() continue elif c == '"': if not self.quoting: self.quoting = True self.delimiters = _QUOTING_DELIMITERS ttype = QUOTED_STRING continue else: self.quoting = False self.delimiters = _DELIMITERS self.skip_whitespace() continue elif c == '\n': return Token(EOL, '\n') elif c == ';': while 1: c = self._get_char() if c == '\n' or c == '': break token += c if want_comment: self._unget_char(c) return Token(COMMENT, token) elif c == '': if self.multiline: raise dns.exception.SyntaxError( 'unbalanced parentheses') return Token(EOF) elif self.multiline: self.skip_whitespace() token = '' continue else: return Token(EOL, '\n') else: # This code exists in case we ever want a # delimiter to be returned. It never produces # a token currently. token = c ttype = DELIMITER else: self._unget_char(c) break elif self.quoting: if c == '\\': c = self._get_char() if c == '': raise dns.exception.UnexpectedEnd if c.isdigit(): c2 = self._get_char() if c2 == '': raise dns.exception.UnexpectedEnd c3 = self._get_char() if c == '': raise dns.exception.UnexpectedEnd if not (c2.isdigit() and c3.isdigit()): raise dns.exception.SyntaxError c = chr(int(c) * 100 + int(c2) * 10 + int(c3)) elif c == '\n': raise dns.exception.SyntaxError('newline in quoted string') elif c == '\\': # # It's an escape. Put it and the next character into # the token; it will be checked later for goodness. # token += c has_escape = True c = self._get_char() if c == '' or c == '\n': raise dns.exception.UnexpectedEnd token += c if token == '' and ttype != QUOTED_STRING: if self.multiline: raise dns.exception.SyntaxError('unbalanced parentheses') ttype = EOF return Token(ttype, token, has_escape) def unget(self, token): """Unget a token. The unget buffer for tokens is only one token large; it is an error to try to unget a token when the unget buffer is not empty. token: the token to unget Raises UngetBufferFull: there is already an ungotten token """ if self.ungotten_token is not None: raise UngetBufferFull self.ungotten_token = token def next(self): """Return the next item in an iteration. Returns a Token. """ token = self.get() if token.is_eof(): raise StopIteration return token __next__ = next def __iter__(self): return self # Helpers def get_int(self, base=10): """Read the next token and interpret it as an integer. Raises dns.exception.SyntaxError if not an integer. Returns an int. """ token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError('expecting an identifier') if not token.value.isdigit(): raise dns.exception.SyntaxError('expecting an integer') return int(token.value, base) def get_uint8(self): """Read the next token and interpret it as an 8-bit unsigned integer. Raises dns.exception.SyntaxError if not an 8-bit unsigned integer. Returns an int. """ value = self.get_int() if value < 0 or value > 255: raise dns.exception.SyntaxError( '%d is not an unsigned 8-bit integer' % value) return value def get_uint16(self, base=10): """Read the next token and interpret it as a 16-bit unsigned integer. Raises dns.exception.SyntaxError if not a 16-bit unsigned integer. Returns an int. """ value = self.get_int(base=base) if value < 0 or value > 65535: if base == 8: raise dns.exception.SyntaxError( '%o is not an octal unsigned 16-bit integer' % value) else: raise dns.exception.SyntaxError( '%d is not an unsigned 16-bit integer' % value) return value def get_uint32(self): """Read the next token and interpret it as a 32-bit unsigned integer. Raises dns.exception.SyntaxError if not a 32-bit unsigned integer. Returns an int. """ token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError('expecting an identifier') if not token.value.isdigit(): raise dns.exception.SyntaxError('expecting an integer') value = long(token.value) if value < 0 or value > long(4294967296): raise dns.exception.SyntaxError( '%d is not an unsigned 32-bit integer' % value) return value def get_string(self, origin=None): """Read the next token and interpret it as a string. Raises dns.exception.SyntaxError if not a string. Returns a string. """ token = self.get().unescape() if not (token.is_identifier() or token.is_quoted_string()): raise dns.exception.SyntaxError('expecting a string') return token.value def get_identifier(self, origin=None): """Read the next token, which should be an identifier. Raises dns.exception.SyntaxError if not an identifier. Returns a string. """ token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError('expecting an identifier') return token.value def get_name(self, origin=None): """Read the next token and interpret it as a DNS name. Raises dns.exception.SyntaxError if not a name. Returns a dns.name.Name. """ token = self.get() if not token.is_identifier(): raise dns.exception.SyntaxError('expecting an identifier') return dns.name.from_text(token.value, origin) def get_eol(self): """Read the next token and raise an exception if it isn't EOL or EOF. Returns a string. """ token = self.get() if not token.is_eol_or_eof(): raise dns.exception.SyntaxError( 'expected EOL or EOF, got %d "%s"' % (token.ttype, token.value)) return token.value def get_ttl(self): """Read the next token and interpret it as a DNS TTL. Raises dns.exception.SyntaxError or dns.ttl.BadTTL if not an identifier or badly formed. Returns an int. """ token = self.get().unescape() if not token.is_identifier(): raise dns.exception.SyntaxError('expecting an identifier') return dns.ttl.from_text(token.value) dnspython-1.16.0/dns/tsig.py000066400000000000000000000172051340301174400157330ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS TSIG support.""" import hashlib import hmac import struct import dns.exception import dns.rdataclass import dns.name from ._compat import long, string_types, text_type class BadTime(dns.exception.DNSException): """The current time is not within the TSIG's validity time.""" class BadSignature(dns.exception.DNSException): """The TSIG signature fails to verify.""" class PeerError(dns.exception.DNSException): """Base class for all TSIG errors generated by the remote peer""" class PeerBadKey(PeerError): """The peer didn't know the key we used""" class PeerBadSignature(PeerError): """The peer didn't like the signature we sent""" class PeerBadTime(PeerError): """The peer didn't like the time we sent""" class PeerBadTruncation(PeerError): """The peer didn't like amount of truncation in the TSIG we sent""" # TSIG Algorithms HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT") HMAC_SHA1 = dns.name.from_text("hmac-sha1") HMAC_SHA224 = dns.name.from_text("hmac-sha224") HMAC_SHA256 = dns.name.from_text("hmac-sha256") HMAC_SHA384 = dns.name.from_text("hmac-sha384") HMAC_SHA512 = dns.name.from_text("hmac-sha512") _hashes = { HMAC_SHA224: hashlib.sha224, HMAC_SHA256: hashlib.sha256, HMAC_SHA384: hashlib.sha384, HMAC_SHA512: hashlib.sha512, HMAC_SHA1: hashlib.sha1, HMAC_MD5: hashlib.md5, } default_algorithm = HMAC_MD5 BADSIG = 16 BADKEY = 17 BADTIME = 18 BADTRUNC = 22 def sign(wire, keyname, secret, time, fudge, original_id, error, other_data, request_mac, ctx=None, multi=False, first=True, algorithm=default_algorithm): """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata for the input parameters, the HMAC MAC calculated by applying the TSIG signature algorithm, and the TSIG digest context. @rtype: (string, string, hmac.HMAC object) @raises ValueError: I{other_data} is too long @raises NotImplementedError: I{algorithm} is not supported """ if isinstance(other_data, text_type): other_data = other_data.encode() (algorithm_name, digestmod) = get_algorithm(algorithm) if first: ctx = hmac.new(secret, digestmod=digestmod) ml = len(request_mac) if ml > 0: ctx.update(struct.pack('!H', ml)) ctx.update(request_mac) id = struct.pack('!H', original_id) ctx.update(id) ctx.update(wire[2:]) if first: ctx.update(keyname.to_digestable()) ctx.update(struct.pack('!H', dns.rdataclass.ANY)) ctx.update(struct.pack('!I', 0)) long_time = time + long(0) upper_time = (long_time >> 32) & long(0xffff) lower_time = long_time & long(0xffffffff) time_mac = struct.pack('!HIH', upper_time, lower_time, fudge) pre_mac = algorithm_name + time_mac ol = len(other_data) if ol > 65535: raise ValueError('TSIG Other Data is > 65535 bytes') post_mac = struct.pack('!HH', error, ol) + other_data if first: ctx.update(pre_mac) ctx.update(post_mac) else: ctx.update(time_mac) mac = ctx.digest() mpack = struct.pack('!H', len(mac)) tsig_rdata = pre_mac + mpack + mac + id + post_mac if multi: ctx = hmac.new(secret, digestmod=digestmod) ml = len(mac) ctx.update(struct.pack('!H', ml)) ctx.update(mac) else: ctx = None return (tsig_rdata, mac, ctx) def hmac_md5(wire, keyname, secret, time, fudge, original_id, error, other_data, request_mac, ctx=None, multi=False, first=True, algorithm=default_algorithm): return sign(wire, keyname, secret, time, fudge, original_id, error, other_data, request_mac, ctx, multi, first, algorithm) def validate(wire, keyname, secret, now, request_mac, tsig_start, tsig_rdata, tsig_rdlen, ctx=None, multi=False, first=True): """Validate the specified TSIG rdata against the other input parameters. @raises FormError: The TSIG is badly formed. @raises BadTime: There is too much time skew between the client and the server. @raises BadSignature: The TSIG signature did not validate @rtype: hmac.HMAC object""" (adcount,) = struct.unpack("!H", wire[10:12]) if adcount == 0: raise dns.exception.FormError adcount -= 1 new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] current = tsig_rdata (aname, used) = dns.name.from_wire(wire, current) current = current + used (upper_time, lower_time, fudge, mac_size) = \ struct.unpack("!HIHH", wire[current:current + 10]) time = ((upper_time + long(0)) << 32) + (lower_time + long(0)) current += 10 mac = wire[current:current + mac_size] current += mac_size (original_id, error, other_size) = \ struct.unpack("!HHH", wire[current:current + 6]) current += 6 other_data = wire[current:current + other_size] current += other_size if current != tsig_rdata + tsig_rdlen: raise dns.exception.FormError if error != 0: if error == BADSIG: raise PeerBadSignature elif error == BADKEY: raise PeerBadKey elif error == BADTIME: raise PeerBadTime elif error == BADTRUNC: raise PeerBadTruncation else: raise PeerError('unknown TSIG error code %d' % error) time_low = time - fudge time_high = time + fudge if now < time_low or now > time_high: raise BadTime (junk, our_mac, ctx) = sign(new_wire, keyname, secret, time, fudge, original_id, error, other_data, request_mac, ctx, multi, first, aname) if our_mac != mac: raise BadSignature return ctx def get_algorithm(algorithm): """Returns the wire format string and the hash module to use for the specified TSIG algorithm @rtype: (string, hash constructor) @raises NotImplementedError: I{algorithm} is not supported """ if isinstance(algorithm, string_types): algorithm = dns.name.from_text(algorithm) try: return (algorithm.to_digestable(), _hashes[algorithm]) except KeyError: raise NotImplementedError("TSIG algorithm " + str(algorithm) + " is not supported") def get_algorithm_and_mac(wire, tsig_rdata, tsig_rdlen): """Return the tsig algorithm for the specified tsig_rdata @raises FormError: The TSIG is badly formed. """ current = tsig_rdata (aname, used) = dns.name.from_wire(wire, current) current = current + used (upper_time, lower_time, fudge, mac_size) = \ struct.unpack("!HIHH", wire[current:current + 10]) current += 10 mac = wire[current:current + mac_size] current += mac_size if current > tsig_rdata + tsig_rdlen: raise dns.exception.FormError return (aname, mac) dnspython-1.16.0/dns/tsigkeyring.py000066400000000000000000000034261340301174400173240ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """A place to store TSIG keys.""" from dns._compat import maybe_decode, maybe_encode import base64 import dns.name def from_text(textring): """Convert a dictionary containing (textual DNS name, base64 secret) pairs into a binary keyring which has (dns.name.Name, binary secret) pairs. @rtype: dict""" keyring = {} for keytext in textring: keyname = dns.name.from_text(keytext) secret = base64.decodestring(maybe_encode(textring[keytext])) keyring[keyname] = secret return keyring def to_text(keyring): """Convert a dictionary containing (dns.name.Name, binary secret) pairs into a text keyring which has (textual DNS name, base64 secret) pairs. @rtype: dict""" textring = {} for keyname in keyring: keytext = maybe_decode(keyname.to_text()) secret = maybe_decode(base64.encodestring(keyring[keyname])) textring[keytext] = secret return textring dnspython-1.16.0/dns/tsigkeyring.pyi000066400000000000000000000002761340301174400174750ustar00rootroot00000000000000from typing import Dict from . import name def from_text(textring : Dict[str,str]) -> Dict[name.Name,bytes]: ... def to_text(keyring : Dict[name.Name,bytes]) -> Dict[str, str]: ... dnspython-1.16.0/dns/ttl.py000066400000000000000000000044551340301174400155730ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS TTL conversion.""" import dns.exception from ._compat import long class BadTTL(dns.exception.SyntaxError): """DNS TTL value is not well-formed.""" def from_text(text): """Convert the text form of a TTL to an integer. The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported. *text*, a ``text``, the textual TTL. Raises ``dns.ttl.BadTTL`` if the TTL is not well-formed. Returns an ``int``. """ if text.isdigit(): total = long(text) else: if not text[0].isdigit(): raise BadTTL total = long(0) current = long(0) for c in text: if c.isdigit(): current *= 10 current += long(c) else: c = c.lower() if c == 'w': total += current * long(604800) elif c == 'd': total += current * long(86400) elif c == 'h': total += current * long(3600) elif c == 'm': total += current * long(60) elif c == 's': total += current else: raise BadTTL("unknown unit '%s'" % c) current = 0 if not current == 0: raise BadTTL("trailing integer") if total < long(0) or total > long(2147483647): raise BadTTL("TTL should be between 0 and 2^31 - 1 (inclusive)") return total dnspython-1.16.0/dns/update.py000066400000000000000000000241471340301174400162520ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Dynamic Update Support""" import dns.message import dns.name import dns.opcode import dns.rdata import dns.rdataclass import dns.rdataset import dns.tsig from ._compat import string_types class Update(dns.message.Message): def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None, keyname=None, keyalgorithm=dns.tsig.default_algorithm): """Initialize a new DNS Update object. See the documentation of the Message class for a complete description of the keyring dictionary. *zone*, a ``dns.name.Name`` or ``text``, the zone which is being updated. *rdclass*, an ``int`` or ``text``, the class of the zone. *keyring*, a ``dict``, the TSIG keyring to use. If a *keyring* is specified but a *keyname* is not, then the key used will be the first key in the *keyring*. Note that the order of keys in a dictionary is not defined, so applications should supply a keyname when a keyring is used, unless they know the keyring contains only one key. *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key to use; defaults to ``None``. The key must be defined in the keyring. *keyalgorithm*, a ``dns.name.Name``, the TSIG algorithm to use. """ super(Update, self).__init__() self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) if isinstance(zone, string_types): zone = dns.name.from_text(zone) self.origin = zone if isinstance(rdclass, string_types): rdclass = dns.rdataclass.from_text(rdclass) self.zone_rdclass = rdclass self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA, create=True, force_unique=True) if keyring is not None: self.use_tsig(keyring, keyname, algorithm=keyalgorithm) def _add_rr(self, name, ttl, rd, deleting=None, section=None): """Add a single RR to the update section.""" if section is None: section = self.authority covers = rd.covers() rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype, covers, deleting, True, True) rrset.add(rd, ttl) def _add(self, replace, section, name, *args): """Add records. *replace* is the replacement mode. If ``False``, RRs are added to an existing RRset; if ``True``, the RRset is replaced with the specified contents. The second argument is the section to add to. The third argument is always a name. The other arguments can be: - rdataset... - ttl, rdata... - ttl, rdtype, string... """ if isinstance(name, string_types): name = dns.name.from_text(name, None) if isinstance(args[0], dns.rdataset.Rdataset): for rds in args: if replace: self.delete(name, rds.rdtype) for rd in rds: self._add_rr(name, rds.ttl, rd, section=section) else: args = list(args) ttl = int(args.pop(0)) if isinstance(args[0], dns.rdata.Rdata): if replace: self.delete(name, args[0].rdtype) for rd in args: self._add_rr(name, ttl, rd, section=section) else: rdtype = args.pop(0) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if replace: self.delete(name, rdtype) for s in args: rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, self.origin) self._add_rr(name, ttl, rd, section=section) def add(self, name, *args): """Add records. The first argument is always a name. The other arguments can be: - rdataset... - ttl, rdata... - ttl, rdtype, string... """ self._add(False, self.authority, name, *args) def delete(self, name, *args): """Delete records. The first argument is always a name. The other arguments can be: - *empty* - rdataset... - rdata... - rdtype, [string...] """ if isinstance(name, string_types): name = dns.name.from_text(name, None) if len(args) == 0: self.find_rrset(self.authority, name, dns.rdataclass.ANY, dns.rdatatype.ANY, dns.rdatatype.NONE, dns.rdatatype.ANY, True, True) elif isinstance(args[0], dns.rdataset.Rdataset): for rds in args: for rd in rds: self._add_rr(name, 0, rd, dns.rdataclass.NONE) else: args = list(args) if isinstance(args[0], dns.rdata.Rdata): for rd in args: self._add_rr(name, 0, rd, dns.rdataclass.NONE) else: rdtype = args.pop(0) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if len(args) == 0: self.find_rrset(self.authority, name, self.zone_rdclass, rdtype, dns.rdatatype.NONE, dns.rdataclass.ANY, True, True) else: for s in args: rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, self.origin) self._add_rr(name, 0, rd, dns.rdataclass.NONE) def replace(self, name, *args): """Replace records. The first argument is always a name. The other arguments can be: - rdataset... - ttl, rdata... - ttl, rdtype, string... Note that if you want to replace the entire node, you should do a delete of the name followed by one or more calls to add. """ self._add(True, self.authority, name, *args) def present(self, name, *args): """Require that an owner name (and optionally an rdata type, or specific rdataset) exists as a prerequisite to the execution of the update. The first argument is always a name. The other arguments can be: - rdataset... - rdata... - rdtype, string... """ if isinstance(name, string_types): name = dns.name.from_text(name, None) if len(args) == 0: self.find_rrset(self.answer, name, dns.rdataclass.ANY, dns.rdatatype.ANY, dns.rdatatype.NONE, None, True, True) elif isinstance(args[0], dns.rdataset.Rdataset) or \ isinstance(args[0], dns.rdata.Rdata) or \ len(args) > 1: if not isinstance(args[0], dns.rdataset.Rdataset): # Add a 0 TTL args = list(args) args.insert(0, 0) self._add(False, self.answer, name, *args) else: rdtype = args[0] if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) self.find_rrset(self.answer, name, dns.rdataclass.ANY, rdtype, dns.rdatatype.NONE, None, True, True) def absent(self, name, rdtype=None): """Require that an owner name (and optionally an rdata type) does not exist as a prerequisite to the execution of the update.""" if isinstance(name, string_types): name = dns.name.from_text(name, None) if rdtype is None: self.find_rrset(self.answer, name, dns.rdataclass.NONE, dns.rdatatype.ANY, dns.rdatatype.NONE, None, True, True) else: if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) self.find_rrset(self.answer, name, dns.rdataclass.NONE, rdtype, dns.rdatatype.NONE, None, True, True) def to_wire(self, origin=None, max_size=65535): """Return a string containing the update in DNS compressed wire format. *origin*, a ``dns.name.Name`` or ``None``, the origin to be appended to any relative names. If *origin* is ``None``, then the origin of the ``dns.update.Update`` message object is used (i.e. the *zone* parameter passed when the Update object was created). *max_size*, an ``int``, the maximum size of the wire format output; default is 0, which means "the message's request payload, if nonzero, or 65535". Returns a ``binary``. """ if origin is None: origin = self.origin return super(Update, self).to_wire(origin, max_size) dnspython-1.16.0/dns/update.pyi000066400000000000000000000017561340301174400164240ustar00rootroot00000000000000from typing import Optional,Dict,Union,Any from . import message, tsig, rdataclass, name class Update(message.Message): def __init__(self, zone : Union[name.Name, str], rdclass : Union[int,str] = rdataclass.IN, keyring : Optional[Dict[name.Name,bytes]] = None, keyname : Optional[name.Name] = None, keyalgorithm : Optional[name.Name] = tsig.default_algorithm) -> None: self.id : int def add(self, name : Union[str,name.Name], *args : Any): ... def delete(self, name, *args : Any): ... def replace(self, name : Union[str,name.Name], *args : Any): ... def present(self, name : Union[str,name.Name], *args : Any): ... def absent(self, name : Union[str,name.Name], rdtype=None): """Require that an owner name (and optionally an rdata type) does not exist as a prerequisite to the execution of the update.""" def to_wire(self, origin : Optional[name.Name] = None, max_size=65535, **kw) -> bytes: ... dnspython-1.16.0/dns/version.py000066400000000000000000000025761340301174400164570ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """dnspython release version information.""" #: MAJOR MAJOR = 1 #: MINOR MINOR = 16 #: MICRO MICRO = 0 #: RELEASELEVEL RELEASELEVEL = 0x0f #: SERIAL SERIAL = 0 if RELEASELEVEL == 0x0f: #: version version = '%d.%d.%d' % (MAJOR, MINOR, MICRO) elif RELEASELEVEL == 0x00: version = '%d.%d.%dx%d' % \ (MAJOR, MINOR, MICRO, SERIAL) else: version = '%d.%d.%d%x%d' % \ (MAJOR, MINOR, MICRO, RELEASELEVEL, SERIAL) #: hexversion hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | \ SERIAL dnspython-1.16.0/dns/wiredata.py000066400000000000000000000072471340301174400165720ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2011,2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Wire Data Helper""" import dns.exception from ._compat import binary_type, string_types, PY2 # Figure out what constant python passes for an unspecified slice bound. # It's supposed to be sys.maxint, yet on 64-bit windows sys.maxint is 2^31 - 1 # but Python uses 2^63 - 1 as the constant. Rather than making pointless # extra comparisons, duplicating code, or weakening WireData, we just figure # out what constant Python will use. class _SliceUnspecifiedBound(binary_type): def __getitem__(self, key): return key.stop if PY2: def __getslice__(self, i, j): # pylint: disable=getslice-method return self.__getitem__(slice(i, j)) _unspecified_bound = _SliceUnspecifiedBound()[1:] class WireData(binary_type): # WireData is a binary type with stricter slicing def __getitem__(self, key): try: if isinstance(key, slice): # make sure we are not going outside of valid ranges, # do stricter control of boundaries than python does # by default start = key.start stop = key.stop if PY2: if stop == _unspecified_bound: # handle the case where the right bound is unspecified stop = len(self) if start < 0 or stop < 0: raise dns.exception.FormError # If it's not an empty slice, access left and right bounds # to make sure they're valid if start != stop: super(WireData, self).__getitem__(start) super(WireData, self).__getitem__(stop - 1) else: for index in (start, stop): if index is None: continue elif abs(index) > len(self): raise dns.exception.FormError return WireData(super(WireData, self).__getitem__( slice(start, stop))) return bytearray(self.unwrap())[key] except IndexError: raise dns.exception.FormError if PY2: def __getslice__(self, i, j): # pylint: disable=getslice-method return self.__getitem__(slice(i, j)) def __iter__(self): i = 0 while 1: try: yield self[i] i += 1 except dns.exception.FormError: raise StopIteration def unwrap(self): return binary_type(self) def maybe_wrap(wire): if isinstance(wire, WireData): return wire elif isinstance(wire, binary_type): return WireData(wire) elif isinstance(wire, string_types): return WireData(wire.encode()) raise ValueError("unhandled type %s" % type(wire)) dnspython-1.16.0/dns/zone.py000066400000000000000000001226151340301174400157420ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """DNS Zones.""" from __future__ import generators import sys import re import os from io import BytesIO import dns.exception import dns.name import dns.node import dns.rdataclass import dns.rdatatype import dns.rdata import dns.rdtypes.ANY.SOA import dns.rrset import dns.tokenizer import dns.ttl import dns.grange from ._compat import string_types, text_type, PY3 class BadZone(dns.exception.DNSException): """The DNS zone is malformed.""" class NoSOA(BadZone): """The DNS zone has no SOA RR at its origin.""" class NoNS(BadZone): """The DNS zone has no NS RRset at its origin.""" class UnknownOrigin(BadZone): """The DNS zone's origin is unknown.""" class Zone(object): """A DNS zone. A Zone is a mapping from names to nodes. The zone object may be treated like a Python dictionary, e.g. zone[name] will retrieve the node associated with that name. The I{name} may be a dns.name.Name object, or it may be a string. In the either case, if the name is relative it is treated as relative to the origin of the zone. @ivar rdclass: The zone's rdata class; the default is class IN. @type rdclass: int @ivar origin: The origin of the zone. @type origin: dns.name.Name object @ivar nodes: A dictionary mapping the names of nodes in the zone to the nodes themselves. @type nodes: dict @ivar relativize: should names in the zone be relativized? @type relativize: bool @cvar node_factory: the factory used to create a new node @type node_factory: class or callable """ node_factory = dns.node.Node __slots__ = ['rdclass', 'origin', 'nodes', 'relativize'] def __init__(self, origin, rdclass=dns.rdataclass.IN, relativize=True): """Initialize a zone object. @param origin: The origin of the zone. @type origin: dns.name.Name object @param rdclass: The zone's rdata class; the default is class IN. @type rdclass: int""" if origin is not None: if isinstance(origin, string_types): origin = dns.name.from_text(origin) elif not isinstance(origin, dns.name.Name): raise ValueError("origin parameter must be convertible to a " "DNS name") if not origin.is_absolute(): raise ValueError("origin parameter must be an absolute name") self.origin = origin self.rdclass = rdclass self.nodes = {} self.relativize = relativize def __eq__(self, other): """Two zones are equal if they have the same origin, class, and nodes. @rtype: bool """ if not isinstance(other, Zone): return False if self.rdclass != other.rdclass or \ self.origin != other.origin or \ self.nodes != other.nodes: return False return True def __ne__(self, other): """Are two zones not equal? @rtype: bool """ return not self.__eq__(other) def _validate_name(self, name): if isinstance(name, string_types): name = dns.name.from_text(name, None) elif not isinstance(name, dns.name.Name): raise KeyError("name parameter must be convertible to a DNS name") if name.is_absolute(): if not name.is_subdomain(self.origin): raise KeyError( "name parameter must be a subdomain of the zone origin") if self.relativize: name = name.relativize(self.origin) return name def __getitem__(self, key): key = self._validate_name(key) return self.nodes[key] def __setitem__(self, key, value): key = self._validate_name(key) self.nodes[key] = value def __delitem__(self, key): key = self._validate_name(key) del self.nodes[key] def __iter__(self): return self.nodes.__iter__() def iterkeys(self): if PY3: return self.nodes.keys() # pylint: disable=dict-keys-not-iterating else: return self.nodes.iterkeys() # pylint: disable=dict-iter-method def keys(self): return self.nodes.keys() # pylint: disable=dict-keys-not-iterating def itervalues(self): if PY3: return self.nodes.values() # pylint: disable=dict-values-not-iterating else: return self.nodes.itervalues() # pylint: disable=dict-iter-method def values(self): return self.nodes.values() # pylint: disable=dict-values-not-iterating def items(self): return self.nodes.items() # pylint: disable=dict-items-not-iterating iteritems = items def get(self, key): key = self._validate_name(key) return self.nodes.get(key) def __contains__(self, other): return other in self.nodes def find_node(self, name, create=False): """Find a node in the zone, possibly creating it. @param name: the name of the node to find @type name: dns.name.Name object or string @param create: should the node be created if it doesn't exist? @type create: bool @raises KeyError: the name is not known and create was not specified. @rtype: dns.node.Node object """ name = self._validate_name(name) node = self.nodes.get(name) if node is None: if not create: raise KeyError node = self.node_factory() self.nodes[name] = node return node def get_node(self, name, create=False): """Get a node in the zone, possibly creating it. This method is like L{find_node}, except it returns None instead of raising an exception if the node does not exist and creation has not been requested. @param name: the name of the node to find @type name: dns.name.Name object or string @param create: should the node be created if it doesn't exist? @type create: bool @rtype: dns.node.Node object or None """ try: node = self.find_node(name, create) except KeyError: node = None return node def delete_node(self, name): """Delete the specified node if it exists. It is not an error if the node does not exist. """ name = self._validate_name(name) if name in self.nodes: del self.nodes[name] def find_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, create=False): """Look for rdata with the specified name and type in the zone, and return an rdataset encapsulating it. The I{name}, I{rdtype}, and I{covers} parameters may be strings, in which case they will be converted to their proper type. The rdataset returned is not a copy; changes to it will change the zone. KeyError is raised if the name or type are not found. Use L{get_rdataset} if you want to have None returned instead. @param name: the owner name to look for @type name: DNS.name.Name object or string @param rdtype: the rdata type desired @type rdtype: int or string @param covers: the covered type (defaults to None) @type covers: int or string @param create: should the node and rdataset be created if they do not exist? @type create: bool @raises KeyError: the node or rdata could not be found @rtype: dns.rdataset.Rdataset object """ name = self._validate_name(name) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) node = self.find_node(name, create) return node.find_rdataset(self.rdclass, rdtype, covers, create) def get_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE, create=False): """Look for rdata with the specified name and type in the zone, and return an rdataset encapsulating it. The I{name}, I{rdtype}, and I{covers} parameters may be strings, in which case they will be converted to their proper type. The rdataset returned is not a copy; changes to it will change the zone. None is returned if the name or type are not found. Use L{find_rdataset} if you want to have KeyError raised instead. @param name: the owner name to look for @type name: DNS.name.Name object or string @param rdtype: the rdata type desired @type rdtype: int or string @param covers: the covered type (defaults to None) @type covers: int or string @param create: should the node and rdataset be created if they do not exist? @type create: bool @rtype: dns.rdataset.Rdataset object or None """ try: rdataset = self.find_rdataset(name, rdtype, covers, create) except KeyError: rdataset = None return rdataset def delete_rdataset(self, name, rdtype, covers=dns.rdatatype.NONE): """Delete the rdataset matching I{rdtype} and I{covers}, if it exists at the node specified by I{name}. The I{name}, I{rdtype}, and I{covers} parameters may be strings, in which case they will be converted to their proper type. It is not an error if the node does not exist, or if there is no matching rdataset at the node. If the node has no rdatasets after the deletion, it will itself be deleted. @param name: the owner name to look for @type name: DNS.name.Name object or string @param rdtype: the rdata type desired @type rdtype: int or string @param covers: the covered type (defaults to None) @type covers: int or string """ name = self._validate_name(name) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) node = self.get_node(name) if node is not None: node.delete_rdataset(self.rdclass, rdtype, covers) if len(node) == 0: self.delete_node(name) def replace_rdataset(self, name, replacement): """Replace an rdataset at name. It is not an error if there is no rdataset matching I{replacement}. Ownership of the I{replacement} object is transferred to the zone; in other words, this method does not store a copy of I{replacement} at the node, it stores I{replacement} itself. If the I{name} node does not exist, it is created. @param name: the owner name @type name: DNS.name.Name object or string @param replacement: the replacement rdataset @type replacement: dns.rdataset.Rdataset """ if replacement.rdclass != self.rdclass: raise ValueError('replacement.rdclass != zone.rdclass') node = self.find_node(name, True) node.replace_rdataset(replacement) def find_rrset(self, name, rdtype, covers=dns.rdatatype.NONE): """Look for rdata with the specified name and type in the zone, and return an RRset encapsulating it. The I{name}, I{rdtype}, and I{covers} parameters may be strings, in which case they will be converted to their proper type. This method is less efficient than the similar L{find_rdataset} because it creates an RRset instead of returning the matching rdataset. It may be more convenient for some uses since it returns an object which binds the owner name to the rdata. This method may not be used to create new nodes or rdatasets; use L{find_rdataset} instead. KeyError is raised if the name or type are not found. Use L{get_rrset} if you want to have None returned instead. @param name: the owner name to look for @type name: DNS.name.Name object or string @param rdtype: the rdata type desired @type rdtype: int or string @param covers: the covered type (defaults to None) @type covers: int or string @raises KeyError: the node or rdata could not be found @rtype: dns.rrset.RRset object """ name = self._validate_name(name) if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) rdataset = self.nodes[name].find_rdataset(self.rdclass, rdtype, covers) rrset = dns.rrset.RRset(name, self.rdclass, rdtype, covers) rrset.update(rdataset) return rrset def get_rrset(self, name, rdtype, covers=dns.rdatatype.NONE): """Look for rdata with the specified name and type in the zone, and return an RRset encapsulating it. The I{name}, I{rdtype}, and I{covers} parameters may be strings, in which case they will be converted to their proper type. This method is less efficient than the similar L{get_rdataset} because it creates an RRset instead of returning the matching rdataset. It may be more convenient for some uses since it returns an object which binds the owner name to the rdata. This method may not be used to create new nodes or rdatasets; use L{find_rdataset} instead. None is returned if the name or type are not found. Use L{find_rrset} if you want to have KeyError raised instead. @param name: the owner name to look for @type name: DNS.name.Name object or string @param rdtype: the rdata type desired @type rdtype: int or string @param covers: the covered type (defaults to None) @type covers: int or string @rtype: dns.rrset.RRset object """ try: rrset = self.find_rrset(name, rdtype, covers) except KeyError: rrset = None return rrset def iterate_rdatasets(self, rdtype=dns.rdatatype.ANY, covers=dns.rdatatype.NONE): """Return a generator which yields (name, rdataset) tuples for all rdatasets in the zone which have the specified I{rdtype} and I{covers}. If I{rdtype} is dns.rdatatype.ANY, the default, then all rdatasets will be matched. @param rdtype: int or string @type rdtype: int or string @param covers: the covered type (defaults to None) @type covers: int or string """ if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) for (name, node) in self.iteritems(): # pylint: disable=dict-iter-method for rds in node: if rdtype == dns.rdatatype.ANY or \ (rds.rdtype == rdtype and rds.covers == covers): yield (name, rds) def iterate_rdatas(self, rdtype=dns.rdatatype.ANY, covers=dns.rdatatype.NONE): """Return a generator which yields (name, ttl, rdata) tuples for all rdatas in the zone which have the specified I{rdtype} and I{covers}. If I{rdtype} is dns.rdatatype.ANY, the default, then all rdatas will be matched. @param rdtype: int or string @type rdtype: int or string @param covers: the covered type (defaults to None) @type covers: int or string """ if isinstance(rdtype, string_types): rdtype = dns.rdatatype.from_text(rdtype) if isinstance(covers, string_types): covers = dns.rdatatype.from_text(covers) for (name, node) in self.iteritems(): # pylint: disable=dict-iter-method for rds in node: if rdtype == dns.rdatatype.ANY or \ (rds.rdtype == rdtype and rds.covers == covers): for rdata in rds: yield (name, rds.ttl, rdata) def to_file(self, f, sorted=True, relativize=True, nl=None): """Write a zone to a file. @param f: file or string. If I{f} is a string, it is treated as the name of a file to open. @param sorted: if True, the file will be written with the names sorted in DNSSEC order from least to greatest. Otherwise the names will be written in whatever order they happen to have in the zone's dictionary. @param relativize: if True, domain names in the output will be relativized to the zone's origin (if possible). @type relativize: bool @param nl: The end of line string. If not specified, the output will use the platform's native end-of-line marker (i.e. LF on POSIX, CRLF on Windows, CR on Macintosh). @type nl: string or None """ if isinstance(f, string_types): f = open(f, 'wb') want_close = True else: want_close = False # must be in this way, f.encoding may contain None, or even attribute # may not be there file_enc = getattr(f, 'encoding', None) if file_enc is None: file_enc = 'utf-8' if nl is None: nl_b = os.linesep.encode(file_enc) # binary mode, '\n' is not enough nl = u'\n' elif isinstance(nl, string_types): nl_b = nl.encode(file_enc) else: nl_b = nl nl = nl.decode() try: if sorted: names = list(self.keys()) names.sort() else: names = self.iterkeys() # pylint: disable=dict-iter-method for n in names: l = self[n].to_text(n, origin=self.origin, relativize=relativize) if isinstance(l, text_type): l_b = l.encode(file_enc) else: l_b = l l = l.decode() try: f.write(l_b) f.write(nl_b) except TypeError: # textual mode f.write(l) f.write(nl) finally: if want_close: f.close() def to_text(self, sorted=True, relativize=True, nl=None): """Return a zone's text as though it were written to a file. @param sorted: if True, the file will be written with the names sorted in DNSSEC order from least to greatest. Otherwise the names will be written in whatever order they happen to have in the zone's dictionary. @param relativize: if True, domain names in the output will be relativized to the zone's origin (if possible). @type relativize: bool @param nl: The end of line string. If not specified, the output will use the platform's native end-of-line marker (i.e. LF on POSIX, CRLF on Windows, CR on Macintosh). @type nl: string or None """ temp_buffer = BytesIO() self.to_file(temp_buffer, sorted, relativize, nl) return_value = temp_buffer.getvalue() temp_buffer.close() return return_value def check_origin(self): """Do some simple checking of the zone's origin. @raises dns.zone.NoSOA: there is no SOA RR @raises dns.zone.NoNS: there is no NS RRset @raises KeyError: there is no origin node """ if self.relativize: name = dns.name.empty else: name = self.origin if self.get_rdataset(name, dns.rdatatype.SOA) is None: raise NoSOA if self.get_rdataset(name, dns.rdatatype.NS) is None: raise NoNS class _MasterReader(object): """Read a DNS master file @ivar tok: The tokenizer @type tok: dns.tokenizer.Tokenizer object @ivar last_ttl: The last seen explicit TTL for an RR @type last_ttl: int @ivar last_ttl_known: Has last TTL been detected @type last_ttl_known: bool @ivar default_ttl: The default TTL from a $TTL directive or SOA RR @type default_ttl: int @ivar default_ttl_known: Has default TTL been detected @type default_ttl_known: bool @ivar last_name: The last name read @type last_name: dns.name.Name object @ivar current_origin: The current origin @type current_origin: dns.name.Name object @ivar relativize: should names in the zone be relativized? @type relativize: bool @ivar zone: the zone @type zone: dns.zone.Zone object @ivar saved_state: saved reader state (used when processing $INCLUDE) @type saved_state: list of (tokenizer, current_origin, last_name, file, last_ttl, last_ttl_known, default_ttl, default_ttl_known) tuples. @ivar current_file: the file object of the $INCLUDed file being parsed (None if no $INCLUDE is active). @ivar allow_include: is $INCLUDE allowed? @type allow_include: bool @ivar check_origin: should sanity checks of the origin node be done? The default is True. @type check_origin: bool """ def __init__(self, tok, origin, rdclass, relativize, zone_factory=Zone, allow_include=False, check_origin=True): if isinstance(origin, string_types): origin = dns.name.from_text(origin) self.tok = tok self.current_origin = origin self.relativize = relativize self.last_ttl = 0 self.last_ttl_known = False self.default_ttl = 0 self.default_ttl_known = False self.last_name = self.current_origin self.zone = zone_factory(origin, rdclass, relativize=relativize) self.saved_state = [] self.current_file = None self.allow_include = allow_include self.check_origin = check_origin def _eat_line(self): while 1: token = self.tok.get() if token.is_eol_or_eof(): break def _rr_line(self): """Process one line from a DNS master file.""" # Name if self.current_origin is None: raise UnknownOrigin token = self.tok.get(want_leading=True) if not token.is_whitespace(): self.last_name = dns.name.from_text( token.value, self.current_origin) else: token = self.tok.get() if token.is_eol_or_eof(): # treat leading WS followed by EOL/EOF as if they were EOL/EOF. return self.tok.unget(token) name = self.last_name if not name.is_subdomain(self.zone.origin): self._eat_line() return if self.relativize: name = name.relativize(self.zone.origin) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError # TTL try: ttl = dns.ttl.from_text(token.value) self.last_ttl = ttl self.last_ttl_known = True token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except dns.ttl.BadTTL: if not (self.last_ttl_known or self.default_ttl_known): raise dns.exception.SyntaxError("Missing default TTL value") if self.default_ttl_known: ttl = self.default_ttl else: ttl = self.last_ttl # Class try: rdclass = dns.rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except dns.exception.SyntaxError: raise dns.exception.SyntaxError except Exception: rdclass = self.zone.rdclass if rdclass != self.zone.rdclass: raise dns.exception.SyntaxError("RR class is not zone's class") # Type try: rdtype = dns.rdatatype.from_text(token.value) except: raise dns.exception.SyntaxError( "unknown rdatatype '%s'" % token.value) n = self.zone.nodes.get(name) if n is None: n = self.zone.node_factory() self.zone.nodes[name] = n try: rd = dns.rdata.from_text(rdclass, rdtype, self.tok, self.current_origin, False) except dns.exception.SyntaxError: # Catch and reraise. (ty, va) = sys.exc_info()[:2] raise va except: # All exceptions that occur in the processing of rdata # are treated as syntax errors. This is not strictly # correct, but it is correct almost all of the time. # We convert them to syntax errors so that we can emit # helpful filename:line info. (ty, va) = sys.exc_info()[:2] raise dns.exception.SyntaxError( "caught exception {}: {}".format(str(ty), str(va))) if not self.default_ttl_known and isinstance(rd, dns.rdtypes.ANY.SOA.SOA): # The pre-RFC2308 and pre-BIND9 behavior inherits the zone default # TTL from the SOA minttl if no $TTL statement is present before the # SOA is parsed. self.default_ttl = rd.minimum self.default_ttl_known = True rd.choose_relativity(self.zone.origin, self.relativize) covers = rd.covers() rds = n.find_rdataset(rdclass, rdtype, covers, True) rds.add(rd, ttl) def _parse_modify(self, side): # Here we catch everything in '{' '}' in a group so we can replace it # with ''. is_generate1 = re.compile("^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$") is_generate2 = re.compile("^.*\$({(\+|-?)(\d+)}).*$") is_generate3 = re.compile("^.*\$({(\+|-?)(\d+),(\d+)}).*$") # Sometimes there are modifiers in the hostname. These come after # the dollar sign. They are in the form: ${offset[,width[,base]]}. # Make names g1 = is_generate1.match(side) if g1: mod, sign, offset, width, base = g1.groups() if sign == '': sign = '+' g2 = is_generate2.match(side) if g2: mod, sign, offset = g2.groups() if sign == '': sign = '+' width = 0 base = 'd' g3 = is_generate3.match(side) if g3: mod, sign, offset, width = g1.groups() if sign == '': sign = '+' width = g1.groups()[2] base = 'd' if not (g1 or g2 or g3): mod = '' sign = '+' offset = 0 width = 0 base = 'd' if base != 'd': raise NotImplementedError() return mod, sign, offset, width, base def _generate_line(self): # range lhs [ttl] [class] type rhs [ comment ] """Process one line containing the GENERATE statement from a DNS master file.""" if self.current_origin is None: raise UnknownOrigin token = self.tok.get() # Range (required) try: start, stop, step = dns.grange.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except: raise dns.exception.SyntaxError # lhs (required) try: lhs = token.value token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except: raise dns.exception.SyntaxError # TTL try: ttl = dns.ttl.from_text(token.value) self.last_ttl = ttl self.last_ttl_known = True token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except dns.ttl.BadTTL: if not (self.last_ttl_known or self.default_ttl_known): raise dns.exception.SyntaxError("Missing default TTL value") if self.default_ttl_known: ttl = self.default_ttl else: ttl = self.last_ttl # Class try: rdclass = dns.rdataclass.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except dns.exception.SyntaxError: raise dns.exception.SyntaxError except Exception: rdclass = self.zone.rdclass if rdclass != self.zone.rdclass: raise dns.exception.SyntaxError("RR class is not zone's class") # Type try: rdtype = dns.rdatatype.from_text(token.value) token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError except Exception: raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value) # lhs (required) try: rhs = token.value except: raise dns.exception.SyntaxError lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) for i in range(start, stop + 1, step): # +1 because bind is inclusive and python is exclusive if lsign == u'+': lindex = i + int(loffset) elif lsign == u'-': lindex = i - int(loffset) if rsign == u'-': rindex = i - int(roffset) elif rsign == u'+': rindex = i + int(roffset) lzfindex = str(lindex).zfill(int(lwidth)) rzfindex = str(rindex).zfill(int(rwidth)) name = lhs.replace(u'$%s' % (lmod), lzfindex) rdata = rhs.replace(u'$%s' % (rmod), rzfindex) self.last_name = dns.name.from_text(name, self.current_origin) name = self.last_name if not name.is_subdomain(self.zone.origin): self._eat_line() return if self.relativize: name = name.relativize(self.zone.origin) n = self.zone.nodes.get(name) if n is None: n = self.zone.node_factory() self.zone.nodes[name] = n try: rd = dns.rdata.from_text(rdclass, rdtype, rdata, self.current_origin, False) except dns.exception.SyntaxError: # Catch and reraise. (ty, va) = sys.exc_info()[:2] raise va except: # All exceptions that occur in the processing of rdata # are treated as syntax errors. This is not strictly # correct, but it is correct almost all of the time. # We convert them to syntax errors so that we can emit # helpful filename:line info. (ty, va) = sys.exc_info()[:2] raise dns.exception.SyntaxError("caught exception %s: %s" % (str(ty), str(va))) rd.choose_relativity(self.zone.origin, self.relativize) covers = rd.covers() rds = n.find_rdataset(rdclass, rdtype, covers, True) rds.add(rd, ttl) def read(self): """Read a DNS master file and build a zone object. @raises dns.zone.NoSOA: No SOA RR was found at the zone origin @raises dns.zone.NoNS: No NS RRset was found at the zone origin """ try: while 1: token = self.tok.get(True, True) if token.is_eof(): if self.current_file is not None: self.current_file.close() if len(self.saved_state) > 0: (self.tok, self.current_origin, self.last_name, self.current_file, self.last_ttl, self.last_ttl_known, self.default_ttl, self.default_ttl_known) = self.saved_state.pop(-1) continue break elif token.is_eol(): continue elif token.is_comment(): self.tok.get_eol() continue elif token.value[0] == u'$': c = token.value.upper() if c == u'$TTL': token = self.tok.get() if not token.is_identifier(): raise dns.exception.SyntaxError("bad $TTL") self.default_ttl = dns.ttl.from_text(token.value) self.default_ttl_known = True self.tok.get_eol() elif c == u'$ORIGIN': self.current_origin = self.tok.get_name() self.tok.get_eol() if self.zone.origin is None: self.zone.origin = self.current_origin elif c == u'$INCLUDE' and self.allow_include: token = self.tok.get() filename = token.value token = self.tok.get() if token.is_identifier(): new_origin =\ dns.name.from_text(token.value, self.current_origin) self.tok.get_eol() elif not token.is_eol_or_eof(): raise dns.exception.SyntaxError( "bad origin in $INCLUDE") else: new_origin = self.current_origin self.saved_state.append((self.tok, self.current_origin, self.last_name, self.current_file, self.last_ttl, self.last_ttl_known, self.default_ttl, self.default_ttl_known)) self.current_file = open(filename, 'r') self.tok = dns.tokenizer.Tokenizer(self.current_file, filename) self.current_origin = new_origin elif c == u'$GENERATE': self._generate_line() else: raise dns.exception.SyntaxError( "Unknown master file directive '" + c + "'") continue self.tok.unget(token) self._rr_line() except dns.exception.SyntaxError as detail: (filename, line_number) = self.tok.where() if detail is None: detail = "syntax error" raise dns.exception.SyntaxError( "%s:%d: %s" % (filename, line_number, detail)) # Now that we're done reading, do some basic checking of the zone. if self.check_origin: self.zone.check_origin() def from_text(text, origin=None, rdclass=dns.rdataclass.IN, relativize=True, zone_factory=Zone, filename=None, allow_include=False, check_origin=True): """Build a zone object from a master file format string. @param text: the master file format input @type text: string. @param origin: The origin of the zone; if not specified, the first $ORIGIN statement in the master file will determine the origin of the zone. @type origin: dns.name.Name object or string @param rdclass: The zone's rdata class; the default is class IN. @type rdclass: int @param relativize: should names be relativized? The default is True @type relativize: bool @param zone_factory: The zone factory to use @type zone_factory: function returning a Zone @param filename: The filename to emit when describing where an error occurred; the default is ''. @type filename: string @param allow_include: is $INCLUDE allowed? @type allow_include: bool @param check_origin: should sanity checks of the origin node be done? The default is True. @type check_origin: bool @raises dns.zone.NoSOA: No SOA RR was found at the zone origin @raises dns.zone.NoNS: No NS RRset was found at the zone origin @rtype: dns.zone.Zone object """ # 'text' can also be a file, but we don't publish that fact # since it's an implementation detail. The official file # interface is from_file(). if filename is None: filename = '' tok = dns.tokenizer.Tokenizer(text, filename) reader = _MasterReader(tok, origin, rdclass, relativize, zone_factory, allow_include=allow_include, check_origin=check_origin) reader.read() return reader.zone def from_file(f, origin=None, rdclass=dns.rdataclass.IN, relativize=True, zone_factory=Zone, filename=None, allow_include=True, check_origin=True): """Read a master file and build a zone object. @param f: file or string. If I{f} is a string, it is treated as the name of a file to open. @param origin: The origin of the zone; if not specified, the first $ORIGIN statement in the master file will determine the origin of the zone. @type origin: dns.name.Name object or string @param rdclass: The zone's rdata class; the default is class IN. @type rdclass: int @param relativize: should names be relativized? The default is True @type relativize: bool @param zone_factory: The zone factory to use @type zone_factory: function returning a Zone @param filename: The filename to emit when describing where an error occurred; the default is '', or the value of I{f} if I{f} is a string. @type filename: string @param allow_include: is $INCLUDE allowed? @type allow_include: bool @param check_origin: should sanity checks of the origin node be done? The default is True. @type check_origin: bool @raises dns.zone.NoSOA: No SOA RR was found at the zone origin @raises dns.zone.NoNS: No NS RRset was found at the zone origin @rtype: dns.zone.Zone object """ str_type = string_types if PY3: opts = 'r' else: opts = 'rU' if isinstance(f, str_type): if filename is None: filename = f f = open(f, opts) want_close = True else: if filename is None: filename = '' want_close = False try: z = from_text(f, origin, rdclass, relativize, zone_factory, filename, allow_include, check_origin) finally: if want_close: f.close() return z def from_xfr(xfr, zone_factory=Zone, relativize=True, check_origin=True): """Convert the output of a zone transfer generator into a zone object. @param xfr: The xfr generator @type xfr: generator of dns.message.Message objects @param relativize: should names be relativized? The default is True. It is essential that the relativize setting matches the one specified to dns.query.xfr(). @type relativize: bool @param check_origin: should sanity checks of the origin node be done? The default is True. @type check_origin: bool @raises dns.zone.NoSOA: No SOA RR was found at the zone origin @raises dns.zone.NoNS: No NS RRset was found at the zone origin @rtype: dns.zone.Zone object """ z = None for r in xfr: if z is None: if relativize: origin = r.origin else: origin = r.answer[0].name rdclass = r.answer[0].rdclass z = zone_factory(origin, rdclass, relativize=relativize) for rrset in r.answer: znode = z.nodes.get(rrset.name) if not znode: znode = z.node_factory() z.nodes[rrset.name] = znode zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype, rrset.covers, True) zrds.update_ttl(rrset.ttl) for rd in rrset: rd.choose_relativity(z.origin, relativize) zrds.add(rd) if check_origin: z.check_origin() return z dnspython-1.16.0/dns/zone.pyi000066400000000000000000000056221340301174400161110ustar00rootroot00000000000000from typing import Generator, Optional, Union, Tuple, Iterable, Callable, Any, Iterator, TextIO, BinaryIO, Dict from . import rdata, zone, rdataclass, name, rdataclass, message, rdatatype, exception, node, rdataset, rrset, rdatatype class BadZone(exception.DNSException): ... class NoSOA(BadZone): ... class NoNS(BadZone): ... class UnknownOrigin(BadZone): ... class Zone: def __getitem__(self, key : str) -> node.Node: ... def __init__(self, origin : Union[str,name.Name], rdclass : int = rdataclass.IN, relativize : bool = True) -> None: self.nodes : Dict[str,node.Node] self.origin = origin def values(self): return self.nodes.values() def iterate_rdatas(self, rdtype : Union[int,str] = rdatatype.ANY, covers : Union[int,str] = None) -> Iterable[Tuple[name.Name, int, rdata.Rdata]]: ... def __iter__(self) -> Iterator[str]: ... def get_node(self, name : Union[name.Name,str], create=False) -> Optional[node.Node]: ... def find_rrset(self, name : Union[str,name.Name], rdtype : Union[int,str], covers=rdatatype.NONE) -> rrset.RRset: ... def find_rdataset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE, create=False) -> rdataset.Rdataset: ... def get_rdataset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE, create=False) -> Optional[rdataset.Rdataset]: ... def get_rrset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE) -> Optional[rrset.RRset]: ... def replace_rdataset(self, name : Union[str,name.Name], replacement : rdataset.Rdataset) -> None: ... def delete_rdataset(self, name : Union[str,name.Name], rdtype : Union[str,int], covers=rdatatype.NONE) -> None: ... def iterate_rdatasets(self, rdtype : Union[str,int] =rdatatype.ANY, covers : Union[str,int] =rdatatype.NONE): ... def to_file(self, f : Union[TextIO, BinaryIO, str], sorted=True, relativize=True, nl : Optional[bytes] = None): ... def to_text(self, sorted=True, relativize=True, nl : Optional[bytes] = None) -> bytes: ... def from_xfr(xfr : Generator[Any,Any,message.Message], zone_factory : Callable[..., zone.Zone] = zone.Zone, relativize=True, check_origin=True): ... def from_text(text : str, origin : Optional[Union[str,name.Name]] = None, rdclass : int = rdataclass.IN, relativize=True, zone_factory : Callable[...,zone.Zone] = zone.Zone, filename : Optional[str] = None, allow_include=False, check_origin=True) -> zone.Zone: ... def from_file(f, origin : Optional[Union[str,name.Name]] = None, rdclass=rdataclass.IN, relativize=True, zone_factory : Callable[..., zone.Zone] = Zone, filename : Optional[str] = None, allow_include=True, check_origin=True) -> zone.Zone: ... dnspython-1.16.0/doc/000077500000000000000000000000001340301174400143675ustar00rootroot00000000000000dnspython-1.16.0/doc/.gitignore000066400000000000000000000000071340301174400163540ustar00rootroot00000000000000_build dnspython-1.16.0/doc/Makefile000066400000000000000000000011361340301174400160300ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = dnspython SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)dnspython-1.16.0/doc/community.rst000066400000000000000000000007161340301174400171510ustar00rootroot00000000000000.. _community: Community ========= Bugs and Feature Requests ------------------------- Bugs and feature requests can be made using the github issues system at `github `_. Mailing Lists ------------- | `dnspython-announce `_ | `dnspython-users `_ | `dnspython-dev `_ dnspython-1.16.0/doc/conf.py000066400000000000000000000114661340301174400156760ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # dnspython documentation build configuration file, created by # sphinx-quickstart on Fri Dec 30 05:55:44 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.githubpages'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'dnspython' copyright = '2016, Nominum, Inc.' author = 'Nominum, Inc.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.16' # The full version, including alpha/beta/rc tags. release = '1.16.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for autodoc -------------------------------------------------- autoclass_content = 'both' # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'dnspythondoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'dnspython.tex', 'dnspython Documentation', 'Nominum, Inc.', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'dnspython', 'dnspython Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'dnspython', 'dnspython Documentation', author, 'dnspython', 'One line description of project.', 'Miscellaneous'), ] dnspython-1.16.0/doc/dnssec.rst000066400000000000000000000021611340301174400164000ustar00rootroot00000000000000.. module:: dns.dnssec .. _dnssec: DNSSEC ====== Dnspython can do simple DNSSEC signature validation, but currently has no facilities for signing. In order to use DNSSEC functions, you must have ``pycryptodome`` or ``pycryptodomex`` installed. If you want to do elliptic curves, you must also have ``ecdsa`` installed. DNSSEC Algorithms ----------------- .. autodata:: dns.dnssec.RSAMD5 .. autodata:: dns.dnssec.DH .. autodata:: dns.dnssec.DSA .. autodata:: dns.dnssec.ECC .. autodata:: dns.dnssec.RSASHA1 .. autodata:: dns.dnssec.DSANSEC3SHA1 .. autodata:: dns.dnssec.RSASHA1NSEC3SHA1 .. autodata:: dns.dnssec.RSASHA256 .. autodata:: dns.dnssec.RSASHA512 .. autodata:: dns.dnssec.ECDSAP256SHA256 .. autodata:: dns.dnssec.ECDSAP384SHA384 .. autodata:: dns.dnssec.INDIRECT .. autodata:: dns.dnssec.PRIVATEDNS .. autodata:: dns.dnssec.PRIVATEOID DNSSEC Functions ---------------- .. autofunction:: dns.dnssec.algorithm_from_text .. autofunction:: dns.dnssec.algorithm_to_text .. autofunction:: dns.dnssec.key_id .. autofunction:: dns.dnssec.make_ds .. autofunction:: dns.dnssec.validate .. autofunction:: dns.dnssec.validate_rrsig dnspython-1.16.0/doc/exceptions.rst000066400000000000000000000041161340301174400173040ustar00rootroot00000000000000.. _exceptions: Exceptions ========== Common Exceptions ----------------- .. automodule:: dns.exception :members: dns.dnssec Exceptions --------------------- .. autoexception:: dns.dnssec.UnsupportedAlgorithm .. autoexception:: dns.dnssec.ValidationFailure dns.message Exceptions ---------------------- .. autoexception:: dns.message.BadEDNS .. autoexception:: dns.message.BadTSIG .. autoexception:: dns.message.ShortHeader .. autoexception:: dns.message.TrailingJunk .. autoexception:: dns.message.UnknownHeaderField .. autoexception:: dns.message.UnknownTSIGKey dns.name Exceptions ------------------- .. autoexception:: dns.name.AbsoluteConcatenation .. autoexception:: dns.name.BadEscape .. autoexception:: dns.name.BadLabelType .. autoexception:: dns.name.BadPointer .. autoexception:: dns.name.EmptyLabel .. autoexception:: dns.name.IDNAException .. autoexception:: dns.name.LabelTooLong .. autoexception:: dns.name.NameTooLong .. autoexception:: dns.name.NeedAbsoluteNameOrOrigin .. autoexception:: dns.name.NoIDNA2008 .. autoexception:: dns.name.NoParent dns.opcode Exceptions --------------------- .. autoexception:: dns.opcode.UnknownOpcode dns.query Exceptions -------------------- .. autoexception:: dns.query.BadResponse .. autoexception:: dns.query.UnexpectedSource .. autoexception:: dns.query.TransferError dns.rcode Exceptions -------------------- .. autoexception:: dns.rcode.UnknownRcode dns.rdataset Exceptions ----------------------- .. autoexception:: dns.rdataset.DifferingCovers .. autoexception:: dns.rdataset.IncompatibleTypes dns.resolver Exceptions ----------------------- .. autoexception:: dns.resolver.NoAnswer .. autoexception:: dns.resolver.NoMetaqueries .. autoexception:: dns.resolver.NoNameservers .. autoexception:: dns.resolver.NoRootSOA .. autoexception:: dns.resolver.NotAbsolute .. autoexception:: dns.resolver.NXDOMAIN .. autoexception:: dns.resolver.YXDOMAIN dns.tokenizer Exceptions ------------------------ .. autoexception:: dns.tokenizer.UngetBufferFull dns.ttl Exceptions ------------------ .. autoexception:: dns.ttl.BadTTL dnspython-1.16.0/doc/index.rst000066400000000000000000000016661340301174400162410ustar00rootroot00000000000000.. dnspython documentation master file dnspython ========= Dnspython is a DNS toolkit for Python. It can be used for queries, zone transfers, dynamic updates, nameserver testing, and many other things. Dnspython provides both high and low level access to the DNS. The high level classes perform queries for data of a given name, type, and class, and return an answer set. The low level classes allow direct manipulation of DNS zones, messages, names, and records. Almost all RR types are supported. dnspython originated at Nominum where it was developed for testing DNS nameservers. Nominum has generously allowed it to be open sourced under a BSD-style license, and helps support its future development by continuing to employ the author. .. toctree:: :maxdepth: 1 :caption: Contents: whatsnew community installation manual rfc Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` dnspython-1.16.0/doc/installation.rst000066400000000000000000000025171340301174400176270ustar00rootroot00000000000000.. _installation: Installation ============ Requirements ------------ Python 2.7, 3.4 or later. Installation ------------ Many free operating system distributions have dnspython packaged for you, so you should check there first. The next easiest option is to use ``pip``:: pip install dnspython If ``pip`` is not available, you can download the latest zip file from `PyPI `_, unzip it. On a UNIX-like system, you then run:: sudo python setup.py install while on a Windows system you would run:: python setup.py install Finally, you have the option of cloning the dnspython source from github and building it:: git clone https://github.com/rthalley/dnspython.git And then run ``setup.py`` as above. Please be aware that the master branch of dnspython on github is under active development and may not always be stable. Optional Modules ---------------- The following modules are optional, but recommended for full functionality. If ``pycryptodome`` / ``pycryptodomex`` is installed, then dnspython will be able to do low-level DNSSEC RSA and DSA signature validation. If ``ecdsa`` is installed, then Elliptic Curve signature algorithms will be available for low-level DNSSEC signature validation. If ``idna`` is installed, then IDNA 2008 will be available. dnspython-1.16.0/doc/manual.rst000066400000000000000000000002701340301174400163750ustar00rootroot00000000000000Dnspython Manual ================ .. toctree:: :maxdepth: 2 :caption: Contents: py2vs3 name rdata message dnssec query resolver exceptions utilities dnspython-1.16.0/doc/message-class.rst000066400000000000000000000103251340301174400176510ustar00rootroot00000000000000.. _message-class: The dns.message.Message Class ----------------------------- .. autoclass:: dns.message.Message :members: .. attribute:: id An ``int``, the query id; the default is a randomly chosen id. .. attribute:: flags An ``int``, the DNS flags of the message. .. attribute:: question The question section, a list of ``dns.rrset.RRset`` objects. .. attribute:: answer The answer section, a list of ``dns.rrset.RRset`` objects. .. attribute:: authority The authority section, a list of ``dns.rrset.RRset`` objects. .. attribute:: additional The additional section, a list of ``dns.rrset.RRset`` objects. .. attribute:: edns An ``int``, the EDNS level to use. The default is -1, no EDNS. .. attribute:: ednsflags An ``int``, the EDNS flags. .. attribute:: payload An ``int``, the EDNS payload size. The default is 0. .. attribute:: options The EDNS options, a list of ``dns.edns.Option`` objects. The default is the empty list. .. attribute:: request_payload The associated request's EDNS payload size. This field is meaningful in response messages, and if set to a non-zero value, will limit the size of the response to the specified size. The default is 0, which means "use the default limit" which is currently 65535. .. attribute:: keyring The TSIG keyring to use. The default is `None`. A TSIG keyring is a dictionary mapping from TSIG key name, a ``dns.name.Name``, to a TSIG secret, a ``binary``. .. attribute:: keyname The TSIG keyname to use, a ``dns.name.Name``. The default is ``None``. .. attribute:: keyalgorithm A ``dns.name.Name``, the TSIG algorithm to use. Defaults to ``dns.tsig.default_algorithm``. Constants for TSIG algorithms are defined the in ``dns.tsig`` module. .. attribute:: request_mac A ``binary``, the TSIG MAC of the request message associated with this message; used when validating TSIG signatures. .. attribute:: fudge An ``int``, the TSIG time fudge. The default is 300 seconds. .. attribute:: original_id An ``int``, the TSIG original id; defaults to the message's id. .. attribute:: tsig_error An ``int``, the TSIG error code. The default is 0. .. attribute:: other_data A ``binary``, the TSIG "other data". The default is the empty ``binary``. .. attribute:: mac A ``binary``, the TSIG MAC for this message. .. attribute:: xfr A ``bool``. This attribute is true when the message being used for the results of a DNS zone transfer. The default is ``False``. .. attribute:: origin A ``dns.name.Name``. The origin of the zone in messages which are used for zone transfers or for DNS dynamic updates. The default is ``None``. .. attribute:: tsig_ctx An ``hmac.HMAC``, the TSIG signature context associated with this message. The default is ``None``. .. attribute:: had_tsig A ``bool``, which is ``True`` if the message had a TSIG signature when it was decoded from wire format. .. attribute:: multi A ``bool``, which is ``True`` if this message is part of a multi-message sequence. The default is ``False``. This attribute is used when validating TSIG signatures on messages which are part of a zone transfer. .. attribute:: first A ``bool``, which is ``True`` if this message is stand-alone, or the first of a multi-message sequence. The default is ``True``. This variable is used when validating TSIG signatures on messages which are part of a zone transfer. .. attribute:: index A ``dict``, an index of RRsets in the message. The index key is ``(section, name, rdclass, rdtype, covers, deleting)``. The default is ``{}``. Indexing improves the performance of finding RRsets. Indexing can be disabled by setting the index to ``None``. The following constants may be used to specify sections in the ``find_rrset()`` and ``get_rrset()`` methods: .. autodata:: dns.message.QUESTION .. autodata:: dns.message.ANSWER .. autodata:: dns.message.AUTHORITY .. autodata:: dns.message.ADDITIONAL dnspython-1.16.0/doc/message-edns.rst000066400000000000000000000016221340301174400174750ustar00rootroot00000000000000.. _message-edns: Message EDNS Options -------------------- EDNS allows for larger messages and also provides an extension mechanism for the protocol. EDNS *options* are typed data, and are treated much like Rdata. For example, if dnsython encouters the EDNS ``ECS`` option code when parsing a DNS wire format message, it will create a ``dns.edns.ECSOption`` object to represent it. .. autodata:: dns.edns.NSID .. autodata:: dns.edns.DAU .. autodata:: dns.edns.DHU .. autodata:: dns.edns.N3U .. autodata:: dns.edns.ECS .. autodata:: dns.edns.EXPIRE .. autodata:: dns.edns.COOKIE .. autodata:: dns.edns.KEEPALIVE .. autodata:: dns.edns.PADDING .. autodata:: dns.edns.CHAIN .. autoclass:: dns.edns.Option :members: .. autoclass:: dns.edns.GenericOption :members: .. autoclass:: dns.edns.ECSOption :members: .. autofunction:: dns.edns.get_option_class .. autofunction:: dns.edns.option_from_wire dnspython-1.16.0/doc/message-flags.rst000066400000000000000000000013511340301174400176370ustar00rootroot00000000000000.. _message-flags: Message Flags ============= DNS message flags are used for signalling of various kinds in the DNS protocol. For example, the ``QR`` flag indicates that a message is a response to a prior query. Messages flags are encoded in two locations: the DNS header and the EDNS flags field. Header Flags ------------ .. autodata:: dns.flags.QR .. autodata:: dns.flags.AA .. autodata:: dns.flags.TC .. autodata:: dns.flags.RD .. autodata:: dns.flags.RA .. autodata:: dns.flags.AD .. autodata:: dns.flags.CD .. autofunction:: dns.flags.from_text .. autofunction:: dns.flags.to_text EDNS Flags ---------- .. autodata:: dns.flags.DO .. autofunction:: dns.flags.edns_from_text .. autofunction:: dns.flags.edns_to_text dnspython-1.16.0/doc/message-make.rst000066400000000000000000000004111340301174400174540ustar00rootroot00000000000000.. _message-make: Making DNS Messages ------------------- .. autofunction:: dns.message.from_file .. autofunction:: dns.message.from_text .. autofunction:: dns.message.from_wire .. autofunction:: dns.message.make_query .. autofunction:: dns.message.make_response dnspython-1.16.0/doc/message-opcode.rst000066400000000000000000000010541340301174400200140ustar00rootroot00000000000000.. _message-opcode: Message Opcodes --------------- DNS Opcodes describe what kind of operation a DNS message is requesting or replying to. Opcodes are embedded in the flags field in the DNS header. .. autodata:: dns.opcode.QUERY .. autodata:: dns.opcode.IQUERY .. autodata:: dns.opcode.STATUS .. autodata:: dns.opcode.NOTIFY .. autodata:: dns.opcode.UPDATE .. autofunction:: dns.opcode.from_text .. autofunction:: dns.opcode.to_text .. autofunction:: dns.opcode.from_flags .. autofunction:: dns.opcode.to_flags .. autofunction:: dns.opcode.is_update dnspython-1.16.0/doc/message-rcode.rst000066400000000000000000000014601340301174400176400ustar00rootroot00000000000000.. _message-rcode: Message Rcodes -------------- A DNS Rcode describes the result of a DNS request. If EDNS is not in use, then the rcode is encoded solely in the DNS header. If EDNS is in use, then the rcode is encoded using bits form both the header and the EDNS OPT RR. .. autodata:: dns.rcode.NOERROR .. autodata:: dns.rcode.FORMERR .. autodata:: dns.rcode.SERVFAIL .. autodata:: dns.rcode.NXDOMAIN .. autodata:: dns.rcode.NOTIMP .. autodata:: dns.rcode.REFUSED .. autodata:: dns.rcode.YXDOMAIN .. autodata:: dns.rcode.YXRRSET .. autodata:: dns.rcode.NXRRSET .. autodata:: dns.rcode.NOTAUTH .. autodata:: dns.rcode.NOTZONE .. autodata:: dns.rcode.BADVERS .. autofunction:: dns.rcode.from_text .. autofunction:: dns.rcode.to_text .. autofunction:: dns.rcode.from_flags .. autofunction:: dns.rcode.to_flags dnspython-1.16.0/doc/message-update.rst000066400000000000000000000004141340301174400200240ustar00rootroot00000000000000.. _message-update: The dns.update.Update Class --------------------------- DNS Dynamic Update message have a complex encoding, so dnspython provides a subclass of ``dns.message.Message`` specialized for creating them. .. autoclass:: dns.update.Update :members: dnspython-1.16.0/doc/message.rst000066400000000000000000000010621340301174400165440ustar00rootroot00000000000000.. module:: dns.message .. _message: DNS Messages ============ Objects of the dns.message.Message class represent a single DNS message, as defined by `RFC 1035 `_ and its many updates and extensions. The module provides tools for constructing and manipulating messages. TSIG signatures and EDNS are also supported. Messages can be dumped to a textual form, and also read from that form. .. toctree:: message-class message-make message-flags message-opcode message-rcode message-edns message-update dnspython-1.16.0/doc/name-class.rst000066400000000000000000000010111340301174400171350ustar00rootroot00000000000000.. _name-class: The dns.name.Name Class and Predefined Names -------------------------------------------- .. autoclass:: dns.name.Name :members: .. attribute:: labels A tuple of ``binary`` in DNS wire format specifying the DNS labels in the name, in order from least-signficiant label (i.e. farthest from the origin) to most-significant label. .. data:: dns.name.root The root name, i.e. ``dns.name.Name([b''])``. .. data:: dns.name.empty The empty name, i.e. ``dns.name.Name([])``. dnspython-1.16.0/doc/name-codecs.rst000066400000000000000000000043221340301174400173000ustar00rootroot00000000000000.. _name-codecs: International Domain Name CODECs -------------------------------- Representing non-ASCII text in the DNS is a complex and evolving topic. Generally speaking, Unicode is converted into an ASCII-only, case-insensitive form called "Punycode" by complex rules. There are two standard specifications for this process, "IDNA 2003", which is widely used, and the revised and not fully compatible standard "IDNA 2008". There are also varying degrees of strictness that can be applied in encoding and decoding. Explaining the standards in detail is out of scope for this document; Unicode Technical Standard #46 http://unicode.org/reports/tr46/ is a good place to start learning more. Dnspython provides "codecs" to implement International Domain Name policy according to the user's desire. .. autoclass:: dns.name.IDNACodec :members: .. autoclass:: dns.name.IDNA2003Codec :members: .. autoclass:: dns.name.IDNA2008Codec :members: .. data:: dns.name.IDNA_2003_Practical The "practical" codec encodes using IDNA 2003 rules and decodes punycode without checking for strict IDNA 2003 compliance. .. data:: dns.name.IDNA_2003_Strict The "strict" codec encodes using IDNA 2003 rules and decodes punycode checking for IDNA 2003 compliance. .. data:: dns.name.IDNA_2003 A synonym for ``dns.name.IDNA_2003_Practical``. .. data:: dns.name.IDNA_2008_Practical The "practical" codec encodes using IDNA 2008 rules with UTS 46 compatibility processing, and allowing pure ASCII labels. It decodes punycode without checking for strict IDNA 2008 compliance. .. data:: dns.name.IDNA_2008_Strict The "strict" codec encodes using IDNA 2008 rules and decodes punycode checking for IDNA 2008 compliance. .. data:: dns.name.IDNA_2008_UTS_46 The "UTS 46" codec encodes using IDNA 2008 rules with UTS 46 compatibility processing and decodes punycode without checking for IDNA 2008 compliance. .. data:: dns.name.IDNA_2008_Transitional The "UTS 46" codec encodes using IDNA 2008 rules with UTS 46 compatibility processing in the "transitional mode" and decodes punycode without checking for IDNA 2008 compliance. .. data:: dns.name.IDNA_2008 A synonym for ``dns.name.IDNA_2008_Practical``. dnspython-1.16.0/doc/name-dict.rst000066400000000000000000000001431340301174400167600ustar00rootroot00000000000000.. _name-dict: Name Dictionary =============== .. autoclass:: dns.namedict.NameDict :members: dnspython-1.16.0/doc/name-helpers.rst000066400000000000000000000023621340301174400175040ustar00rootroot00000000000000.. _name-helpers: Name Helpers ------------ Sometimes you want to look up an address in the DNS instead of a name. Dnspython provides a helper functions for converting between addresses and their "reverse map" form in the DNS. For example: ========= ========================================================================= Address DNS Reverse Name ========= ========================================================================= 127.0.0.1 1.0.0.127.in-addr.arpa. ::1 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. ========= ========================================================================= | .. autofunction:: dns.reversename.from_address .. autofunction:: dns.reversename.to_address Dnspython also provides helpers for converting E.164 numbers (i.e. telephone numbers) into the names used for them in the DNS. For example: ================ ================================== Number DNS E.164 Name ================ ================================== +1.650.555.1212 2.1.2.1.5.5.5.0.5.6.1.e164.arpa. +44 20 7946 0123 3.2.1.0.6.4.9.7.0.2.4.4.e164.arpa. ================ ================================== | .. autofunction:: dns.e164.from_e164 .. autofunction:: dns.e164.to_e164 dnspython-1.16.0/doc/name-make.rst000066400000000000000000000002451340301174400167550ustar00rootroot00000000000000.. _name-make: Making DNS Names ---------------- .. autofunction:: dns.name.from_text .. autofunction:: dns.name.from_unicode .. autofunction:: dns.name.from_wire dnspython-1.16.0/doc/name.rst000066400000000000000000000033261340301174400160450ustar00rootroot00000000000000.. module:: dns.name .. _name: DNS Names ========= Objects of the dns.name.Name class represent an immutable domain name. The representation is a tuple of labels, with each lable being a ``binary`` object in the DNS wire format. Typically names are not created by supplying the labels tuple directly, but rather by converting from DNS text format or the DNS wire format. Labels are in the same order as in the DNS textual form, e.g. the labels value for ``www.dnspython.org.`` is ``(b'www', b'dnspython', b'org', b'')`` on Python 3, and ``('www', 'dnspython', 'org', '')`` on Python 2. Names may be *absolute* or *relative*. Absolute names end in the root label, which is an empty ``binary``. Relative names do not end in the root label. To convert a relative name to an absolute name requires specifying an *origin*. Typically the origin is known by context. Dnspython provides tools to relativize and derelativize names. It's a good idea not to mix relative and absolute names, other than in the context of a zone. Names encoded in the DNS wire protocol are always absolute. Dnspython's functions to make names from text also default to an origin of the root name, and thus to make a relative name using them you must specify an origin of None or ``dns.name.empty``. Names are compared and ordered according to the rules of the DNS. The order is the DNSSEC canonical ordering. Relative names always sort before absolute names. Names may also be compared according to the DNS tree hierarchy with the ``fullcompare()`` method. For example ```www.dnspython.org.`` is a subdomain of ``dnspython.org.``. See the method description for full details. .. toctree:: name-class name-make name-dict name-helpers name-codecs dnspython-1.16.0/doc/py2vs3.rst000066400000000000000000000014041340301174400162660ustar00rootroot00000000000000Python 2 vs. Python 3 --------------------- Dnspython was originally written in Python 2, and for some years had a separate Python 3 branch. Thanks to some excellent work by contributors to the project, there is now a single source tree that works for both. The most significant user-visible differences between the two are in the representations of binary data and textual data. For Python 3, binary data is stored using the `bytes` type, and textual data is stored using the `str` type. For Python 2, binary data is stored using the `str` type, and textual data can use the `str` or `unicode` types. Because there is a single source tree, the documentation will refer to `binary` and `text` when describing the types of binary data or textual data, respectively. dnspython-1.16.0/doc/query.rst000066400000000000000000000015131340301174400162660ustar00rootroot00000000000000.. module:: dns.query .. _query: DNS Query Support ================= The ``dns.query`` module is for sending messages to DNS servers, and processing their responses. If you want "stub resolver" behavior, then you should use the higher level ``dns.resolver`` module; see :ref:`resolver`. For UDP and TCP, the module provides a single "do everything" query function, and also provides the send and receive halves of this function individually for situations where more sophisticated I/O handling is in being used by the application. UDP --- .. autofunction:: dns.query.udp .. autofunction:: dns.query.send_udp .. autofunction:: dns.query.receive_udp TCP --- .. autofunction:: dns.query.tcp .. autofunction:: dns.query.send_tcp .. autofunction:: dns.query.receive_tcp Zone Transfers -------------- .. autofunction:: dns.query.xfr dnspython-1.16.0/doc/rdata-class.rst000066400000000000000000000010111340301174400173100ustar00rootroot00000000000000.. _rdata-class: DNS Rdata Base Class ==================== All Rdata objects are instances of some subclass of ``dns.rdata.Rdata``. The Rdata factory functions described in :ref:`rdata-make` will create objects which are instances of the most appropriate subclass. For example, a AAAA record will be an instance of the ``dns.rdtypes.IN.AAAA`` class, but a record of TYPE12345, which we don't know anything specific about, will be an instance of ``dns.rdata.GenericRdata``. .. autoclass:: dns.rdata.Rdata :members: dnspython-1.16.0/doc/rdata-make.rst000066400000000000000000000002001340301174400171170ustar00rootroot00000000000000.. _rdata-make: Making DNS Rdata ---------------- .. autofunction:: dns.rdata.from_text .. autofunction:: dns.rdata.from_wire dnspython-1.16.0/doc/rdata-set-classes.rst000066400000000000000000000014501340301174400204400ustar00rootroot00000000000000.. _rdata-set-classes: Rdataset, RRset and Node Classes ================================ An ``Rdataset`` is a set of ``Rdata`` objects which all have the same rdatatype, rdataclass, and covered type. ``Rdatasets`` also have a ``ttl`` (DNS time-to-live) field. Rdatasets support the normal Python set API, but are also ordered. An ``RRset`` is a subclass of ``Rdataset`` that also has an owner name, i.e. a ``dns.name.Name`` that says where in the DNS tree this set is located. A ``Node`` is a set of ``Rdataset`` objects, the Rdatasets being interpreted as at the same place (i.e. same owner name) int the DNS tree. Nodes are primarily used in ``Zone`` objects. .. autoclass:: dns.rdataset.Rdataset :members: .. autoclass:: dns.rrset.RRset :members: .. autoclass:: dns.node.Node :members: dnspython-1.16.0/doc/rdata-set-make.rst000066400000000000000000000006461340301174400177260ustar00rootroot00000000000000.. _rdata-make: Making DNS Rdatasets and RRsets =============================== .. autofunction:: dns.rdataset.from_text .. autofunction:: dns.rdataset.from_text_list .. autofunction:: dns.rdataset.from_rdata .. autofunction:: dns.rdataset.from_rdata_list .. autofunction:: dns.rrset.from_text .. autofunction:: dns.rrset.from_text_list .. autofunction:: dns.rrset.from_rdata .. autofunction:: dns.rrset.from_rdata_list dnspython-1.16.0/doc/rdata-subclasses.rst000066400000000000000000000060001340301174400203550ustar00rootroot00000000000000.. _rdata-subclasses: Rdata Subclass Reference ======================== XXXRTH This is just a placeholder for proper documentation of all the subclasses, which will basically just be documenting the attributes of the subclass, as well as any method specific to the subclass. .. autoclass:: dns.rdata.GenericRdata :members: .. automodule:: dns.rdtypes.dnskeybase :members: .. automodule:: dns.rdtypes.dsbase :members: .. automodule:: dns.rdtypes.euibase :members: .. automodule:: dns.rdtypes.mxbase :members: .. automodule:: dns.rdtypes.nsbase :members: .. automodule:: dns.rdtypes.txtbase :members: .. automodule:: dns.rdtypes.ANY.AFSDB :members: .. automodule:: dns.rdtypes.ANY.AVC :members: .. automodule:: dns.rdtypes.ANY.CAA :members: .. automodule:: dns.rdtypes.ANY.CDNSKEY :members: .. automodule:: dns.rdtypes.ANY.CDS :members: .. automodule:: dns.rdtypes.ANY.CERT :members: .. automodule:: dns.rdtypes.ANY.CNAME :members: .. automodule:: dns.rdtypes.ANY.CSYNC :members: .. automodule:: dns.rdtypes.ANY.DLV :members: .. automodule:: dns.rdtypes.ANY.DNAME :members: .. automodule:: dns.rdtypes.ANY.DNSKEY :members: .. automodule:: dns.rdtypes.ANY.DS :members: .. automodule:: dns.rdtypes.ANY.EUI48 :members: .. automodule:: dns.rdtypes.ANY.EUI64 :members: .. automodule:: dns.rdtypes.ANY.GPOS :members: .. automodule:: dns.rdtypes.ANY.HINFO :members: .. automodule:: dns.rdtypes.ANY.HIP :members: .. automodule:: dns.rdtypes.ANY.ISDN :members: .. automodule:: dns.rdtypes.ANY.LOC :members: .. automodule:: dns.rdtypes.ANY.MX :members: .. automodule:: dns.rdtypes.ANY.NS :members: .. automodule:: dns.rdtypes.ANY.NSEC :members: .. automodule:: dns.rdtypes.ANY.NSEC3 :members: .. automodule:: dns.rdtypes.ANY.NSEC3PARAM :members: .. automodule:: dns.rdtypes.ANY.OPENPGPKEY :members: .. automodule:: dns.rdtypes.ANY.PTR :members: .. automodule:: dns.rdtypes.ANY.RP :members: .. automodule:: dns.rdtypes.ANY.RRSIG :members: .. automodule:: dns.rdtypes.ANY.RT :members: .. automodule:: dns.rdtypes.ANY.SOA :members: .. automodule:: dns.rdtypes.ANY.SPF :members: .. automodule:: dns.rdtypes.ANY.SSHFP :members: .. automodule:: dns.rdtypes.ANY.TLSA :members: .. automodule:: dns.rdtypes.ANY.TXT :members: .. automodule:: dns.rdtypes.ANY.URI :members: .. automodule:: dns.rdtypes.ANY.X25 :members: .. automodule:: dns.rdtypes.IN.A :members: .. automodule:: dns.rdtypes.IN.AAAA :members: .. automodule:: dns.rdtypes.IN.APL :members: .. automodule:: dns.rdtypes.IN.DHCID :members: .. automodule:: dns.rdtypes.IN.IPSECKEY :members: .. automodule:: dns.rdtypes.IN.KX :members: .. automodule:: dns.rdtypes.IN.NAPTR :members: .. automodule:: dns.rdtypes.IN.NSAP :members: .. automodule:: dns.rdtypes.IN.NSAP_PTR :members: .. automodule:: dns.rdtypes.IN.PX :members: .. automodule:: dns.rdtypes.IN.SRV :members: .. automodule:: dns.rdtypes.IN.WKS :members: dnspython-1.16.0/doc/rdata-types.rst000066400000000000000000000013341340301174400173570ustar00rootroot00000000000000.. _rdata-types: Rdata classes and types ----------------------- Sets of typed data can be associated with a given name. A single typed datum is called an *rdata*. The type of an rdata is specified by its *rdataclass* and *rdatatype*. The class is almost always `IN`, the Internet class, and may often be omitted in the dnspython APIs. The ``dns.rdataclass`` module provides constants for each defined rdata class, as well as some helpful functions. The ``dns.rdatatype`` module does the same for rdata types. Examples of the constants are:: dns.rdataclass.IN dns.rdatatype.AAAA .. automodule:: dns.rdataclass :members: .. automodule:: dns.rdatatype :members: .. toctree:: rdataclass-list rdatatype-list dnspython-1.16.0/doc/rdata.rst000066400000000000000000000002261340301174400162140ustar00rootroot00000000000000.. _rdata: DNS Rdata ========= .. toctree:: rdata-types rdata-class rdata-make rdata-subclasses rdata-set-classes rdata-set-make dnspython-1.16.0/doc/rdataclass-list.rst000066400000000000000000000010011340301174400202030ustar00rootroot00000000000000Rdataclasses ============ .. py:data:: dns.rdataclass.ANY :annotation: = 255 .. py:data:: dns.rdataclass.CH :annotation: = 3 .. py:data:: dns.rdataclass.CHAOS :annotation: = 3 .. py:data:: dns.rdataclass.HESIOD :annotation: = 4 .. py:data:: dns.rdataclass.HS :annotation: = 4 .. py:data:: dns.rdataclass.IN :annotation: = 1 .. py:data:: dns.rdataclass.INTERNET :annotation: = 1 .. py:data:: dns.rdataclass.NONE :annotation: = 254 .. py:data:: dns.rdataclass.RESERVED0 :annotation: = 0 dnspython-1.16.0/doc/rdatatype-list.rst000066400000000000000000000071321340301174400200720ustar00rootroot00000000000000Rdatatypes ========== .. py:data:: dns.rdatatype.A :annotation: = 1 .. py:data:: dns.rdatatype.A6 :annotation: = 38 .. py:data:: dns.rdatatype.AAAA :annotation: = 28 .. py:data:: dns.rdatatype.AFSDB :annotation: = 18 .. py:data:: dns.rdatatype.ANY :annotation: = 255 .. py:data:: dns.rdatatype.APL :annotation: = 42 .. py:data:: dns.rdatatype.AVC :annotation: = 258 .. py:data:: dns.rdatatype.AXFR :annotation: = 252 .. py:data:: dns.rdatatype.CAA :annotation: = 257 .. py:data:: dns.rdatatype.CDNSKEY :annotation: = 60 .. py:data:: dns.rdatatype.CDS :annotation: = 59 .. py:data:: dns.rdatatype.CERT :annotation: = 37 .. py:data:: dns.rdatatype.CNAME :annotation: = 5 .. py:data:: dns.rdatatype.CSYNC :annotation: = 62 .. py:data:: dns.rdatatype.DHCID :annotation: = 49 .. py:data:: dns.rdatatype.DLV :annotation: = 32769 .. py:data:: dns.rdatatype.DNAME :annotation: = 39 .. py:data:: dns.rdatatype.DNSKEY :annotation: = 48 .. py:data:: dns.rdatatype.DS :annotation: = 43 .. py:data:: dns.rdatatype.EUI48 :annotation: = 108 .. py:data:: dns.rdatatype.EUI64 :annotation: = 109 .. py:data:: dns.rdatatype.GPOS :annotation: = 27 .. py:data:: dns.rdatatype.HINFO :annotation: = 13 .. py:data:: dns.rdatatype.HIP :annotation: = 55 .. py:data:: dns.rdatatype.IPSECKEY :annotation: = 45 .. py:data:: dns.rdatatype.ISDN :annotation: = 20 .. py:data:: dns.rdatatype.IXFR :annotation: = 251 .. py:data:: dns.rdatatype.KEY :annotation: = 25 .. py:data:: dns.rdatatype.KX :annotation: = 36 .. py:data:: dns.rdatatype.LOC :annotation: = 29 .. py:data:: dns.rdatatype.MAILA :annotation: = 254 .. py:data:: dns.rdatatype.MAILB :annotation: = 253 .. py:data:: dns.rdatatype.MB :annotation: = 7 .. py:data:: dns.rdatatype.MD :annotation: = 3 .. py:data:: dns.rdatatype.MF :annotation: = 4 .. py:data:: dns.rdatatype.MG :annotation: = 8 .. py:data:: dns.rdatatype.MINFO :annotation: = 14 .. py:data:: dns.rdatatype.MR :annotation: = 9 .. py:data:: dns.rdatatype.MX :annotation: = 15 .. py:data:: dns.rdatatype.NAPTR :annotation: = 35 .. py:data:: dns.rdatatype.NONE :annotation: = 0 .. py:data:: dns.rdatatype.NS :annotation: = 2 .. py:data:: dns.rdatatype.NSAP :annotation: = 22 .. py:data:: dns.rdatatype.NSAP-PTR :annotation: = 23 .. py:data:: dns.rdatatype.NSEC :annotation: = 47 .. py:data:: dns.rdatatype.NSEC3 :annotation: = 50 .. py:data:: dns.rdatatype.NSEC3PARAM :annotation: = 51 .. py:data:: dns.rdatatype.NULL :annotation: = 10 .. py:data:: dns.rdatatype.NXT :annotation: = 30 .. py:data:: dns.rdatatype.OPT :annotation: = 41 .. py:data:: dns.rdatatype.PTR :annotation: = 12 .. py:data:: dns.rdatatype.PX :annotation: = 26 .. py:data:: dns.rdatatype.RP :annotation: = 17 .. py:data:: dns.rdatatype.RRSIG :annotation: = 46 .. py:data:: dns.rdatatype.RT :annotation: = 21 .. py:data:: dns.rdatatype.SIG :annotation: = 24 .. py:data:: dns.rdatatype.SOA :annotation: = 6 .. py:data:: dns.rdatatype.SPF :annotation: = 99 .. py:data:: dns.rdatatype.SRV :annotation: = 33 .. py:data:: dns.rdatatype.SSHFP :annotation: = 44 .. py:data:: dns.rdatatype.TA :annotation: = 32768 .. py:data:: dns.rdatatype.TKEY :annotation: = 249 .. py:data:: dns.rdatatype.TLSA :annotation: = 52 .. py:data:: dns.rdatatype.TSIG :annotation: = 250 .. py:data:: dns.rdatatype.TXT :annotation: = 16 .. py:data:: dns.rdatatype.UNSPEC :annotation: = 103 .. py:data:: dns.rdatatype.URI :annotation: = 256 .. py:data:: dns.rdatatype.WKS :annotation: = 11 .. py:data:: dns.rdatatype.X25 :annotation: = 19 dnspython-1.16.0/doc/resolver-caching.rst000066400000000000000000000003051340301174400203520ustar00rootroot00000000000000.. _resolver-caching: Resolver Caching Classes ======================== This is a placeholder. .. autoclass:: dns.resolver.Cache :members: .. autoclass:: dns.resolver.LRUCache :members: dnspython-1.16.0/doc/resolver-class.rst000066400000000000000000000073251340301174400200740ustar00rootroot00000000000000.. _resolver-class: The dns.resolver.Resolver and dns.resolver.Answer Classes --------------------------------------------------------- .. autoclass:: dns.resolver.Resolver :members: .. attribute:: domain A ``dns.name.Name``, the domain of this host. .. attribute:: nameservers A ``list`` of ``text``, each item containing an IPv4 or IPv6 address. .. attribute:: search A ``list`` of dns.name.Name objects. If the query name is a relative name, the resolver will construct absolute query names to try by appending values from the search list. .. attribute:: port An ``int``, the default DNS port to send to if not overriden by *nameserver_ports*. The default value is 53. .. attribute:: nameserver_ports A ``dict`` mapping an IPv4 or IPv6 address ``text`` to an ``int``. This specifies the port to use when sending to a nameserver. If a port is not defined for an address, the value of the *port* attribute will be used. .. attribute:: timeout A ``float``, the number of seconds to wait for a response from a server. .. attribute:: lifetime A ``float``, the number of seconds to spend trying to get an answer to the question. If the lifetime expires a ``dns.exception.Timeout`` exception will be raised. .. attribute:: cache: An object implementing the caching protocol, e.g. a ``dns.resolver.Cache`` or a ``dns.resolver.LRUCache``. The default is ``None``, in which case there is no local caching. .. attribute:: retry_servfail A ``bool``. Should we retry a nameserver if it says ``SERVFAIL``? The default is ``False``. .. attribute:: keyring A ``dict``, the TSIG keyring to use. If a *keyring* is specified but a *keyname* is not, then the key used will be the first key in the *keyring*. Note that the order of keys in a dictionary is not defined, so applications should supply a keyname when a keyring is used, unless they know the keyring contains only one key. .. attribute:: keyname A ``dns.name.Name`` or ``None``, the name of the TSIG key to use; defaults to ``None``. The key must be defined in the keyring. .. attribute:: keyalgorithm A ``dns.name.Name`` or ``text``, the TSIG algorithm to use. *edns*, an ``int``, is the EDNS level to use. Specifying ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case the other parameters are ignored. Specifying ``True`` is equivalent to specifying 0, i.e. "use EDNS0". .. attribute:: ednsflags An ``int``, the EDNS flag values. .. attribute:: payload An ``int``, is the EDNS sender's payload field, which is the maximum size of UDP datagram the sender can handle. I.e. how big a response to this message can be. .. attribute:: flags An ``int`` or ``None``, the message flags to use. If ``None``, then the default flags as set by the ``dns.message.Message`` constructor will be used. .. autoclass:: dns.resolver.Answer :members: .. attribute:: qname A ``dns.name.Name``, the query name. .. attribute:: rdclass An ``int``, the query class. .. attribute:: rdtype An ``int``, the query type. .. attribute:: response A ``dns.message.Message``, the response message. .. attribute:: rrset A ``dns.rrset.RRset`` or ``None``, the answer RRset. .. attribute:: expiration A ``float``, the time when the answer expires. .. attribute:: canonical_name A ``dns.name.Name``, the canonical name of the query name, i.e. the owner name of the answer RRset after any CNAME and DNAME chaining. dnspython-1.16.0/doc/resolver-functions.rst000066400000000000000000000005321340301174400207700ustar00rootroot00000000000000.. _resolver-functions: Resolver Functions and The Default Resolver =========================================== .. autofunction:: dns.resolver.query .. autofunction:: dns.resolver.zone_for_name .. autodata:: dns.resolver.default_resolver .. autofunction:: dns.resolver.get_default_resolver .. autofunction:: dns.resolver.reset_default_resolver dnspython-1.16.0/doc/resolver-override.rst000066400000000000000000000007461340301174400206060ustar00rootroot00000000000000.. _resolver-override: Overriding the System Resolver ------------------------------ Sometimes it can be useful to make all of Python use dnspython's resolver rather than the default functionality in the ``socket`` module. Dnspython can redefine the entires in the socket module to point at its own code, and it can also restore them back to the regular Python defaults. .. autofunction:: dns.resolver.override_system_resolver .. autofunction:: dns.resolver.restore_system_resolver dnspython-1.16.0/doc/resolver.rst000066400000000000000000000002741340301174400167650ustar00rootroot00000000000000.. module:: dns.resolver .. _resolver: Stub Resolver ============= This is a placeholder. .. toctree:: resolver-class resolver-functions resolver-caching resolver-override dnspython-1.16.0/doc/rfc.rst000066400000000000000000000111661340301174400157000ustar00rootroot00000000000000.. _rfc: DNS RFC Reference ================= The DNS is defined by a large number of RFCs, many of which have been extensively updated or obsoleted. This chapter aims to provide a roadmap and reference for this confusing space. The chapter does not aim to be encyclopedically complete, however, as the key information would then be lost in the noise. The curious are encouraged to click on the "Updated by" links on the IETF pages to see the finer points, or the "Obsoletes" links to go spelunking into the history of the DNS. DNSSEC gets its own section instead of being included in the "Core" list because there are many DNSSEC related RFCs and it's helpful to group them together. It's not a statement that DNSSEC isn't part of the "Core" of the DNS. The IANA `DNS Parameters `_ registry is the offical reference site for all DNS constants. Core RFCs --------- `RFC 1034 `_ Introduction to the DNS and description of basic behavior. `RFC 1035 `_ The core DNS wire protocol and master file format. `RFC 1995 `_ Incremental zone transfer (IXFR). `RFC 1996 `_ The NOTIFY protocol. `RFC 2181 `_ Clarifications to the specification. `RFC 2308 `_ Negative Caching. `RFC 2845 `_ Transaction Sigatures (TSIG) `RFC 3007 `_ Dynamic Updates `RFC 3645 `_ GSS-TSIG. Note that dnspython does not currently have GSS-TSIG support. GSS-TSIG is most frequently used when updating Microsoft Active-Directory-based DNS servers. `RFC 5936 `_ Zone transfers (AXFR). `RFC 6891 `_ EDNS (version 0) `RFC 8020 `_ Clarification on the meaning of NXDOMAIN. DNSSEC RFCs ----------- `RFC 4033 `_ Introduction and requirements. `RFC 4034 `_ Resource records. `RFC 4035 `_ Protocol. `RFC 4470 `_ Minimally covering NSEC records and On-line Signing. `RFC 6840 `_ Clarifications and implementation Notes. Misc RFCs --------- `RFC 1101 `_ Reverse mapping name form for IPv4. `RFC 1982 `_ Serial number arithmetic. `RFC 4343 `_ Case-sensitivity clarification. RFCs for RR types ----------------- There are many more RR types than are listed here; if a type is not listed it means it is obsolete, deprecated, or rare "in the wild". Some newer types that are currently rare are listed because they may well be more heavily used in the not-to-distant future. See the IANA `DNS Parameters `_ registry for a complete list. A `RFC 1035 `_ AAAA `RFC 3596 `_ CDS `RFC 7344 `_ CDNSKEY `RFC 7344 `_ CNAME `RFC 1035 `_ CSYNC `RFC 7477 `_ DNAME `RFC 6672 `_ DNSKEY `RFC 4034 `_ DS `RFC 4034 `_ LOC `RFC 1876 `_ MX `RFC 1035 `_ NAPTR `RFC 3403 `_ NS `RFC 1035 `_ NSEC `RFC 4034 `_ NSEC3 `RFC 5155 `_ NSEC3PARAM `RFC 5155 `_ OPENPGPKEY `RFC 7929 `_ PTR `RFC 1035 `_ RRSIG `RFC 4034 `_ SOA `RFC 1035 `_ SPF `RFC 7208 `_ SRV `RFC 2782 `_ TLSA `RFC 6698 `_ TXT `RFC 1035 `_ dnspython-1.16.0/doc/util/000077500000000000000000000000001340301174400153445ustar00rootroot00000000000000dnspython-1.16.0/doc/util/auto-values.py000066400000000000000000000005101340301174400201570ustar00rootroot00000000000000#!/usr/bin/env python3 import importlib import sys name = sys.argv[1] title = sys.argv[2] print(title) print('=' * len(title)) print() module = importlib.import_module(name) for t in sorted(module._by_text.keys()): print('.. py:data:: {}.{}'.format(name, t)) print(' :annotation: = {}'.format(module._by_text[t])) dnspython-1.16.0/doc/utilities.rst000066400000000000000000000004521340301174400171350ustar00rootroot00000000000000.. _utilities: Miscellaneous Utilities ----------------------- .. automodule:: dns.inet :members: .. automodule:: dns.ipv4 :members: .. automodule:: dns.ipv6 :members: .. autofunction:: dns.ttl.from_text .. automodule:: dns.set :members: .. automodule:: dns.version :members: dnspython-1.16.0/doc/whatsnew.rst000066400000000000000000000001761340301174400167650ustar00rootroot00000000000000.. _whatsnew: What's New in dnspython 1.16.0 ============================== New Features ------------ Bug Fixes --------- dnspython-1.16.0/examples/000077500000000000000000000000001340301174400154405ustar00rootroot00000000000000dnspython-1.16.0/examples/ddns.py000077500000000000000000000022641340301174400167510ustar00rootroot00000000000000#!/usr/bin/env python # # Use a TSIG-signed DDNS update to update our hostname-to-address # mapping. # # usage: ddns.py # # On linux systems, you can automatically update your DNS any time an # interface comes up by adding an ifup-local script that invokes this # python code. # # E.g. on my systems I have this # # #!/bin/sh # # DEVICE=$1 # # if [ "X${DEVICE}" == "Xeth0" ]; then # IPADDR=`LANG= LC_ALL= ifconfig ${DEVICE} | grep 'inet addr' | # awk -F: '{ print $2 } ' | awk '{ print $1 }'` # /usr/local/sbin/ddns.py $IPADDR # fi # # in /etc/ifup-local. # import sys import dns.update import dns.query import dns.tsigkeyring # # Replace the keyname and secret with appropriate values for your # configuration. # keyring = dns.tsigkeyring.from_text({ 'keyname.' : 'NjHwPsMKjdN++dOfE5iAiQ==' }) # # Replace "example." with your domain, and "host" with your hostname. # update = dns.update.Update('example.', keyring=keyring) update.replace('host', 300, 'A', sys.argv[1]) # # Replace "10.0.0.1" with the IP address of your master server. # response = dns.query.tcp(update, '10.0.0.1', timeout=10) dnspython-1.16.0/examples/e164.py000077500000000000000000000002301340301174400164670ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import dns.e164 n = dns.e164.from_e164("+1 555 1212") print(n) print(dns.e164.to_e164(n)) dnspython-1.16.0/examples/mx.py000077500000000000000000000003411340301174400164370ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import dns.resolver answers = dns.resolver.query('nominum.com', 'MX') for rdata in answers: print('Host', rdata.exchange, 'has preference', rdata.preference) dnspython-1.16.0/examples/name.py000077500000000000000000000007241340301174400167400ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import dns.name n = dns.name.from_text('www.dnspython.org') o = dns.name.from_text('dnspython.org') print(n.is_subdomain(o)) # True print(n.is_superdomain(o)) # False print(n > o) # True rel = n.relativize(o) # rel is the relative name www n2 = rel + o print(n2 == n) # True print(n.labels) # ['www', 'dnspython', 'org', ''] dnspython-1.16.0/examples/query_specific.py000066400000000000000000000015231340301174400210250ustar00rootroot00000000000000#!/usr/bin/env python # Two ways of querying a specific nameserver. from __future__ import print_function import dns.message import dns.rdataclass import dns.rdatatype import dns.query # This way is just like nslookup/dig: qname = dns.name.from_text('amazon.com') q = dns.message.make_query(qname, dns.rdatatype.NS) print('The query is:') print(q) print('') r = dns.query.udp(q, '8.8.8.8') print('The response is:') print(r) print('') print('The nameservers are:') ns_rrset = r.find_rrset(r.answer, qname, dns.rdataclass.IN, dns.rdatatype.NS) for rr in ns_rrset: print(rr.target) print('') print('') # A higher-level way import dns.resolver resolver = dns.resolver.Resolver(configure=False) resolver.nameservers = ['8.8.8.8'] answer = resolver.query('amazon.com', 'NS') print('The nameservers are:') for rr in answer: print(rr.target) dnspython-1.16.0/examples/receive_notify.py000066400000000000000000000017071340301174400210310ustar00rootroot00000000000000#!/usr/bin/env python3 # This is just a toy, real code would check that the received message # really was a NOTIFY, and otherwise handle errors. from __future__ import print_function import socket import dns.flags import dns.message import dns.rdataclass import dns.rdatatype import dns.name from typing import cast address = '127.0.0.1' port = 53535 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind((address, port)) while True: (wire, address) = s.recvfrom(512) notify = dns.message.from_wire(wire) soa = notify.find_rrset(notify.answer, notify.question[0].name, dns.rdataclass.IN, dns.rdatatype.SOA) # Do something with the SOA RR here print('The serial number for', soa.name, 'is', soa[0].serial) response = dns.message.make_response(notify) # type: dns.message.Message response.flags |= dns.flags.AA wire = response.to_wire(cast(dns.name.Name, response)) s.sendto(wire, address) dnspython-1.16.0/examples/reverse.py000077500000000000000000000026611340301174400174750ustar00rootroot00000000000000#!/usr/bin/env python # Usage: reverse.py ... # # This demo script will load in all of the zones specified by the # filenames on the command line, find all the A RRs in them, and # construct a reverse mapping table that maps each IP address used to # the list of names mapping to that address. The table is then sorted # nicely and printed. # # Note! The zone name is taken from the basename of the filename, so # you must use filenames like "/wherever/you/like/dnspython.org" and # not something like "/wherever/you/like/foo.db" (unless you're # working with the ".db" GTLD, of course :)). # # If this weren't a demo script, there'd be a way of specifying the # origin for each zone instead of constructing it from the filename. from __future__ import print_function import dns.zone import dns.ipv4 import os.path import sys from typing import Dict, List # pylint: disable=unused-import reverse_map = {} # type: Dict[str, List[str]] for filename in sys.argv[1:]: zone = dns.zone.from_file(filename, os.path.basename(filename), relativize=False) for (name, ttl, rdata) in zone.iterate_rdatas('A'): print(type(rdata)) try: reverse_map[rdata.address].append(name.to_text()) except KeyError: reverse_map[rdata.address] = [name.to_text()] for k in sorted(reverse_map.keys(), key=dns.ipv4.inet_aton): v = reverse_map[k] v.sort() print(k, v) dnspython-1.16.0/examples/reverse_name.py000077500000000000000000000002611340301174400204670ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import dns.reversename n = dns.reversename.from_address("127.0.0.1") print(n) print(dns.reversename.to_address(n)) dnspython-1.16.0/examples/xfr.py000077500000000000000000000005661340301174400166230ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import dns.query import dns.resolver import dns.zone soa_answer = dns.resolver.query('dnspython.org', 'SOA') master_answer = dns.resolver.query(soa_answer[0].mname, 'A') z = dns.zone.from_xfr(dns.query.xfr(master_answer[0].address, 'dnspython.org')) for n in sorted(z.nodes.keys()): print(z[n].to_text(n)) dnspython-1.16.0/examples/zonediff.py000077500000000000000000000271001340301174400176210ustar00rootroot00000000000000#!/usr/bin/env python # # Small library and commandline tool to do logical diffs of zonefiles # ./zonediff -h gives you help output # # Requires dnspython to do all the heavy lifting # # (c)2009 Dennis Kaarsemaker # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """See diff_zones.__doc__ for more information""" from __future__ import print_function from typing import cast, Union, Any # pylint: disable=unused-import __all__ = ['diff_zones', 'format_changes_plain', 'format_changes_html'] try: import dns.zone import dns.node except ImportError: raise SystemExit("Please install dnspython") def diff_zones(zone1, # type: dns.zone.Zone zone2, # type: dns.zone.Zone ignore_ttl=False, ignore_soa=False ): # type: (...) -> list """diff_zones(zone1, zone2, ignore_ttl=False, ignore_soa=False) -> changes Compares two dns.zone.Zone objects and returns a list of all changes in the format (name, oldnode, newnode). If ignore_ttl is true, a node will not be added to this list if the only change is its TTL. If ignore_soa is true, a node will not be added to this list if the only changes is a change in a SOA Rdata set. The returned nodes do include all Rdata sets, including unchanged ones. """ changes = [] for name in zone1: namestr = str(name) n1 = cast(dns.node.Node, zone1.get_node(namestr)) n2 = cast(dns.node.Node, zone2.get_node(namestr)) if not n2: changes.append((str(name), n1, n2)) elif _nodes_differ(n1, n2, ignore_ttl, ignore_soa): changes.append((str(name), n1, n2)) for name in zone2: n3 = cast(dns.node.Node, zone1.get_node(name)) if not n3: n4 = cast(dns.node.Node, zone2.get_node(name)) changes.append((str(name), n3, n4)) return changes def _nodes_differ(n1, # type: dns.node.Node n2, # type: dns.node.Node ignore_ttl, # type: bool ignore_soa # type: bool ): # type: (...) -> bool if ignore_soa or not ignore_ttl: # Compare datasets directly for r in n1.rdatasets: if ignore_soa and r.rdtype == dns.rdatatype.SOA: continue if r not in n2.rdatasets: return True if not ignore_ttl: return r.ttl != n2.find_rdataset(r.rdclass, r.rdtype).ttl for r in n2.rdatasets: if ignore_soa and r.rdtype == dns.rdatatype.SOA: continue if r not in n1.rdatasets: return True assert False else: return n1 != n2 def format_changes_plain(oldf, # type: str newf, # type: str changes, # type: list ignore_ttl=False ): # type: (...) -> str """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str Given 2 filenames and a list of changes from diff_zones, produce diff-like output. If ignore_ttl is True, TTL-only changes are not displayed""" ret = "--- {}\n+++ {}\n".format(oldf, newf) for name, old, new in changes: ret += "@ %s\n" % name if not old: for r in new.rdatasets: ret += "+ %s\n" % str(r).replace('\n', '\n+ ') elif not new: for r in old.rdatasets: ret += "- %s\n" % str(r).replace('\n', '\n+ ') else: for r in old.rdatasets: if r not in new.rdatasets or ( r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl ): ret += "- %s\n" % str(r).replace('\n', '\n+ ') for r in new.rdatasets: if r not in old.rdatasets or ( r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl ): ret += "+ %s\n" % str(r).replace('\n', '\n+ ') return ret def format_changes_html(oldf, # type: str newf, # type: str changes, # type: list ignore_ttl=False ): # type: (...) -> str """format_changes(oldfile, newfile, changes, ignore_ttl=False) -> str Given 2 filenames and a list of changes from diff_zones, produce nice html output. If ignore_ttl is True, TTL-only changes are not displayed""" ret = '''\n''' % (oldf, newf) for name, old, new in changes: ret += ' \n \n' % name if not old: for r in new.rdatasets: ret += ( ' \n' ' \n' ) % str(r).replace('\n', '
') elif not new: for r in old.rdatasets: ret += ( ' \n' ' \n' ) % str(r).replace('\n', '
') else: ret += ' \n' ret += ' \n' ret += ' \n' return ret + ' \n
  %s %s
%s %s%s ' for r in old.rdatasets: if r not in new.rdatasets or ( r.ttl != new.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl ): ret += str(r).replace('\n', '
') ret += '
' for r in new.rdatasets: if r not in old.rdatasets or ( r.ttl != old.find_rdataset(r.rdclass, r.rdtype).ttl and not ignore_ttl ): ret += str(r).replace('\n', '
') ret += '
' # Make this module usable as a script too. def main(): # type: () -> None import argparse import subprocess import sys import traceback usage = """%prog zonefile1 zonefile2 - Show differences between zones in a diff-like format %prog [--git|--bzr|--rcs] zonefile rev1 [rev2] - Show differences between two revisions of a zonefile The differences shown will be logical differences, not textual differences. """ p = argparse.ArgumentParser(usage=usage) p.add_argument('-s', '--ignore-soa', action="store_true", default=False, dest="ignore_soa", help="Ignore SOA-only changes to records") p.add_argument('-t', '--ignore-ttl', action="store_true", default=False, dest="ignore_ttl", help="Ignore TTL-only changes to Rdata") p.add_argument('-T', '--traceback', action="store_true", default=False, dest="tracebacks", help="Show python tracebacks when errors occur") p.add_argument('-H', '--html', action="store_true", default=False, dest="html", help="Print HTML output") p.add_argument('-g', '--git', action="store_true", default=False, dest="use_git", help="Use git revisions instead of real files") p.add_argument('-b', '--bzr', action="store_true", default=False, dest="use_bzr", help="Use bzr revisions instead of real files") p.add_argument('-r', '--rcs', action="store_true", default=False, dest="use_rcs", help="Use rcs revisions instead of real files") opts, args = p.parse_args() opts.use_vc = opts.use_git or opts.use_bzr or opts.use_rcs def _open(what, err): # type: (Union[list,str], str) -> Any if isinstance(what, list): # Must be a list, open subprocess try: proc = subprocess.Popen(what, stdout=subprocess.PIPE) proc.wait() if proc.returncode == 0: return proc.stdout sys.stderr.write(err + "\n") except Exception: sys.stderr.write(err + "\n") if opts.tracebacks: traceback.print_exc() else: # Open as normal file try: return open(what, 'rb') except IOError: sys.stderr.write(err + "\n") if opts.tracebacks: traceback.print_exc() if not opts.use_vc and len(args) != 2: p.print_help() sys.exit(64) if opts.use_vc and len(args) not in (2, 3): p.print_help() sys.exit(64) # Open file descriptors if not opts.use_vc: oldn, newn = args else: if len(args) == 3: filename, oldr, newr = args oldn = "{}:{}".format(oldr, filename) newn = "{}:{}".format(newr, filename) else: filename, oldr = args newr = None oldn = "{}:{}".format(oldr, filename) newn = filename old, new = None, None oldz, newz = None, None if opts.use_bzr: old = _open(["bzr", "cat", "-r" + oldr, filename], "Unable to retrieve revision {} of {}".format(oldr, filename)) if newr is not None: new = _open(["bzr", "cat", "-r" + newr, filename], "Unable to retrieve revision {} of {}".format(newr, filename)) elif opts.use_git: old = _open(["git", "show", oldn], "Unable to retrieve revision {} of {}".format(oldr, filename)) if newr is not None: new = _open(["git", "show", newn], "Unable to retrieve revision {} of {}".format(newr, filename)) elif opts.use_rcs: old = _open(["co", "-q", "-p", "-r" + oldr, filename], "Unable to retrieve revision {} of {}".format(oldr, filename)) if newr is not None: new = _open(["co", "-q", "-p", "-r" + newr, filename], "Unable to retrieve revision {} of {}".format(newr, filename)) if not opts.use_vc: old = _open(oldn, "Unable to open %s" % oldn) if not opts.use_vc or newr is None: new = _open(newn, "Unable to open %s" % newn) if not old or not new: sys.exit(65) # Parse the zones try: oldz = dns.zone.from_file(old, origin='.', check_origin=False) except dns.exception.DNSException: sys.stderr.write("Incorrect zonefile: %s\n" % old) if opts.tracebacks: traceback.print_exc() try: newz = dns.zone.from_file(new, origin='.', check_origin=False) except dns.exception.DNSException: sys.stderr.write("Incorrect zonefile: %s\n" % new) if opts.tracebacks: traceback.print_exc() if not oldz or not newz: sys.exit(65) changes = diff_zones(oldz, newz, opts.ignore_ttl, opts.ignore_soa) changes.sort() if not changes: sys.exit(0) if opts.html: print(format_changes_html(oldn, newn, changes, opts.ignore_ttl)) else: print(format_changes_plain(oldn, newn, changes, opts.ignore_ttl)) sys.exit(1) if __name__ == '__main__': main() dnspython-1.16.0/pylintrc000066400000000000000000000026001340301174400154070ustar00rootroot00000000000000[MASTER] # Pickle collected data for later comparisons. persistent=no # Use multiple processes to speed up Pylint, auto-detect number of cores. jobs=0 [MESSAGES CONTROL] enable= all, python3 # It's worth looking at len-as-condition for optimization, but it's disabled # here as it is not a correctness thing. Similarly eq-without-hash is # probably worth improving. disable= R, I, anomalous-backslash-in-string, arguments-differ, assigning-non-slot, bad-builtin, bad-continuation, broad-except, deprecated-method, fixme, global-statement, invalid-name, missing-docstring, no-absolute-import, no-member, protected-access, redefined-builtin, too-many-lines, unused-argument, unused-variable, wrong-import-order, wrong-import-position, len-as-condition, eq-without-hash, next-method-defined, [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html. You can also give a reporter class, eg # mypackage.mymodule.MyReporterClass. output-format=colorized # Tells whether to display a full report or only the messages reports=no # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg})' dnspython-1.16.0/setup.cfg000066400000000000000000000000771340301174400154470ustar00rootroot00000000000000[bdist_wheel] universal = 1 [metadata] license_file = LICENSE dnspython-1.16.0/setup.py000077500000000000000000000064601340301174400153450ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license #!/usr/bin/env python # # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import sys from setuptools import setup version = '1.16.0' try: sys.argv.remove("--cython-compile") except ValueError: compile_cython = False else: compile_cython = True from Cython.Build import cythonize ext_modules = cythonize(['dns/*.py', 'dns/rdtypes/*.py', 'dns/rdtypes/*/*.py']) kwargs = { 'name' : 'dnspython', 'version' : version, 'description' : 'DNS toolkit', 'long_description' : \ """dnspython is a DNS toolkit for Python. It supports almost all record types. It can be used for queries, zone transfers, and dynamic updates. It supports TSIG authenticated messages and EDNS0. dnspython provides both high and low level access to DNS. The high level classes perform queries for data of a given name, type, and class, and return an answer set. The low level classes allow direct manipulation of DNS zones, messages, names, and records.""", 'author' : 'Bob Halley', 'author_email' : 'halley@dnspython.org', 'license' : 'BSD-like', 'url' : 'http://www.dnspython.org', 'packages' : ['dns', 'dns.rdtypes', 'dns.rdtypes.IN', 'dns.rdtypes.ANY', 'dns.rdtypes.CH'], 'package_data' : {'dns': ['py.typed']}, 'download_url' : \ 'http://www.dnspython.org/kits/{}/dnspython-{}.tar.gz'.format(version, version), 'classifiers' : [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: Freeware", "Operating System :: Microsoft :: Windows :: Windows 95/98/2000", "Operating System :: POSIX", "Programming Language :: Python", "Topic :: Internet :: Name Service (DNS)", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", ], 'python_requires': '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', 'test_suite': 'tests', 'provides': ['dns'], 'extras_require': { 'IDNA': ['idna>=2.1'], 'DNSSEC': ['pycryptodome', 'ecdsa>=0.13'], }, 'ext_modules': ext_modules if compile_cython else None, 'zip_safe': False if compile_cython else None, } setup(**kwargs) dnspython-1.16.0/tests/000077500000000000000000000000001340301174400147645ustar00rootroot00000000000000dnspython-1.16.0/tests/Makefile000066400000000000000000000017201340301174400164240ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # $Id: Makefile,v 1.5 2004/03/19 00:17:27 halley Exp $ PYTHON=python check: test test: ${PYTHON} ./utest.py dnspython-1.16.0/tests/__init__.py000066400000000000000000000000001340301174400170630ustar00rootroot00000000000000dnspython-1.16.0/tests/example000066400000000000000000000243631340301174400163520ustar00rootroot00000000000000; Copyright (C) 2000, 2001 Internet Software Consortium. ; ; Permission to use, copy, modify, and distribute this software for any ; purpose with or without fee is hereby granted, provided that the above ; copyright notice and this permission notice appear in all copies. ; ; THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM ; DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ; INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, ; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING ; FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, ; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION ; WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ; $Id: example 265924 2016-05-20 18:26:42Z bwelling $ $ORIGIN . $TTL 300 ; 5 minutes example IN SOA ns1.example. hostmaster.example. 1 2 3 4 5 example. NS ns1.example. ns1.example. A 10.53.0.1 example. NS ns2.example. ns2.example. A 10.53.0.2 $ORIGIN example. @ NSEC3PARAM 1 1 12 aabbccdd @ NSEC3PARAM 1 1 12 - * MX 10 mail a TXT "foo foo foo" PTR foo.net. $TTL 3600 ; 1 hour a01 A 0.0.0.0 a02 A 255.255.255.255 ;; ;a601 A6 0 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ; A6 64 ::ffff:ffff:ffff:ffff foo. ; A6 127 ::1 foo. ; A6 128 . aaaa01 AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff aaaa02 AAAA ::1 afsdb01 AFSDB 0 hostname afsdb02 AFSDB 65535 . $TTL 300 ; 5 minutes b CNAME foo.net. c A 73.80.65.49 $TTL 3600 ; 1 hour cert01 CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl d80jEeC8aTrO+KKmCaY= cname01 CNAME cname-target. cname02 CNAME cname-target cname03 CNAME . dhcid01 DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA= dhcid02 DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No= dhcid03 DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6VytcKD//7es/deY= $TTL 300 ; 5 minutes d A 73.80.65.49 $TTL 3600 ; 1 hour dname01 DNAME dname-target. dname02 DNAME dname-target dname03 DNAME . $TTL 300 ; 5 minutes e MX 10 mail TXT "one" TXT "three" TXT "two" A 73.80.65.49 A 73.80.65.50 A 73.80.65.52 A 73.80.65.51 f A 73.80.65.52 $TTL 3600 ; 1 hour gpos01 GPOS "-22.6882" "116.8652" "250.0" ;; ;; XXXRTH I have commented out the following line because I don't think ;; it is a valid GPOS record. ;; ;;gpos02 GPOS "" "" "" hinfo01 HINFO "Generic PC clone" "NetBSD-1.4" hinfo02 HINFO "PC" "NetBSD" isdn01 ISDN "isdn-address" isdn02 ISDN "isdn-address" "subaddress" isdn03 ISDN "isdn-address" isdn04 ISDN "isdn-address" "subaddress" ;key01 KEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o jqf0BaqHT+8= ;key02 KEY HOST|FLAG4 DNSSEC RSAMD5 AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o jqf0BaqHT+8= kx01 KX 10 kdc kx02 KX 10 . loc01 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20m 2000m 20m loc02 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20m 2000m 20m loc03 LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000m 20m loc04 LOC 60 9 1.5 N 24 39 0.000 E 10.00m 90000000.00m 2000m 20m loc05 LOC 60 9 1.51 N 24 39 0.000 E 10.00m 90000000.00m 2000m 20m loc06 LOC 60 9 1 N 24 39 0.000 E 10.00m 90000000.00m 2000m 20m loc07 LOC 0 9 1 N 24 39 0.000 E 10.00m 90000000.00m 2000m 20m loc08 LOC 0 9 1 S 24 39 0.000 E 10.00m 90000000.00m 2000m 20m ;; ;; XXXRTH These are all obsolete and unused. dnspython doesn't implement ;; them ;;mb01 MG madname ;;mb02 MG . ;;mg01 MG mgmname ;;mg02 MG . ;;minfo01 MINFO rmailbx emailbx ;;minfo02 MINFO . . ;;mr01 MR mrname ;;mr02 MR . mx01 MX 10 mail mx02 MX 10 . naptr01 NAPTR 0 0 "" "" "" . naptr02 NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. nsap-ptr01 NSAP-PTR foo. NSAP-PTR . nsap01 NSAP 0x47000580005a0000000001e133ffffff00016100 nsap02 NSAP 0x47.000580005a0000000001e133ffffff000161.00 ;nxt01 NXT a.secure ( NS SOA MX SIG KEY LOC NXT ) ;nxt02 NXT . ( NSAP-PTR NXT ) ;nxt03 NXT . ( A ) ;nxt04 NXT . ( 127 ) ptr01 PTR example. px01 PX 65535 foo. bar. px02 PX 65535 . . rp01 RP mbox-dname txt-dname rp02 RP . . rt01 RT 0 intermediate-host rt02 RT 65535 . $TTL 300 ; 5 minutes s NS ns.s $ORIGIN s.example. ns A 73.80.65.49 $ORIGIN example. $TTL 3600 ; 1 hour ;sig01 SIG NXT 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl d80jEeC8aTrO+KKmCaY= srv01 SRV 0 0 0 . srv02 SRV 65535 65535 65535 old-slow-box.example.com. $TTL 301 ; 5 minutes 1 second t A 73.80.65.49 $TTL 3600 ; 1 hour tlsa1 TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 tlsa2 TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 tlsa3 TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 txt01 TXT "foo" txt02 TXT "foo" "bar" txt03 TXT "foo" txt04 TXT "foo" "bar" txt05 TXT "foo bar" txt06 TXT "foo bar" txt07 TXT "foo bar" txt08 TXT "foo\010bar" txt09 TXT "foo\010bar" txt10 TXT "foo bar" txt11 TXT "\"foo\"" txt12 TXT "\"foo\"" txt13 TXT foo $TTL 300 ; 5 minutes u TXT "txt-not-in-nxt" $ORIGIN u.example. a A 73.80.65.49 b A 73.80.65.49 $ORIGIN example. $TTL 3600 ; 1 hour wks01 WKS 10.0.0.1 6 ( 0 1 2 21 23 ) wks02 WKS 10.0.0.1 17 ( 0 1 2 53 ) wks03 WKS 10.0.0.2 6 ( 65535 ) x2501 X25 "123456789" ds01 DS 12345 3 1 123456789abcdef67890123456789abcdef67890 apl01 APL 1:192.168.32.0/21 !1:192.168.38.0/28 apl02 APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 unknown2 TYPE999 \# 8 0a0000010a000001 unknown3 A \# 4 7f000002 rrsig01 RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz45IkskceFGgi WCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nl d80jEeC8aTrO+KKmCaY= nsec01 NSEC a.secure A MX RRSIG NSEC TYPE1234 nsec02 NSEC . ( NSAP-PTR NSEC ) nsec03 NSEC . ( NSEC TYPE65535 ) nsec301 NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG nsec302 NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG dnskey01 DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o jqf0BaqHT+8= dnskey02 DNSKEY 257 3 RSAMD5 ( AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aR yzWZriO6i2odGWWQVucZqKVsENW91IOW4vqudngPZsY3 GvQ/xVA8/7pyFj6b7Esga60zyGW6LFe9r8n6paHrlG5o jqf0BaqHT+8= ) sshfp1 SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab spf SPF "v=spf1 mx -all" ipseckey01 IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey02 IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey03 IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey04 IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey05 IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== hip01 HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D hip02 HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. hip03 HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example.com. cds01 CDS 12345 3 1 123456789abcdef67890123456789abcdef67890 cdnskey01 CDNSKEY 256 3 8 ( AwEAAbmiLgh411Pz3v3XCSBrvYf52A/Gv55ItN1NbOLH Cqt3Ec3p+VB/kQ87VjjMrycanZFnZT4l9uCFuYh21Ccy xVpcxExbM0UuhX5rJoDyeFSXoQlkHrB01osPl5Vri5Ym KtcmqGxZ9An0VSunohkyiX1SrNRZSdQnk9/pIHDe/c8D ) uri01 URI 10 1 "ftp://ftp1.example.com/public" uri02 URI 10 1 "http://www.example.com/path" caa01 CAA 0 issue "ca.example.net" caa02 CAA 0 iodef "mailto:security@example.com" caa03 CAA 0 iodef "http://iodef.example.com/" caa04 CAA 0 issue "ca.example.net; account=230123" caa05 CAA 0 issue "ca.example.net; policy=ev" caa06 CAA 128 tbs "Unknown" openpgpkey OPENPGPKEY ( mQENBEteQDsBCADYnatn9+5t43AdJlVk9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv Q8ifX6mefquTBsDZC279uXShyTffYzQtvP2r9ewkK7zmSv52Ar563TSULAMwiLpe 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 ldx2Jz//kuru4YqROLBYyB8D6V2jNUFOdaP6j5C5prh9dxfYFp2O/xFeAKLWlWuH 9o96INUoIhgdEyj9PHPT3c821NMZu8tCvsZgUB+QPbHA/QYGa+aollcdGkJpVxXo Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHIUYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh bGxleUBkbnNweXRob24ub3JnPokBPgQTAQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7ZqRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDGZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT B9yNJ8v/WERlFdGaUveEUiFU8g75xp1Hj9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 7EQdFGgqQFynR53md7cmVhAGopKLwMkpCtToKUlxxlfnDfpKZhhXThmhA0PsUQUk JptfGwYwH3O2N3KzfUw3wXRvLa3hona3TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUxpoI+A7WWk9ThfjbynoZxRD820Kbqidqx BSgtFF36SRWzmX8DZfKKAskT9ZGU1odeSKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrBRjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd0IWcTI/MJQARAQABiQElBBgBAgAPBQJL XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkMDMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs Hl/QosTbSQluis/PEvJkTQXHaKHB3bFhwA90c/3HNhrLGugt9AmcfLf9LAynXDgN LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8CPKS8DILFbwCZbRxUogyrZf/7AiHAGdJi 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQRaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg0qzwVBNGb84v/ex2MouwtAYScwc= ) csync0 CSYNC 12345 0 A MX RRSIG NSEC TYPE1234 avc01 AVC "app-name:WOLFGANG|app-class:OAM|business=yes" dnspython-1.16.0/tests/example1.good000066400000000000000000000211641340301174400173560ustar00rootroot00000000000000@ 300 IN SOA ns1 hostmaster 1 2 3 4 5 @ 300 IN NS ns1 @ 300 IN NS ns2 @ 300 IN NSEC3PARAM 1 1 12 aabbccdd @ 300 IN NSEC3PARAM 1 1 12 - * 300 IN MX 10 mail a 300 IN TXT "foo foo foo" a 300 IN PTR foo.net. a01 3600 IN A 0.0.0.0 a02 3600 IN A 255.255.255.255 aaaa01 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff aaaa02 3600 IN AAAA ::1 afsdb01 3600 IN AFSDB 0 hostname afsdb02 3600 IN AFSDB 65535 . apl01 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 apl02 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 avc01 3600 IN AVC "app-name:WOLFGANG|app-class:OAM|business=yes" b 300 IN CNAME foo.net. c 300 IN A 73.80.65.49 caa01 3600 IN CAA 0 issue "ca.example.net" caa02 3600 IN CAA 0 iodef "mailto:security@example.com" caa03 3600 IN CAA 0 iodef "http://iodef.example.com/" caa04 3600 IN CAA 0 issue "ca.example.net; account=230123" caa05 3600 IN CAA 0 issue "ca.example.net; policy=ev" caa06 3600 IN CAA 128 tbs "Unknown" cdnskey01 3600 IN CDNSKEY 256 3 8 AwEAAbmiLgh411Pz3v3XCSBrvYf52A/G v55ItN1NbOLHCqt3Ec3p+VB/kQ87VjjM rycanZFnZT4l9uCFuYh21CcyxVpcxExb M0UuhX5rJoDyeFSXoQlkHrB01osPl5Vr i5YmKtcmqGxZ9An0VSunohkyiX1SrNRZ SdQnk9/pIHDe/c8D cds01 3600 IN CDS 12345 3 1 123456789abcdef67890123456789abcdef67890 cert01 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= cname01 3600 IN CNAME cname-target. cname02 3600 IN CNAME cname-target cname03 3600 IN CNAME . csync0 3600 IN CSYNC 12345 0 A MX RRSIG NSEC TYPE1234 d 300 IN A 73.80.65.49 dhcid01 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= dhcid02 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= dhcid03 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= dname01 3600 IN DNAME dname-target. dname02 3600 IN DNAME dname-target dname03 3600 IN DNAME . dnskey01 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= dnskey02 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= ds01 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 e 300 IN MX 10 mail e 300 IN TXT "one" e 300 IN TXT "three" e 300 IN TXT "two" e 300 IN A 73.80.65.49 e 300 IN A 73.80.65.50 e 300 IN A 73.80.65.52 e 300 IN A 73.80.65.51 f 300 IN A 73.80.65.52 gpos01 3600 IN GPOS -22.6882 116.8652 250.0 hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" hinfo02 3600 IN HINFO "PC" "NetBSD" hip01 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D hip02 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. hip03 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example.com. ipseckey01 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey02 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey03 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey04 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey05 3600 IN IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== isdn01 3600 IN ISDN "isdn-address" isdn02 3600 IN ISDN "isdn-address" "subaddress" isdn03 3600 IN ISDN "isdn-address" isdn04 3600 IN ISDN "isdn-address" "subaddress" kx01 3600 IN KX 10 kdc kx02 3600 IN KX 10 . loc01 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m loc02 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m loc03 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc04 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc05 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc06 3600 IN LOC 60 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc07 3600 IN LOC 0 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc08 3600 IN LOC 0 9 1.000 S 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m mx01 3600 IN MX 10 mail mx02 3600 IN MX 10 . naptr01 3600 IN NAPTR 0 0 "" "" "" . naptr02 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. ns1 300 IN A 10.53.0.1 ns2 300 IN A 10.53.0.2 nsap-ptr01 3600 IN NSAP-PTR foo. nsap-ptr01 3600 IN NSAP-PTR . nsap01 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 nsap02 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 nsec01 3600 IN NSEC a.secure A MX RRSIG NSEC TYPE1234 nsec02 3600 IN NSEC . NSAP-PTR NSEC nsec03 3600 IN NSEC . NSEC TYPE65535 nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM openpgpkey 3600 IN OPENPGPKEY mQENBEteQDsBCADYnatn9+5t43AdJlVk 9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv Q8ifX6mefquTBsDZC279uXShyTffYzQt vP2r9ewkK7zmSv52Ar563TSULAMwiLpe 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+ AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 ldx2Jz//kuru4YqROLBYyB8D6V2jNUFO daP6j5C5prh9dxfYFp2O/xFeAKLWlWuH 9o96INUoIhgdEyj9PHPT3c821NMZu8tC vsZgUB+QPbHA/QYGa+aollcdGkJpVxXo Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHI UYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh bGxleUBkbnNweXRob24ub3JnPokBPgQT AQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ 6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7Z qRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDG ZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT B9yNJ8v/WERlFdGaUveEUiFU8g75xp1H j9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 7EQdFGgqQFynR53md7cmVhAGopKLwMkp CtToKUlxxlfnDfpKZhhXThmhA0PsUQUk JptfGwYwH3O2N3KzfUw3wXRvLa3hona3 TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1 SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUx poI+A7WWk9ThfjbynoZxRD820Kbqidqx BSgtFF36SRWzmX8DZfKKAskT9ZGU1ode SKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrB RjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E 7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd 0IWcTI/MJQARAQABiQElBBgBAgAPBQJL XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12 HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkM DMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs Hl/QosTbSQluis/PEvJkTQXHaKHB3bFh wA90c/3HNhrLGugt9AmcfLf9LAynXDgN LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8C PKS8DILFbwCZbRxUogyrZf/7AiHAGdJi 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQ RaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg 0qzwVBNGb84v/ex2MouwtAYScwc= ptr01 3600 IN PTR @ px01 3600 IN PX 65535 foo. bar. px02 3600 IN PX 65535 . . rp01 3600 IN RP mbox-dname txt-dname rp02 3600 IN RP . . rrsig01 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= rt01 3600 IN RT 0 intermediate-host rt02 3600 IN RT 65535 . s 300 IN NS ns.s ns.s 300 IN A 73.80.65.49 spf 3600 IN SPF "v=spf1 mx -all" srv01 3600 IN SRV 0 0 0 . srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab t 301 IN A 73.80.65.49 tlsa1 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 tlsa2 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 tlsa3 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 txt01 3600 IN TXT "foo" txt02 3600 IN TXT "foo" "bar" txt03 3600 IN TXT "foo" txt04 3600 IN TXT "foo" "bar" txt05 3600 IN TXT "foo bar" txt06 3600 IN TXT "foo bar" txt07 3600 IN TXT "foo bar" txt08 3600 IN TXT "foo\010bar" txt09 3600 IN TXT "foo\010bar" txt10 3600 IN TXT "foo bar" txt11 3600 IN TXT "\"foo\"" txt12 3600 IN TXT "\"foo\"" txt13 3600 IN TXT "foo" u 300 IN TXT "txt-not-in-nxt" a.u 300 IN A 73.80.65.49 b.u 300 IN A 73.80.65.49 unknown2 3600 IN TYPE999 \# 8 0a0000010a000001 unknown3 3600 IN A 127.0.0.2 uri01 3600 IN URI 10 1 "ftp://ftp1.example.com/public" uri02 3600 IN URI 10 1 "http://www.example.com/path" wks01 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 wks02 3600 IN WKS 10.0.0.1 17 0 1 2 53 wks03 3600 IN WKS 10.0.0.2 6 65535 x2501 3600 IN X25 "123456789" dnspython-1.16.0/tests/example2.good000066400000000000000000000237111340301174400173570ustar00rootroot00000000000000example. 300 IN SOA ns1.example. hostmaster.example. 1 2 3 4 5 example. 300 IN NS ns1.example. example. 300 IN NS ns2.example. example. 300 IN NSEC3PARAM 1 1 12 aabbccdd example. 300 IN NSEC3PARAM 1 1 12 - *.example. 300 IN MX 10 mail.example. a.example. 300 IN TXT "foo foo foo" a.example. 300 IN PTR foo.net. a01.example. 3600 IN A 0.0.0.0 a02.example. 3600 IN A 255.255.255.255 aaaa01.example. 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff aaaa02.example. 3600 IN AAAA ::1 afsdb01.example. 3600 IN AFSDB 0 hostname.example. afsdb02.example. 3600 IN AFSDB 65535 . apl01.example. 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 apl02.example. 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 avc01.example. 3600 IN AVC "app-name:WOLFGANG|app-class:OAM|business=yes" b.example. 300 IN CNAME foo.net. c.example. 300 IN A 73.80.65.49 caa01.example. 3600 IN CAA 0 issue "ca.example.net" caa02.example. 3600 IN CAA 0 iodef "mailto:security@example.com" caa03.example. 3600 IN CAA 0 iodef "http://iodef.example.com/" caa04.example. 3600 IN CAA 0 issue "ca.example.net; account=230123" caa05.example. 3600 IN CAA 0 issue "ca.example.net; policy=ev" caa06.example. 3600 IN CAA 128 tbs "Unknown" cdnskey01.example. 3600 IN CDNSKEY 256 3 8 AwEAAbmiLgh411Pz3v3XCSBrvYf52A/G v55ItN1NbOLHCqt3Ec3p+VB/kQ87VjjM rycanZFnZT4l9uCFuYh21CcyxVpcxExb M0UuhX5rJoDyeFSXoQlkHrB01osPl5Vr i5YmKtcmqGxZ9An0VSunohkyiX1SrNRZ SdQnk9/pIHDe/c8D cds01.example. 3600 IN CDS 12345 3 1 123456789abcdef67890123456789abcdef67890 cert01.example. 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= cname01.example. 3600 IN CNAME cname-target. cname02.example. 3600 IN CNAME cname-target.example. cname03.example. 3600 IN CNAME . csync0.example. 3600 IN CSYNC 12345 0 A MX RRSIG NSEC TYPE1234 d.example. 300 IN A 73.80.65.49 dhcid01.example. 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= dhcid02.example. 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= dhcid03.example. 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= dname01.example. 3600 IN DNAME dname-target. dname02.example. 3600 IN DNAME dname-target.example. dname03.example. 3600 IN DNAME . dnskey01.example. 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= dnskey02.example. 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= ds01.example. 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 e.example. 300 IN MX 10 mail.example. e.example. 300 IN TXT "one" e.example. 300 IN TXT "three" e.example. 300 IN TXT "two" e.example. 300 IN A 73.80.65.49 e.example. 300 IN A 73.80.65.50 e.example. 300 IN A 73.80.65.52 e.example. 300 IN A 73.80.65.51 f.example. 300 IN A 73.80.65.52 gpos01.example. 3600 IN GPOS -22.6882 116.8652 250.0 hinfo01.example. 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" hinfo02.example. 3600 IN HINFO "PC" "NetBSD" hip01.example. 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D hip02.example. 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. hip03.example. 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example.com. ipseckey01.example. 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey02.example. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey03.example. 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey04.example. 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey05.example. 3600 IN IPSECKEY 10 3 2 mygateway2.example. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== isdn01.example. 3600 IN ISDN "isdn-address" isdn02.example. 3600 IN ISDN "isdn-address" "subaddress" isdn03.example. 3600 IN ISDN "isdn-address" isdn04.example. 3600 IN ISDN "isdn-address" "subaddress" kx01.example. 3600 IN KX 10 kdc.example. kx02.example. 3600 IN KX 10 . loc01.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m loc02.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m loc03.example. 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc04.example. 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc05.example. 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc06.example. 3600 IN LOC 60 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc07.example. 3600 IN LOC 0 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc08.example. 3600 IN LOC 0 9 1.000 S 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m mx01.example. 3600 IN MX 10 mail.example. mx02.example. 3600 IN MX 10 . naptr01.example. 3600 IN NAPTR 0 0 "" "" "" . naptr02.example. 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. ns1.example. 300 IN A 10.53.0.1 ns2.example. 300 IN A 10.53.0.2 nsap-ptr01.example. 3600 IN NSAP-PTR foo. nsap-ptr01.example. 3600 IN NSAP-PTR . nsap01.example. 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 nsap02.example. 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 nsec01.example. 3600 IN NSEC a.secure.example. A MX RRSIG NSEC TYPE1234 nsec02.example. 3600 IN NSEC . NSAP-PTR NSEC nsec03.example. 3600 IN NSEC . NSEC TYPE65535 nsec301.example. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM nsec302.example. 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM openpgpkey.example. 3600 IN OPENPGPKEY mQENBEteQDsBCADYnatn9+5t43AdJlVk 9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv Q8ifX6mefquTBsDZC279uXShyTffYzQt vP2r9ewkK7zmSv52Ar563TSULAMwiLpe 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+ AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 ldx2Jz//kuru4YqROLBYyB8D6V2jNUFO daP6j5C5prh9dxfYFp2O/xFeAKLWlWuH 9o96INUoIhgdEyj9PHPT3c821NMZu8tC vsZgUB+QPbHA/QYGa+aollcdGkJpVxXo Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHI UYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh bGxleUBkbnNweXRob24ub3JnPokBPgQT AQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ 6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7Z qRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDG ZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT B9yNJ8v/WERlFdGaUveEUiFU8g75xp1H j9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 7EQdFGgqQFynR53md7cmVhAGopKLwMkp CtToKUlxxlfnDfpKZhhXThmhA0PsUQUk JptfGwYwH3O2N3KzfUw3wXRvLa3hona3 TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1 SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUx poI+A7WWk9ThfjbynoZxRD820Kbqidqx BSgtFF36SRWzmX8DZfKKAskT9ZGU1ode SKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrB RjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E 7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd 0IWcTI/MJQARAQABiQElBBgBAgAPBQJL XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12 HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkM DMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs Hl/QosTbSQluis/PEvJkTQXHaKHB3bFh wA90c/3HNhrLGugt9AmcfLf9LAynXDgN LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8C PKS8DILFbwCZbRxUogyrZf/7AiHAGdJi 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQ RaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg 0qzwVBNGb84v/ex2MouwtAYScwc= ptr01.example. 3600 IN PTR example. px01.example. 3600 IN PX 65535 foo. bar. px02.example. 3600 IN PX 65535 . . rp01.example. 3600 IN RP mbox-dname.example. txt-dname.example. rp02.example. 3600 IN RP . . rrsig01.example. 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo.example. MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= rt01.example. 3600 IN RT 0 intermediate-host.example. rt02.example. 3600 IN RT 65535 . s.example. 300 IN NS ns.s.example. ns.s.example. 300 IN A 73.80.65.49 spf.example. 3600 IN SPF "v=spf1 mx -all" srv01.example. 3600 IN SRV 0 0 0 . srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. sshfp1.example. 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab t.example. 301 IN A 73.80.65.49 tlsa1.example. 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 tlsa2.example. 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 tlsa3.example. 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 txt01.example. 3600 IN TXT "foo" txt02.example. 3600 IN TXT "foo" "bar" txt03.example. 3600 IN TXT "foo" txt04.example. 3600 IN TXT "foo" "bar" txt05.example. 3600 IN TXT "foo bar" txt06.example. 3600 IN TXT "foo bar" txt07.example. 3600 IN TXT "foo bar" txt08.example. 3600 IN TXT "foo\010bar" txt09.example. 3600 IN TXT "foo\010bar" txt10.example. 3600 IN TXT "foo bar" txt11.example. 3600 IN TXT "\"foo\"" txt12.example. 3600 IN TXT "\"foo\"" txt13.example. 3600 IN TXT "foo" u.example. 300 IN TXT "txt-not-in-nxt" a.u.example. 300 IN A 73.80.65.49 b.u.example. 300 IN A 73.80.65.49 unknown2.example. 3600 IN TYPE999 \# 8 0a0000010a000001 unknown3.example. 3600 IN A 127.0.0.2 uri01.example. 3600 IN URI 10 1 "ftp://ftp1.example.com/public" uri02.example. 3600 IN URI 10 1 "http://www.example.com/path" wks01.example. 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 wks02.example. 3600 IN WKS 10.0.0.1 17 0 1 2 53 wks03.example. 3600 IN WKS 10.0.0.2 6 65535 x2501.example. 3600 IN X25 "123456789" dnspython-1.16.0/tests/example3.good000066400000000000000000000211641340301174400173600ustar00rootroot00000000000000@ 300 IN SOA ns1 hostmaster 1 2 3 4 5 @ 300 IN NS ns1 @ 300 IN NS ns2 @ 300 IN NSEC3PARAM 1 1 12 aabbccdd @ 300 IN NSEC3PARAM 1 1 12 - * 300 IN MX 10 mail a 300 IN TXT "foo foo foo" a 300 IN PTR foo.net. a01 3600 IN A 0.0.0.0 a02 3600 IN A 255.255.255.255 aaaa01 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff aaaa02 3600 IN AAAA ::1 afsdb01 3600 IN AFSDB 0 hostname afsdb02 3600 IN AFSDB 65535 . apl01 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 apl02 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 avc01 3600 IN AVC "app-name:WOLFGANG|app-class:OAM|business=yes" b 300 IN CNAME foo.net. c 300 IN A 73.80.65.49 caa01 3600 IN CAA 0 issue "ca.example.net" caa02 3600 IN CAA 0 iodef "mailto:security@example.com" caa03 3600 IN CAA 0 iodef "http://iodef.example.com/" caa04 3600 IN CAA 0 issue "ca.example.net; account=230123" caa05 3600 IN CAA 0 issue "ca.example.net; policy=ev" caa06 3600 IN CAA 128 tbs "Unknown" cdnskey01 3600 IN CDNSKEY 256 3 8 AwEAAbmiLgh411Pz3v3XCSBrvYf52A/G v55ItN1NbOLHCqt3Ec3p+VB/kQ87VjjM rycanZFnZT4l9uCFuYh21CcyxVpcxExb M0UuhX5rJoDyeFSXoQlkHrB01osPl5Vr i5YmKtcmqGxZ9An0VSunohkyiX1SrNRZ SdQnk9/pIHDe/c8D cds01 3600 IN CDS 12345 3 1 123456789abcdef67890123456789abcdef67890 cert01 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= cname01 3600 IN CNAME cname-target. cname02 3600 IN CNAME cname-target cname03 3600 IN CNAME . csync0 3600 IN CSYNC 12345 0 A MX RRSIG NSEC TYPE1234 d 300 IN A 73.80.65.49 dhcid01 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= dhcid02 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= dhcid03 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= dname01 3600 IN DNAME dname-target. dname02 3600 IN DNAME dname-target dname03 3600 IN DNAME . dnskey01 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= dnskey02 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= ds01 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 e 300 IN MX 10 mail e 300 IN TXT "one" e 300 IN TXT "three" e 300 IN TXT "two" e 300 IN A 73.80.65.49 e 300 IN A 73.80.65.50 e 300 IN A 73.80.65.52 e 300 IN A 73.80.65.51 f 300 IN A 73.80.65.52 gpos01 3600 IN GPOS -22.6882 116.8652 250.0 hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" hinfo02 3600 IN HINFO "PC" "NetBSD" hip01 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D hip02 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. hip03 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example.com. ipseckey01 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey02 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey03 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey04 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== ipseckey05 3600 IN IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== isdn01 3600 IN ISDN "isdn-address" isdn02 3600 IN ISDN "isdn-address" "subaddress" isdn03 3600 IN ISDN "isdn-address" isdn04 3600 IN ISDN "isdn-address" "subaddress" kx01 3600 IN KX 10 kdc kx02 3600 IN KX 10 . loc01 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m loc02 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m loc03 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc04 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc05 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc06 3600 IN LOC 60 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc07 3600 IN LOC 0 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m loc08 3600 IN LOC 0 9 1.000 S 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m mx01 3600 IN MX 10 mail mx02 3600 IN MX 10 . naptr01 3600 IN NAPTR 0 0 "" "" "" . naptr02 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. ns1 300 IN A 10.53.0.1 ns2 300 IN A 10.53.0.2 nsap-ptr01 3600 IN NSAP-PTR foo. nsap-ptr01 3600 IN NSAP-PTR . nsap01 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 nsap02 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 nsec01 3600 IN NSEC a.secure A MX RRSIG NSEC TYPE1234 nsec02 3600 IN NSEC . NSAP-PTR NSEC nsec03 3600 IN NSEC . NSEC TYPE65535 nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM openpgpkey 3600 IN OPENPGPKEY mQENBEteQDsBCADYnatn9+5t43AdJlVk 9dZC2RM0idPQcmrrKcjeAWDnISqoJzkv Q8ifX6mefquTBsDZC279uXShyTffYzQt vP2r9ewkK7zmSv52Ar563TSULAMwiLpe 0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+ AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3 ldx2Jz//kuru4YqROLBYyB8D6V2jNUFO daP6j5C5prh9dxfYFp2O/xFeAKLWlWuH 9o96INUoIhgdEyj9PHPT3c821NMZu8tC vsZgUB+QPbHA/QYGa+aollcdGkJpVxXo Hhbu6aMx/B+pXg55WM5pqOxmoVjyViHI UYfPABEBAAG0IUJvYiBIYWxsZXkgPGhh bGxleUBkbnNweXRob24ub3JnPokBPgQT AQIAKAUCS15AOwIbAwUJA8JnAAYLCQgH AwIGFQgCCQoLBBYCAwECHgECF4AACgkQ 6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d 24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7Z qRXAcOATNteQmpzqexx+BRKDWU8ZgYx1 2J4GZmC06jABr2JDWxgvbMX9qjkUUgDG ZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKT B9yNJ8v/WERlFdGaUveEUiFU8g75xp1H j9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE6 7EQdFGgqQFynR53md7cmVhAGopKLwMkp CtToKUlxxlfnDfpKZhhXThmhA0PsUQUk JptfGwYwH3O2N3KzfUw3wXRvLa3hona3 TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1 kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1 SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUD u4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUx poI+A7WWk9ThfjbynoZxRD820Kbqidqx BSgtFF36SRWzmX8DZfKKAskT9ZGU1ode SKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE 6Xd65JO6ufhad+ELhgFt95vRwTiFvVrB RjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu 0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E 7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5V UqRR25ubXLuzx9PaHYiC5GiQIU45pWAd 0IWcTI/MJQARAQABiQElBBgBAgAPBQJL XkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12 HRsIAKrB9E++9X9W6VTXBfdkShCFv0yk ZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkM DMdGVe6bn4A3oIAbf0Tjykq1AetZLVPs Hl/QosTbSQluis/PEvJkTQXHaKHB3bFh wA90c/3HNhrLGugt9AmcfLf9LAynXDgN LV5eYdPYqfKE+27qjEBARf6PYh/8WQ8C PKS8DILFbwCZbRxUogyrZf/7AiHAGdJi 8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQ RaHBhD4pHU1S/IRSlx9/3Ytww32PYD9A yO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg 0qzwVBNGb84v/ex2MouwtAYScwc= ptr01 3600 IN PTR @ px01 3600 IN PX 65535 foo. bar. px02 3600 IN PX 65535 . . rp01 3600 IN RP mbox-dname txt-dname rp02 3600 IN RP . . rrsig01 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= rt01 3600 IN RT 0 intermediate-host rt02 3600 IN RT 65535 . s 300 IN NS ns.s ns.s 300 IN A 73.80.65.49 spf 3600 IN SPF "v=spf1 mx -all" srv01 3600 IN SRV 0 0 0 . srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab t 301 IN A 73.80.65.49 tlsa1 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 tlsa2 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 tlsa3 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 txt01 3600 IN TXT "foo" txt02 3600 IN TXT "foo" "bar" txt03 3600 IN TXT "foo" txt04 3600 IN TXT "foo" "bar" txt05 3600 IN TXT "foo bar" txt06 3600 IN TXT "foo bar" txt07 3600 IN TXT "foo bar" txt08 3600 IN TXT "foo\010bar" txt09 3600 IN TXT "foo\010bar" txt10 3600 IN TXT "foo bar" txt11 3600 IN TXT "\"foo\"" txt12 3600 IN TXT "\"foo\"" txt13 3600 IN TXT "foo" u 300 IN TXT "txt-not-in-nxt" a.u 300 IN A 73.80.65.49 b.u 300 IN A 73.80.65.49 unknown2 3600 IN TYPE999 \# 8 0a0000010a000001 unknown3 3600 IN A 127.0.0.2 uri01 3600 IN URI 10 1 "ftp://ftp1.example.com/public" uri02 3600 IN URI 10 1 "http://www.example.com/path" wks01 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 wks02 3600 IN WKS 10.0.0.1 17 0 1 2 53 wks03 3600 IN WKS 10.0.0.2 6 65535 x2501 3600 IN X25 "123456789" dnspython-1.16.0/tests/test_bugs.py000066400000000000000000000101211340301174400173300ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from io import BytesIO import unittest import binascii import dns.rdata import dns.rdataclass import dns.rdatatype import dns.rdtypes.ANY.TXT import dns.ttl class BugsTestCase(unittest.TestCase): def test_float_LOC(self): rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.LOC, u"30 30 0.000 N 100 30 0.000 W 10.00m 20m 2000m 20m") self.failUnless(rdata.float_latitude == 30.5) self.failUnless(rdata.float_longitude == -100.5) def test_SOA_BIND8_TTL(self): rdata1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, u"a b 100 1s 1m 1h 1d") rdata2 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, u"a b 100 1 60 3600 86400") self.failUnless(rdata1 == rdata2) def test_TTL_bounds_check(self): def bad(): dns.ttl.from_text("2147483648") self.failUnlessRaises(dns.ttl.BadTTL, bad) def test_empty_NSEC3_window(self): rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3, u"1 0 100 ABCD SCBCQHKU35969L2A68P3AD59LHF30715") self.failUnless(rdata.windows == []) def test_zero_size_APL(self): rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.APL, "") rdata2 = dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.APL, "", 0, 0) self.failUnless(rdata == rdata2) def test_CAA_from_wire(self): rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CAA, u'0 issue "ca.example.net"') f = BytesIO() rdata.to_wire(f) wire = f.getvalue() rdlen = len(wire) wire += b"trailing garbage" rdata2 = dns.rdata.from_wire(dns.rdataclass.IN, dns.rdatatype.CAA, wire, 0, rdlen) self.failUnless(rdata == rdata2) def test_trailing_zero_APL(self): in4 = "!1:127.0.0.0/1" rd4 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.APL, in4) out4 = rd4.to_digestable(dns.name.from_text("test")) text4 = binascii.hexlify(out4).decode('ascii') self.failUnless(text4 == '000101817f') in6 = "!2:::1000/1" rd6 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.APL, in6) out6 = rd6.to_digestable(dns.name.from_text("test")) text6 = binascii.hexlify(out6).decode('ascii') self.failUnless(text6 == '0002018f000000000000000000000000000010') def test_TXT_conversions(self): t1 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, [b'foo']) t2 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, b'foo') t3 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, 'foo') t4 = dns.rdtypes.ANY.TXT.TXT(dns.rdataclass.IN, dns.rdatatype.TXT, ['foo']) self.failUnless(t1 == t2) self.failUnless(t1 == t2) self.failUnless(t1 == t4) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_dnssec.py000066400000000000000000000332541340301174400176630ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import print_function import unittest import dns.dnssec import dns.name import dns.rdata import dns.rdataclass import dns.rdatatype import dns.rrset # pylint: disable=line-too-long abs_dnspython_org = dns.name.from_text('dnspython.org') abs_keys = { abs_dnspython_org: dns.rrset.from_text( 'dnspython.org.', 3600, 'IN', 'DNSKEY', '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF' ) } abs_keys_duplicate_keytag = { abs_dnspython_org: dns.rrset.from_text( 'dnspython.org.', 3600, 'IN', 'DNSKEY', '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', '256 3 5 AwEAAdSSg++++THIS/IS/NOT/THE/CORRECT/KEY++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AaOSydAF', '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF' ) } rel_keys = { dns.name.empty: dns.rrset.from_text( '@', 3600, 'IN', 'DNSKEY', '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=', '256 3 5 AwEAAdSSghOGjU33IQZgwZM2Hh771VGXX05olJK49FxpSyuEAjDBXY58 LGU9R2Zgeecnk/b9EAhFu/vCV9oECtiTCvwuVAkt9YEweqYDluQInmgP NGMJCKdSLlnX93DkjDw8rMYv5dqXCuSGPlKChfTJOLQxIAxGloS7lL+c 0CTZydAF' ) } when = 1290250287 abs_soa = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'SOA', 'howl.dnspython.org. hostmaster.dnspython.org. 2010020047 3600 1800 604800 3600') abs_other_soa = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'SOA', 'foo.dnspython.org. hostmaster.dnspython.org. 2010020047 3600 1800 604800 3600') abs_soa_rrsig = dns.rrset.from_text('dnspython.org.', 3600, 'IN', 'RRSIG', 'SOA 5 2 3600 20101127004331 20101119213831 61695 dnspython.org. sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=') rel_soa = dns.rrset.from_text('@', 3600, 'IN', 'SOA', 'howl hostmaster 2010020047 3600 1800 604800 3600') rel_other_soa = dns.rrset.from_text('@', 3600, 'IN', 'SOA', 'foo hostmaster 2010020047 3600 1800 604800 3600') rel_soa_rrsig = dns.rrset.from_text('@', 3600, 'IN', 'RRSIG', 'SOA 5 2 3600 20101127004331 20101119213831 61695 @ sDUlltRlFTQw5ITFxOXW3TgmrHeMeNpdqcZ4EXxM9FHhIlte6V9YCnDw t6dvM9jAXdIEi03l9H/RAd9xNNW6gvGMHsBGzpvvqFQxIBR2PoiZA1mX /SWHZFdbt4xjYTtXqpyYvrMK0Dt7bUYPadyhPFCJ1B+I8Zi7B5WJEOd0 8vs=') sep_key = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, '257 3 5 AwEAAenVTr9L1OMlL1/N2ta0Qj9LLLnnmFWIr1dJoAsWM9BQfsbV7kFZ XbAkER/FY9Ji2o7cELxBwAsVBuWn6IUUAJXLH74YbC1anY0lifjgt29z SwDzuB7zmC7yVYZzUunBulVW4zT0tg1aePbpVL2EtTL8VzREqbJbE25R KuQYHZtFwG8S4iBxJUmT2Bbd0921LLxSQgVoFXlQx/gFV2+UERXcJ5ce iX6A6wc02M/pdg/YbJd2rBa0MYL3/Fz/Xltre0tqsImZGxzi6YtYDs45 NC8gH+44egz82e2DATCVM1ICPmRDjXYTLldQiWA2ZXIWnK0iitl5ue24 7EsWJefrIhE=') good_ds = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, '57349 5 2 53A79A3E7488AB44FFC56B2D1109F0699D1796DD977E72108B841F96 E47D7013') when2 = 1290425644 abs_example = dns.name.from_text('example') abs_dsa_keys = { abs_example: dns.rrset.from_text( 'example.', 86400, 'IN', 'DNSKEY', '257 3 3 CI3nCqyJsiCJHTjrNsJOT4RaszetzcJPYuoH3F9ZTVt3KJXncCVR3bwn 1w0iavKljb9hDlAYSfHbFCp4ic/rvg4p1L8vh5s8ToMjqDNl40A0hUGQ Ybx5hsECyK+qHoajilUX1phYSAD8d9WAGO3fDWzUPBuzR7o85NiZCDxz yXuNVfni0uhj9n1KYhEO5yAbbruDGN89wIZcxMKuQsdUY2GYD93ssnBv a55W6XRABYWayKZ90WkRVODLVYLSn53Pj/wwxGH+XdhIAZJXimrZL4yl My7rtBsLMqq8Ihs4Tows7LqYwY7cp6y/50tw6pj8tFqMYcPUjKZV36l1 M/2t5BVg3i7IK61Aidt6aoC3TDJtzAxg3ZxfjZWJfhHjMJqzQIfbW5b9 q1mjFsW5EUv39RaNnX+3JWPRLyDqD4pIwDyqfutMsdk/Py3paHn82FGp CaOg+nicqZ9TiMZURN/XXy5JoXUNQ3RNvbHCUiPUe18KUkY6mTfnyHld 1l9YCWmzXQVClkx/hOYxjJ4j8Ife58+Obu5X', '256 3 3 CJE1yb9YRQiw5d2xZrMUMR+cGCTt1bp1KDCefmYKmS+Z1+q9f42ETVhx JRiQwXclYwmxborzIkSZegTNYIV6mrYwbNB27Q44c3UGcspb3PiOw5TC jNPRYEcdwGvDZ2wWy+vkSV/S9tHXY8O6ODiE6abZJDDg/RnITyi+eoDL R3KZ5n/V1f1T1b90rrV6EewhBGQJpQGDogaXb2oHww9Tm6NfXyo7SoMM pbwbzOckXv+GxRPJIQNSF4D4A9E8XCksuzVVdE/0lr37+uoiAiPia38U 5W2QWe/FJAEPLjIp2eTzf0TrADc1pKP1wrA2ASpdzpm/aX3IB5RPp8Ew S9U72eBFZJAUwg635HxJVxH1maG6atzorR566E+e0OZSaxXS9o1o6QqN 3oPlYLGPORDiExilKfez3C/x/yioOupW9K5eKF0gmtaqrHX0oq9s67f/ RIM2xVaKHgG9Vf2cgJIZkhv7sntujr+E4htnRmy9P9BxyFxsItYxPI6Z bzygHAZpGhlI/7ltEGlIwKxyTK3ZKBm67q7B' ) } abs_dsa_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', 'ns1.example. hostmaster.example. 2 10800 3600 604800 86400') abs_other_dsa_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', 'ns1.example. hostmaster.example. 2 10800 3600 604800 86401') abs_dsa_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', 'SOA 3 1 86400 20101129143231 20101122112731 42088 example. CGul9SuBofsktunV8cJs4eRs6u+3NCS3yaPKvBbD+pB2C76OUXDZq9U=') example_sep_key = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DNSKEY, '257 3 3 CI3nCqyJsiCJHTjrNsJOT4RaszetzcJPYuoH3F9ZTVt3KJXncCVR3bwn 1w0iavKljb9hDlAYSfHbFCp4ic/rvg4p1L8vh5s8ToMjqDNl40A0hUGQ Ybx5hsECyK+qHoajilUX1phYSAD8d9WAGO3fDWzUPBuzR7o85NiZCDxz yXuNVfni0uhj9n1KYhEO5yAbbruDGN89wIZcxMKuQsdUY2GYD93ssnBv a55W6XRABYWayKZ90WkRVODLVYLSn53Pj/wwxGH+XdhIAZJXimrZL4yl My7rtBsLMqq8Ihs4Tows7LqYwY7cp6y/50tw6pj8tFqMYcPUjKZV36l1 M/2t5BVg3i7IK61Aidt6aoC3TDJtzAxg3ZxfjZWJfhHjMJqzQIfbW5b9 q1mjFsW5EUv39RaNnX+3JWPRLyDqD4pIwDyqfutMsdk/Py3paHn82FGp CaOg+nicqZ9TiMZURN/XXy5JoXUNQ3RNvbHCUiPUe18KUkY6mTfnyHld 1l9YCWmzXQVClkx/hOYxjJ4j8Ife58+Obu5X') example_ds_sha1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, '18673 3 1 71b71d4f3e11bbd71b4eff12cde69f7f9215bbe7') example_ds_sha256 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, '18673 3 2 eb8344cbbf07c9d3d3d6c81d10c76653e28d8611a65e639ef8f716e4e4e5d913') when3 = 1379801800 abs_ecdsa256_keys = { abs_example: dns.rrset.from_text( 'example.', 86400, 'IN', 'DNSKEY', "256 3 13 +3ss1sCpdARVA61DJigEsL/8quo2a8MszKtn2gkkfxgzFs8S2UHtpb4N fY+XFmNW+JK6MsCkI3jHYN8eEQUgMw==", "257 3 13 eJCEVH7AS3wnoaQpaNlAXH0W8wxymtT9P6P3qjN2ZCV641ED8pF7wZ5V yWfOpgTs6oaZevbJgehl/GaRPUgVyQ==" ) } abs_ecdsa256_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', 'ns1.example. hostmaster.example. 4 10800 3600 604800 86400') abs_other_ecdsa256_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', 'ns1.example. hostmaster.example. 2 10800 3600 604800 86401') abs_ecdsa256_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', "SOA 13 1 86400 20130921221753 20130921221638 7460 example. Sm09SOGz1ULB5D/duwdE2Zpn8bWbVBM77H6N1wPkc42LevvVO+kZEjpq 2nq4GOMJcih52667GIAbMrwmU5P2MQ==") when4 = 1379804850 abs_ecdsa384_keys = { abs_example: dns.rrset.from_text( 'example.', 86400, 'IN', 'DNSKEY', "256 3 14 1bG8qWviKNXQX3BIuG6/T5jrP1FISiLW/8qGF6BsM9DQtWYhhZUA3Owr OAEiyHAhQwjkN2kTvWiAYoPN80Ii+5ff9/atzY4F9W50P4l75Dj9PYrL HN/hLUgWMNVc9pvA", "257 3 14 mSub2n0KRt6u2FaD5XJ3oQu0R4XvB/9vUJcyW6+oo0y+KzfQeTdkf1ro ZMVKoyWXW9zUKBYGJpMUIdbAxzrYi7f5HyZ3yDpBFz1hw9+o3CX+gtgb +RyhHfJDwwFXBid9" ) } abs_ecdsa384_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', 'ns1.example. hostmaster.example. 2 10800 3600 604800 86400') abs_other_ecdsa384_soa = dns.rrset.from_text('example.', 86400, 'IN', 'SOA', 'ns1.example. hostmaster.example. 2 10800 3600 604800 86401') abs_ecdsa384_soa_rrsig = dns.rrset.from_text('example.', 86400, 'IN', 'RRSIG', "SOA 14 1 86400 20130929021229 20130921230729 63571 example. CrnCu34EeeRz0fEhL9PLlwjpBKGYW8QjBjFQTwd+ViVLRAS8tNkcDwQE NhSV89NEjj7ze1a/JcCfcJ+/mZgnvH4NHLNg3Tf6KuLZsgs2I4kKQXEk 37oIHravPEOlGYNI") @unittest.skipUnless(dns.dnssec._have_pycrypto, "Pycryptodome cannot be imported") class DNSSECValidatorTestCase(unittest.TestCase): def testAbsoluteRSAGood(self): # type: () -> None dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys, None, when) def testDuplicateKeytag(self): # type: () -> None dns.dnssec.validate(abs_soa, abs_soa_rrsig, abs_keys_duplicate_keytag, None, when) def testAbsoluteRSABad(self): # type: () -> None def bad(): # type: () -> None dns.dnssec.validate(abs_other_soa, abs_soa_rrsig, abs_keys, None, when) self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) def testRelativeRSAGood(self): # type: () -> None dns.dnssec.validate(rel_soa, rel_soa_rrsig, rel_keys, abs_dnspython_org, when) def testRelativeRSABad(self): # type: () -> None def bad(): # type: () -> None dns.dnssec.validate(rel_other_soa, rel_soa_rrsig, rel_keys, abs_dnspython_org, when) self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) def testMakeSHA256DS(self): # type: () -> None ds = dns.dnssec.make_ds(abs_dnspython_org, sep_key, 'SHA256') self.failUnless(ds == good_ds) def testAbsoluteDSAGood(self): # type: () -> None dns.dnssec.validate(abs_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, when2) def testAbsoluteDSABad(self): # type: () -> None def bad(): # type: () -> None dns.dnssec.validate(abs_other_dsa_soa, abs_dsa_soa_rrsig, abs_dsa_keys, None, when2) self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) def testMakeExampleSHA1DS(self): # type: () -> None ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA1') self.failUnless(ds == example_ds_sha1) def testMakeExampleSHA256DS(self): # type: () -> None ds = dns.dnssec.make_ds(abs_example, example_sep_key, 'SHA256') self.failUnless(ds == example_ds_sha256) @unittest.skipUnless(dns.dnssec._have_ecdsa, "python ECDSA cannot be imported") def testAbsoluteECDSA256Good(self): # type: () -> None dns.dnssec.validate(abs_ecdsa256_soa, abs_ecdsa256_soa_rrsig, abs_ecdsa256_keys, None, when3) @unittest.skipUnless(dns.dnssec._have_ecdsa, "python ECDSA cannot be imported") def testAbsoluteECDSA256Bad(self): # type: () -> None def bad(): # type: () -> None dns.dnssec.validate(abs_other_ecdsa256_soa, abs_ecdsa256_soa_rrsig, abs_ecdsa256_keys, None, when3) self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) @unittest.skipUnless(dns.dnssec._have_ecdsa, "python ECDSA cannot be imported") def testAbsoluteECDSA384Good(self): # type: () -> None dns.dnssec.validate(abs_ecdsa384_soa, abs_ecdsa384_soa_rrsig, abs_ecdsa384_keys, None, when4) @unittest.skipUnless(dns.dnssec._have_ecdsa, "python ECDSA cannot be imported") def testAbsoluteECDSA384Bad(self): # type: () -> None def bad(): # type: () -> None dns.dnssec.validate(abs_other_ecdsa384_soa, abs_ecdsa384_soa_rrsig, abs_ecdsa384_keys, None, when4) self.failUnlessRaises(dns.dnssec.ValidationFailure, bad) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_edns.py000066400000000000000000000043211340301174400173260ustar00rootroot00000000000000# -*- coding: utf-8 # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import print_function import unittest from io import BytesIO import dns.edns class OptionTestCase(unittest.TestCase): def testGenericOption(self): opt = dns.edns.GenericOption(3, b'data') io = BytesIO() opt.to_wire(io) data = io.getvalue() self.assertEqual(data, b'data') def testECSOption_prefix_length(self): opt = dns.edns.ECSOption('1.2.255.33', 20) io = BytesIO() opt.to_wire(io) data = io.getvalue() self.assertEqual(data, b'\x00\x01\x14\x00\x01\x02\xf0') def testECSOption_from_wire(self): opt = dns.edns.option_from_wire(8, b'\x00\x01\x14\x00\x01\x02\xf0', 0, 7) self.assertEqual(opt.otype, dns.edns.ECS) self.assertEqual(opt.address, '1.2.240.0') self.assertEqual(opt.srclen, 20) self.assertEqual(opt.scopelen, 0) def testECSOption(self): opt = dns.edns.ECSOption('1.2.3.4', 24) io = BytesIO() opt.to_wire(io) data = io.getvalue() self.assertEqual(data, b'\x00\x01\x18\x00\x01\x02\x03') def testECSOption_v6(self): opt = dns.edns.ECSOption('2001:4b98::1') io = BytesIO() opt.to_wire(io) data = io.getvalue() self.assertEqual(data, b'\x00\x02\x38\x00\x20\x01\x4b\x98\x00\x00\x00') dnspython-1.16.0/tests/test_exceptions.py000066400000000000000000000042201340301174400205540ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest from dns.exception import DNSException class FormatedError(DNSException): fmt = "Custom format: {parameter}" supp_kwargs = {'parameter'} class ExceptionTestCase(unittest.TestCase): def test_custom_message(self): msg = "this is a custom message" try: raise DNSException(msg) except DNSException as ex: self.assertEqual(str(ex), msg) def test_implicit_message(self): try: raise DNSException() except DNSException as ex: self.assertEqual(ex.__class__.__doc__, str(ex)) def test_formatted_error(self): """Exceptions with explicit format has to respect it.""" params = {'parameter': 'value'} try: raise FormatedError(**params) except FormatedError as ex: msg = FormatedError.fmt.format(**params) self.assertEqual(msg, str(ex)) def test_kwargs_only(self): """Kwargs cannot be combined with args.""" with self.assertRaises(AssertionError): raise FormatedError(1, a=2) def test_kwargs_unsupported(self): """Only supported kwargs are accepted.""" with self.assertRaises(AssertionError): raise FormatedError(unsupported=2) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_flags.py000066400000000000000000000041701340301174400174730ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.flags import dns.rcode import dns.opcode class FlagsTestCase(unittest.TestCase): def test_rcode1(self): self.failUnless(dns.rcode.from_text('FORMERR') == dns.rcode.FORMERR) def test_rcode2(self): self.failUnless(dns.rcode.to_text(dns.rcode.FORMERR) == "FORMERR") def test_rcode3(self): self.failUnless(dns.rcode.to_flags(dns.rcode.FORMERR) == (1, 0)) def test_rcode4(self): self.failUnless(dns.rcode.to_flags(dns.rcode.BADVERS) == \ (0, 0x01000000)) def test_rcode6(self): self.failUnless(dns.rcode.from_flags(0, 0x01000000) == \ dns.rcode.BADVERS) def test_rcode7(self): self.failUnless(dns.rcode.from_flags(5, 0) == dns.rcode.REFUSED) def test_rcode8(self): def bad(): dns.rcode.to_flags(4096) self.failUnlessRaises(ValueError, bad) def test_flags1(self): self.failUnless(dns.flags.from_text("RA RD AA QR") == \ dns.flags.QR|dns.flags.AA|dns.flags.RD|dns.flags.RA) def test_flags2(self): flags = dns.flags.QR|dns.flags.AA|dns.flags.RD|dns.flags.RA self.failUnless(dns.flags.to_text(flags) == "QR AA RD RA") if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_generate.py000066400000000000000000000503131340301174400201710ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import sys sys.path.insert(0, '../') # Force the local project to be *the* dns import unittest import dns.exception import dns.rdata import dns.rdataclass import dns.rdatatype import dns.rrset import dns.zone from dns._compat import long import pprint pp = pprint.PrettyPrinter(indent=2) example_text = """$TTL 1h $ORIGIN 0.0.192.IN-ADDR.ARPA. $GENERATE 1-2 0 CNAME SERVER$.EXAMPLE. """ example_text1 = """$TTL 1h $ORIGIN 0.0.192.IN-ADDR.ARPA. $GENERATE 1-10 fooo$ CNAME $.0 """ example_text2 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 3-5 foo$ A 10.0.0.$ """ example_text3 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 4-8/2 foo$ A 10.0.0.$ """ example_text4 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 11-13 wp-db${-10,2,d}.services.mozilla.com 0 CNAME SERVER.FOOBAR. """ example_text5 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 11-13 wp-db${10,2,d}.services.mozilla.com 0 CNAME SERVER.FOOBAR. """ example_text6 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 11-13 wp-db${+10,2,d}.services.mozilla.com 0 CNAME SERVER.FOOBAR. """ example_text7 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 11-13 sync${-10}.db IN A 10.10.16.0 """ example_text8 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 11-12 wp-db${-10,2,d} IN A 10.10.16.0 """ example_text9 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 11-12 wp-db${-10,2,d} IN A 10.10.16.0 $GENERATE 11-13 sync${-10}.db IN A 10.10.16.0 """ example_text10 = """$TTL 1h @ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 $GENERATE 27-28 $.2 PTR zlb${-26}.oob """ def _rdata_sort(a): return (a[0], a[2].rdclass, a[2].to_text()) class GenerateTestCase(unittest.TestCase): def testFromText(self): # type: () -> None def bad(): # type: () -> None dns.zone.from_text(example_text, 'example.', relativize=True) self.failUnlessRaises(dns.zone.NoSOA, bad) def testFromText1(self): # type: () -> None def bad(): # type: () -> None dns.zone.from_text(example_text1, 'example.', relativize=True) self.failUnlessRaises(dns.zone.NoSOA, bad) def testIterateAllRdatas2(self): # type: () -> None z = dns.zone.from_text(example_text2, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), 300, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('foo3', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.3')), (dns.name.from_text('foo4', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.4')), (dns.name.from_text('foo5', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.5'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) def testIterateAllRdatas3(self): # type: () -> None z = dns.zone.from_text(example_text3, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), 300, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('foo4', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.4')), (dns.name.from_text('foo6', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.6')), (dns.name.from_text('foo8', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.8'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) def testGenerate1(self): # type: () -> None z = dns.zone.from_text(example_text4, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), long(300), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('wp-db01.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.')), (dns.name.from_text('wp-db02.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.')), (dns.name.from_text('wp-db03.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.'))] exl.sort(key=_rdata_sort) self.assertEqual(l, exl) def testGenerate2(self): # type: () -> None z = dns.zone.from_text(example_text5, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), long(300), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('wp-db21.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.')), (dns.name.from_text('wp-db22.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.')), (dns.name.from_text('wp-db23.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) def testGenerate3(self): # type: () -> None z = dns.zone.from_text(example_text6, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), long(300), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('wp-db21.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.')), (dns.name.from_text('wp-db22.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.')), (dns.name.from_text('wp-db23.services.mozilla.com', None), long(0), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.CNAME, 'SERVER.FOOBAR.'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) def testGenerate4(self): # type: () -> None z = dns.zone.from_text(example_text7, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), long(300), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('sync1.db', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0')), (dns.name.from_text('sync2.db', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0')), (dns.name.from_text('sync3.db', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) def testGenerate6(self): # type: () -> None z = dns.zone.from_text(example_text9, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), long(300), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('wp-db01', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0')), (dns.name.from_text('wp-db02', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0')), (dns.name.from_text('sync1.db', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0')), (dns.name.from_text('sync2.db', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0')), (dns.name.from_text('sync3.db', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.10.16.0'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) def testGenerate7(self): # type: () -> None z = dns.zone.from_text(example_text10, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), long(300), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2')), (dns.name.from_text('27.2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, 'zlb1.oob')), (dns.name.from_text('28.2', None), long(3600), dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.PTR, 'zlb2.oob'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_grange.py000066400000000000000000000055631340301174400176510ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import sys sys.path.insert(0, '../') import unittest import dns import dns.exception import dns.grange class GRangeTestCase(unittest.TestCase): def testFromText1(self): start, stop, step = dns.grange.from_text('1-1') self.assertEqual(start, 1) self.assertEqual(stop, 1) self.assertEqual(step, 1) def testFromText2(self): start, stop, step = dns.grange.from_text('1-4') self.assertEqual(start, 1) self.assertEqual(stop, 4) self.assertEqual(step, 1) def testFromText3(self): start, stop, step = dns.grange.from_text('4-255') self.assertEqual(start, 4) self.assertEqual(stop, 255) self.assertEqual(step, 1) def testFromText4(self): start, stop, step = dns.grange.from_text('1-1/1') self.assertEqual(start, 1) self.assertEqual(stop, 1) self.assertEqual(step, 1) def testFromText5(self): start, stop, step = dns.grange.from_text('1-4/2') self.assertEqual(start, 1) self.assertEqual(stop, 4) self.assertEqual(step, 2) def testFromText6(self): start, stop, step = dns.grange.from_text('4-255/77') self.assertEqual(start, 4) self.assertEqual(stop, 255) self.assertEqual(step, 77) def testFailFromText1(self): def bad(): start = 2 stop = 1 step = 1 dns.grange.from_text('%d-%d/%d' % (start, stop, step)) self.assertRaises(AssertionError, bad) def testFailFromText2(self): def bad(): start = '-1' stop = 3 step = 1 dns.grange.from_text('%s-%d/%d' % (start, stop, step)) self.assertRaises(dns.exception.SyntaxError, bad) def testFailFromText3(self): def bad(): start = 1 stop = 4 step = '-2' dns.grange.from_text('%d-%d/%s' % (start, stop, step)) self.assertRaises(dns.exception.SyntaxError, bad) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_message.py000066400000000000000000000153371340301174400200320ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import binascii import dns.exception import dns.flags import dns.message import dns.name import dns.rdataclass import dns.rdatatype from dns._compat import xrange query_text = """id 1234 opcode QUERY rcode NOERROR flags RD edns 0 eflags DO payload 4096 ;QUESTION wwww.dnspython.org. IN A ;ANSWER ;AUTHORITY ;ADDITIONAL""" goodhex = b'04d201000001000000000001047777777709646e73707974686f6e' \ b'036f726700000100010000291000000080000000' goodwire = binascii.unhexlify(goodhex) answer_text = """id 1234 opcode QUERY rcode NOERROR flags QR AA RD ;QUESTION dnspython.org. IN SOA ;ANSWER dnspython.org. 3600 IN SOA woof.dnspython.org. hostmaster.dnspython.org. 2003052700 3600 1800 604800 3600 ;AUTHORITY dnspython.org. 3600 IN NS ns1.staff.nominum.org. dnspython.org. 3600 IN NS ns2.staff.nominum.org. dnspython.org. 3600 IN NS woof.play-bow.org. ;ADDITIONAL woof.play-bow.org. 3600 IN A 204.152.186.150 """ goodhex2 = '04d2 8500 0001 0001 0003 0001' \ '09646e73707974686f6e036f726700 0006 0001' \ 'c00c 0006 0001 00000e10 0028 ' \ '04776f6f66c00c 0a686f73746d6173746572c00c' \ '7764289c 00000e10 00000708 00093a80 00000e10' \ 'c00c 0002 0001 00000e10 0014' \ '036e7331057374616666076e6f6d696e756dc016' \ 'c00c 0002 0001 00000e10 0006 036e7332c063' \ 'c00c 0002 0001 00000e10 0010 04776f6f6608706c61792d626f77c016' \ 'c091 0001 0001 00000e10 0004 cc98ba96' goodwire2 = binascii.unhexlify(goodhex2.replace(' ', '').encode()) query_text_2 = """id 1234 opcode QUERY rcode 4095 flags RD edns 0 eflags DO payload 4096 ;QUESTION wwww.dnspython.org. IN A ;ANSWER ;AUTHORITY ;ADDITIONAL""" goodhex3 = b'04d2010f0001000000000001047777777709646e73707974686f6e' \ b'036f726700000100010000291000ff0080000000' goodwire3 = binascii.unhexlify(goodhex3) class MessageTestCase(unittest.TestCase): def test_comparison_eq1(self): q1 = dns.message.from_text(query_text) q2 = dns.message.from_text(query_text) self.failUnless(q1 == q2) def test_comparison_ne1(self): q1 = dns.message.from_text(query_text) q2 = dns.message.from_text(query_text) q2.id = 10 self.failUnless(q1 != q2) def test_comparison_ne2(self): q1 = dns.message.from_text(query_text) q2 = dns.message.from_text(query_text) q2.question = [] self.failUnless(q1 != q2) def test_comparison_ne3(self): q1 = dns.message.from_text(query_text) self.failUnless(q1 != 1) def test_EDNS_to_wire1(self): q = dns.message.from_text(query_text) w = q.to_wire() self.failUnless(w == goodwire) def test_EDNS_from_wire1(self): m = dns.message.from_wire(goodwire) self.assertEqual(str(m), query_text) def test_EDNS_to_wire2(self): q = dns.message.from_text(query_text_2) w = q.to_wire() self.failUnless(w == goodwire3) def test_EDNS_from_wire2(self): m = dns.message.from_wire(goodwire3) self.failUnless(str(m) == query_text_2) def test_TooBig(self): def bad(): q = dns.message.from_text(query_text) for i in xrange(0, 25): rrset = dns.rrset.from_text('foo%d.' % i, 3600, dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.%d' % i) q.additional.append(rrset) q.to_wire(max_size=512) self.failUnlessRaises(dns.exception.TooBig, bad) def test_answer1(self): a = dns.message.from_text(answer_text) wire = a.to_wire(want_shuffle=False) self.failUnless(wire == goodwire2) def test_TrailingJunk(self): def bad(): badwire = goodwire + b'\x00' dns.message.from_wire(badwire) self.failUnlessRaises(dns.message.TrailingJunk, bad) def test_ShortHeader(self): def bad(): badwire = b'\x00' * 11 dns.message.from_wire(badwire) self.failUnlessRaises(dns.message.ShortHeader, bad) def test_RespondingToResponse(self): def bad(): q = dns.message.make_query('foo', 'A') r1 = dns.message.make_response(q) dns.message.make_response(r1) self.failUnlessRaises(dns.exception.FormError, bad) def test_ExtendedRcodeSetting(self): m = dns.message.make_query('foo', 'A') m.set_rcode(4095) self.failUnless(m.rcode() == 4095) m.set_rcode(2) self.failUnless(m.rcode() == 2) def test_EDNSVersionCoherence(self): m = dns.message.make_query('foo', 'A') m.use_edns(1) self.failUnless((m.ednsflags >> 16) & 0xFF == 1) def test_SettingNoEDNSOptionsImpliesNoEDNS(self): m = dns.message.make_query('foo', 'A') self.failUnless(m.edns == -1) def test_SettingEDNSFlagsImpliesEDNS(self): m = dns.message.make_query('foo', 'A', ednsflags=dns.flags.DO) self.failUnless(m.edns == 0) def test_SettingEDNSPayloadImpliesEDNS(self): m = dns.message.make_query('foo', 'A', payload=4096) self.failUnless(m.edns == 0) def test_SettingEDNSRequestPayloadImpliesEDNS(self): m = dns.message.make_query('foo', 'A', request_payload=4096) self.failUnless(m.edns == 0) def test_SettingOptionsImpliesEDNS(self): m = dns.message.make_query('foo', 'A', options=[]) self.failUnless(m.edns == 0) def test_FindRRset(self): a = dns.message.from_text(answer_text) n = dns.name.from_text('dnspython.org.') rrs1 = a.find_rrset(a.answer, n, dns.rdataclass.IN, dns.rdatatype.SOA) rrs2 = a.find_rrset(dns.message.ANSWER, n, dns.rdataclass.IN, dns.rdatatype.SOA) self.failUnless(rrs1 == rrs2) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_name.py000066400000000000000000000705001340301174400173170ustar00rootroot00000000000000# -*- coding: utf-8 # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import print_function from typing import Dict # pylint: disable=unused-import import unittest from io import BytesIO import dns.name import dns.reversename import dns.e164 # pylint: disable=line-too-long,unsupported-assignment-operation class NameTestCase(unittest.TestCase): def setUp(self): self.origin = dns.name.from_text('example.') def testFromTextRel1(self): n = dns.name.from_text('foo.bar') self.assertEqual(n.labels, (b'foo', b'bar', b'')) def testFromTextRel2(self): n = dns.name.from_text('foo.bar', origin=self.origin) self.assertEqual(n.labels, (b'foo', b'bar', b'example', b'')) def testFromTextRel3(self): n = dns.name.from_text('foo.bar', origin=None) self.assertEqual(n.labels, (b'foo', b'bar')) def testFromTextRel4(self): n = dns.name.from_text('@', origin=None) self.failUnless(n == dns.name.empty) def testFromTextRel5(self): n = dns.name.from_text('@', origin=self.origin) self.failUnless(n == self.origin) def testFromTextAbs1(self): n = dns.name.from_text('foo.bar.') self.assertEqual(n.labels, (b'foo', b'bar', b'')) def testTortureFromText(self): good = [ br'.', br'a', br'a.', br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', br'\000.\008.\010.\032.\046.\092.\099.\255', br'\\', br'\..\.', br'\\.\\', br'!"#%&/()=+-', br'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255', ] bad = [ br'..', br'.a', br'\\..', b'\\', # yes, we don't want the 'r' prefix! br'\0', br'\00', br'\00Z', br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', br'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255', ] for t in good: try: dns.name.from_text(t) except Exception: self.fail("good test '%s' raised an exception" % t) for t in bad: caught = False try: dns.name.from_text(t) except Exception: caught = True if not caught: self.fail("bad test '%s' did not raise an exception" % t) def testImmutable1(self): def bad(): self.origin.labels = () self.failUnlessRaises(TypeError, bad) def testImmutable2(self): def bad(): self.origin.labels[0] = 'foo' self.failUnlessRaises(TypeError, bad) def testAbs1(self): self.failUnless(dns.name.root.is_absolute()) def testAbs2(self): self.failUnless(not dns.name.empty.is_absolute()) def testAbs3(self): self.failUnless(self.origin.is_absolute()) def testAbs4(self): n = dns.name.from_text('foo', origin=None) self.failUnless(not n.is_absolute()) def testWild1(self): n = dns.name.from_text('*.foo', origin=None) self.failUnless(n.is_wild()) def testWild2(self): n = dns.name.from_text('*a.foo', origin=None) self.failUnless(not n.is_wild()) def testWild3(self): n = dns.name.from_text('a.*.foo', origin=None) self.failUnless(not n.is_wild()) def testWild4(self): self.failUnless(not dns.name.root.is_wild()) def testWild5(self): self.failUnless(not dns.name.empty.is_wild()) def testHash1(self): n1 = dns.name.from_text('fOo.COM') n2 = dns.name.from_text('foo.com') self.assertEqual(hash(n1), hash(n2)) def testCompare1(self): n1 = dns.name.from_text('a') n2 = dns.name.from_text('b') self.failUnless(n1 < n2) self.failUnless(n2 > n1) def testCompare2(self): n1 = dns.name.from_text('') n2 = dns.name.from_text('b') self.failUnless(n1 < n2) self.failUnless(n2 > n1) def testCompare3(self): self.failUnless(dns.name.empty < dns.name.root) self.failUnless(dns.name.root > dns.name.empty) def testCompare4(self): self.failUnless(dns.name.root != 1) def testSubdomain1(self): self.failUnless(not dns.name.empty.is_subdomain(dns.name.root)) def testSubdomain2(self): self.failUnless(not dns.name.root.is_subdomain(dns.name.empty)) def testSubdomain3(self): n = dns.name.from_text('foo', origin=self.origin) self.failUnless(n.is_subdomain(self.origin)) def testSubdomain4(self): n = dns.name.from_text('foo', origin=self.origin) self.failUnless(n.is_subdomain(dns.name.root)) def testSubdomain5(self): n = dns.name.from_text('foo', origin=self.origin) self.failUnless(n.is_subdomain(n)) def testSuperdomain1(self): self.failUnless(not dns.name.empty.is_superdomain(dns.name.root)) def testSuperdomain2(self): self.failUnless(not dns.name.root.is_superdomain(dns.name.empty)) def testSuperdomain3(self): n = dns.name.from_text('foo', origin=self.origin) self.failUnless(self.origin.is_superdomain(n)) def testSuperdomain4(self): n = dns.name.from_text('foo', origin=self.origin) self.failUnless(dns.name.root.is_superdomain(n)) def testSuperdomain5(self): n = dns.name.from_text('foo', origin=self.origin) self.failUnless(n.is_superdomain(n)) def testCanonicalize1(self): n = dns.name.from_text('FOO.bar', origin=self.origin) c = n.canonicalize() self.assertEqual(c.labels, (b'foo', b'bar', b'example', b'')) def testToText1(self): n = dns.name.from_text('FOO.bar', origin=self.origin) t = n.to_text() self.assertEqual(t, 'FOO.bar.example.') def testToText2(self): n = dns.name.from_text('FOO.bar', origin=self.origin) t = n.to_text(True) self.assertEqual(t, 'FOO.bar.example') def testToText3(self): n = dns.name.from_text('FOO.bar', origin=None) t = n.to_text() self.assertEqual(t, 'FOO.bar') def testToText4(self): t = dns.name.empty.to_text() self.assertEqual(t, '@') def testToText5(self): t = dns.name.root.to_text() self.assertEqual(t, '.') def testToText6(self): n = dns.name.from_text('FOO bar', origin=None) t = n.to_text() self.assertEqual(t, r'FOO\032bar') def testToText7(self): n = dns.name.from_text(r'FOO\.bar', origin=None) t = n.to_text() self.assertEqual(t, 'FOO\.bar') def testToText8(self): n = dns.name.from_text(r'\070OO\.bar', origin=None) t = n.to_text() self.assertEqual(t, 'FOO\.bar') def testToText9(self): n = dns.name.from_text('FOO bar', origin=None) t = n.to_unicode() self.assertEqual(t, 'FOO\\032bar') def testToText10(self): t = dns.name.empty.to_unicode() self.assertEqual(t, '@') def testToText11(self): t = dns.name.root.to_unicode() self.assertEqual(t, '.') def testSlice1(self): n = dns.name.from_text(r'a.b.c.', origin=None) s = n[:] self.assertEqual(s, (b'a', b'b', b'c', b'')) def testSlice2(self): n = dns.name.from_text(r'a.b.c.', origin=None) s = n[:2] self.assertEqual(s, (b'a', b'b')) def testSlice3(self): n = dns.name.from_text(r'a.b.c.', origin=None) s = n[2:] self.assertEqual(s, (b'c', b'')) def testEmptyLabel1(self): def bad(): dns.name.Name(['a', '', 'b']) self.failUnlessRaises(dns.name.EmptyLabel, bad) def testEmptyLabel2(self): def bad(): dns.name.Name(['', 'b']) self.failUnlessRaises(dns.name.EmptyLabel, bad) def testEmptyLabel3(self): n = dns.name.Name(['b', '']) self.failUnless(n) def testLongLabel(self): n = dns.name.Name(['a' * 63]) self.failUnless(n) def testLabelTooLong(self): def bad(): dns.name.Name(['a' * 64, 'b']) self.failUnlessRaises(dns.name.LabelTooLong, bad) def testLongName(self): n = dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 62]) self.failUnless(n) def testNameTooLong(self): def bad(): dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 63]) self.failUnlessRaises(dns.name.NameTooLong, bad) def testConcat1(self): n1 = dns.name.Name(['a', 'b']) n2 = dns.name.Name(['c', 'd']) e = dns.name.Name(['a', 'b', 'c', 'd']) r = n1 + n2 self.failUnless(r == e) def testConcat2(self): n1 = dns.name.Name(['a', 'b']) n2 = dns.name.Name([]) e = dns.name.Name(['a', 'b']) r = n1 + n2 self.failUnless(r == e) def testConcat3(self): n1 = dns.name.Name([]) n2 = dns.name.Name(['a', 'b']) e = dns.name.Name(['a', 'b']) r = n1 + n2 self.failUnless(r == e) def testConcat4(self): n1 = dns.name.Name(['a', 'b', '']) n2 = dns.name.Name([]) e = dns.name.Name(['a', 'b', '']) r = n1 + n2 self.failUnless(r == e) def testConcat5(self): n1 = dns.name.Name(['a', 'b']) n2 = dns.name.Name(['c', '']) e = dns.name.Name(['a', 'b', 'c', '']) r = n1 + n2 self.failUnless(r == e) def testConcat6(self): def bad(): n1 = dns.name.Name(['a', 'b', '']) n2 = dns.name.Name(['c']) return n1 + n2 self.failUnlessRaises(dns.name.AbsoluteConcatenation, bad) def testBadEscape(self): def bad(): n = dns.name.from_text(r'a.b\0q1.c.') print(n) self.failUnlessRaises(dns.name.BadEscape, bad) def testDigestable1(self): n = dns.name.from_text('FOO.bar') d = n.to_digestable() self.assertEqual(d, b'\x03foo\x03bar\x00') def testDigestable2(self): n1 = dns.name.from_text('FOO.bar') n2 = dns.name.from_text('foo.BAR.') d1 = n1.to_digestable() d2 = n2.to_digestable() self.failUnless(d1 == d2) def testDigestable3(self): d = dns.name.root.to_digestable() self.assertEqual(d, b'\x00') def testDigestable4(self): n = dns.name.from_text('FOO.bar', None) d = n.to_digestable(dns.name.root) self.assertEqual(d, b'\x03foo\x03bar\x00') def testBadDigestable(self): def bad(): n = dns.name.from_text('FOO.bar', None) n.to_digestable() self.failUnlessRaises(dns.name.NeedAbsoluteNameOrOrigin, bad) def testToWire1(self): n = dns.name.from_text('FOO.bar') f = BytesIO() compress = {} # type: Dict[dns.name.Name,int] n.to_wire(f, compress) self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00') def testToWire2(self): n = dns.name.from_text('FOO.bar') f = BytesIO() compress = {} # type: Dict[dns.name.Name,int] n.to_wire(f, compress) n.to_wire(f, compress) self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\xc0\x00') def testToWire3(self): n1 = dns.name.from_text('FOO.bar') n2 = dns.name.from_text('foo.bar') f = BytesIO() compress = {} # type: Dict[dns.name.Name,int] n1.to_wire(f, compress) n2.to_wire(f, compress) self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\xc0\x00') def testToWire4(self): n1 = dns.name.from_text('FOO.bar') n2 = dns.name.from_text('a.foo.bar') f = BytesIO() compress = {} # type: Dict[dns.name.Name,int] n1.to_wire(f, compress) n2.to_wire(f, compress) self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\x01\x61\xc0\x00') def testToWire5(self): n1 = dns.name.from_text('FOO.bar') n2 = dns.name.from_text('a.foo.bar') f = BytesIO() compress = {} # type: Dict[dns.name.Name,int] n1.to_wire(f, compress) n2.to_wire(f, None) self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\x01\x61\x03foo\x03bar\x00') def testToWire6(self): n = dns.name.from_text('FOO.bar') v = n.to_wire() self.assertEqual(v, b'\x03FOO\x03bar\x00') def testBadToWire(self): def bad(): n = dns.name.from_text('FOO.bar', None) f = BytesIO() compress = {} # type: Dict[dns.name.Name,int] n.to_wire(f, compress) self.failUnlessRaises(dns.name.NeedAbsoluteNameOrOrigin, bad) def testSplit1(self): n = dns.name.from_text('foo.bar.') (prefix, suffix) = n.split(2) ep = dns.name.from_text('foo', None) es = dns.name.from_text('bar.', None) self.failUnless(prefix == ep and suffix == es) def testSplit2(self): n = dns.name.from_text('foo.bar.') (prefix, suffix) = n.split(1) ep = dns.name.from_text('foo.bar', None) es = dns.name.from_text('.', None) self.failUnless(prefix == ep and suffix == es) def testSplit3(self): n = dns.name.from_text('foo.bar.') (prefix, suffix) = n.split(0) ep = dns.name.from_text('foo.bar.', None) es = dns.name.from_text('', None) self.failUnless(prefix == ep and suffix == es) def testSplit4(self): n = dns.name.from_text('foo.bar.') (prefix, suffix) = n.split(3) ep = dns.name.from_text('', None) es = dns.name.from_text('foo.bar.', None) self.failUnless(prefix == ep and suffix == es) def testBadSplit1(self): def bad(): n = dns.name.from_text('foo.bar.') n.split(-1) self.failUnlessRaises(ValueError, bad) def testBadSplit2(self): def bad(): n = dns.name.from_text('foo.bar.') n.split(4) self.failUnlessRaises(ValueError, bad) def testRelativize1(self): n = dns.name.from_text('a.foo.bar.', None) o = dns.name.from_text('bar.', None) e = dns.name.from_text('a.foo', None) self.failUnless(n.relativize(o) == e) def testRelativize2(self): n = dns.name.from_text('a.foo.bar.', None) o = n e = dns.name.empty self.failUnless(n.relativize(o) == e) def testRelativize3(self): n = dns.name.from_text('a.foo.bar.', None) o = dns.name.from_text('blaz.', None) e = n self.failUnless(n.relativize(o) == e) def testRelativize4(self): n = dns.name.from_text('a.foo', None) o = dns.name.root e = n self.failUnless(n.relativize(o) == e) def testDerelativize1(self): n = dns.name.from_text('a.foo', None) o = dns.name.from_text('bar.', None) e = dns.name.from_text('a.foo.bar.', None) self.failUnless(n.derelativize(o) == e) def testDerelativize2(self): n = dns.name.empty o = dns.name.from_text('a.foo.bar.', None) e = o self.failUnless(n.derelativize(o) == e) def testDerelativize3(self): n = dns.name.from_text('a.foo.bar.', None) o = dns.name.from_text('blaz.', None) e = n self.failUnless(n.derelativize(o) == e) def testChooseRelativity1(self): n = dns.name.from_text('a.foo.bar.', None) o = dns.name.from_text('bar.', None) e = dns.name.from_text('a.foo', None) self.failUnless(n.choose_relativity(o, True) == e) def testChooseRelativity2(self): n = dns.name.from_text('a.foo.bar.', None) o = dns.name.from_text('bar.', None) e = n self.failUnless(n.choose_relativity(o, False) == e) def testChooseRelativity3(self): n = dns.name.from_text('a.foo', None) o = dns.name.from_text('bar.', None) e = dns.name.from_text('a.foo.bar.', None) self.failUnless(n.choose_relativity(o, False) == e) def testChooseRelativity4(self): n = dns.name.from_text('a.foo', None) o = None e = n self.failUnless(n.choose_relativity(o, True) == e) def testChooseRelativity5(self): n = dns.name.from_text('a.foo', None) o = None e = n self.failUnless(n.choose_relativity(o, False) == e) def testChooseRelativity6(self): n = dns.name.from_text('a.foo.', None) o = None e = n self.failUnless(n.choose_relativity(o, True) == e) def testChooseRelativity7(self): n = dns.name.from_text('a.foo.', None) o = None e = n self.failUnless(n.choose_relativity(o, False) == e) def testFromWire1(self): w = b'\x03foo\x00\xc0\x00' (n1, cused1) = dns.name.from_wire(w, 0) (n2, cused2) = dns.name.from_wire(w, cused1) en1 = dns.name.from_text('foo.') en2 = en1 ecused1 = 5 ecused2 = 2 self.failUnless(n1 == en1 and cused1 == ecused1 and \ n2 == en2 and cused2 == ecused2) def testFromWire2(self): w = b'\x03foo\x00\x01a\xc0\x00\x01b\xc0\x05' current = 0 (n1, cused1) = dns.name.from_wire(w, current) current += cused1 (n2, cused2) = dns.name.from_wire(w, current) current += cused2 (n3, cused3) = dns.name.from_wire(w, current) en1 = dns.name.from_text('foo.') en2 = dns.name.from_text('a.foo.') en3 = dns.name.from_text('b.a.foo.') ecused1 = 5 ecused2 = 4 ecused3 = 4 self.failUnless(n1 == en1 and cused1 == ecused1 and \ n2 == en2 and cused2 == ecused2 and \ n3 == en3 and cused3 == ecused3) def testBadFromWire1(self): def bad(): w = b'\x03foo\xc0\x04' dns.name.from_wire(w, 0) self.failUnlessRaises(dns.name.BadPointer, bad) def testBadFromWire2(self): def bad(): w = b'\x03foo\xc0\x05' dns.name.from_wire(w, 0) self.failUnlessRaises(dns.name.BadPointer, bad) def testBadFromWire3(self): def bad(): w = b'\xbffoo' dns.name.from_wire(w, 0) self.failUnlessRaises(dns.name.BadLabelType, bad) def testBadFromWire4(self): def bad(): w = b'\x41foo' dns.name.from_wire(w, 0) self.failUnlessRaises(dns.name.BadLabelType, bad) def testParent1(self): n = dns.name.from_text('foo.bar.') self.failUnless(n.parent() == dns.name.from_text('bar.')) self.failUnless(n.parent().parent() == dns.name.root) def testParent2(self): n = dns.name.from_text('foo.bar', None) self.failUnless(n.parent() == dns.name.from_text('bar', None)) self.failUnless(n.parent().parent() == dns.name.empty) def testParent3(self): def bad(): n = dns.name.root n.parent() self.failUnlessRaises(dns.name.NoParent, bad) def testParent4(self): def bad(): n = dns.name.empty n.parent() self.failUnlessRaises(dns.name.NoParent, bad) def testFromUnicode1(self): n = dns.name.from_text(u'foo.bar') self.assertEqual(n.labels, (b'foo', b'bar', b'')) def testFromUnicode2(self): n = dns.name.from_text(u'foo\u1234bar.bar') self.assertEqual(n.labels, (b'xn--foobar-r5z', b'bar', b'')) def testFromUnicodeAlternateDot1(self): n = dns.name.from_text(u'foo\u3002bar') self.assertEqual(n.labels, (b'foo', b'bar', b'')) def testFromUnicodeAlternateDot2(self): n = dns.name.from_text(u'foo\uff0ebar') self.assertEqual(n.labels, (b'foo', b'bar', b'')) def testFromUnicodeAlternateDot3(self): n = dns.name.from_text(u'foo\uff61bar') self.assertEqual(n.labels, (b'foo', b'bar', b'')) def testFromUnicodeIDNA2003Explicit(self): t = u'Königsgäßchen' e = dns.name.from_unicode(t, idna_codec=dns.name.IDNA_2003) self.assertEqual(str(e), 'xn--knigsgsschen-lcb0w.') def testFromUnicodeIDNA2003Default(self): t = u'Königsgäßchen' e = dns.name.from_unicode(t) self.assertEqual(str(e), 'xn--knigsgsschen-lcb0w.') def testFromUnicodeIDNA2008(self): if dns.name.have_idna_2008: t = u'Königsgäßchen' def bad(): codec = dns.name.IDNA_2008_Strict return dns.name.from_unicode(t, idna_codec=codec) self.failUnlessRaises(dns.name.IDNAException, bad) e1 = dns.name.from_unicode(t, idna_codec=dns.name.IDNA_2008) self.assertEqual(str(e1), 'xn--knigsgchen-b4a3dun.') c2 = dns.name.IDNA_2008_Transitional e2 = dns.name.from_unicode(t, idna_codec=c2) self.assertEqual(str(e2), 'xn--knigsgsschen-lcb0w.') def testFromUnicodeIDNA2008Mixed(self): # the IDN rules for names are very restrictive, disallowing # practical names like u'_sip._tcp.Königsgäßchen'. Dnspython # has a "practical" mode which permits labels which are purely # ASCII to go straight through, and thus not invalid useful # things in the real world. if dns.name.have_idna_2008: t = u'_sip._tcp.Königsgäßchen' def bad1(): codec = dns.name.IDNA_2008_Strict return dns.name.from_unicode(t, idna_codec=codec) def bad2(): codec = dns.name.IDNA_2008_UTS_46 return dns.name.from_unicode(t, idna_codec=codec) def bad3(): codec = dns.name.IDNA_2008_Transitional return dns.name.from_unicode(t, idna_codec=codec) self.failUnlessRaises(dns.name.IDNAException, bad1) self.failUnlessRaises(dns.name.IDNAException, bad2) self.failUnlessRaises(dns.name.IDNAException, bad3) e = dns.name.from_unicode(t, idna_codec=dns.name.IDNA_2008_Practical) self.assertEqual(str(e), '_sip._tcp.xn--knigsgchen-b4a3dun.') def testToUnicode1(self): n = dns.name.from_text(u'foo.bar') s = n.to_unicode() self.assertEqual(s, u'foo.bar.') def testToUnicode2(self): n = dns.name.from_text(u'foo\u1234bar.bar') s = n.to_unicode() self.assertEqual(s, u'foo\u1234bar.bar.') def testToUnicode3(self): n = dns.name.from_text('foo.bar') s = n.to_unicode() self.assertEqual(s, u'foo.bar.') def testToUnicode4(self): if dns.name.have_idna_2008: n = dns.name.from_text(u'ドメイン.テスト', idna_codec=dns.name.IDNA_2008) s = n.to_unicode() self.assertEqual(str(n), 'xn--eckwd4c7c.xn--zckzah.') self.assertEqual(s, u'ドメイン.テスト.') def testDefaultDecodeIsJustPunycode(self): # groß.com. in IDNA2008 form, pre-encoded. n = dns.name.from_text('xn--gro-7ka.com') # output using default codec which just decodes the punycode and # doesn't test for IDNA2003 or IDNA2008. self.assertEqual(n.to_unicode(), u'groß.com.') def testStrictINDA2003Decode(self): # groß.com. in IDNA2008 form, pre-encoded. n = dns.name.from_text('xn--gro-7ka.com') def bad(): # This throws in IDNA2003 because it doesn't "round trip". n.to_unicode(idna_codec=dns.name.IDNA_2003_Strict) self.failUnlessRaises(dns.name.IDNAException, bad) def testReverseIPv4(self): e = dns.name.from_text('1.0.0.127.in-addr.arpa.') n = dns.reversename.from_address('127.0.0.1') self.assertEqual(e, n) def testReverseIPv6(self): e = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') n = dns.reversename.from_address(b'::1') self.assertEqual(e, n) def testReverseIPv6MappedIpv4(self): e = dns.name.from_text('1.0.0.127.in-addr.arpa.') n = dns.reversename.from_address('::ffff:127.0.0.1') self.failUnless(e == n) def testBadReverseIPv4(self): def bad(): dns.reversename.from_address('127.0.foo.1') self.failUnlessRaises(dns.exception.SyntaxError, bad) def testBadReverseIPv6(self): def bad(): dns.reversename.from_address('::1::1') self.failUnlessRaises(dns.exception.SyntaxError, bad) def testForwardIPv4(self): n = dns.name.from_text('1.0.0.127.in-addr.arpa.') e = '127.0.0.1' text = dns.reversename.to_address(n) self.assertEqual(text, e) def testForwardIPv6(self): n = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.') e = '::1' text = dns.reversename.to_address(n) self.assertEqual(text, e) def testE164ToEnum(self): text = '+1 650 555 1212' e = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') n = dns.e164.from_e164(text) self.failUnless(n == e) def testEnumToE164(self): n = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.') e = '+16505551212' text = dns.e164.to_e164(n) self.assertEqual(text, e) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_namedict.py000066400000000000000000000066721340301174400201740ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.name import dns.namedict class NameTestCase(unittest.TestCase): def setUp(self): self.ndict = dns.namedict.NameDict() n1 = dns.name.from_text('foo.bar.') n2 = dns.name.from_text('bar.') self.ndict[n1] = 1 self.ndict[n2] = 2 self.rndict = dns.namedict.NameDict() n1 = dns.name.from_text('foo.bar', None) n2 = dns.name.from_text('bar', None) self.rndict[n1] = 1 self.rndict[n2] = 2 def testDepth(self): self.failUnless(self.ndict.max_depth == 3) def testLookup1(self): k = dns.name.from_text('foo.bar.') self.failUnless(self.ndict[k] == 1) def testLookup2(self): k = dns.name.from_text('foo.bar.') self.failUnless(self.ndict.get_deepest_match(k)[1] == 1) def testLookup3(self): k = dns.name.from_text('a.b.c.foo.bar.') self.failUnless(self.ndict.get_deepest_match(k)[1] == 1) def testLookup4(self): k = dns.name.from_text('a.b.c.bar.') self.failUnless(self.ndict.get_deepest_match(k)[1] == 2) def testLookup5(self): def bad(): n = dns.name.from_text('a.b.c.') self.ndict.get_deepest_match(n) self.failUnlessRaises(KeyError, bad) def testLookup6(self): def bad(): self.ndict.get_deepest_match(dns.name.empty) self.failUnlessRaises(KeyError, bad) def testLookup7(self): self.ndict[dns.name.empty] = 100 n = dns.name.from_text('a.b.c.') v = self.ndict.get_deepest_match(n)[1] self.failUnless(v == 100) def testLookup8(self): def bad(): self.ndict['foo'] = 100 self.failUnlessRaises(ValueError, bad) def testRelDepth(self): self.failUnless(self.rndict.max_depth == 2) def testRelLookup1(self): k = dns.name.from_text('foo.bar', None) self.failUnless(self.rndict[k] == 1) def testRelLookup2(self): k = dns.name.from_text('foo.bar', None) self.failUnless(self.rndict.get_deepest_match(k)[1] == 1) def testRelLookup3(self): k = dns.name.from_text('a.b.c.foo.bar', None) self.failUnless(self.rndict.get_deepest_match(k)[1] == 1) def testRelLookup4(self): k = dns.name.from_text('a.b.c.bar', None) self.failUnless(self.rndict.get_deepest_match(k)[1] == 2) def testRelLookup7(self): self.rndict[dns.name.empty] = 100 n = dns.name.from_text('a.b.c', None) v = self.rndict.get_deepest_match(n)[1] self.failUnless(v == 100) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_nsec3.py000066400000000000000000000030331340301174400174070ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.rdata import dns.rdataclass import dns.rdatatype import dns.rdtypes.ANY.TXT import dns.ttl class NSEC3TestCase(unittest.TestCase): def test_NSEC3_bitmap(self): rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NSEC3, u"1 0 100 ABCD SCBCQHKU35969L2A68P3AD59LHF30715 A CAA TYPE65534") bitmap = bytearray(b'\0' * 32) bitmap[31] = bitmap[31] | 2 self.assertEqual(rdata.windows, [(0, bytearray(b'@')), (1, bytearray(b'@')), # CAA = 257 (255, bitmap) ]) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_ntoaaton.py000066400000000000000000000160671340301174400202320ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from __future__ import print_function import unittest import binascii import dns.exception import dns.ipv4 import dns.ipv6 import dns.inet # for convenience aton4 = dns.ipv4.inet_aton ntoa4 = dns.ipv4.inet_ntoa aton6 = dns.ipv6.inet_aton ntoa6 = dns.ipv6.inet_ntoa v4_bad_addrs = ['256.1.1.1', '1.1.1', '1.1.1.1.1', '+1.1.1.1', '1.1.1.1+', '1..2.3.4', '.1.2.3.4', '1.2.3.4.'] class NtoAAtoNTestCase(unittest.TestCase): def test_aton1(self): a = aton6('::') self.failUnless(a == b'\x00' * 16) def test_aton2(self): a = aton6('::1') self.failUnless(a == b'\x00' * 15 + b'\x01') def test_aton3(self): a = aton6('::10.0.0.1') self.failUnless(a == b'\x00' * 12 + b'\x0a\x00\x00\x01') def test_aton4(self): a = aton6('abcd::dcba') self.failUnless(a == b'\xab\xcd' + b'\x00' * 12 + b'\xdc\xba') def test_aton5(self): a = aton6('1:2:3:4:5:6:7:8') self.assertEqual(a, binascii.unhexlify(b'00010002000300040005000600070008')) def test_bad_aton1(self): def bad(): aton6('abcd:dcba') self.failUnlessRaises(dns.exception.SyntaxError, bad) def test_bad_aton2(self): def bad(): aton6('abcd::dcba::1') self.failUnlessRaises(dns.exception.SyntaxError, bad) def test_bad_aton3(self): def bad(): aton6('1:2:3:4:5:6:7:8:9') self.failUnlessRaises(dns.exception.SyntaxError, bad) def test_aton6(self): a = aton6('::') self.assertEqual(a, b'\x00' * 16) def test_aton7(self): a = aton6('::1') self.assertEqual(a, b'\x00' * 15 + b'\x01') def test_aton8(self): a = aton6('::10.0.0.1') self.assertEqual(a, b'\x00' * 12 + b'\x0a\x00\x00\x01') def test_aton9(self): a = aton6('abcd::dcba') self.assertEqual(a, b'\xab\xcd' + b'\x00' * 12 + b'\xdc\xba') def test_ntoa1(self): b = binascii.unhexlify(b'00010002000300040005000600070008') t = ntoa6(b) self.assertEqual(t, '1:2:3:4:5:6:7:8') def test_ntoa2(self): b = b'\x00' * 16 t = ntoa6(b) self.assertEqual(t, '::') def test_ntoa3(self): b = b'\x00' * 15 + b'\x01' t = ntoa6(b) self.assertEqual(t, '::1') def test_ntoa4(self): b = b'\x80' + b'\x00' * 15 t = ntoa6(b) self.assertEqual(t, '8000::') def test_ntoa5(self): b = b'\x01\xcd' + b'\x00' * 12 + b'\x03\xef' t = ntoa6(b) self.assertEqual(t, '1cd::3ef') def test_ntoa6(self): b = binascii.unhexlify(b'ffff00000000ffff000000000000ffff') t = ntoa6(b) self.assertEqual(t, 'ffff:0:0:ffff::ffff') def test_ntoa7(self): b = binascii.unhexlify(b'00000000ffff000000000000ffffffff') t = ntoa6(b) self.assertEqual(t, '0:0:ffff::ffff:ffff') def test_ntoa8(self): b = binascii.unhexlify(b'ffff0000ffff00000000ffff00000000') t = ntoa6(b) self.assertEqual(t, 'ffff:0:ffff::ffff:0:0') def test_ntoa9(self): b = binascii.unhexlify(b'0000000000000000000000000a000001') t = ntoa6(b) self.assertEqual(t, '::10.0.0.1') def test_ntoa10(self): b = binascii.unhexlify(b'0000000000000000000000010a000001') t = ntoa6(b) self.assertEqual(t, '::1:a00:1') def test_ntoa11(self): b = binascii.unhexlify(b'00000000000000000000ffff0a000001') t = ntoa6(b) self.assertEqual(t, '::ffff:10.0.0.1') def test_ntoa12(self): b = binascii.unhexlify(b'000000000000000000000000ffffffff') t = ntoa6(b) self.assertEqual(t, '::255.255.255.255') def test_ntoa13(self): b = binascii.unhexlify(b'00000000000000000000ffffffffffff') t = ntoa6(b) self.assertEqual(t, '::ffff:255.255.255.255') def test_ntoa14(self): b = binascii.unhexlify(b'0000000000000000000000000001ffff') t = ntoa6(b) self.assertEqual(t, '::0.1.255.255') def test_bad_ntoa1(self): def bad(): ntoa6('') self.failUnlessRaises(ValueError, bad) def test_bad_ntoa2(self): def bad(): ntoa6('\x00' * 17) self.failUnlessRaises(ValueError, bad) def test_good_v4_aton(self): pairs = [('1.2.3.4', b'\x01\x02\x03\x04'), ('255.255.255.255', b'\xff\xff\xff\xff'), ('0.0.0.0', b'\x00\x00\x00\x00')] for (t, b) in pairs: b1 = aton4(t) t1 = ntoa4(b1) self.assertEqual(b1, b) self.assertEqual(t1, t) def test_bad_v4_aton(self): def make_bad(a): def bad(): return aton4(a) return bad for addr in v4_bad_addrs: print(addr) self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr)) def test_bad_v6_aton(self): addrs = ['+::0', '0::0::', '::0::', '1:2:3:4:5:6:7:8:9', ':::::::'] embedded = ['::' + x for x in v4_bad_addrs] addrs.extend(embedded) def make_bad(a): def bad(): x = aton6(a) return bad for addr in addrs: self.failUnlessRaises(dns.exception.SyntaxError, make_bad(addr)) def test_rfc5952_section_4_2_2(self): addr = '2001:db8:0:1:1:1:1:1' b1 = aton6(addr) t1 = ntoa6(b1) self.assertEqual(t1, addr) def test_is_mapped(self): t1 = '2001:db8:0:1:1:1:1:1' t2 = '::ffff:127.0.0.1' t3 = '1::ffff:127.0.0.1' self.failIf(dns.ipv6.is_mapped(aton6(t1))) self.failUnless(dns.ipv6.is_mapped(aton6(t2))) self.failIf(dns.ipv6.is_mapped(aton6(t3))) def test_is_multicast(self): t1 = '223.0.0.1' t2 = '240.0.0.1' t3 = '224.0.0.1' t4 = '239.0.0.1' t5 = 'fe00::1' t6 = 'ff00::1' self.failIf(dns.inet.is_multicast(t1)) self.failIf(dns.inet.is_multicast(t2)) self.failUnless(dns.inet.is_multicast(t3)) self.failUnless(dns.inet.is_multicast(t4)) self.failIf(dns.inet.is_multicast(t5)) self.failUnless(dns.inet.is_multicast(t6)) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_rdata.py000066400000000000000000000037371340301174400175020ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.rdata import dns.rdataclass import dns.rdatatype import tests.ttxt_module class RdataTestCase(unittest.TestCase): def test_str(self): rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4") self.failUnless(rdata.address == "1.2.3.4") def test_unicode(self): rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, u"1.2.3.4") self.failUnless(rdata.address == "1.2.3.4") def test_module_registration(self): TTXT = 64001 dns.rdata.register_type(tests.ttxt_module, TTXT, 'TTXT') rdata = dns.rdata.from_text(dns.rdataclass.IN, TTXT, 'hello world') self.failUnless(rdata.strings == [b'hello', b'world']) self.failUnless(dns.rdatatype.to_text(TTXT) == 'TTXT') self.failUnless(dns.rdatatype.from_text('TTXT') == TTXT) def test_module_reregistration(self): def bad(): TTXTTWO = dns.rdatatype.TXT dns.rdata.register_type(tests.ttxt_module, TTXTTWO, 'TTXTTWO') self.failUnlessRaises(dns.rdata.RdatatypeExists, bad) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_rdtypeandclass.py000066400000000000000000000103211340301174400214120ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.rdataclass import dns.rdatatype class RdTypeAndClassTestCase(unittest.TestCase): # Classes def test_class_meta1(self): self.failUnless(dns.rdataclass.is_metaclass(dns.rdataclass.ANY)) def test_class_meta2(self): self.failUnless(not dns.rdataclass.is_metaclass(dns.rdataclass.IN)) def test_class_bytext1(self): self.failUnless(dns.rdataclass.from_text('IN') == dns.rdataclass.IN) def test_class_bytext2(self): self.failUnless(dns.rdataclass.from_text('CLASS1') == dns.rdataclass.IN) def test_class_bytext_bounds1(self): self.failUnless(dns.rdataclass.from_text('CLASS0') == 0) self.failUnless(dns.rdataclass.from_text('CLASS65535') == 65535) def test_class_bytext_bounds2(self): def bad(): dns.rdataclass.from_text('CLASS65536') self.failUnlessRaises(ValueError, bad) def test_class_bytext_unknown(self): def bad(): dns.rdataclass.from_text('XXX') self.failUnlessRaises(dns.rdataclass.UnknownRdataclass, bad) def test_class_totext1(self): self.failUnless(dns.rdataclass.to_text(dns.rdataclass.IN) == 'IN') def test_class_totext2(self): self.failUnless(dns.rdataclass.to_text(999) == 'CLASS999') def test_class_totext_bounds1(self): def bad(): dns.rdataclass.to_text(-1) self.failUnlessRaises(ValueError, bad) def test_class_totext_bounds2(self): def bad(): dns.rdataclass.to_text(65536) self.failUnlessRaises(ValueError, bad) # Types def test_type_meta1(self): self.failUnless(dns.rdatatype.is_metatype(dns.rdatatype.ANY)) def test_type_meta2(self): self.failUnless(dns.rdatatype.is_metatype(dns.rdatatype.OPT)) def test_type_meta3(self): self.failUnless(not dns.rdatatype.is_metatype(dns.rdatatype.A)) def test_type_singleton1(self): self.failUnless(dns.rdatatype.is_singleton(dns.rdatatype.SOA)) def test_type_singleton2(self): self.failUnless(not dns.rdatatype.is_singleton(dns.rdatatype.A)) def test_type_bytext1(self): self.failUnless(dns.rdatatype.from_text('A') == dns.rdatatype.A) def test_type_bytext2(self): self.failUnless(dns.rdatatype.from_text('TYPE1') == dns.rdatatype.A) def test_type_bytext_bounds1(self): self.failUnless(dns.rdatatype.from_text('TYPE0') == 0) self.failUnless(dns.rdatatype.from_text('TYPE65535') == 65535) def test_type_bytext_bounds2(self): def bad(): dns.rdatatype.from_text('TYPE65536') self.failUnlessRaises(ValueError, bad) def test_type_bytext_unknown(self): def bad(): dns.rdatatype.from_text('XXX') self.failUnlessRaises(dns.rdatatype.UnknownRdatatype, bad) def test_type_totext1(self): self.failUnless(dns.rdatatype.to_text(dns.rdatatype.A) == 'A') def test_type_totext2(self): self.failUnless(dns.rdatatype.to_text(999) == 'TYPE999') def test_type_totext_bounds1(self): def bad(): dns.rdatatype.to_text(-1) self.failUnlessRaises(ValueError, bad) def test_type_totext_bounds2(self): def bad(): dns.rdatatype.to_text(65536) self.failUnlessRaises(ValueError, bad) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_rdtypeanydnskey.py000066400000000000000000000060221340301174400216320ustar00rootroot00000000000000# Copyright (C) 2014 Red Hat, Inc. # Author: Petr Spacek # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.rrset import dns.rdtypes.ANY.DNSKEY from typing import Set # pylint: disable=unused-import class RdtypeAnyDnskeyTestCase(unittest.TestCase): def testFlagsEmpty(self): # type: () -> None '''Test DNSKEY flag to/from text conversion for zero flag/empty set.''' good_s = set() #type: Set[str] good_f = 0 from_flags = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(good_f) self.failUnless(from_flags == good_s, '"{}" != "{}"'.format(from_flags, good_s)) from_set = dns.rdtypes.ANY.DNSKEY.flags_from_text_set(good_s) self.failUnless(from_set == good_f, '"0x{:x}" != "0x{:x}"'.format(from_set, good_f)) def testFlagsAll(self): # type: () -> None '''Test that all defined flags are recognized.''' good_s = {'SEP', 'REVOKE', 'ZONE'} good_f = 0x181 from_flags = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(good_f) self.failUnless(from_flags == good_s, '"{}" != "{}"'.format(from_flags, good_s)) from_text = dns.rdtypes.ANY.DNSKEY.flags_from_text_set(good_s) self.failUnless(from_text == good_f, '"0x{:x}" != "0x{:x}"'.format(from_text, good_f)) def testFlagsUnknownToText(self): # type: () -> None '''Test that undefined flags are returned in hexadecimal notation.''' unk_s = {'0x8000'} flags_s = dns.rdtypes.ANY.DNSKEY.flags_to_text_set(0x8000) self.failUnless(flags_s == unk_s, '"{}" != "{}"'.format(flags_s, unk_s)) def testFlagsUnknownToFlags(self): # type: () -> None '''Test that conversion from undefined mnemonic raises error.''' self.failUnlessRaises(NotImplementedError, dns.rdtypes.ANY.DNSKEY.flags_from_text_set, (['0x8000'])) def testFlagsRRToText(self): # type: () -> None '''Test that RR method returns correct flags.''' rr = dns.rrset.from_text('foo', 300, 'IN', 'DNSKEY', '257 3 8 KEY=')[0] rr_s = {'ZONE', 'SEP'} flags_s = rr.flags_to_text_set() self.failUnless(flags_s == rr_s, '"{}" != "{}"'.format(flags_s, rr_s)) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_rdtypeanyeui.py000066400000000000000000000220161340301174400211200ustar00rootroot00000000000000# Copyright (C) 2015 Red Hat, Inc. # Author: Petr Spacek # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest from io import BytesIO import dns.rrset import dns.rdtypes.ANY.EUI48 import dns.rdtypes.ANY.EUI64 import dns.exception class RdtypeAnyEUI48TestCase(unittest.TestCase): def testInstOk(self): '''Valid binary input.''' eui = b'\x01\x23\x45\x67\x89\xab' inst = dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN, dns.rdatatype.EUI48, eui) self.assertEqual(inst.eui, eui) def testInstLength(self): '''Incorrect input length.''' eui = b'\x01\x23\x45\x67\x89\xab\xcd' with self.assertRaises(dns.exception.FormError): dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN, dns.rdatatype.EUI48, eui) def testFromTextOk(self): '''Valid text input.''' r1 = dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '01-23-45-67-89-ab') eui = b'\x01\x23\x45\x67\x89\xab' self.assertEqual(r1[0].eui, eui) def testFromTextLength(self): '''Invalid input length.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '00-01-23-45-67-89-ab') def testFromTextDelim(self): '''Invalid delimiter.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '01_23-45-67-89-ab') def testFromTextExtraDash(self): '''Extra dash instead of hex digit.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '0--23-45-67-89-ab') def testFromTextMultipleTokens(self): '''Invalid input divided to multiple tokens.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '01 23-45-67-89-ab') def testFromTextInvalidHex(self): '''Invalid hexadecimal input.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI48', 'g0-23-45-67-89-ab') def testToTextOk(self): '''Valid text output.''' eui = b'\x01\x23\x45\x67\x89\xab' exp_text = '01-23-45-67-89-ab' inst = dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN, dns.rdatatype.EUI48, eui) text = inst.to_text() self.assertEqual(exp_text, text) def testToWire(self): '''Valid wire format.''' eui = b'\x01\x23\x45\x67\x89\xab' inst = dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN, dns.rdatatype.EUI48, eui) buff = BytesIO() inst.to_wire(buff) self.assertEqual(buff.getvalue(), eui) def testFromWireOk(self): '''Valid wire format.''' eui = b'\x01\x23\x45\x67\x89\xab' pad_len = 100 wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2) inst = dns.rdtypes.ANY.EUI48.EUI48.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI48, wire, pad_len, len(eui)) self.assertEqual(inst.eui, eui) def testFromWireLength(self): '''Valid wire format.''' eui = b'\x01\x23\x45\x67\x89' pad_len = 100 wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2) with self.assertRaises(dns.exception.FormError): dns.rdtypes.ANY.EUI48.EUI48.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI48, wire, pad_len, len(eui)) class RdtypeAnyEUI64TestCase(unittest.TestCase): def testInstOk(self): '''Valid binary input.''' eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef' inst = dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN, dns.rdatatype.EUI64, eui) self.assertEqual(inst.eui, eui) def testInstLength(self): '''Incorrect input length.''' eui = b'\x01\x23\x45\x67\x89\xab' with self.assertRaises(dns.exception.FormError): dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN, dns.rdatatype.EUI64, eui) def testFromTextOk(self): '''Valid text input.''' r1 = dns.rrset.from_text('foo', 300, 'IN', 'EUI64', '01-23-45-67-89-ab-cd-ef') eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef' self.assertEqual(r1[0].eui, eui) def testFromTextLength(self): '''Invalid input length.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI64', '01-23-45-67-89-ab') def testFromTextDelim(self): '''Invalid delimiter.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI64', '01_23-45-67-89-ab-cd-ef') def testFromTextExtraDash(self): '''Extra dash instead of hex digit.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI64', '0--23-45-67-89-ab-cd-ef') def testFromTextMultipleTokens(self): '''Invalid input divided to multiple tokens.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI64', '01 23-45-67-89-ab-cd-ef') def testFromTextInvalidHex(self): '''Invalid hexadecimal input.''' with self.assertRaises(dns.exception.SyntaxError): dns.rrset.from_text('foo', 300, 'IN', 'EUI64', 'g0-23-45-67-89-ab-cd-ef') def testToTextOk(self): '''Valid text output.''' eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef' exp_text = '01-23-45-67-89-ab-cd-ef' inst = dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN, dns.rdatatype.EUI64, eui) text = inst.to_text() self.assertEqual(exp_text, text) def testToWire(self): '''Valid wire format.''' eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef' inst = dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN, dns.rdatatype.EUI64, eui) buff = BytesIO() inst.to_wire(buff) self.assertEqual(buff.getvalue(), eui) def testFromWireOk(self): '''Valid wire format.''' eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef' pad_len = 100 wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2) inst = dns.rdtypes.ANY.EUI64.EUI64.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI64, wire, pad_len, len(eui)) self.assertEqual(inst.eui, eui) def testFromWireLength(self): '''Valid wire format.''' eui = b'\x01\x23\x45\x67\x89' pad_len = 100 wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2) with self.assertRaises(dns.exception.FormError): dns.rdtypes.ANY.EUI64.EUI64.from_wire(dns.rdataclass.IN, dns.rdatatype.EUI64, wire, pad_len, len(eui)) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_rdtypeanyloc.py000066400000000000000000000066021340301174400211160ustar00rootroot00000000000000# Copyright (C) 2014 Red Hat, Inc. # Author: Petr Spacek # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.rrset import dns.rdtypes.ANY.LOC class RdtypeAnyLocTestCase(unittest.TestCase): def testEqual1(self): '''Test default values for size, horizontal and vertical precision.''' r1 = dns.rrset.from_text('foo', 300, 'IN', 'LOC', '49 11 42.400 N 16 36 29.600 E 227.64m') r2 = dns.rrset.from_text('FOO', 600, 'in', 'loc', '49 11 42.400 N 16 36 29.600 E 227.64m ' '1.00m 10000.00m 10.00m') self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) def testEqual2(self): '''Test default values for size, horizontal and vertical precision.''' r1 = dns.rdtypes.ANY.LOC.LOC(1, 29, (49, 11, 42, 400, 1), (16, 36, 29, 600, 1), 22764.0) # centimeters r2 = dns.rdtypes.ANY.LOC.LOC(1, 29, (49, 11, 42, 400, 1), (16, 36, 29, 600, 1), 22764.0, # centimeters 100.0, 1000000.00, 1000.0) # centimeters self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) def testEqual3(self): '''Test size, horizontal and vertical precision parsers: 100 cm == 1 m. Parsers in from_text() and __init__() have to produce equal results.''' r1 = dns.rdtypes.ANY.LOC.LOC(1, 29, (49, 11, 42, 400, 1), (16, 36, 29, 600, 1), 22764.0, 200.0, 1000.00, 200.0) # centimeters r2 = dns.rrset.from_text('FOO', 600, 'in', 'loc', '49 11 42.400 N 16 36 29.600 E 227.64m ' '2.00m 10.00m 2.00m')[0] self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) def testEqual4(self): '''Test size, horizontal and vertical precision parsers without unit. Parsers in from_text() and __init__() have produce equal result for values with and without trailing "m".''' r1 = dns.rdtypes.ANY.LOC.LOC(1, 29, (49, 11, 42, 400, 1), (16, 36, 29, 600, 1), 22764.0, 200.0, 1000.00, 200.0) # centimeters r2 = dns.rrset.from_text('FOO', 600, 'in', 'loc', '49 11 42.400 N 16 36 29.600 E 227.64 ' '2 10 2')[0] # meters without explicit unit self.failUnless(r1 == r2, '"{}" != "{}"'.format(r1, r2)) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_resolver.py000066400000000000000000000403731340301174400202450ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2017 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from io import StringIO import select import sys import socket import time import unittest import dns.message import dns.name import dns.rdataclass import dns.rdatatype import dns.resolver from dns._compat import xrange, PY3 # Some tests require the internet to be available to run, so let's # skip those if it's not there. _network_available = True try: socket.gethostbyname('dnspython.org') except socket.gaierror: _network_available = False resolv_conf = u""" /t/t # comment 1 ; comment 2 domain foo nameserver 10.0.0.1 nameserver 10.0.0.2 """ message_text = """id 1234 opcode QUERY rcode NOERROR flags QR AA RD ;QUESTION example. IN A ;ANSWER example. 1 IN A 10.0.0.1 ;AUTHORITY ;ADDITIONAL """ dangling_cname_0_message_text = """id 10000 opcode QUERY rcode NOERROR flags QR AA RD RA ;QUESTION 91.11.17.172.in-addr.arpa.none. IN PTR ;ANSWER ;AUTHORITY ;ADDITIONAL """ dangling_cname_1_message_text = """id 10001 opcode QUERY rcode NOERROR flags QR AA RD RA ;QUESTION 91.11.17.172.in-addr.arpa. IN PTR ;ANSWER 11.17.172.in-addr.arpa. 86400 IN DNAME 11.8-22.17.172.in-addr.arpa. 91.11.17.172.in-addr.arpa. 86400 IN CNAME 91.11.8-22.17.172.in-addr.arpa. ;AUTHORITY ;ADDITIONAL """ dangling_cname_2_message_text = """id 10002 opcode QUERY rcode NOERROR flags QR AA RD RA ;QUESTION 91.11.17.172.in-addr.arpa.example. IN PTR ;ANSWER 91.11.17.172.in-addr.arpa.example. 86400 IN CNAME 91.11.17.172.in-addr.arpa.base. 91.11.17.172.in-addr.arpa.base. 86400 IN CNAME 91.11.17.172.clients.example. 91.11.17.172.clients.example. 86400 IN CNAME 91-11-17-172.dynamic.example. ;AUTHORITY ;ADDITIONAL """ class FakeAnswer(object): def __init__(self, expiration): self.expiration = expiration class BaseResolverTests(unittest.TestCase): if sys.platform != 'win32': def testRead(self): f = StringIO(resolv_conf) r = dns.resolver.Resolver(f) self.failUnless(r.nameservers == ['10.0.0.1', '10.0.0.2'] and r.domain == dns.name.from_text('foo')) def testCacheExpiration(self): message = dns.message.from_text(message_text) name = dns.name.from_text('example.') answer = dns.resolver.Answer(name, dns.rdatatype.A, dns.rdataclass.IN, message) cache = dns.resolver.Cache() cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) time.sleep(2) self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) is None) def testCacheCleaning(self): message = dns.message.from_text(message_text) name = dns.name.from_text('example.') answer = dns.resolver.Answer(name, dns.rdatatype.A, dns.rdataclass.IN, message) cache = dns.resolver.Cache(cleaning_interval=1.0) cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) time.sleep(2) self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) is None) def testIndexErrorOnEmptyRRsetAccess(self): def bad(): message = dns.message.from_text(message_text) name = dns.name.from_text('example.') answer = dns.resolver.Answer(name, dns.rdatatype.MX, dns.rdataclass.IN, message, False) return answer[0] self.failUnlessRaises(IndexError, bad) def testIndexErrorOnEmptyRRsetDelete(self): def bad(): message = dns.message.from_text(message_text) name = dns.name.from_text('example.') answer = dns.resolver.Answer(name, dns.rdatatype.MX, dns.rdataclass.IN, message, False) del answer[0] self.failUnlessRaises(IndexError, bad) @unittest.skipIf(not _network_available, "Internet not reachable") def testZoneForName1(self): name = dns.name.from_text('www.dnspython.org.') ezname = dns.name.from_text('dnspython.org.') zname = dns.resolver.zone_for_name(name) self.failUnless(zname == ezname) @unittest.skipIf(not _network_available, "Internet not reachable") def testZoneForName2(self): name = dns.name.from_text('a.b.www.dnspython.org.') ezname = dns.name.from_text('dnspython.org.') zname = dns.resolver.zone_for_name(name) self.failUnless(zname == ezname) @unittest.skipIf(not _network_available, "Internet not reachable") def testZoneForName3(self): name = dns.name.from_text('dnspython.org.') ezname = dns.name.from_text('dnspython.org.') zname = dns.resolver.zone_for_name(name) self.failUnless(zname == ezname) def testZoneForName4(self): def bad(): name = dns.name.from_text('dnspython.org', None) dns.resolver.zone_for_name(name) self.failUnlessRaises(dns.resolver.NotAbsolute, bad) def testLRUReplace(self): cache = dns.resolver.LRUCache(4) for i in xrange(0, 5): name = dns.name.from_text('example%d.' % i) answer = FakeAnswer(time.time() + 1) cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) for i in xrange(0, 5): name = dns.name.from_text('example%d.' % i) if i == 0: self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) is None) else: self.failUnless(not cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) is None) def testLRUDoesLRU(self): cache = dns.resolver.LRUCache(4) for i in xrange(0, 4): name = dns.name.from_text('example%d.' % i) answer = FakeAnswer(time.time() + 1) cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) name = dns.name.from_text('example0.') cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) # The LRU is now example1. name = dns.name.from_text('example4.') answer = FakeAnswer(time.time() + 1) cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) for i in xrange(0, 5): name = dns.name.from_text('example%d.' % i) if i == 1: self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) is None) else: self.failUnless(not cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) is None) def testLRUExpiration(self): cache = dns.resolver.LRUCache(4) for i in xrange(0, 4): name = dns.name.from_text('example%d.' % i) answer = FakeAnswer(time.time() + 1) cache.put((name, dns.rdatatype.A, dns.rdataclass.IN), answer) time.sleep(2) for i in xrange(0, 4): name = dns.name.from_text('example%d.' % i) self.failUnless(cache.get((name, dns.rdatatype.A, dns.rdataclass.IN)) is None) def testEmptyAnswerSection(self): # TODO: dangling_cname_0_message_text was the only sample message # with an empty answer section. Other than that it doesn't # apply. message = dns.message.from_text(dangling_cname_0_message_text) name = dns.name.from_text('example.') answer = dns.resolver.Answer(name, dns.rdatatype.A, dns.rdataclass.IN, message, raise_on_no_answer=False) def test_python_internal_truth(answer): if answer: return True else: return False self.assertFalse(test_python_internal_truth(answer)) for a in answer: pass class PollingMonkeyPatchMixin(object): def setUp(self): self.__native_polling_backend = dns.query._polling_backend dns.query._set_polling_backend(self.polling_backend()) unittest.TestCase.setUp(self) def tearDown(self): dns.query._set_polling_backend(self.__native_polling_backend) unittest.TestCase.tearDown(self) class SelectResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase): def polling_backend(self): return dns.query._select_for if hasattr(select, 'poll'): class PollResolverTestCase(PollingMonkeyPatchMixin, BaseResolverTests, unittest.TestCase): def polling_backend(self): return dns.query._poll_for class NXDOMAINExceptionTestCase(unittest.TestCase): # pylint: disable=broad-except def test_nxdomain_compatible(self): n1 = dns.name.Name(('a', 'b', '')) n2 = dns.name.Name(('a', 'b', 's', '')) try: raise dns.resolver.NXDOMAIN except dns.exception.DNSException as e: if not PY3: # pylint: disable=exception-message-attribute self.assertTrue((e.message == e.__doc__)) self.assertTrue((e.args == (e.__doc__,))) self.assertTrue(('kwargs' in dir(e))) self.assertTrue((str(e) == e.__doc__), str(e)) self.assertTrue(('qnames' not in e.kwargs)) self.assertTrue(('responses' not in e.kwargs)) try: raise dns.resolver.NXDOMAIN("errmsg") except dns.exception.DNSException as e: if not PY3: # pylint: disable=exception-message-attribute self.assertTrue((e.message == "errmsg")) self.assertTrue((e.args == ("errmsg",))) self.assertTrue(('kwargs' in dir(e))) self.assertTrue((str(e) == "errmsg"), str(e)) self.assertTrue(('qnames' not in e.kwargs)) self.assertTrue(('responses' not in e.kwargs)) try: raise dns.resolver.NXDOMAIN("errmsg", -1) except dns.exception.DNSException as e: if not PY3: # pylint: disable=exception-message-attribute self.assertTrue((e.message == "")) self.assertTrue((e.args == ("errmsg", -1))) self.assertTrue(('kwargs' in dir(e))) self.assertTrue((str(e) == "('errmsg', -1)"), str(e)) self.assertTrue(('qnames' not in e.kwargs)) self.assertTrue(('responses' not in e.kwargs)) try: raise dns.resolver.NXDOMAIN(qnames=None) except Exception as e: self.assertTrue((isinstance(e, AttributeError))) try: raise dns.resolver.NXDOMAIN(qnames=n1) except Exception as e: self.assertTrue((isinstance(e, AttributeError))) try: raise dns.resolver.NXDOMAIN(qnames=[]) except Exception as e: self.assertTrue((isinstance(e, AttributeError))) try: raise dns.resolver.NXDOMAIN(qnames=[n1]) except dns.exception.DNSException as e: MSG = "The DNS query name does not exist: a.b." if not PY3: # pylint: disable=exception-message-attribute self.assertTrue((e.message == MSG), e.message) self.assertTrue((e.args == (MSG,)), repr(e.args)) self.assertTrue(('kwargs' in dir(e))) self.assertTrue((str(e) == MSG), str(e)) self.assertTrue(('qnames' in e.kwargs)) self.assertTrue((e.kwargs['qnames'] == [n1])) self.assertTrue(('responses' in e.kwargs)) self.assertTrue((e.kwargs['responses'] == {})) try: raise dns.resolver.NXDOMAIN(qnames=[n2, n1]) except dns.resolver.NXDOMAIN as e: e0 = dns.resolver.NXDOMAIN("errmsg") e = e0 + e MSG = "None of DNS query names exist: a.b.s., a.b." if not PY3: # pylint: disable=exception-message-attribute self.assertTrue((e.message == MSG), e.message) self.assertTrue((e.args == (MSG,)), repr(e.args)) self.assertTrue(('kwargs' in dir(e))) self.assertTrue((str(e) == MSG), str(e)) self.assertTrue(('qnames' in e.kwargs)) self.assertTrue((e.kwargs['qnames'] == [n2, n1])) self.assertTrue(('responses' in e.kwargs)) self.assertTrue((e.kwargs['responses'] == {})) try: raise dns.resolver.NXDOMAIN(qnames=[n1], responses=['r1.1']) except Exception as e: self.assertTrue((isinstance(e, AttributeError))) try: raise dns.resolver.NXDOMAIN(qnames=[n1], responses={n1: 'r1.1'}) except dns.resolver.NXDOMAIN as e: MSG = "The DNS query name does not exist: a.b." if not PY3: # pylint: disable=exception-message-attribute self.assertTrue((e.message == MSG), e.message) self.assertTrue((e.args == (MSG,)), repr(e.args)) self.assertTrue(('kwargs' in dir(e))) self.assertTrue((str(e) == MSG), str(e)) self.assertTrue(('qnames' in e.kwargs)) self.assertTrue((e.kwargs['qnames'] == [n1])) self.assertTrue(('responses' in e.kwargs)) self.assertTrue((e.kwargs['responses'] == {n1: 'r1.1'})) def test_nxdomain_merge(self): n1 = dns.name.Name(('a', 'b', '')) n2 = dns.name.Name(('a', 'b', '')) n3 = dns.name.Name(('a', 'b', 'c', '')) n4 = dns.name.Name(('a', 'b', 'd', '')) responses1 = {n1: 'r1.1', n2: 'r1.2', n4: 'r1.4'} qnames1 = [n1, n4] # n2 == n1 responses2 = {n2: 'r2.2', n3: 'r2.3'} qnames2 = [n2, n3] e0 = dns.resolver.NXDOMAIN() e1 = dns.resolver.NXDOMAIN(qnames=qnames1, responses=responses1) e2 = dns.resolver.NXDOMAIN(qnames=qnames2, responses=responses2) e = e1 + e0 + e2 self.assertRaises(AttributeError, lambda: e0 + e0) self.assertTrue(e.kwargs['qnames'] == [n1, n4, n3], repr(e.kwargs['qnames'])) self.assertTrue(e.kwargs['responses'][n1].startswith('r2.')) self.assertTrue(e.kwargs['responses'][n2].startswith('r2.')) self.assertTrue(e.kwargs['responses'][n3].startswith('r2.')) self.assertTrue(e.kwargs['responses'][n4].startswith('r1.')) def test_nxdomain_canonical_name(self): cname1 = "91.11.8-22.17.172.in-addr.arpa." cname2 = "91-11-17-172.dynamic.example." message0 = dns.message.from_text(dangling_cname_0_message_text) message1 = dns.message.from_text(dangling_cname_1_message_text) message2 = dns.message.from_text(dangling_cname_2_message_text) qname0 = message0.question[0].name qname1 = message1.question[0].name qname2 = message2.question[0].name responses = {qname0: message0, qname1: message1, qname2: message2} eX = dns.resolver.NXDOMAIN() e0 = dns.resolver.NXDOMAIN(qnames=[qname0], responses=responses) e1 = dns.resolver.NXDOMAIN(qnames=[qname0, qname1, qname2], responses=responses) e2 = dns.resolver.NXDOMAIN(qnames=[qname0, qname2, qname1], responses=responses) self.assertRaises(TypeError, lambda: eX.canonical_name) self.assertTrue(e0.canonical_name == qname0) self.assertTrue(e1.canonical_name == dns.name.from_text(cname1)) self.assertTrue(e2.canonical_name == dns.name.from_text(cname2)) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_rrset.py000066400000000000000000000044571340301174400175460ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.rrset class RRsetTestCase(unittest.TestCase): def testEqual1(self): r1 = dns.rrset.from_text('foo', 300, 'in', 'a', '10.0.0.1', '10.0.0.2') r2 = dns.rrset.from_text('FOO', 300, 'in', 'a', '10.0.0.2', '10.0.0.1') self.failUnless(r1 == r2) def testEqual2(self): r1 = dns.rrset.from_text('foo', 300, 'in', 'a', '10.0.0.1', '10.0.0.2') r2 = dns.rrset.from_text('FOO', 600, 'in', 'a', '10.0.0.2', '10.0.0.1') self.failUnless(r1 == r2) def testNotEqual1(self): r1 = dns.rrset.from_text('fooa', 30, 'in', 'a', '10.0.0.1', '10.0.0.2') r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') self.failUnless(r1 != r2) def testNotEqual2(self): r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1', '10.0.0.3') r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') self.failUnless(r1 != r2) def testNotEqual3(self): r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1', '10.0.0.2', '10.0.0.3') r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') self.failUnless(r1 != r2) def testNotEqual4(self): r1 = dns.rrset.from_text('foo', 30, 'in', 'a', '10.0.0.1') r2 = dns.rrset.from_text('FOO', 30, 'in', 'a', '10.0.0.2', '10.0.0.1') self.failUnless(r1 != r2) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_set.py000066400000000000000000000123241340301174400171720ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.set # for convenience S = dns.set.Set class SimpleSetTestCase(unittest.TestCase): def testLen1(self): s1 = S() self.failUnless(len(s1) == 0) def testLen2(self): s1 = S([1, 2, 3]) self.failUnless(len(s1) == 3) def testLen3(self): s1 = S([1, 2, 3, 3, 3]) self.failUnless(len(s1) == 3) def testUnion1(self): s1 = S([1, 2, 3]) s2 = S([1, 2, 3]) e = S([1, 2, 3]) self.failUnless(s1 | s2 == e) def testUnion2(self): s1 = S([1, 2, 3]) s2 = S([]) e = S([1, 2, 3]) self.failUnless(s1 | s2 == e) def testUnion3(self): s1 = S([1, 2, 3]) s2 = S([3, 4]) e = S([1, 2, 3, 4]) self.failUnless(s1 | s2 == e) def testIntersection1(self): s1 = S([1, 2, 3]) s2 = S([1, 2, 3]) e = S([1, 2, 3]) self.failUnless(s1 & s2 == e) def testIntersection2(self): s1 = S([0, 1, 2, 3]) s2 = S([1, 2, 3, 4]) e = S([1, 2, 3]) self.failUnless(s1 & s2 == e) def testIntersection3(self): s1 = S([1, 2, 3]) s2 = S([]) e = S([]) self.failUnless(s1 & s2 == e) def testIntersection4(self): s1 = S([1, 2, 3]) s2 = S([5, 4]) e = S([]) self.failUnless(s1 & s2 == e) def testDifference1(self): s1 = S([1, 2, 3]) s2 = S([5, 4]) e = S([1, 2, 3]) self.failUnless(s1 - s2 == e) def testDifference2(self): s1 = S([1, 2, 3]) s2 = S([]) e = S([1, 2, 3]) self.failUnless(s1 - s2 == e) def testDifference3(self): s1 = S([1, 2, 3]) s2 = S([3, 2]) e = S([1]) self.failUnless(s1 - s2 == e) def testDifference4(self): s1 = S([1, 2, 3]) s2 = S([3, 2, 1]) e = S([]) self.failUnless(s1 - s2 == e) def testSubset1(self): s1 = S([1, 2, 3]) s2 = S([3, 2, 1]) self.failUnless(s1.issubset(s2)) def testSubset2(self): s1 = S([1, 2, 3]) self.failUnless(s1.issubset(s1)) def testSubset3(self): s1 = S([]) s2 = S([1, 2, 3]) self.failUnless(s1.issubset(s2)) def testSubset4(self): s1 = S([1]) s2 = S([1, 2, 3]) self.failUnless(s1.issubset(s2)) def testSubset5(self): s1 = S([]) s2 = S([]) self.failUnless(s1.issubset(s2)) def testSubset6(self): s1 = S([1, 4]) s2 = S([1, 2, 3]) self.failUnless(not s1.issubset(s2)) def testSuperset1(self): s1 = S([1, 2, 3]) s2 = S([3, 2, 1]) self.failUnless(s1.issuperset(s2)) def testSuperset2(self): s1 = S([1, 2, 3]) self.failUnless(s1.issuperset(s1)) def testSuperset3(self): s1 = S([1, 2, 3]) s2 = S([]) self.failUnless(s1.issuperset(s2)) def testSuperset4(self): s1 = S([1, 2, 3]) s2 = S([1]) self.failUnless(s1.issuperset(s2)) def testSuperset5(self): s1 = S([]) s2 = S([]) self.failUnless(s1.issuperset(s2)) def testSuperset6(self): s1 = S([1, 2, 3]) s2 = S([1, 4]) self.failUnless(not s1.issuperset(s2)) def testUpdate1(self): s1 = S([1, 2, 3]) u = (4, 5, 6) e = S([1, 2, 3, 4, 5, 6]) s1.update(u) self.failUnless(s1 == e) def testUpdate2(self): s1 = S([1, 2, 3]) u = [] e = S([1, 2, 3]) s1.update(u) self.failUnless(s1 == e) def testGetitem(self): s1 = S([1, 2, 3]) i0 = s1[0] i1 = s1[1] i2 = s1[2] s2 = S([i0, i1, i2]) self.failUnless(s1 == s2) def testGetslice(self): s1 = S([1, 2, 3]) slice = s1[0:2] self.failUnless(len(slice) == 2) item = s1[2] slice.append(item) s2 = S(slice) self.failUnless(s1 == s2) def testDelitem(self): s1 = S([1, 2, 3]) del s1[0] i1 = s1[0] i2 = s1[1] self.failUnless(i1 != i2) self.failUnless(i1 == 1 or i1 == 2 or i1 == 3) self.failUnless(i2 == 1 or i2 == 2 or i2 == 3) def testDelslice(self): s1 = S([1, 2, 3]) del s1[0:2] i1 = s1[0] self.failUnless(i1 == 1 or i1 == 2 or i1 == 3) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_tokenizer.py000066400000000000000000000157711340301174400204220ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import dns.exception import dns.tokenizer Token = dns.tokenizer.Token class TokenizerTestCase(unittest.TestCase): def testStr(self): tok = dns.tokenizer.Tokenizer('foo') token = tok.get() self.failUnless(token == Token(dns.tokenizer.IDENTIFIER, 'foo')) def testUnicode(self): tok = dns.tokenizer.Tokenizer(u'foo') token = tok.get() self.failUnless(token == Token(dns.tokenizer.IDENTIFIER, 'foo')) def testQuotedString1(self): tok = dns.tokenizer.Tokenizer(r'"foo"') token = tok.get() self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo')) def testQuotedString2(self): tok = dns.tokenizer.Tokenizer(r'""') token = tok.get() self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, '')) def testQuotedString3(self): tok = dns.tokenizer.Tokenizer(r'"\"foo\""') token = tok.get() self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, '"foo"')) def testQuotedString4(self): tok = dns.tokenizer.Tokenizer(r'"foo\010bar"') token = tok.get() self.failUnless(token == Token(dns.tokenizer.QUOTED_STRING, 'foo\x0abar')) def testQuotedString5(self): def bad(): tok = dns.tokenizer.Tokenizer(r'"foo') tok.get() self.failUnlessRaises(dns.exception.UnexpectedEnd, bad) def testQuotedString6(self): def bad(): tok = dns.tokenizer.Tokenizer(r'"foo\01') tok.get() self.failUnlessRaises(dns.exception.SyntaxError, bad) def testQuotedString7(self): def bad(): tok = dns.tokenizer.Tokenizer('"foo\nbar"') tok.get() self.failUnlessRaises(dns.exception.SyntaxError, bad) def testEmpty1(self): tok = dns.tokenizer.Tokenizer('') token = tok.get() self.failUnless(token.is_eof()) def testEmpty2(self): tok = dns.tokenizer.Tokenizer('') token1 = tok.get() token2 = tok.get() self.failUnless(token1.is_eof() and token2.is_eof()) def testEOL(self): tok = dns.tokenizer.Tokenizer('\n') token1 = tok.get() token2 = tok.get() self.failUnless(token1.is_eol() and token2.is_eof()) def testWS1(self): tok = dns.tokenizer.Tokenizer(' \n') token1 = tok.get() self.failUnless(token1.is_eol()) def testWS2(self): tok = dns.tokenizer.Tokenizer(' \n') token1 = tok.get(want_leading=True) self.failUnless(token1.is_whitespace()) def testComment1(self): tok = dns.tokenizer.Tokenizer(' ;foo\n') token1 = tok.get() self.failUnless(token1.is_eol()) def testComment2(self): tok = dns.tokenizer.Tokenizer(' ;foo\n') token1 = tok.get(want_comment=True) token2 = tok.get() self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo') and token2.is_eol()) def testComment3(self): tok = dns.tokenizer.Tokenizer(' ;foo bar\n') token1 = tok.get(want_comment=True) token2 = tok.get() self.failUnless(token1 == Token(dns.tokenizer.COMMENT, 'foo bar') and token2.is_eol()) def testMultiline1(self): tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)') tokens = list(iter(tok)) self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'), Token(dns.tokenizer.IDENTIFIER, 'bar')]) def testMultiline2(self): tok = dns.tokenizer.Tokenizer('( foo\n\n bar\n)\n') tokens = list(iter(tok)) self.failUnless(tokens == [Token(dns.tokenizer.IDENTIFIER, 'foo'), Token(dns.tokenizer.IDENTIFIER, 'bar'), Token(dns.tokenizer.EOL, '\n')]) def testMultiline3(self): def bad(): tok = dns.tokenizer.Tokenizer('foo)') list(iter(tok)) self.failUnlessRaises(dns.exception.SyntaxError, bad) def testMultiline4(self): def bad(): tok = dns.tokenizer.Tokenizer('((foo)') list(iter(tok)) self.failUnlessRaises(dns.exception.SyntaxError, bad) def testUnget1(self): tok = dns.tokenizer.Tokenizer('foo') t1 = tok.get() tok.unget(t1) t2 = tok.get() self.failUnless(t1 == t2 and t1.ttype == dns.tokenizer.IDENTIFIER and \ t1.value == 'foo') def testUnget2(self): def bad(): tok = dns.tokenizer.Tokenizer('foo') t1 = tok.get() tok.unget(t1) tok.unget(t1) self.failUnlessRaises(dns.tokenizer.UngetBufferFull, bad) def testGetEOL1(self): tok = dns.tokenizer.Tokenizer('\n') t = tok.get_eol() self.failUnless(t == '\n') def testGetEOL2(self): tok = dns.tokenizer.Tokenizer('') t = tok.get_eol() self.failUnless(t == '') def testEscapedDelimiter1(self): tok = dns.tokenizer.Tokenizer(r'ch\ ld') t = tok.get() self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ ld') def testEscapedDelimiter2(self): tok = dns.tokenizer.Tokenizer(r'ch\032ld') t = tok.get() self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\032ld') def testEscapedDelimiter3(self): tok = dns.tokenizer.Tokenizer(r'ch\ild') t = tok.get() self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch\ild') def testEscapedDelimiter1u(self): tok = dns.tokenizer.Tokenizer(r'ch\ ld') t = tok.get().unescape() self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'ch ld') def testEscapedDelimiter2u(self): tok = dns.tokenizer.Tokenizer(r'ch\032ld') t = tok.get().unescape() self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == 'ch ld') def testEscapedDelimiter3u(self): tok = dns.tokenizer.Tokenizer(r'ch\ild') t = tok.get().unescape() self.failUnless(t.ttype == dns.tokenizer.IDENTIFIER and t.value == r'child') if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_update.py000066400000000000000000000100061340301174400176540ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import unittest import binascii import dns.update import dns.rdata import dns.rdataset goodhex = '0001 2800 0001 0005 0007 0000' \ '076578616d706c6500 0006 0001' \ '03666f6fc00c 00ff 00ff 00000000 0000' \ 'c019 0001 00ff 00000000 0000' \ '03626172c00c 0001 0001 00000000 0004 0a000005' \ '05626c617a32c00c 00ff 00fe 00000000 0000' \ 'c049 0001 00fe 00000000 0000' \ 'c019 0001 00ff 00000000 0000' \ 'c019 0001 0001 0000012c 0004 0a000001' \ 'c019 0001 0001 0000012c 0004 0a000002' \ 'c035 0001 0001 0000012c 0004 0a000003' \ 'c035 0001 00fe 00000000 0004 0a000004' \ '04626c617ac00c 0001 00ff 00000000 0000' \ 'c049 00ff 00ff 00000000 0000' goodwire = binascii.unhexlify(goodhex.replace(' ', '').encode()) update_text = """id 1 opcode UPDATE rcode NOERROR ;ZONE example. IN SOA ;PREREQ foo ANY ANY foo ANY A bar 0 IN A 10.0.0.5 blaz2 NONE ANY blaz2 NONE A ;UPDATE foo ANY A foo 300 IN A 10.0.0.1 foo 300 IN A 10.0.0.2 bar 300 IN A 10.0.0.3 bar 0 NONE A 10.0.0.4 blaz ANY A blaz2 ANY ANY """ class UpdateTestCase(unittest.TestCase): def test_to_wire1(self): # type: () -> None update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', 300, 'a', '10.0.0.3') update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.failUnless(update.to_wire() == goodwire) def test_to_wire2(self): # type: () -> None update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', 300, dns.rdata.from_text(1, 1, '10.0.0.3')) update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.failUnless(update.to_wire() == goodwire) def test_to_wire3(self): # type: () -> None update = dns.update.Update('example') update.id = 1 update.present('foo') update.present('foo', 'a') update.present('bar', 'a', '10.0.0.5') update.absent('blaz2') update.absent('blaz2', 'a') update.replace('foo', 300, 'a', '10.0.0.1', '10.0.0.2') update.add('bar', dns.rdataset.from_text(1, 1, 300, '10.0.0.3')) update.delete('bar', 'a', '10.0.0.4') update.delete('blaz', 'a') update.delete('blaz2') self.failUnless(update.to_wire() == goodwire) def test_from_text1(self): # type: () -> None update = dns.message.from_text(update_text) w = update.to_wire(origin=dns.name.from_text('example'), want_shuffle=False) self.failUnless(w == goodwire) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/test_wiredata.py000066400000000000000000000104711340301174400202000ustar00rootroot00000000000000# Copyright (C) 2016 # Author: Martin Basti # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. import unittest from dns.exception import FormError from dns.wiredata import WireData class WireDataSlicingTestCase(unittest.TestCase): def testSliceAll(self): """Get all data""" inst = WireData(b'0123456789') self.assertEqual(inst[:], WireData(b'0123456789')) def testSliceAllExplicitlyDefined(self): """Get all data""" inst = WireData(b'0123456789') self.assertEqual(inst[0:10], WireData(b'0123456789')) def testSliceLowerHalf(self): """Get lower half of data""" inst = WireData(b'0123456789') self.assertEqual(inst[:5], WireData(b'01234')) def testSliceLowerHalfWithNegativeIndex(self): """Get lower half of data""" inst = WireData(b'0123456789') self.assertEqual(inst[:-5], WireData(b'01234')) def testSliceUpperHalf(self): """Get upper half of data""" inst = WireData(b'0123456789') self.assertEqual(inst[5:], WireData(b'56789')) def testSliceMiddle(self): """Get data from middle""" inst = WireData(b'0123456789') self.assertEqual(inst[3:6], WireData(b'345')) def testSliceMiddleWithNegativeIndex(self): """Get data from middle""" inst = WireData(b'0123456789') self.assertEqual(inst[-6:-3], WireData(b'456')) def testSliceMiddleWithMixedIndex(self): """Get data from middle""" inst = WireData(b'0123456789') self.assertEqual(inst[-8:3], WireData(b'2')) self.assertEqual(inst[5:-3], WireData(b'56')) def testGetOne(self): """Get data one by one item""" data = b'0123456789' inst = WireData(data) for i, byte in enumerate(bytearray(data)): self.assertEqual(inst[i], byte) for i in range(-1, len(data) * -1, -1): self.assertEqual(inst[i], bytearray(data)[i]) def testEmptySlice(self): """Test empty slice""" data = b'0123456789' inst = WireData(data) for i, byte in enumerate(data): self.assertEqual(inst[i:i], b'') for i in range(-1, len(data) * -1, -1): self.assertEqual(inst[i:i], b'') self.assertEqual(inst[-3:-6], b'') def testSliceStartOutOfLowerBorder(self): """Get data from out of lower border""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[-11:] # pylint: disable=pointless-statement def testSliceStopOutOfLowerBorder(self): """Get data from out of lower border""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[:-11] # pylint: disable=pointless-statement def testSliceBothOutOfLowerBorder(self): """Get data from out of lower border""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[-12:-11] # pylint: disable=pointless-statement def testSliceStartOutOfUpperBorder(self): """Get data from out of upper border""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[11:] # pylint: disable=pointless-statement def testSliceStopOutOfUpperBorder(self): """Get data from out of upper border""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[:11] # pylint: disable=pointless-statement def testSliceBothOutOfUpperBorder(self): """Get data from out of lower border""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[10:20] # pylint: disable=pointless-statement def testGetOneOutOfLowerBorder(self): """Get item outside of range""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[-11] # pylint: disable=pointless-statement def testGetOneOutOfUpperBorder(self): """Get item outside of range""" inst = WireData(b'0123456789') with self.assertRaises(FormError): inst[10] # pylint: disable=pointless-statement dnspython-1.16.0/tests/test_zone.py000066400000000000000000000503761340301174400173630ustar00rootroot00000000000000# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose with or without fee is hereby granted, # provided that the above copyright notice and this permission notice # appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from io import BytesIO, StringIO import filecmp import os import unittest from typing import cast import dns.exception import dns.rdata import dns.rdataset import dns.rdataclass import dns.rdatatype import dns.rrset import dns.zone import dns.node def here(filename): return os.path.join(os.path.dirname(__file__), filename) example_text = """$TTL 3600 $ORIGIN example. @ soa foo bar 1 2 3 4 5 @ ns ns1 @ ns ns2 ns1 a 10.0.0.1 ns2 a 10.0.0.2 $TTL 300 $ORIGIN foo.example. bar mx 0 blaz """ example_text_output = """@ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.2 """ something_quite_similar = """@ 3600 IN SOA foo bar 1 2 3 4 5 @ 3600 IN NS ns1 @ 3600 IN NS ns2 bar.foo 300 IN MX 0 blaz.foo ns1 3600 IN A 10.0.0.1 ns2 3600 IN A 10.0.0.3 """ something_different = """@ 3600 IN SOA fooa bar 1 2 3 4 5 @ 3600 IN NS ns11 @ 3600 IN NS ns21 bar.fooa 300 IN MX 0 blaz.fooa ns11 3600 IN A 10.0.0.11 ns21 3600 IN A 10.0.0.21 """ ttl_example_text = """$TTL 1h $ORIGIN example. @ soa foo bar 1 2 3 4 5 @ ns ns1 @ ns ns2 ns1 1d1s a 10.0.0.1 ns2 1w1D1h1m1S a 10.0.0.2 """ # No $TTL so default TTL for RRs should be inherited from SOA minimum TTL ( # not from the last explicit RR TTL). ttl_from_soa_text = """$ORIGIN example. @ 1h soa foo bar 1 2 3 4 5 @ 1h ns ns1 @ 1h ns ns2 ns1 1w1D1h1m1S a 10.0.0.2 ns2 a 10.0.0.1 """ # No $TTL and no SOA, so default TTL for RRs should be inherited from last # explicit RR TTL. ttl_from_last_text = """$ORIGIN example. @ 1h ns ns1 @ 1h ns ns2 ns1 a 10.0.0.1 ns2 1w1D1h1m1S a 10.0.0.2 """ # No $TTL and no SOA should raise SyntaxError as no TTL can be determined. no_ttl_text = """$ORIGIN example. @ ns ns1 @ ns ns2 ns1 a 10.0.0.1 ns2 a 10.0.0.2 """ no_soa_text = """$TTL 1h $ORIGIN example. @ ns ns1 @ ns ns2 ns1 1d1s a 10.0.0.1 ns2 1w1D1h1m1S a 10.0.0.2 """ no_ns_text = """$TTL 1h $ORIGIN example. @ soa foo bar 1 2 3 4 5 """ include_text = """$INCLUDE "%s" """ % here("example") bad_directive_text = """$FOO bar $ORIGIN example. @ soa foo bar 1 2 3 4 5 @ ns ns1 @ ns ns2 ns1 1d1s a 10.0.0.1 ns2 1w1D1h1m1S a 10.0.0.2 """ _keep_output = True def _rdata_sort(a): return (a[0], a[2].rdclass, a[2].to_text()) class ZoneTestCase(unittest.TestCase): def testFromFile1(self): # type: () -> None z = dns.zone.from_file(here('example'), 'example') ok = False try: z.to_file(here('example1.out'), nl=b'\x0a') ok = filecmp.cmp(here('example1.out'), here('example1.good')) finally: if not _keep_output: os.unlink(here('example1.out')) self.failUnless(ok) def testFromFile2(self): # type: () -> None z = dns.zone.from_file(here('example'), 'example', relativize=False) ok = False try: z.to_file(here('example2.out'), relativize=False, nl=b'\x0a') ok = filecmp.cmp(here('example2.out'), here('example2.good')) finally: if not _keep_output: os.unlink(here('example2.out')) self.failUnless(ok) def testToFileTextualStream(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) f = StringIO() z.to_file(f) out = f.getvalue() f.close() self.assertEqual(out, example_text_output) def testToFileBinaryStream(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) f = BytesIO() z.to_file(f) out = f.getvalue() f.close() self.assertEqual(out, example_text_output.encode()) def testToFileTextual(self): # type: () -> None z = dns.zone.from_file(here('example'), 'example') try: f = open(here('example3-textual.out'), 'w') z.to_file(f) f.close() ok = filecmp.cmp(here('example3-textual.out'), here('example3.good')) finally: if not _keep_output: os.unlink(here('example3-textual.out')) self.failUnless(ok) def testToFileBinary(self): # type: () -> None z = dns.zone.from_file(here('example'), 'example') try: f = open(here('example3-binary.out'), 'wb') z.to_file(f) f.close() ok = filecmp.cmp(here('example3-binary.out'), here('example3.good')) finally: if not _keep_output: os.unlink(here('example3-binary.out')) self.failUnless(ok) def testToFileFilename(self): # type: () -> None z = dns.zone.from_file(here('example'), 'example') try: z.to_file(here('example3-filename.out')) ok = filecmp.cmp(here('example3-filename.out'), here('example3.good')) finally: if not _keep_output: os.unlink(here('example3-filename.out')) self.failUnless(ok) def testToText(self): # type: () -> None z = dns.zone.from_file(here('example'), 'example') ok = False try: text_zone = z.to_text(nl=b'\x0a') f = open(here('example3.out'), 'wb') f.write(text_zone) f.close() ok = filecmp.cmp(here('example3.out'), here('example3.good')) finally: if not _keep_output: os.unlink(here('example3.out')) self.failUnless(ok) def testFromText(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) f = StringIO() names = list(z.nodes.keys()) names.sort() for n in names: f.write(z[n].to_text(n)) f.write(u'\n') self.assertEqual(f.getvalue(), example_text_output) def testTorture1(self): # type: () -> None # # Read a zone containing all our supported RR types, and # for each RR in the zone, convert the rdata into wire format # and then back out, and see if we get equal rdatas. # f = BytesIO() o = dns.name.from_text('example.') z = dns.zone.from_file(here('example'), o) for node in z.values(): for rds in node: for rd in rds: f.seek(0) f.truncate() rd.to_wire(f, origin=o) wire = f.getvalue() rd2 = dns.rdata.from_wire(rds.rdclass, rds.rdtype, wire, 0, len(wire), origin=o) self.failUnless(rd == rd2) def testEqual(self): # type: () -> None z1 = dns.zone.from_text(example_text, 'example.', relativize=True) z2 = dns.zone.from_text(example_text_output, 'example.', relativize=True) self.failUnless(z1 == z2) def testNotEqual1(self): # type: () -> None z1 = dns.zone.from_text(example_text, 'example.', relativize=True) z2 = dns.zone.from_text(something_quite_similar, 'example.', relativize=True) self.failUnless(z1 != z2) def testNotEqual2(self): # type: () -> None z1 = dns.zone.from_text(example_text, 'example.', relativize=True) z2 = dns.zone.from_text(something_different, 'example.', relativize=True) self.failUnless(z1 != z2) def testNotEqual3(self): # type: () -> None z1 = dns.zone.from_text(example_text, 'example.', relativize=True) z2 = dns.zone.from_text(something_different, 'example2.', relativize=True) self.failUnless(z1 != z2) def testFindRdataset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rds = z.find_rdataset('@', 'soa') exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') self.failUnless(rds == exrds) def testFindRdataset2(self): # type: () -> None def bad(): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) z.find_rdataset('@', 'loc') self.failUnlessRaises(KeyError, bad) def testFindRRset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rrs = z.find_rrset('@', 'soa') exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') self.failUnless(rrs == exrrs) def testFindRRset2(self): # type: () -> None def bad(): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) z.find_rrset('@', 'loc') self.failUnlessRaises(KeyError, bad) def testGetRdataset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rds = z.get_rdataset('@', 'soa') exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') self.failUnless(rds == exrds) def testGetRdataset2(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rds = z.get_rdataset('@', 'loc') self.failUnless(rds is None) def testGetRRset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rrs = z.get_rrset('@', 'soa') exrrs = dns.rrset.from_text('@', 300, 'IN', 'SOA', 'foo bar 1 2 3 4 5') self.failUnless(rrs == exrrs) def testGetRRset2(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rrs = z.get_rrset('@', 'loc') self.failUnless(rrs is None) def testReplaceRdataset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rdataset = dns.rdataset.from_text('in', 'ns', 300, 'ns3', 'ns4') z.replace_rdataset('@', rdataset) rds = z.get_rdataset('@', 'ns') self.failUnless(rds is rdataset) def testReplaceRdataset2(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) rdataset = dns.rdataset.from_text('in', 'txt', 300, '"foo"') z.replace_rdataset('@', rdataset) rds = z.get_rdataset('@', 'txt') self.failUnless(rds is rdataset) def testDeleteRdataset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) z.delete_rdataset('@', 'ns') rds = z.get_rdataset('@', 'ns') self.failUnless(rds is None) def testDeleteRdataset2(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) z.delete_rdataset('ns1', 'a') node = z.get_node('ns1') self.failUnless(node is None) def testNodeFindRdataset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) node = z['@'] rds = node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') self.failUnless(rds == exrds) def testNodeFindRdataset2(self): # type: () -> None def bad(): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) node = z['@'] node.find_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) self.failUnlessRaises(KeyError, bad) def testNodeGetRdataset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) node = z['@'] rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) exrds = dns.rdataset.from_text('IN', 'SOA', 300, 'foo bar 1 2 3 4 5') self.failUnless(rds == exrds) def testNodeGetRdataset2(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) node = z['@'] rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) self.failUnless(rds is None) def testNodeDeleteRdataset1(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) node = z['@'] node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA) self.failUnless(rds is None) def testNodeDeleteRdataset2(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) node = z['@'] node.delete_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) rds = node.get_rdataset(dns.rdataclass.IN, dns.rdatatype.LOC) self.failUnless(rds is None) def testIterateRdatasets(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) ns = [n for n, r in z.iterate_rdatasets('A')] ns.sort() self.failUnless(ns == [dns.name.from_text('ns1', None), dns.name.from_text('ns2', None)]) def testIterateAllRdatasets(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) ns = [n for n, r in z.iterate_rdatasets()] ns.sort() self.failUnless(ns == [dns.name.from_text('@', None), dns.name.from_text('@', None), dns.name.from_text('bar.foo', None), dns.name.from_text('ns1', None), dns.name.from_text('ns2', None)]) def testIterateRdatas(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) l = list(z.iterate_rdatas('A')) l.sort() exl = [(dns.name.from_text('ns1', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2'))] self.failUnless(l == exl) def testIterateAllRdatas(self): # type: () -> None z = dns.zone.from_text(example_text, 'example.', relativize=True) l = list(z.iterate_rdatas()) l.sort(key=_rdata_sort) exl = [(dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1')), (dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2')), (dns.name.from_text('@', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA, 'foo bar 1 2 3 4 5')), (dns.name.from_text('bar.foo', None), 300, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.MX, '0 blaz.foo')), (dns.name.from_text('ns1', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.1')), (dns.name.from_text('ns2', None), 3600, dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '10.0.0.2'))] exl.sort(key=_rdata_sort) self.failUnless(l == exl) def testTTLs(self): # type: () -> None z = dns.zone.from_text(ttl_example_text, 'example.', relativize=True) n = z['@'] # type: dns.node.Node rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA)) self.failUnless(rds.ttl == 3600) n = z['ns1'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) self.failUnless(rds.ttl == 86401) n = z['ns2'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) self.failUnless(rds.ttl == 694861) def testTTLFromSOA(self): # type: () -> None z = dns.zone.from_text(ttl_from_soa_text, 'example.', relativize=True) n = z['@'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.SOA)) self.failUnless(rds.ttl == 3600) soa_rd = rds[0] n = z['ns1'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) self.failUnless(rds.ttl == 694861) n = z['ns2'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) self.failUnless(rds.ttl == soa_rd.minimum) def testTTLFromLast(self): # type: () -> None z = dns.zone.from_text(ttl_from_last_text, 'example.', check_origin=False) n = z['@'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.NS)) self.failUnless(rds.ttl == 3600) n = z['ns1'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) self.failUnless(rds.ttl == 3600) n = z['ns2'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) self.failUnless(rds.ttl == 694861) def testNoTTL(self): # type: () -> None def bad(): # type: () -> None dns.zone.from_text(no_ttl_text, 'example.', check_origin=False) self.failUnlessRaises(dns.exception.SyntaxError, bad) def testNoSOA(self): # type: () -> None def bad(): # type: () -> None dns.zone.from_text(no_soa_text, 'example.', relativize=True) self.failUnlessRaises(dns.zone.NoSOA, bad) def testNoNS(self): # type: () -> None def bad(): # type: () -> None dns.zone.from_text(no_ns_text, 'example.', relativize=True) self.failUnlessRaises(dns.zone.NoNS, bad) def testInclude(self): # type: () -> None z1 = dns.zone.from_text(include_text, 'example.', relativize=True, allow_include=True) z2 = dns.zone.from_file(here('example'), 'example.', relativize=True) self.failUnless(z1 == z2) def testBadDirective(self): # type: () -> None def bad(): # type: () -> None dns.zone.from_text(bad_directive_text, 'example.', relativize=True) self.failUnlessRaises(dns.exception.SyntaxError, bad) def testFirstRRStartsWithWhitespace(self): # type: () -> None # no name is specified, so default to the initial origin z = dns.zone.from_text(' 300 IN A 10.0.0.1', origin='example.', check_origin=False) n = z['@'] rds = cast(dns.rdataset.Rdataset, n.get_rdataset(dns.rdataclass.IN, dns.rdatatype.A)) self.failUnless(rds.ttl == 300) def testZoneOrigin(self): # type: () -> None z = dns.zone.Zone('example.') self.failUnless(z.origin == dns.name.from_text('example.')) def bad1(): # type: () -> None o = dns.name.from_text('example', None) dns.zone.Zone(o) self.failUnlessRaises(ValueError, bad1) def bad2(): # type: () -> None dns.zone.Zone(cast(str, 1.0)) self.failUnlessRaises(ValueError, bad2) def testZoneOriginNone(self): # type: () -> None dns.zone.Zone(cast(str, None)) if __name__ == '__main__': unittest.main() dnspython-1.16.0/tests/ttxt_module.py000066400000000000000000000001441340301174400177050ustar00rootroot00000000000000import dns.rdtypes.txtbase class TTXT(dns.rdtypes.txtbase.TXTBase): """Test TXT-like record""" dnspython-1.16.0/tests/utest.py000066400000000000000000000005641340301174400165070ustar00rootroot00000000000000import os.path import sys import unittest if __name__ == '__main__': sys.path.insert(0, os.path.realpath('..')) if len(sys.argv) > 1: pattern = sys.argv[1] else: pattern = 'test*.py' suites = unittest.defaultTestLoader.discover('.', pattern) if not unittest.TextTestRunner(verbosity=2).run(suites).wasSuccessful(): sys.exit(1) dnspython-1.16.0/tox.ini000066400000000000000000000011651340301174400151400ustar00rootroot00000000000000[tox] envlist = py27, py34, py35, py36, py37, flake8, pylint, mypy, coverage [testenv] commands= python setup.py test deps= [testenv:py27] deps = typing [testenv:flake8] commands = pip install flake8 flake8 dns [testenv:pylint] commands = pip install pylint pylint dns [testenv:mypy] commands = pip install mypy mypy examples tests [testenv:coverage] basepython = python2 deps = coverage commands = python setup.py install coverage run --rcfile=.coverage.ini setup.py test coverage report [pep8] show-pep8 = True show-source = True