mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-03 12:21:35 +00:00
3P library updates
This commit is contained in:
@@ -16,7 +16,7 @@ __all__ = (
|
|||||||
'cached', 'cachedmethod'
|
'cached', 'cachedmethod'
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = '2.1.0'
|
__version__ = '3.1.0'
|
||||||
|
|
||||||
if hasattr(functools.update_wrapper(lambda f: f(), lambda: 42), '__wrapped__'):
|
if hasattr(functools.update_wrapper(lambda f: f(), lambda: 42), '__wrapped__'):
|
||||||
_update_wrapper = functools.update_wrapper
|
_update_wrapper = functools.update_wrapper
|
||||||
@@ -79,7 +79,7 @@ def cachedmethod(cache, key=keys.hashkey, lock=None):
|
|||||||
c = cache(self)
|
c = cache(self)
|
||||||
if c is None:
|
if c is None:
|
||||||
return method(self, *args, **kwargs)
|
return method(self, *args, **kwargs)
|
||||||
k = key(self, *args, **kwargs)
|
k = key(*args, **kwargs)
|
||||||
try:
|
try:
|
||||||
return c[k]
|
return c[k]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -95,7 +95,7 @@ def cachedmethod(cache, key=keys.hashkey, lock=None):
|
|||||||
c = cache(self)
|
c = cache(self)
|
||||||
if c is None:
|
if c is None:
|
||||||
return method(self, *args, **kwargs)
|
return method(self, *args, **kwargs)
|
||||||
k = key(self, *args, **kwargs)
|
k = key(*args, **kwargs)
|
||||||
try:
|
try:
|
||||||
with lock(self):
|
with lock(self):
|
||||||
return c[k]
|
return c[k]
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import collections
|
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
try:
|
||||||
|
from collections.abc import MutableMapping
|
||||||
|
except ImportError:
|
||||||
|
from collections import MutableMapping
|
||||||
|
|
||||||
class DefaultMapping(collections.MutableMapping):
|
|
||||||
|
class DefaultMapping(MutableMapping):
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from .abc import DefaultMapping
|
from .abc import DefaultMapping
|
||||||
|
|
||||||
|
|
||||||
@@ -16,20 +14,12 @@ class _DefaultSize(object):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
_deprecated = object()
|
|
||||||
|
|
||||||
|
|
||||||
class Cache(DefaultMapping):
|
class Cache(DefaultMapping):
|
||||||
"""Mutable mapping to serve as a simple cache or cache base class."""
|
"""Mutable mapping to serve as a simple cache or cache base class."""
|
||||||
|
|
||||||
__size = _DefaultSize()
|
__size = _DefaultSize()
|
||||||
|
|
||||||
def __init__(self, maxsize, missing=_deprecated, getsizeof=None):
|
def __init__(self, maxsize, getsizeof=None):
|
||||||
if missing is not _deprecated:
|
|
||||||
warn("Cache constructor parameter 'missing' is deprecated",
|
|
||||||
DeprecationWarning, 3)
|
|
||||||
if missing:
|
|
||||||
self.__missing = missing
|
|
||||||
if getsizeof:
|
if getsizeof:
|
||||||
self.getsizeof = getsizeof
|
self.getsizeof = getsizeof
|
||||||
if self.getsizeof is not Cache.getsizeof:
|
if self.getsizeof is not Cache.getsizeof:
|
||||||
@@ -77,12 +67,7 @@ class Cache(DefaultMapping):
|
|||||||
return key in self.__data
|
return key in self.__data
|
||||||
|
|
||||||
def __missing__(self, key):
|
def __missing__(self, key):
|
||||||
value = self.__missing(key)
|
raise KeyError(key)
|
||||||
try:
|
|
||||||
self.__setitem__(key, value)
|
|
||||||
except ValueError:
|
|
||||||
pass # value too large
|
|
||||||
return value
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.__data)
|
return iter(self.__data)
|
||||||
@@ -104,7 +89,3 @@ class Cache(DefaultMapping):
|
|||||||
def getsizeof(value):
|
def getsizeof(value):
|
||||||
"""Return the size of a cache element's value."""
|
"""Return the size of a cache element's value."""
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __missing(key):
|
|
||||||
raise KeyError(key)
|
|
||||||
|
|||||||
@@ -5,11 +5,15 @@ from __future__ import absolute_import
|
|||||||
import collections
|
import collections
|
||||||
import functools
|
import functools
|
||||||
import random
|
import random
|
||||||
import time
|
|
||||||
|
try:
|
||||||
|
from time import monotonic as default_timer
|
||||||
|
except ImportError:
|
||||||
|
from time import time as default_timer
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
from dummy_threading import RLock
|
from dummy_threading import RLock
|
||||||
|
|
||||||
from . import keys
|
from . import keys
|
||||||
@@ -26,6 +30,24 @@ _CacheInfo = collections.namedtuple('CacheInfo', [
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class _UnboundCache(dict):
|
||||||
|
|
||||||
|
maxsize = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def currsize(self):
|
||||||
|
return len(self)
|
||||||
|
|
||||||
|
|
||||||
|
class _UnboundTTLCache(TTLCache):
|
||||||
|
def __init__(self, ttl, timer):
|
||||||
|
TTLCache.__init__(self, float('inf'), ttl, timer)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maxsize(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _cache(cache, typed=False):
|
def _cache(cache, typed=False):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
key = keys.typedkey if typed else keys.hashkey
|
key = keys.typedkey if typed else keys.hashkey
|
||||||
@@ -77,6 +99,9 @@ def lfu_cache(maxsize=128, typed=False):
|
|||||||
algorithm.
|
algorithm.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if maxsize is None:
|
||||||
|
return _cache(_UnboundCache(), typed)
|
||||||
|
else:
|
||||||
return _cache(LFUCache(maxsize), typed)
|
return _cache(LFUCache(maxsize), typed)
|
||||||
|
|
||||||
|
|
||||||
@@ -86,6 +111,9 @@ def lru_cache(maxsize=128, typed=False):
|
|||||||
algorithm.
|
algorithm.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if maxsize is None:
|
||||||
|
return _cache(_UnboundCache(), typed)
|
||||||
|
else:
|
||||||
return _cache(LRUCache(maxsize), typed)
|
return _cache(LRUCache(maxsize), typed)
|
||||||
|
|
||||||
|
|
||||||
@@ -95,12 +123,18 @@ def rr_cache(maxsize=128, choice=random.choice, typed=False):
|
|||||||
algorithm.
|
algorithm.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if maxsize is None:
|
||||||
|
return _cache(_UnboundCache(), typed)
|
||||||
|
else:
|
||||||
return _cache(RRCache(maxsize, choice), typed)
|
return _cache(RRCache(maxsize, choice), typed)
|
||||||
|
|
||||||
|
|
||||||
def ttl_cache(maxsize=128, ttl=600, timer=time.time, typed=False):
|
def ttl_cache(maxsize=128, ttl=600, timer=default_timer, typed=False):
|
||||||
"""Decorator to wrap a function with a memoizing callable that saves
|
"""Decorator to wrap a function with a memoizing callable that saves
|
||||||
up to `maxsize` results based on a Least Recently Used (LRU)
|
up to `maxsize` results based on a Least Recently Used (LRU)
|
||||||
algorithm with a per-item time-to-live (TTL) value.
|
algorithm with a per-item time-to-live (TTL) value.
|
||||||
"""
|
"""
|
||||||
|
if maxsize is None:
|
||||||
|
return _cache(_UnboundTTLCache(ttl, timer), typed)
|
||||||
|
else:
|
||||||
return _cache(TTLCache(maxsize, ttl, timer), typed)
|
return _cache(TTLCache(maxsize, ttl, timer), typed)
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ from __future__ import absolute_import
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from .cache import Cache, _deprecated
|
from .cache import Cache
|
||||||
|
|
||||||
|
|
||||||
class LFUCache(Cache):
|
class LFUCache(Cache):
|
||||||
"""Least Frequently Used (LFU) cache implementation."""
|
"""Least Frequently Used (LFU) cache implementation."""
|
||||||
|
|
||||||
def __init__(self, maxsize, missing=_deprecated, getsizeof=None):
|
def __init__(self, maxsize, getsizeof=None):
|
||||||
Cache.__init__(self, maxsize, missing, getsizeof)
|
Cache.__init__(self, maxsize, getsizeof)
|
||||||
self.__counter = collections.Counter()
|
self.__counter = collections.Counter()
|
||||||
|
|
||||||
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ from __future__ import absolute_import
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from .cache import Cache, _deprecated
|
from .cache import Cache
|
||||||
|
|
||||||
|
|
||||||
class LRUCache(Cache):
|
class LRUCache(Cache):
|
||||||
"""Least Recently Used (LRU) cache implementation."""
|
"""Least Recently Used (LRU) cache implementation."""
|
||||||
|
|
||||||
def __init__(self, maxsize, missing=_deprecated, getsizeof=None):
|
def __init__(self, maxsize, getsizeof=None):
|
||||||
Cache.__init__(self, maxsize, missing, getsizeof)
|
Cache.__init__(self, maxsize, getsizeof)
|
||||||
self.__order = collections.OrderedDict()
|
self.__order = collections.OrderedDict()
|
||||||
|
|
||||||
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import absolute_import
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from .cache import Cache, _deprecated
|
from .cache import Cache
|
||||||
|
|
||||||
|
|
||||||
# random.choice cannot be pickled in Python 2.7
|
# random.choice cannot be pickled in Python 2.7
|
||||||
@@ -13,9 +13,8 @@ def _choice(seq):
|
|||||||
class RRCache(Cache):
|
class RRCache(Cache):
|
||||||
"""Random Replacement (RR) cache implementation."""
|
"""Random Replacement (RR) cache implementation."""
|
||||||
|
|
||||||
def __init__(self, maxsize, choice=random.choice, missing=_deprecated,
|
def __init__(self, maxsize, choice=random.choice, getsizeof=None):
|
||||||
getsizeof=None):
|
Cache.__init__(self, maxsize, getsizeof)
|
||||||
Cache.__init__(self, maxsize, missing, getsizeof)
|
|
||||||
# TODO: use None as default, assing to self.choice directly?
|
# TODO: use None as default, assing to self.choice directly?
|
||||||
if choice is random.choice:
|
if choice is random.choice:
|
||||||
self.__choice = _choice
|
self.__choice = _choice
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import time
|
|
||||||
|
|
||||||
from .cache import Cache, _deprecated
|
try:
|
||||||
|
from time import monotonic as default_timer
|
||||||
|
except ImportError:
|
||||||
|
from time import time as default_timer
|
||||||
|
|
||||||
|
from .cache import Cache
|
||||||
|
|
||||||
|
|
||||||
class _Link(object):
|
class _Link(object):
|
||||||
@@ -57,9 +61,8 @@ class _Timer(object):
|
|||||||
class TTLCache(Cache):
|
class TTLCache(Cache):
|
||||||
"""LRU Cache implementation with per-item time-to-live (TTL) value."""
|
"""LRU Cache implementation with per-item time-to-live (TTL) value."""
|
||||||
|
|
||||||
def __init__(self, maxsize, ttl, timer=time.time, missing=_deprecated,
|
def __init__(self, maxsize, ttl, timer=default_timer, getsizeof=None):
|
||||||
getsizeof=None):
|
Cache.__init__(self, maxsize, getsizeof)
|
||||||
Cache.__init__(self, maxsize, missing, getsizeof)
|
|
||||||
self.__root = root = _Link()
|
self.__root = root = _Link()
|
||||||
root.prev = root.next = root
|
root.prev = root.next = root
|
||||||
self.__links = collections.OrderedDict()
|
self.__links = collections.OrderedDict()
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import sys
|
|||||||
import decimal
|
import decimal
|
||||||
from decimal import Context
|
from decimal import Context
|
||||||
|
|
||||||
if sys.version_info > (3,):
|
PY3 = sys.version_info[0] == 3
|
||||||
|
PY2 = sys.version_info[0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
long = int
|
long = int
|
||||||
xrange = range
|
xrange = range
|
||||||
else:
|
else:
|
||||||
@@ -10,7 +14,7 @@ else:
|
|||||||
xrange = xrange # pylint: disable=xrange-builtin
|
xrange = xrange # pylint: disable=xrange-builtin
|
||||||
|
|
||||||
# unicode / binary types
|
# unicode / binary types
|
||||||
if sys.version_info > (3,):
|
if PY3:
|
||||||
text_type = str
|
text_type = str
|
||||||
binary_type = bytes
|
binary_type = bytes
|
||||||
string_types = (str,)
|
string_types = (str,)
|
||||||
@@ -19,6 +23,10 @@ if sys.version_info > (3,):
|
|||||||
return x.decode()
|
return x.decode()
|
||||||
def maybe_encode(x):
|
def maybe_encode(x):
|
||||||
return x.encode()
|
return x.encode()
|
||||||
|
def maybe_chr(x):
|
||||||
|
return x
|
||||||
|
def maybe_ord(x):
|
||||||
|
return x
|
||||||
else:
|
else:
|
||||||
text_type = unicode # pylint: disable=unicode-builtin, undefined-variable
|
text_type = unicode # pylint: disable=unicode-builtin, undefined-variable
|
||||||
binary_type = str
|
binary_type = str
|
||||||
@@ -30,6 +38,10 @@ else:
|
|||||||
return x
|
return x
|
||||||
def maybe_encode(x):
|
def maybe_encode(x):
|
||||||
return x
|
return x
|
||||||
|
def maybe_chr(x):
|
||||||
|
return chr(x)
|
||||||
|
def maybe_ord(x):
|
||||||
|
return ord(x)
|
||||||
|
|
||||||
|
|
||||||
def round_py2_compat(what):
|
def round_py2_compat(what):
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -20,7 +22,6 @@ import struct
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import dns.exception
|
import dns.exception
|
||||||
import dns.hash
|
|
||||||
import dns.name
|
import dns.name
|
||||||
import dns.node
|
import dns.node
|
||||||
import dns.rdataset
|
import dns.rdataset
|
||||||
@@ -31,27 +32,40 @@ from ._compat import string_types
|
|||||||
|
|
||||||
|
|
||||||
class UnsupportedAlgorithm(dns.exception.DNSException):
|
class UnsupportedAlgorithm(dns.exception.DNSException):
|
||||||
|
|
||||||
"""The DNSSEC algorithm is not supported."""
|
"""The DNSSEC algorithm is not supported."""
|
||||||
|
|
||||||
|
|
||||||
class ValidationFailure(dns.exception.DNSException):
|
class ValidationFailure(dns.exception.DNSException):
|
||||||
|
|
||||||
"""The DNSSEC signature is invalid."""
|
"""The DNSSEC signature is invalid."""
|
||||||
|
|
||||||
|
|
||||||
|
#: RSAMD5
|
||||||
RSAMD5 = 1
|
RSAMD5 = 1
|
||||||
|
#: DH
|
||||||
DH = 2
|
DH = 2
|
||||||
|
#: DSA
|
||||||
DSA = 3
|
DSA = 3
|
||||||
|
#: ECC
|
||||||
ECC = 4
|
ECC = 4
|
||||||
|
#: RSASHA1
|
||||||
RSASHA1 = 5
|
RSASHA1 = 5
|
||||||
|
#: DSANSEC3SHA1
|
||||||
DSANSEC3SHA1 = 6
|
DSANSEC3SHA1 = 6
|
||||||
|
#: RSASHA1NSEC3SHA1
|
||||||
RSASHA1NSEC3SHA1 = 7
|
RSASHA1NSEC3SHA1 = 7
|
||||||
|
#: RSASHA256
|
||||||
RSASHA256 = 8
|
RSASHA256 = 8
|
||||||
|
#: RSASHA512
|
||||||
RSASHA512 = 10
|
RSASHA512 = 10
|
||||||
|
#: ECDSAP256SHA256
|
||||||
ECDSAP256SHA256 = 13
|
ECDSAP256SHA256 = 13
|
||||||
|
#: ECDSAP384SHA384
|
||||||
ECDSAP384SHA384 = 14
|
ECDSAP384SHA384 = 14
|
||||||
|
#: INDIRECT
|
||||||
INDIRECT = 252
|
INDIRECT = 252
|
||||||
|
#: PRIVATEDNS
|
||||||
PRIVATEDNS = 253
|
PRIVATEDNS = 253
|
||||||
|
#: PRIVATEOID
|
||||||
PRIVATEOID = 254
|
PRIVATEOID = 254
|
||||||
|
|
||||||
_algorithm_by_text = {
|
_algorithm_by_text = {
|
||||||
@@ -75,12 +89,14 @@ _algorithm_by_text = {
|
|||||||
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
||||||
# would cause the mapping not to be true inverse.
|
# would cause the mapping not to be true inverse.
|
||||||
|
|
||||||
_algorithm_by_value = dict((y, x) for x, y in _algorithm_by_text.items())
|
_algorithm_by_value = {y: x for x, y in _algorithm_by_text.items()}
|
||||||
|
|
||||||
|
|
||||||
def algorithm_from_text(text):
|
def algorithm_from_text(text):
|
||||||
"""Convert text into a DNSSEC algorithm value
|
"""Convert text into a DNSSEC algorithm value.
|
||||||
@rtype: int"""
|
|
||||||
|
Returns an ``int``.
|
||||||
|
"""
|
||||||
|
|
||||||
value = _algorithm_by_text.get(text.upper())
|
value = _algorithm_by_text.get(text.upper())
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -90,7 +106,9 @@ def algorithm_from_text(text):
|
|||||||
|
|
||||||
def algorithm_to_text(value):
|
def algorithm_to_text(value):
|
||||||
"""Convert a DNSSEC algorithm value to text
|
"""Convert a DNSSEC algorithm value to text
|
||||||
@rtype: string"""
|
|
||||||
|
Returns a ``str``.
|
||||||
|
"""
|
||||||
|
|
||||||
text = _algorithm_by_value.get(value)
|
text = _algorithm_by_value.get(value)
|
||||||
if text is None:
|
if text is None:
|
||||||
@@ -105,6 +123,14 @@ def _to_rdata(record, origin):
|
|||||||
|
|
||||||
|
|
||||||
def key_id(key, origin=None):
|
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 = _to_rdata(key, origin)
|
||||||
rdata = bytearray(rdata)
|
rdata = bytearray(rdata)
|
||||||
if key.algorithm == RSAMD5:
|
if key.algorithm == RSAMD5:
|
||||||
@@ -121,12 +147,28 @@ def key_id(key, origin=None):
|
|||||||
|
|
||||||
|
|
||||||
def make_ds(name, key, algorithm, origin=None):
|
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':
|
if algorithm.upper() == 'SHA1':
|
||||||
dsalg = 1
|
dsalg = 1
|
||||||
hash = dns.hash.hashes['SHA1']()
|
hash = SHA1.new()
|
||||||
elif algorithm.upper() == 'SHA256':
|
elif algorithm.upper() == 'SHA256':
|
||||||
dsalg = 2
|
dsalg = 2
|
||||||
hash = dns.hash.hashes['SHA256']()
|
hash = SHA256.new()
|
||||||
else:
|
else:
|
||||||
raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm)
|
raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm)
|
||||||
|
|
||||||
@@ -198,15 +240,15 @@ def _is_sha512(algorithm):
|
|||||||
|
|
||||||
def _make_hash(algorithm):
|
def _make_hash(algorithm):
|
||||||
if _is_md5(algorithm):
|
if _is_md5(algorithm):
|
||||||
return dns.hash.hashes['MD5']()
|
return MD5.new()
|
||||||
if _is_sha1(algorithm):
|
if _is_sha1(algorithm):
|
||||||
return dns.hash.hashes['SHA1']()
|
return SHA1.new()
|
||||||
if _is_sha256(algorithm):
|
if _is_sha256(algorithm):
|
||||||
return dns.hash.hashes['SHA256']()
|
return SHA256.new()
|
||||||
if _is_sha384(algorithm):
|
if _is_sha384(algorithm):
|
||||||
return dns.hash.hashes['SHA384']()
|
return SHA384.new()
|
||||||
if _is_sha512(algorithm):
|
if _is_sha512(algorithm):
|
||||||
return dns.hash.hashes['SHA512']()
|
return SHA512.new()
|
||||||
raise ValidationFailure('unknown hash for algorithm %u' % algorithm)
|
raise ValidationFailure('unknown hash for algorithm %u' % algorithm)
|
||||||
|
|
||||||
|
|
||||||
@@ -232,31 +274,32 @@ def _make_algorithm_id(algorithm):
|
|||||||
def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
||||||
"""Validate an RRset against a single signature rdata
|
"""Validate an RRset against a single signature rdata
|
||||||
|
|
||||||
The owner name of the rrsig is assumed to be the same as the owner name
|
The owner name of *rrsig* is assumed to be the same as the owner name
|
||||||
of the rrset.
|
of *rrset*.
|
||||||
|
|
||||||
@param rrset: The RRset to validate
|
*rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or
|
||||||
@type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset)
|
a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
|
||||||
tuple
|
|
||||||
@param rrsig: The signature rdata
|
*rrsig* is a ``dns.rdata.Rdata``, the signature to validate.
|
||||||
@type rrsig: dns.rrset.Rdata
|
|
||||||
@param keys: The key dictionary.
|
*keys* is the key dictionary, used to find the DNSKEY associated with
|
||||||
@type keys: a dictionary keyed by dns.name.Name with node or rdataset
|
a given name. The dictionary is keyed by a ``dns.name.Name``, and has
|
||||||
values
|
``dns.node.Node`` or ``dns.rdataset.Rdataset`` values.
|
||||||
@param origin: The origin to use for relative names
|
|
||||||
@type origin: dns.name.Name or None
|
*origin* is a ``dns.name.Name``, the origin to use for relative names.
|
||||||
@param now: The time to use when validating the signatures. The default
|
|
||||||
is the current time.
|
*now* is an ``int``, the time to use when validating the signatures,
|
||||||
@type now: int
|
in seconds since the UNIX epoch. The default is the current time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(origin, string_types):
|
if isinstance(origin, string_types):
|
||||||
origin = dns.name.from_text(origin, dns.name.root)
|
origin = dns.name.from_text(origin, dns.name.root)
|
||||||
|
|
||||||
for candidate_key in _find_candidate_keys(keys, rrsig):
|
candidate_keys = _find_candidate_keys(keys, rrsig)
|
||||||
if not candidate_key:
|
if candidate_keys is None:
|
||||||
raise ValidationFailure('unknown key')
|
raise ValidationFailure('unknown key')
|
||||||
|
|
||||||
|
for candidate_key in candidate_keys:
|
||||||
# For convenience, allow the rrset to be specified as a (name,
|
# For convenience, allow the rrset to be specified as a (name,
|
||||||
# rdataset) tuple as well as a proper rrset
|
# rdataset) tuple as well as a proper rrset
|
||||||
if isinstance(rrset, tuple):
|
if isinstance(rrset, tuple):
|
||||||
@@ -284,11 +327,13 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
|||||||
keyptr = keyptr[2:]
|
keyptr = keyptr[2:]
|
||||||
rsa_e = keyptr[0:bytes_]
|
rsa_e = keyptr[0:bytes_]
|
||||||
rsa_n = keyptr[bytes_:]
|
rsa_n = keyptr[bytes_:]
|
||||||
keylen = len(rsa_n) * 8
|
try:
|
||||||
pubkey = Crypto.PublicKey.RSA.construct(
|
pubkey = CryptoRSA.construct(
|
||||||
(Crypto.Util.number.bytes_to_long(rsa_n),
|
(number.bytes_to_long(rsa_n),
|
||||||
Crypto.Util.number.bytes_to_long(rsa_e)))
|
number.bytes_to_long(rsa_e)))
|
||||||
sig = (Crypto.Util.number.bytes_to_long(rrsig.signature),)
|
except ValueError:
|
||||||
|
raise ValidationFailure('invalid public key')
|
||||||
|
sig = rrsig.signature
|
||||||
elif _is_dsa(rrsig.algorithm):
|
elif _is_dsa(rrsig.algorithm):
|
||||||
keyptr = candidate_key.key
|
keyptr = candidate_key.key
|
||||||
(t,) = struct.unpack('!B', keyptr[0:1])
|
(t,) = struct.unpack('!B', keyptr[0:1])
|
||||||
@@ -301,36 +346,37 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
|||||||
dsa_g = keyptr[0:octets]
|
dsa_g = keyptr[0:octets]
|
||||||
keyptr = keyptr[octets:]
|
keyptr = keyptr[octets:]
|
||||||
dsa_y = keyptr[0:octets]
|
dsa_y = keyptr[0:octets]
|
||||||
pubkey = Crypto.PublicKey.DSA.construct(
|
pubkey = CryptoDSA.construct(
|
||||||
(Crypto.Util.number.bytes_to_long(dsa_y),
|
(number.bytes_to_long(dsa_y),
|
||||||
Crypto.Util.number.bytes_to_long(dsa_g),
|
number.bytes_to_long(dsa_g),
|
||||||
Crypto.Util.number.bytes_to_long(dsa_p),
|
number.bytes_to_long(dsa_p),
|
||||||
Crypto.Util.number.bytes_to_long(dsa_q)))
|
number.bytes_to_long(dsa_q)))
|
||||||
(dsa_r, dsa_s) = struct.unpack('!20s20s', rrsig.signature[1:])
|
sig = rrsig.signature[1:]
|
||||||
sig = (Crypto.Util.number.bytes_to_long(dsa_r),
|
|
||||||
Crypto.Util.number.bytes_to_long(dsa_s))
|
|
||||||
elif _is_ecdsa(rrsig.algorithm):
|
elif _is_ecdsa(rrsig.algorithm):
|
||||||
|
# use ecdsa for NIST-384p -- not currently supported by pycryptodome
|
||||||
|
|
||||||
|
keyptr = candidate_key.key
|
||||||
|
|
||||||
if rrsig.algorithm == ECDSAP256SHA256:
|
if rrsig.algorithm == ECDSAP256SHA256:
|
||||||
curve = ecdsa.curves.NIST256p
|
curve = ecdsa.curves.NIST256p
|
||||||
key_len = 32
|
key_len = 32
|
||||||
elif rrsig.algorithm == ECDSAP384SHA384:
|
elif rrsig.algorithm == ECDSAP384SHA384:
|
||||||
curve = ecdsa.curves.NIST384p
|
curve = ecdsa.curves.NIST384p
|
||||||
key_len = 48
|
key_len = 48
|
||||||
else:
|
|
||||||
# shouldn't happen
|
x = number.bytes_to_long(keyptr[0:key_len])
|
||||||
raise ValidationFailure('unknown ECDSA curve')
|
y = number.bytes_to_long(keyptr[key_len:key_len * 2])
|
||||||
keyptr = candidate_key.key
|
if not ecdsa.ecdsa.point_is_valid(curve.generator, x, y):
|
||||||
x = Crypto.Util.number.bytes_to_long(keyptr[0:key_len])
|
raise ValidationFailure('invalid ECDSA key')
|
||||||
y = Crypto.Util.number.bytes_to_long(keyptr[key_len:key_len * 2])
|
|
||||||
assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y)
|
|
||||||
point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
|
point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
|
||||||
verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point,
|
verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point,
|
||||||
curve)
|
curve)
|
||||||
pubkey = ECKeyWrapper(verifying_key, key_len)
|
pubkey = ECKeyWrapper(verifying_key, key_len)
|
||||||
r = rrsig.signature[:key_len]
|
r = rrsig.signature[:key_len]
|
||||||
s = rrsig.signature[key_len:]
|
s = rrsig.signature[key_len:]
|
||||||
sig = ecdsa.ecdsa.Signature(Crypto.Util.number.bytes_to_long(r),
|
sig = ecdsa.ecdsa.Signature(number.bytes_to_long(r),
|
||||||
Crypto.Util.number.bytes_to_long(s))
|
number.bytes_to_long(s))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
|
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
|
||||||
|
|
||||||
@@ -352,44 +398,49 @@ def _validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
|||||||
hash.update(rrlen)
|
hash.update(rrlen)
|
||||||
hash.update(rrdata)
|
hash.update(rrdata)
|
||||||
|
|
||||||
digest = hash.digest()
|
try:
|
||||||
|
|
||||||
if _is_rsa(rrsig.algorithm):
|
if _is_rsa(rrsig.algorithm):
|
||||||
# PKCS1 algorithm identifier goop
|
verifier = pkcs1_15.new(pubkey)
|
||||||
digest = _make_algorithm_id(rrsig.algorithm) + digest
|
# will raise ValueError if verify fails:
|
||||||
padlen = keylen // 8 - len(digest) - 3
|
verifier.verify(hash, sig)
|
||||||
digest = struct.pack('!%dB' % (2 + padlen + 1),
|
elif _is_dsa(rrsig.algorithm):
|
||||||
*([0, 1] + [0xFF] * padlen + [0])) + digest
|
verifier = DSS.new(pubkey, 'fips-186-3')
|
||||||
elif _is_dsa(rrsig.algorithm) or _is_ecdsa(rrsig.algorithm):
|
verifier.verify(hash, sig)
|
||||||
pass
|
elif _is_ecdsa(rrsig.algorithm):
|
||||||
|
digest = hash.digest()
|
||||||
|
if not pubkey.verify(digest, sig):
|
||||||
|
raise ValueError
|
||||||
else:
|
else:
|
||||||
# Raise here for code clarity; this won't actually ever happen
|
# Raise here for code clarity; this won't actually ever happen
|
||||||
# since if the algorithm is really unknown we'd already have
|
# since if the algorithm is really unknown we'd already have
|
||||||
# raised an exception above
|
# raised an exception above
|
||||||
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
|
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
|
||||||
|
# If we got here, we successfully verified so we can return without error
|
||||||
if pubkey.verify(digest, sig):
|
|
||||||
return
|
return
|
||||||
|
except ValueError:
|
||||||
|
# this happens on an individual validation failure
|
||||||
|
continue
|
||||||
|
# nothing verified -- raise failure:
|
||||||
raise ValidationFailure('verify failure')
|
raise ValidationFailure('verify failure')
|
||||||
|
|
||||||
|
|
||||||
def _validate(rrset, rrsigset, keys, origin=None, now=None):
|
def _validate(rrset, rrsigset, keys, origin=None, now=None):
|
||||||
"""Validate an RRset
|
"""Validate an RRset.
|
||||||
|
|
||||||
@param rrset: The RRset to validate
|
*rrset* is the RRset to validate. It can be a ``dns.rrset.RRset`` or
|
||||||
@type rrset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset)
|
a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
|
||||||
tuple
|
|
||||||
@param rrsigset: The signature RRset
|
*rrsigset* is the signature RRset to be validated. It can be a
|
||||||
@type rrsigset: dns.rrset.RRset or (dns.name.Name, dns.rdataset.Rdataset)
|
``dns.rrset.RRset`` or a ``(dns.name.Name, dns.rdataset.Rdataset)`` tuple.
|
||||||
tuple
|
|
||||||
@param keys: The key dictionary.
|
*keys* is the key dictionary, used to find the DNSKEY associated with
|
||||||
@type keys: a dictionary keyed by dns.name.Name with node or rdataset
|
a given name. The dictionary is keyed by a ``dns.name.Name``, and has
|
||||||
values
|
``dns.node.Node`` or ``dns.rdataset.Rdataset`` values.
|
||||||
@param origin: The origin to use for relative names
|
|
||||||
@type origin: dns.name.Name or None
|
*origin* is a ``dns.name.Name``, the origin to use for relative names.
|
||||||
@param now: The time to use when validating the signatures. The default
|
|
||||||
is the current time.
|
*now* is an ``int``, the time to use when validating the signatures,
|
||||||
@type now: int
|
in seconds since the UNIX epoch. The default is the current time.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(origin, string_types):
|
if isinstance(origin, string_types):
|
||||||
@@ -408,7 +459,7 @@ def _validate(rrset, rrsigset, keys, origin=None, now=None):
|
|||||||
rrsigrdataset = rrsigset
|
rrsigrdataset = rrsigset
|
||||||
|
|
||||||
rrname = rrname.choose_relativity(origin)
|
rrname = rrname.choose_relativity(origin)
|
||||||
rrsigname = rrname.choose_relativity(origin)
|
rrsigname = rrsigname.choose_relativity(origin)
|
||||||
if rrname != rrsigname:
|
if rrname != rrsigname:
|
||||||
raise ValidationFailure("owner names do not match")
|
raise ValidationFailure("owner names do not match")
|
||||||
|
|
||||||
@@ -422,25 +473,39 @@ def _validate(rrset, rrsigset, keys, origin=None, now=None):
|
|||||||
|
|
||||||
|
|
||||||
def _need_pycrypto(*args, **kwargs):
|
def _need_pycrypto(*args, **kwargs):
|
||||||
raise NotImplementedError("DNSSEC validation requires pycrypto")
|
raise NotImplementedError("DNSSEC validation requires pycryptodome/pycryptodomex")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import Crypto.PublicKey.RSA
|
try:
|
||||||
import Crypto.PublicKey.DSA
|
# test we're using pycryptodome, not pycrypto (which misses SHA1 for example)
|
||||||
import Crypto.Util.number
|
from Crypto.Hash import MD5, SHA1, SHA256, SHA384, SHA512
|
||||||
validate = _validate
|
from Crypto.PublicKey import RSA as CryptoRSA, DSA as CryptoDSA
|
||||||
validate_rrsig = _validate_rrsig
|
from Crypto.Signature import pkcs1_15, DSS
|
||||||
_have_pycrypto = True
|
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:
|
except ImportError:
|
||||||
validate = _need_pycrypto
|
validate = _need_pycrypto
|
||||||
validate_rrsig = _need_pycrypto
|
validate_rrsig = _need_pycrypto
|
||||||
_have_pycrypto = False
|
_have_pycrypto = False
|
||||||
|
_have_ecdsa = False
|
||||||
|
else:
|
||||||
|
validate = _validate
|
||||||
|
validate_rrsig = _validate_rrsig
|
||||||
|
_have_pycrypto = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import ecdsa
|
import ecdsa
|
||||||
import ecdsa.ecdsa
|
import ecdsa.ecdsa
|
||||||
import ecdsa.ellipticcurve
|
import ecdsa.ellipticcurve
|
||||||
import ecdsa.keys
|
import ecdsa.keys
|
||||||
|
except ImportError:
|
||||||
|
_have_ecdsa = False
|
||||||
|
else:
|
||||||
_have_ecdsa = True
|
_have_ecdsa = True
|
||||||
|
|
||||||
class ECKeyWrapper(object):
|
class ECKeyWrapper(object):
|
||||||
@@ -450,8 +515,5 @@ try:
|
|||||||
self.key_len = key_len
|
self.key_len = key_len
|
||||||
|
|
||||||
def verify(self, digest, sig):
|
def verify(self, digest, sig):
|
||||||
diglong = Crypto.Util.number.bytes_to_long(digest)
|
diglong = number.bytes_to_long(digest)
|
||||||
return self.key.pubkey.verifies(diglong, sig)
|
return self.key.pubkey.verifies(diglong, sig)
|
||||||
|
|
||||||
except ImportError:
|
|
||||||
_have_ecdsa = False
|
|
||||||
|
|||||||
19
src/dns/dnssec.pyi
Normal file
19
src/dns/dnssec.pyi
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from 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:
|
||||||
|
...
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2006, 2007, 2009, 2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -13,31 +15,32 @@
|
|||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
"""DNS E.164 helpers
|
"""DNS E.164 helpers."""
|
||||||
|
|
||||||
@var public_enum_domain: The DNS public ENUM domain, e164.arpa.
|
|
||||||
@type public_enum_domain: dns.name.Name object
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import dns.exception
|
import dns.exception
|
||||||
import dns.name
|
import dns.name
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
from ._compat import string_types
|
from ._compat import string_types, maybe_decode
|
||||||
|
|
||||||
|
#: The public E.164 domain.
|
||||||
public_enum_domain = dns.name.from_text('e164.arpa.')
|
public_enum_domain = dns.name.from_text('e164.arpa.')
|
||||||
|
|
||||||
|
|
||||||
def from_e164(text, origin=public_enum_domain):
|
def from_e164(text, origin=public_enum_domain):
|
||||||
"""Convert an E.164 number in textual form into a Name object whose
|
"""Convert an E.164 number in textual form into a Name object whose
|
||||||
value is the ENUM domain name for that number.
|
value is the ENUM domain name for that number.
|
||||||
@param text: an E.164 number in textual form.
|
|
||||||
@type text: str
|
Non-digits in the text are ignored, i.e. "16505551212",
|
||||||
@param origin: The domain in which the number should be constructed.
|
"+1.650.555.1212" and "1 (650) 555-1212" are all the same.
|
||||||
The default is e164.arpa.
|
|
||||||
@type origin: dns.name.Name object or None
|
*text*, a ``text``, is an E.164 number in textual form.
|
||||||
@rtype: dns.name.Name object
|
|
||||||
|
*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 = [d for d in text if d.isdigit()]
|
||||||
parts.reverse()
|
parts.reverse()
|
||||||
return dns.name.from_text('.'.join(parts), origin=origin)
|
return dns.name.from_text('.'.join(parts), origin=origin)
|
||||||
@@ -45,14 +48,23 @@ def from_e164(text, origin=public_enum_domain):
|
|||||||
|
|
||||||
def to_e164(name, origin=public_enum_domain, want_plus_prefix=True):
|
def to_e164(name, origin=public_enum_domain, want_plus_prefix=True):
|
||||||
"""Convert an ENUM domain name into an E.164 number.
|
"""Convert an ENUM domain name into an E.164 number.
|
||||||
@param name: the ENUM domain name.
|
|
||||||
@type name: dns.name.Name object.
|
Note that dnspython does not have any information about preferred
|
||||||
@param origin: A domain containing the ENUM domain name. The
|
number formats within national numbering plans, so all numbers are
|
||||||
name is relativized to this domain before being converted to text.
|
emitted as a simple string of digits, prefixed by a '+' (unless
|
||||||
@type origin: dns.name.Name object or None
|
*want_plus_prefix* is ``False``).
|
||||||
@param want_plus_prefix: if True, add a '+' to the beginning of the
|
|
||||||
returned number.
|
*name* is a ``dns.name.Name``, the ENUM domain name.
|
||||||
@rtype: str
|
|
||||||
|
*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:
|
if origin is not None:
|
||||||
name = name.relativize(origin)
|
name = name.relativize(origin)
|
||||||
@@ -63,14 +75,22 @@ def to_e164(name, origin=public_enum_domain, want_plus_prefix=True):
|
|||||||
text = b''.join(dlabels)
|
text = b''.join(dlabels)
|
||||||
if want_plus_prefix:
|
if want_plus_prefix:
|
||||||
text = b'+' + text
|
text = b'+' + text
|
||||||
return text
|
return maybe_decode(text)
|
||||||
|
|
||||||
|
|
||||||
def query(number, domains, resolver=None):
|
def query(number, domains, resolver=None):
|
||||||
"""Look for NAPTR RRs for the specified number in the specified domains.
|
"""Look for NAPTR RRs for the specified number in the specified domains.
|
||||||
|
|
||||||
e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.'])
|
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:
|
if resolver is None:
|
||||||
resolver = dns.resolver.get_default_resolver()
|
resolver = dns.resolver.get_default_resolver()
|
||||||
e_nx = dns.resolver.NXDOMAIN()
|
e_nx = dns.resolver.NXDOMAIN()
|
||||||
|
|||||||
10
src/dns/e164.pyi
Normal file
10
src/dns/e164.pyi
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from 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:
|
||||||
|
...
|
||||||
179
src/dns/edns.py
179
src/dns/edns.py
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2009, 2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -15,18 +17,42 @@
|
|||||||
|
|
||||||
"""EDNS Options"""
|
"""EDNS Options"""
|
||||||
|
|
||||||
NSID = 3
|
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):
|
class Option(object):
|
||||||
|
|
||||||
"""Base class for all EDNS option types.
|
"""Base class for all EDNS option types."""
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, otype):
|
def __init__(self, otype):
|
||||||
"""Initialize an option.
|
"""Initialize an option.
|
||||||
@param otype: The rdata type
|
|
||||||
@type otype: int
|
*otype*, an ``int``, is the option type.
|
||||||
"""
|
"""
|
||||||
self.otype = otype
|
self.otype = otype
|
||||||
|
|
||||||
@@ -37,23 +63,26 @@ class Option(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_wire(cls, otype, wire, current, olen):
|
def from_wire(cls, otype, wire, current, olen):
|
||||||
"""Build an EDNS option object from wire format
|
"""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``.
|
||||||
|
"""
|
||||||
|
|
||||||
@param otype: The option type
|
|
||||||
@type otype: int
|
|
||||||
@param wire: The wire-format message
|
|
||||||
@type wire: string
|
|
||||||
@param current: The offset in wire of the beginning of the rdata.
|
|
||||||
@type current: int
|
|
||||||
@param olen: The length of the wire-format option data
|
|
||||||
@type olen: int
|
|
||||||
@rtype: dns.edns.Option instance"""
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _cmp(self, other):
|
def _cmp(self, other):
|
||||||
"""Compare an EDNS option with another option of the same type.
|
"""Compare an EDNS option with another option of the same type.
|
||||||
Return < 0 if self < other, 0 if self == other,
|
|
||||||
and > 0 if self > other.
|
Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@@ -98,7 +127,7 @@ class Option(object):
|
|||||||
|
|
||||||
class GenericOption(Option):
|
class GenericOption(Option):
|
||||||
|
|
||||||
"""Generate Rdata Class
|
"""Generic Option Class
|
||||||
|
|
||||||
This class is used for EDNS option types for which we have no better
|
This class is used for EDNS option types for which we have no better
|
||||||
implementation.
|
implementation.
|
||||||
@@ -111,6 +140,9 @@ class GenericOption(Option):
|
|||||||
def to_wire(self, file):
|
def to_wire(self, file):
|
||||||
file.write(self.data)
|
file.write(self.data)
|
||||||
|
|
||||||
|
def to_text(self):
|
||||||
|
return "Generic %d" % self.otype
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_wire(cls, otype, wire, current, olen):
|
def from_wire(cls, otype, wire, current, olen):
|
||||||
return cls(otype, wire[current: current + olen])
|
return cls(otype, wire[current: current + olen])
|
||||||
@@ -122,11 +154,96 @@ class GenericOption(Option):
|
|||||||
return 1
|
return 1
|
||||||
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 = {
|
_type_to_class = {
|
||||||
|
ECS: ECSOption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_option_class(otype):
|
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)
|
cls = _type_to_class.get(otype)
|
||||||
if cls is None:
|
if cls is None:
|
||||||
cls = GenericOption
|
cls = GenericOption
|
||||||
@@ -134,17 +251,19 @@ def get_option_class(otype):
|
|||||||
|
|
||||||
|
|
||||||
def option_from_wire(otype, wire, current, olen):
|
def option_from_wire(otype, wire, current, olen):
|
||||||
"""Build an EDNS option object from wire format
|
"""Build an EDNS option object from wire format.
|
||||||
|
|
||||||
@param otype: The option type
|
*otype*, an ``int``, is the option type.
|
||||||
@type otype: int
|
|
||||||
@param wire: The wire-format message
|
*wire*, a ``binary``, is the wire-format message.
|
||||||
@type wire: string
|
|
||||||
@param current: The offset in wire of the beginning of the rdata.
|
*current*, an ``int``, is the offset in *wire* of the beginning
|
||||||
@type current: int
|
of the rdata.
|
||||||
@param olen: The length of the wire-format option data
|
|
||||||
@type olen: int
|
*olen*, an ``int``, is the length of the wire-format option data
|
||||||
@rtype: dns.edns.Option instance"""
|
|
||||||
|
Returns an instance of a subclass of ``dns.edns.Option``.
|
||||||
|
"""
|
||||||
|
|
||||||
cls = get_option_class(otype)
|
cls = get_option_class(otype)
|
||||||
return cls.from_wire(otype, wire, current, olen)
|
return cls.from_wire(otype, wire, current, olen)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2009, 2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -25,6 +27,11 @@ except ImportError:
|
|||||||
|
|
||||||
class EntropyPool(object):
|
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):
|
def __init__(self, seed=None):
|
||||||
self.pool_index = 0
|
self.pool_index = 0
|
||||||
self.digest = None
|
self.digest = None
|
||||||
|
|||||||
10
src/dns/entropy.pyi
Normal file
10
src/dns/entropy.pyi
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from typing import Optional
|
||||||
|
from random import SystemRandom
|
||||||
|
|
||||||
|
system_random : Optional[SystemRandom]
|
||||||
|
|
||||||
|
def random_16() -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def between(first: int, last: int) -> int:
|
||||||
|
pass
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -13,32 +15,35 @@
|
|||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
"""Common DNS Exceptions."""
|
"""Common DNS Exceptions.
|
||||||
|
|
||||||
|
Dnspython modules may also define their own exceptions, which will
|
||||||
|
always be subclasses of ``DNSException``.
|
||||||
|
"""
|
||||||
|
|
||||||
class DNSException(Exception):
|
class DNSException(Exception):
|
||||||
|
|
||||||
"""Abstract base class shared by all dnspython exceptions.
|
"""Abstract base class shared by all dnspython exceptions.
|
||||||
|
|
||||||
It supports two basic modes of operation:
|
It supports two basic modes of operation:
|
||||||
|
|
||||||
a) Old/compatible mode is used if __init__ was called with
|
a) Old/compatible mode is used if ``__init__`` was called with
|
||||||
empty **kwargs.
|
empty *kwargs*. In compatible mode all *args* are passed
|
||||||
In compatible mode all *args are passed to standard Python Exception class
|
to the standard Python Exception class as before and all *args* are
|
||||||
as before and all *args are printed by standard __str__ implementation.
|
printed by the standard ``__str__`` implementation. Class variable
|
||||||
Class variable msg (or doc string if msg is None) is returned from str()
|
``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()``
|
||||||
if *args is empty.
|
if *args* is empty.
|
||||||
|
|
||||||
b) New/parametrized mode is used if __init__ was called with
|
b) New/parametrized mode is used if ``__init__`` was called with
|
||||||
non-empty **kwargs.
|
non-empty *kwargs*.
|
||||||
In the new mode *args has to be empty and all kwargs has to exactly match
|
In the new mode *args* must be empty and all kwargs must match
|
||||||
set in class variable self.supp_kwargs. All kwargs are stored inside
|
those set in class variable ``supp_kwargs``. All kwargs are stored inside
|
||||||
self.kwargs and used in new __str__ implementation to construct
|
``self.kwargs`` and used in a new ``__str__`` implementation to construct
|
||||||
formatted message based on self.fmt string.
|
a formatted message based on the ``fmt`` class variable, a ``string``.
|
||||||
|
|
||||||
In the simplest case it is enough to override supp_kwargs and fmt
|
In the simplest case it is enough to override the ``supp_kwargs``
|
||||||
class variables to get nice parametrized messages.
|
and ``fmt`` class variables to get nice parametrized messages.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
msg = None # non-parametrized message
|
msg = None # non-parametrized message
|
||||||
supp_kwargs = set() # accepted parameters for _fmt_kwargs (sanity check)
|
supp_kwargs = set() # accepted parameters for _fmt_kwargs (sanity check)
|
||||||
fmt = None # message parametrized with results from _fmt_kwargs
|
fmt = None # message parametrized with results from _fmt_kwargs
|
||||||
@@ -102,27 +107,22 @@ class DNSException(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class FormError(DNSException):
|
class FormError(DNSException):
|
||||||
|
|
||||||
"""DNS message is malformed."""
|
"""DNS message is malformed."""
|
||||||
|
|
||||||
|
|
||||||
class SyntaxError(DNSException):
|
class SyntaxError(DNSException):
|
||||||
|
|
||||||
"""Text input is malformed."""
|
"""Text input is malformed."""
|
||||||
|
|
||||||
|
|
||||||
class UnexpectedEnd(SyntaxError):
|
class UnexpectedEnd(SyntaxError):
|
||||||
|
|
||||||
"""Text input ended unexpectedly."""
|
"""Text input ended unexpectedly."""
|
||||||
|
|
||||||
|
|
||||||
class TooBig(DNSException):
|
class TooBig(DNSException):
|
||||||
|
|
||||||
"""The DNS message is too big."""
|
"""The DNS message is too big."""
|
||||||
|
|
||||||
|
|
||||||
class Timeout(DNSException):
|
class Timeout(DNSException):
|
||||||
|
|
||||||
"""The DNS operation timed out."""
|
"""The DNS operation timed out."""
|
||||||
supp_kwargs = set(['timeout'])
|
supp_kwargs = {'timeout'}
|
||||||
fmt = "The DNS operation timed out after {timeout} seconds"
|
fmt = "The DNS operation timed out after {timeout} seconds"
|
||||||
|
|||||||
9
src/dns/exception.pyi
Normal file
9
src/dns/exception.pyi
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from typing import Set, Optional, Dict
|
||||||
|
|
||||||
|
class DNSException(Exception):
|
||||||
|
supp_kwargs : Set[str]
|
||||||
|
kwargs : Optional[Dict]
|
||||||
|
|
||||||
|
class SyntaxError(DNSException): ...
|
||||||
|
class FormError(DNSException): ...
|
||||||
|
class Timeout(DNSException): ...
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -17,16 +19,24 @@
|
|||||||
|
|
||||||
# Standard DNS flags
|
# Standard DNS flags
|
||||||
|
|
||||||
|
#: Query Response
|
||||||
QR = 0x8000
|
QR = 0x8000
|
||||||
|
#: Authoritative Answer
|
||||||
AA = 0x0400
|
AA = 0x0400
|
||||||
|
#: Truncated Response
|
||||||
TC = 0x0200
|
TC = 0x0200
|
||||||
|
#: Recursion Desired
|
||||||
RD = 0x0100
|
RD = 0x0100
|
||||||
|
#: Recursion Available
|
||||||
RA = 0x0080
|
RA = 0x0080
|
||||||
|
#: Authentic Data
|
||||||
AD = 0x0020
|
AD = 0x0020
|
||||||
|
#: Checking Disabled
|
||||||
CD = 0x0010
|
CD = 0x0010
|
||||||
|
|
||||||
# EDNS flags
|
# EDNS flags
|
||||||
|
|
||||||
|
#: DNSSEC answer OK
|
||||||
DO = 0x8000
|
DO = 0x8000
|
||||||
|
|
||||||
_by_text = {
|
_by_text = {
|
||||||
@@ -48,9 +58,9 @@ _edns_by_text = {
|
|||||||
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
||||||
# would cause the mappings not to be true inverses.
|
# would cause the mappings not to be true inverses.
|
||||||
|
|
||||||
_by_value = dict((y, x) for x, y in _by_text.items())
|
_by_value = {y: x for x, y in _by_text.items()}
|
||||||
|
|
||||||
_edns_by_value = dict((y, x) for x, y in _edns_by_text.items())
|
_edns_by_value = {y: x for x, y in _edns_by_text.items()}
|
||||||
|
|
||||||
|
|
||||||
def _order_flags(table):
|
def _order_flags(table):
|
||||||
@@ -83,7 +93,9 @@ def _to_text(flags, table, order):
|
|||||||
def from_text(text):
|
def from_text(text):
|
||||||
"""Convert a space-separated list of flag text values into a flags
|
"""Convert a space-separated list of flag text values into a flags
|
||||||
value.
|
value.
|
||||||
@rtype: int"""
|
|
||||||
|
Returns an ``int``
|
||||||
|
"""
|
||||||
|
|
||||||
return _from_text(text, _by_text)
|
return _from_text(text, _by_text)
|
||||||
|
|
||||||
@@ -91,7 +103,9 @@ def from_text(text):
|
|||||||
def to_text(flags):
|
def to_text(flags):
|
||||||
"""Convert a flags value into a space-separated list of flag text
|
"""Convert a flags value into a space-separated list of flag text
|
||||||
values.
|
values.
|
||||||
@rtype: string"""
|
|
||||||
|
Returns a ``text``.
|
||||||
|
"""
|
||||||
|
|
||||||
return _to_text(flags, _by_value, _flags_order)
|
return _to_text(flags, _by_value, _flags_order)
|
||||||
|
|
||||||
@@ -99,7 +113,9 @@ def to_text(flags):
|
|||||||
def edns_from_text(text):
|
def edns_from_text(text):
|
||||||
"""Convert a space-separated list of EDNS flag text values into a EDNS
|
"""Convert a space-separated list of EDNS flag text values into a EDNS
|
||||||
flags value.
|
flags value.
|
||||||
@rtype: int"""
|
|
||||||
|
Returns an ``int``
|
||||||
|
"""
|
||||||
|
|
||||||
return _from_text(text, _edns_by_text)
|
return _from_text(text, _edns_by_text)
|
||||||
|
|
||||||
@@ -107,6 +123,8 @@ def edns_from_text(text):
|
|||||||
def edns_to_text(flags):
|
def edns_to_text(flags):
|
||||||
"""Convert an EDNS flags value into a space-separated list of EDNS flag
|
"""Convert an EDNS flags value into a space-separated list of EDNS flag
|
||||||
text values.
|
text values.
|
||||||
@rtype: string"""
|
|
||||||
|
Returns a ``text``.
|
||||||
|
"""
|
||||||
|
|
||||||
return _to_text(flags, _edns_by_value, _edns_flags_order)
|
return _to_text(flags, _edns_by_value, _edns_flags_order)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -17,18 +19,16 @@
|
|||||||
|
|
||||||
import dns
|
import dns
|
||||||
|
|
||||||
|
|
||||||
def from_text(text):
|
def from_text(text):
|
||||||
"""Convert the text form of a range in a GENERATE statement to an
|
"""Convert the text form of a range in a ``$GENERATE`` statement to an
|
||||||
integer.
|
integer.
|
||||||
|
|
||||||
@param text: the textual range
|
*text*, a ``str``, the textual range in ``$GENERATE`` form.
|
||||||
@type text: string
|
|
||||||
@return: The start, stop and step values.
|
|
||||||
@rtype: tuple
|
|
||||||
"""
|
|
||||||
# TODO, figure out the bounds on start, stop and step.
|
|
||||||
|
|
||||||
|
Returns a tuple of three ``int`` values ``(start, stop, step)``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO, figure out the bounds on start, stop and step.
|
||||||
step = 1
|
step = 1
|
||||||
cur = ''
|
cur = ''
|
||||||
state = 0
|
state = 0
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2011 Nominum, Inc.
|
# Copyright (C) 2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -16,7 +18,11 @@
|
|||||||
"""Hashing backwards compatibility wrapper"""
|
"""Hashing backwards compatibility wrapper"""
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"dns.hash module will be removed in future versions. Please use hashlib instead.",
|
||||||
|
DeprecationWarning)
|
||||||
|
|
||||||
hashes = {}
|
hashes = {}
|
||||||
hashes['MD5'] = hashlib.md5
|
hashes['MD5'] = hashlib.md5
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -20,6 +22,7 @@ import socket
|
|||||||
import dns.ipv4
|
import dns.ipv4
|
||||||
import dns.ipv6
|
import dns.ipv6
|
||||||
|
|
||||||
|
from ._compat import maybe_ord
|
||||||
|
|
||||||
# We assume that AF_INET is always defined.
|
# We assume that AF_INET is always defined.
|
||||||
|
|
||||||
@@ -38,13 +41,14 @@ except AttributeError:
|
|||||||
def inet_pton(family, text):
|
def inet_pton(family, text):
|
||||||
"""Convert the textual form of a network address into its binary form.
|
"""Convert the textual form of a network address into its binary form.
|
||||||
|
|
||||||
@param family: the address family
|
*family* is an ``int``, the address family.
|
||||||
@type family: int
|
|
||||||
@param text: the textual address
|
*text* is a ``text``, the textual address.
|
||||||
@type text: string
|
|
||||||
@raises NotImplementedError: the address family specified is not
|
Raises ``NotImplementedError`` if the address family specified is not
|
||||||
implemented.
|
implemented.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``binary``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if family == AF_INET:
|
if family == AF_INET:
|
||||||
@@ -58,14 +62,16 @@ def inet_pton(family, text):
|
|||||||
def inet_ntop(family, address):
|
def inet_ntop(family, address):
|
||||||
"""Convert the binary form of a network address into its textual form.
|
"""Convert the binary form of a network address into its textual form.
|
||||||
|
|
||||||
@param family: the address family
|
*family* is an ``int``, the address family.
|
||||||
@type family: int
|
|
||||||
@param address: the binary address
|
*address* is a ``binary``, the network address in binary form.
|
||||||
@type address: string
|
|
||||||
@raises NotImplementedError: the address family specified is not
|
Raises ``NotImplementedError`` if the address family specified is not
|
||||||
implemented.
|
implemented.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if family == AF_INET:
|
if family == AF_INET:
|
||||||
return dns.ipv4.inet_ntoa(address)
|
return dns.ipv4.inet_ntoa(address)
|
||||||
elif family == AF_INET6:
|
elif family == AF_INET6:
|
||||||
@@ -77,11 +83,14 @@ def inet_ntop(family, address):
|
|||||||
def af_for_address(text):
|
def af_for_address(text):
|
||||||
"""Determine the address family of a textual-form network address.
|
"""Determine the address family of a textual-form network address.
|
||||||
|
|
||||||
@param text: the textual address
|
*text*, a ``text``, the textual address.
|
||||||
@type text: string
|
|
||||||
@raises ValueError: the address family cannot be determined from the input.
|
Raises ``ValueError`` if the address family cannot be determined
|
||||||
@rtype: int
|
from the input.
|
||||||
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dns.ipv4.inet_aton(text)
|
dns.ipv4.inet_aton(text)
|
||||||
return AF_INET
|
return AF_INET
|
||||||
@@ -96,16 +105,20 @@ def af_for_address(text):
|
|||||||
def is_multicast(text):
|
def is_multicast(text):
|
||||||
"""Is the textual-form network address a multicast address?
|
"""Is the textual-form network address a multicast address?
|
||||||
|
|
||||||
@param text: the textual address
|
*text*, a ``text``, the textual address.
|
||||||
@raises ValueError: the address family cannot be determined from the input.
|
|
||||||
@rtype: bool
|
Raises ``ValueError`` if the address family cannot be determined
|
||||||
|
from the input.
|
||||||
|
|
||||||
|
Returns a ``bool``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
first = ord(dns.ipv4.inet_aton(text)[0])
|
first = maybe_ord(dns.ipv4.inet_aton(text)[0])
|
||||||
return first >= 224 and first <= 239
|
return first >= 224 and first <= 239
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
first = ord(dns.ipv6.inet_aton(text)[0])
|
first = maybe_ord(dns.ipv6.inet_aton(text)[0])
|
||||||
return first == 255
|
return first == 255
|
||||||
except Exception:
|
except Exception:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|||||||
4
src/dns/inet.pyi
Normal file
4
src/dns/inet.pyi
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from typing import Union
|
||||||
|
from socket import AddressFamily
|
||||||
|
|
||||||
|
AF_INET6 : Union[int, AddressFamily]
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -21,26 +23,28 @@ import dns.exception
|
|||||||
from ._compat import binary_type
|
from ._compat import binary_type
|
||||||
|
|
||||||
def inet_ntoa(address):
|
def inet_ntoa(address):
|
||||||
"""Convert an IPv4 address in network form to text form.
|
"""Convert an IPv4 address in binary form to text form.
|
||||||
|
|
||||||
@param address: The IPv4 address
|
*address*, a ``binary``, the IPv4 address in binary form.
|
||||||
@type address: string
|
|
||||||
@returns: string
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if len(address) != 4:
|
if len(address) != 4:
|
||||||
raise dns.exception.SyntaxError
|
raise dns.exception.SyntaxError
|
||||||
if not isinstance(address, bytearray):
|
if not isinstance(address, bytearray):
|
||||||
address = bytearray(address)
|
address = bytearray(address)
|
||||||
return (u'%u.%u.%u.%u' % (address[0], address[1],
|
return ('%u.%u.%u.%u' % (address[0], address[1],
|
||||||
address[2], address[3])).encode()
|
address[2], address[3]))
|
||||||
|
|
||||||
def inet_aton(text):
|
def inet_aton(text):
|
||||||
"""Convert an IPv4 address in text form to network form.
|
"""Convert an IPv4 address in text form to binary form.
|
||||||
|
|
||||||
@param text: The IPv4 address
|
*text*, a ``text``, the IPv4 address in textual form.
|
||||||
@type text: string
|
|
||||||
@returns: string
|
Returns a ``binary``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(text, binary_type):
|
if not isinstance(text, binary_type):
|
||||||
text = text.encode()
|
text = text.encode()
|
||||||
parts = text.split(b'.')
|
parts = text.split(b'.')
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -22,15 +24,15 @@ import dns.exception
|
|||||||
import dns.ipv4
|
import dns.ipv4
|
||||||
from ._compat import xrange, binary_type, maybe_decode
|
from ._compat import xrange, binary_type, maybe_decode
|
||||||
|
|
||||||
_leading_zero = re.compile(b'0+([0-9a-f]+)')
|
_leading_zero = re.compile(r'0+([0-9a-f]+)')
|
||||||
|
|
||||||
def inet_ntoa(address):
|
def inet_ntoa(address):
|
||||||
"""Convert a network format IPv6 address into text.
|
"""Convert an IPv6 address in binary form to text form.
|
||||||
|
|
||||||
@param address: the binary address
|
*address*, a ``binary``, the IPv6 address in binary form.
|
||||||
@type address: string
|
|
||||||
@rtype: string
|
Raises ``ValueError`` if the address isn't 16 bytes long.
|
||||||
@raises ValueError: the address isn't 16 bytes long
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if len(address) != 16:
|
if len(address) != 16:
|
||||||
@@ -40,7 +42,7 @@ def inet_ntoa(address):
|
|||||||
i = 0
|
i = 0
|
||||||
l = len(hex)
|
l = len(hex)
|
||||||
while i < l:
|
while i < l:
|
||||||
chunk = hex[i : i + 4]
|
chunk = maybe_decode(hex[i : i + 4])
|
||||||
# strip leading zeros. we do this with an re instead of
|
# strip leading zeros. we do this with an re instead of
|
||||||
# with lstrip() because lstrip() didn't support chars until
|
# with lstrip() because lstrip() didn't support chars until
|
||||||
# python 2.2.2
|
# python 2.2.2
|
||||||
@@ -57,7 +59,7 @@ def inet_ntoa(address):
|
|||||||
start = -1
|
start = -1
|
||||||
last_was_zero = False
|
last_was_zero = False
|
||||||
for i in xrange(8):
|
for i in xrange(8):
|
||||||
if chunks[i] != b'0':
|
if chunks[i] != '0':
|
||||||
if last_was_zero:
|
if last_was_zero:
|
||||||
end = i
|
end = i
|
||||||
current_len = end - start
|
current_len = end - start
|
||||||
@@ -77,31 +79,30 @@ def inet_ntoa(address):
|
|||||||
if best_len > 1:
|
if best_len > 1:
|
||||||
if best_start == 0 and \
|
if best_start == 0 and \
|
||||||
(best_len == 6 or
|
(best_len == 6 or
|
||||||
best_len == 5 and chunks[5] == b'ffff'):
|
best_len == 5 and chunks[5] == 'ffff'):
|
||||||
# We have an embedded IPv4 address
|
# We have an embedded IPv4 address
|
||||||
if best_len == 6:
|
if best_len == 6:
|
||||||
prefix = b'::'
|
prefix = '::'
|
||||||
else:
|
else:
|
||||||
prefix = b'::ffff:'
|
prefix = '::ffff:'
|
||||||
hex = prefix + dns.ipv4.inet_ntoa(address[12:])
|
hex = prefix + dns.ipv4.inet_ntoa(address[12:])
|
||||||
else:
|
else:
|
||||||
hex = b':'.join(chunks[:best_start]) + b'::' + \
|
hex = ':'.join(chunks[:best_start]) + '::' + \
|
||||||
b':'.join(chunks[best_start + best_len:])
|
':'.join(chunks[best_start + best_len:])
|
||||||
else:
|
else:
|
||||||
hex = b':'.join(chunks)
|
hex = ':'.join(chunks)
|
||||||
return maybe_decode(hex)
|
return hex
|
||||||
|
|
||||||
_v4_ending = re.compile(b'(.*):(\d+\.\d+\.\d+\.\d+)$')
|
_v4_ending = re.compile(br'(.*):(\d+\.\d+\.\d+\.\d+)$')
|
||||||
_colon_colon_start = re.compile(b'::.*')
|
_colon_colon_start = re.compile(br'::.*')
|
||||||
_colon_colon_end = re.compile(b'.*::$')
|
_colon_colon_end = re.compile(br'.*::$')
|
||||||
|
|
||||||
def inet_aton(text):
|
def inet_aton(text):
|
||||||
"""Convert a text format IPv6 address into network format.
|
"""Convert an IPv6 address in text form to binary form.
|
||||||
|
|
||||||
@param text: the textual address
|
*text*, a ``text``, the IPv6 address in textual form.
|
||||||
@type text: string
|
|
||||||
@rtype: string
|
Returns a ``binary``.
|
||||||
@raises dns.exception.SyntaxError: the text was not properly formatted
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -118,8 +119,9 @@ def inet_aton(text):
|
|||||||
m = _v4_ending.match(text)
|
m = _v4_ending.match(text)
|
||||||
if not m is None:
|
if not m is None:
|
||||||
b = bytearray(dns.ipv4.inet_aton(m.group(2)))
|
b = bytearray(dns.ipv4.inet_aton(m.group(2)))
|
||||||
text = (u"%s:%02x%02x:%02x%02x" % (m.group(1).decode(), b[0], b[1],
|
text = (u"{}:{:02x}{:02x}:{:02x}{:02x}".format(m.group(1).decode(),
|
||||||
b[2], b[3])).encode()
|
b[0], b[1], b[2],
|
||||||
|
b[3])).encode()
|
||||||
#
|
#
|
||||||
# Try to turn '::<whatever>' into ':<whatever>'; if no match try to
|
# Try to turn '::<whatever>' into ':<whatever>'; if no match try to
|
||||||
# turn '<whatever>::' into '<whatever>:'
|
# turn '<whatever>::' into '<whatever>:'
|
||||||
@@ -169,4 +171,11 @@ def inet_aton(text):
|
|||||||
_mapped_prefix = b'\x00' * 10 + b'\xff\xff'
|
_mapped_prefix = b'\x00' * 10 + b'\xff\xff'
|
||||||
|
|
||||||
def is_mapped(address):
|
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)
|
return address.startswith(_mapped_prefix)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -40,114 +42,46 @@ from ._compat import long, xrange, string_types
|
|||||||
|
|
||||||
|
|
||||||
class ShortHeader(dns.exception.FormError):
|
class ShortHeader(dns.exception.FormError):
|
||||||
|
|
||||||
"""The DNS packet passed to from_wire() is too short."""
|
"""The DNS packet passed to from_wire() is too short."""
|
||||||
|
|
||||||
|
|
||||||
class TrailingJunk(dns.exception.FormError):
|
class TrailingJunk(dns.exception.FormError):
|
||||||
|
|
||||||
"""The DNS packet passed to from_wire() has extra junk at the end of it."""
|
"""The DNS packet passed to from_wire() has extra junk at the end of it."""
|
||||||
|
|
||||||
|
|
||||||
class UnknownHeaderField(dns.exception.DNSException):
|
class UnknownHeaderField(dns.exception.DNSException):
|
||||||
|
|
||||||
"""The header field name was not recognized when converting from text
|
"""The header field name was not recognized when converting from text
|
||||||
into a message."""
|
into a message."""
|
||||||
|
|
||||||
|
|
||||||
class BadEDNS(dns.exception.FormError):
|
class BadEDNS(dns.exception.FormError):
|
||||||
|
"""An OPT record occurred somewhere other than the start of
|
||||||
"""OPT record occurred somewhere other than the start of
|
|
||||||
the additional data section."""
|
the additional data section."""
|
||||||
|
|
||||||
|
|
||||||
class BadTSIG(dns.exception.FormError):
|
class BadTSIG(dns.exception.FormError):
|
||||||
|
|
||||||
"""A TSIG record occurred somewhere other than the end of
|
"""A TSIG record occurred somewhere other than the end of
|
||||||
the additional data section."""
|
the additional data section."""
|
||||||
|
|
||||||
|
|
||||||
class UnknownTSIGKey(dns.exception.DNSException):
|
class UnknownTSIGKey(dns.exception.DNSException):
|
||||||
|
|
||||||
"""A TSIG with an unknown key was received."""
|
"""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):
|
class Message(object):
|
||||||
|
"""A DNS message."""
|
||||||
"""A DNS message.
|
|
||||||
|
|
||||||
@ivar id: The query id; the default is a randomly chosen id.
|
|
||||||
@type id: int
|
|
||||||
@ivar flags: The DNS flags of the message. @see: RFC 1035 for an
|
|
||||||
explanation of these flags.
|
|
||||||
@type flags: int
|
|
||||||
@ivar question: The question section.
|
|
||||||
@type question: list of dns.rrset.RRset objects
|
|
||||||
@ivar answer: The answer section.
|
|
||||||
@type answer: list of dns.rrset.RRset objects
|
|
||||||
@ivar authority: The authority section.
|
|
||||||
@type authority: list of dns.rrset.RRset objects
|
|
||||||
@ivar additional: The additional data section.
|
|
||||||
@type additional: list of dns.rrset.RRset objects
|
|
||||||
@ivar edns: The EDNS level to use. The default is -1, no Edns.
|
|
||||||
@type edns: int
|
|
||||||
@ivar ednsflags: The EDNS flags
|
|
||||||
@type ednsflags: long
|
|
||||||
@ivar payload: The EDNS payload size. The default is 0.
|
|
||||||
@type payload: int
|
|
||||||
@ivar options: The EDNS options
|
|
||||||
@type options: list of dns.edns.Option objects
|
|
||||||
@ivar request_payload: The associated request's EDNS payload size.
|
|
||||||
@type request_payload: int
|
|
||||||
@ivar keyring: The TSIG keyring to use. The default is None.
|
|
||||||
@type keyring: dict
|
|
||||||
@ivar keyname: The TSIG keyname to use. The default is None.
|
|
||||||
@type keyname: dns.name.Name object
|
|
||||||
@ivar keyalgorithm: The TSIG algorithm to use; defaults to
|
|
||||||
dns.tsig.default_algorithm. Constants for TSIG algorithms are defined
|
|
||||||
in dns.tsig, and the currently implemented algorithms are
|
|
||||||
HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
|
|
||||||
HMAC_SHA512.
|
|
||||||
@type keyalgorithm: string
|
|
||||||
@ivar request_mac: The TSIG MAC of the request message associated with
|
|
||||||
this message; used when validating TSIG signatures. @see: RFC 2845 for
|
|
||||||
more information on TSIG fields.
|
|
||||||
@type request_mac: string
|
|
||||||
@ivar fudge: TSIG time fudge; default is 300 seconds.
|
|
||||||
@type fudge: int
|
|
||||||
@ivar original_id: TSIG original id; defaults to the message's id
|
|
||||||
@type original_id: int
|
|
||||||
@ivar tsig_error: TSIG error code; default is 0.
|
|
||||||
@type tsig_error: int
|
|
||||||
@ivar other_data: TSIG other data.
|
|
||||||
@type other_data: string
|
|
||||||
@ivar mac: The TSIG MAC for this message.
|
|
||||||
@type mac: string
|
|
||||||
@ivar xfr: Is the message being used to contain the results of a DNS
|
|
||||||
zone transfer? The default is False.
|
|
||||||
@type xfr: bool
|
|
||||||
@ivar origin: The origin of the zone in messages which are used for
|
|
||||||
zone transfers or for DNS dynamic updates. The default is None.
|
|
||||||
@type origin: dns.name.Name object
|
|
||||||
@ivar tsig_ctx: The TSIG signature context associated with this
|
|
||||||
message. The default is None.
|
|
||||||
@type tsig_ctx: hmac.HMAC object
|
|
||||||
@ivar had_tsig: Did the message decoded from wire format have a TSIG
|
|
||||||
signature?
|
|
||||||
@type had_tsig: bool
|
|
||||||
@ivar multi: Is this message part of a multi-message sequence? The
|
|
||||||
default is false. This variable is used when validating TSIG signatures
|
|
||||||
on messages which are part of a zone transfer.
|
|
||||||
@type multi: bool
|
|
||||||
@ivar first: Is this message standalone, or the first of a multi
|
|
||||||
message sequence? This variable is used when validating TSIG signatures
|
|
||||||
on messages which are part of a zone transfer.
|
|
||||||
@type first: bool
|
|
||||||
@ivar index: An index of rrsets in the message. The index key is
|
|
||||||
(section, name, rdclass, rdtype, covers, deleting). Indexing can be
|
|
||||||
disabled by setting the index to None.
|
|
||||||
@type index: dict
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, id=None):
|
def __init__(self, id=None):
|
||||||
if id is None:
|
if id is None:
|
||||||
@@ -167,12 +101,12 @@ class Message(object):
|
|||||||
self.keyring = None
|
self.keyring = None
|
||||||
self.keyname = None
|
self.keyname = None
|
||||||
self.keyalgorithm = dns.tsig.default_algorithm
|
self.keyalgorithm = dns.tsig.default_algorithm
|
||||||
self.request_mac = ''
|
self.request_mac = b''
|
||||||
self.other_data = ''
|
self.other_data = b''
|
||||||
self.tsig_error = 0
|
self.tsig_error = 0
|
||||||
self.fudge = 300
|
self.fudge = 300
|
||||||
self.original_id = self.id
|
self.original_id = self.id
|
||||||
self.mac = ''
|
self.mac = b''
|
||||||
self.xfr = False
|
self.xfr = False
|
||||||
self.origin = None
|
self.origin = None
|
||||||
self.tsig_ctx = None
|
self.tsig_ctx = None
|
||||||
@@ -190,10 +124,10 @@ class Message(object):
|
|||||||
def to_text(self, origin=None, relativize=True, **kw):
|
def to_text(self, origin=None, relativize=True, **kw):
|
||||||
"""Convert the message to text.
|
"""Convert the message to text.
|
||||||
|
|
||||||
The I{origin}, I{relativize}, and any other keyword
|
The *origin*, *relativize*, and any other keyword
|
||||||
arguments are passed to the rrset to_wire() method.
|
arguments are passed to the RRset ``to_wire()`` method.
|
||||||
|
|
||||||
@rtype: string
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
s = StringIO()
|
s = StringIO()
|
||||||
@@ -209,6 +143,8 @@ class Message(object):
|
|||||||
s.write(u'eflags %s\n' %
|
s.write(u'eflags %s\n' %
|
||||||
dns.flags.edns_to_text(self.ednsflags))
|
dns.flags.edns_to_text(self.ednsflags))
|
||||||
s.write(u'payload %d\n' % self.payload)
|
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)
|
is_update = dns.opcode.is_update(self.flags)
|
||||||
if is_update:
|
if is_update:
|
||||||
s.write(u';ZONE\n')
|
s.write(u';ZONE\n')
|
||||||
@@ -245,7 +181,10 @@ class Message(object):
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Two messages are equal if they have the same content in the
|
"""Two messages are equal if they have the same content in the
|
||||||
header, question, answer, and authority sections.
|
header, question, answer, and authority sections.
|
||||||
@rtype: bool"""
|
|
||||||
|
Returns a ``bool``.
|
||||||
|
"""
|
||||||
|
|
||||||
if not isinstance(other, Message):
|
if not isinstance(other, Message):
|
||||||
return False
|
return False
|
||||||
if self.id != other.id:
|
if self.id != other.id:
|
||||||
@@ -273,13 +212,14 @@ class Message(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
"""Are two messages not equal?
|
|
||||||
@rtype: bool"""
|
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
def is_response(self, other):
|
def is_response(self, other):
|
||||||
"""Is other a response to self?
|
"""Is this message a response to *other*?
|
||||||
@rtype: bool"""
|
|
||||||
|
Returns a ``bool``.
|
||||||
|
"""
|
||||||
|
|
||||||
if other.flags & dns.flags.QR == 0 or \
|
if other.flags & dns.flags.QR == 0 or \
|
||||||
self.id != other.id or \
|
self.id != other.id or \
|
||||||
dns.opcode.from_flags(self.flags) != \
|
dns.opcode.from_flags(self.flags) != \
|
||||||
@@ -299,14 +239,48 @@ class Message(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def section_number(self, section):
|
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:
|
if section is self.question:
|
||||||
return 0
|
return QUESTION
|
||||||
elif section is self.answer:
|
elif section is self.answer:
|
||||||
return 1
|
return ANSWER
|
||||||
elif section is self.authority:
|
elif section is self.authority:
|
||||||
return 2
|
return AUTHORITY
|
||||||
elif section is self.additional:
|
elif section is self.additional:
|
||||||
return 3
|
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:
|
else:
|
||||||
raise ValueError('unknown section')
|
raise ValueError('unknown section')
|
||||||
|
|
||||||
@@ -315,30 +289,45 @@ class Message(object):
|
|||||||
force_unique=False):
|
force_unique=False):
|
||||||
"""Find the RRset with the given attributes in the specified section.
|
"""Find the RRset with the given attributes in the specified section.
|
||||||
|
|
||||||
@param section: the section of the message to look in, e.g.
|
*section*, an ``int`` section number, or one of the section
|
||||||
self.answer.
|
attributes of this message. This specifies the
|
||||||
@type section: list of dns.rrset.RRset objects
|
the section of the message to search. For example::
|
||||||
@param name: the name of the RRset
|
|
||||||
@type name: dns.name.Name object
|
|
||||||
@param rdclass: the class of the RRset
|
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: the type of the RRset
|
|
||||||
@type rdtype: int
|
|
||||||
@param covers: the covers value of the RRset
|
|
||||||
@type covers: int
|
|
||||||
@param deleting: the deleting value of the RRset
|
|
||||||
@type deleting: int
|
|
||||||
@param create: If True, create the RRset if it is not found.
|
|
||||||
The created RRset is appended to I{section}.
|
|
||||||
@type create: bool
|
|
||||||
@param force_unique: If True and create is also True, create a
|
|
||||||
new RRset regardless of whether a matching RRset exists already.
|
|
||||||
@type force_unique: bool
|
|
||||||
@raises KeyError: the RRset was not found and create was False
|
|
||||||
@rtype: dns.rrset.RRset object"""
|
|
||||||
|
|
||||||
key = (self.section_number(section),
|
my_message.find_rrset(my_message.answer, name, rdclass, rdtype)
|
||||||
name, rdclass, rdtype, covers, deleting)
|
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 not force_unique:
|
||||||
if self.index is not None:
|
if self.index is not None:
|
||||||
rrset = self.index.get(key)
|
rrset = self.index.get(key)
|
||||||
@@ -363,26 +352,35 @@ class Message(object):
|
|||||||
|
|
||||||
If the RRset is not found, None is returned.
|
If the RRset is not found, None is returned.
|
||||||
|
|
||||||
@param section: the section of the message to look in, e.g.
|
*section*, an ``int`` section number, or one of the section
|
||||||
self.answer.
|
attributes of this message. This specifies the
|
||||||
@type section: list of dns.rrset.RRset objects
|
the section of the message to search. For example::
|
||||||
@param name: the name of the RRset
|
|
||||||
@type name: dns.name.Name object
|
my_message.get_rrset(my_message.answer, name, rdclass, rdtype)
|
||||||
@param rdclass: the class of the RRset
|
my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype)
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: the type of the RRset
|
*name*, a ``dns.name.Name``, the name of the RRset.
|
||||||
@type rdtype: int
|
|
||||||
@param covers: the covers value of the RRset
|
*rdclass*, an ``int``, the class of the RRset.
|
||||||
@type covers: int
|
|
||||||
@param deleting: the deleting value of the RRset
|
*rdtype*, an ``int``, the type of the RRset.
|
||||||
@type deleting: int
|
|
||||||
@param create: If True, create the RRset if it is not found.
|
*covers*, an ``int`` or ``None``, the covers value of the RRset.
|
||||||
The created RRset is appended to I{section}.
|
The default is ``None``.
|
||||||
@type create: bool
|
|
||||||
@param force_unique: If True and create is also True, create a
|
*deleting*, an ``int`` or ``None``, the deleting value of the RRset.
|
||||||
new RRset regardless of whether a matching RRset exists already.
|
The default is ``None``.
|
||||||
@type force_unique: bool
|
|
||||||
@rtype: dns.rrset.RRset object or 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:
|
try:
|
||||||
rrset = self.find_rrset(section, name, rdclass, rdtype, covers,
|
rrset = self.find_rrset(section, name, rdclass, rdtype, covers,
|
||||||
@@ -395,17 +393,19 @@ class Message(object):
|
|||||||
"""Return a string containing the message in DNS compressed wire
|
"""Return a string containing the message in DNS compressed wire
|
||||||
format.
|
format.
|
||||||
|
|
||||||
Additional keyword arguments are passed to the rrset to_wire()
|
Additional keyword arguments are passed to the RRset ``to_wire()``
|
||||||
method.
|
method.
|
||||||
|
|
||||||
@param origin: The origin to be appended to any relative names.
|
*origin*, a ``dns.name.Name`` or ``None``, the origin to be appended
|
||||||
@type origin: dns.name.Name object
|
to any relative names.
|
||||||
@param max_size: The maximum size of the wire format output; default
|
|
||||||
is 0, which means 'the message's request payload, if nonzero, or
|
*max_size*, an ``int``, the maximum size of the wire format
|
||||||
65536'.
|
output; default is 0, which means "the message's request
|
||||||
@type max_size: int
|
payload, if nonzero, or 65535".
|
||||||
@raises dns.exception.TooBig: max_size was exceeded
|
|
||||||
@rtype: string
|
Raises ``dns.exception.TooBig`` if *max_size* was exceeded.
|
||||||
|
|
||||||
|
Returns a ``binary``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if max_size == 0:
|
if max_size == 0:
|
||||||
@@ -438,30 +438,34 @@ class Message(object):
|
|||||||
return r.get_wire()
|
return r.get_wire()
|
||||||
|
|
||||||
def use_tsig(self, keyring, keyname=None, fudge=300,
|
def use_tsig(self, keyring, keyname=None, fudge=300,
|
||||||
original_id=None, tsig_error=0, other_data='',
|
original_id=None, tsig_error=0, other_data=b'',
|
||||||
algorithm=dns.tsig.default_algorithm):
|
algorithm=dns.tsig.default_algorithm):
|
||||||
"""When sending, a TSIG signature using the specified keyring
|
"""When sending, a TSIG signature using the specified keyring
|
||||||
and keyname should be added.
|
and keyname should be added.
|
||||||
|
|
||||||
@param keyring: The TSIG keyring to use; defaults to None.
|
See the documentation of the Message class for a complete
|
||||||
@type keyring: dict
|
description of the keyring dictionary.
|
||||||
@param keyname: The name of the TSIG key to use; defaults to None.
|
|
||||||
The key must be defined in the keyring. If a keyring is specified
|
*keyring*, a ``dict``, the TSIG keyring to use. If a
|
||||||
but a keyname is not, then the key used will be the first key in the
|
*keyring* is specified but a *keyname* is not, then the key
|
||||||
keyring. Note that the order of keys in a dictionary is not defined,
|
used will be the first key in the *keyring*. Note that the
|
||||||
so applications should supply a keyname when a keyring is used, unless
|
order of keys in a dictionary is not defined, so applications
|
||||||
they know the keyring contains only one key.
|
should supply a keyname when a keyring is used, unless they
|
||||||
@type keyname: dns.name.Name or string
|
know the keyring contains only one key.
|
||||||
@param fudge: TSIG time fudge; default is 300 seconds.
|
|
||||||
@type fudge: int
|
*keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key
|
||||||
@param original_id: TSIG original id; defaults to the message's id
|
to use; defaults to ``None``. The key must be defined in the keyring.
|
||||||
@type original_id: int
|
|
||||||
@param tsig_error: TSIG error code; default is 0.
|
*fudge*, an ``int``, the TSIG time fudge.
|
||||||
@type tsig_error: int
|
|
||||||
@param other_data: TSIG other data.
|
*original_id*, an ``int``, the TSIG original id. If ``None``,
|
||||||
@type other_data: string
|
the message's id is used.
|
||||||
@param algorithm: The TSIG algorithm to use; defaults to
|
|
||||||
dns.tsig.default_algorithm
|
*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
|
self.keyring = keyring
|
||||||
@@ -483,23 +487,26 @@ class Message(object):
|
|||||||
def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None,
|
def use_edns(self, edns=0, ednsflags=0, payload=1280, request_payload=None,
|
||||||
options=None):
|
options=None):
|
||||||
"""Configure EDNS behavior.
|
"""Configure EDNS behavior.
|
||||||
@param edns: The EDNS level to use. Specifying None, False, or -1
|
|
||||||
means 'do not use EDNS', and in this case the other parameters are
|
*edns*, an ``int``, is the EDNS level to use. Specifying
|
||||||
ignored. Specifying True is equivalent to specifying 0, i.e. 'use
|
``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case
|
||||||
EDNS0'.
|
the other parameters are ignored. Specifying ``True`` is
|
||||||
@type edns: int or bool or None
|
equivalent to specifying 0, i.e. "use EDNS0".
|
||||||
@param ednsflags: EDNS flag values.
|
|
||||||
@type ednsflags: int
|
*ednsflags*, an ``int``, the EDNS flag values.
|
||||||
@param payload: The EDNS sender's payload field, which is the maximum
|
|
||||||
size of UDP datagram the sender can handle.
|
*payload*, an ``int``, is the EDNS sender's payload field, which is the
|
||||||
@type payload: int
|
maximum size of UDP datagram the sender can handle. I.e. how big
|
||||||
@param request_payload: The EDNS payload size to use when sending
|
a response to this message can be.
|
||||||
this message. If not specified, defaults to the value of payload.
|
|
||||||
@type request_payload: int or None
|
*request_payload*, an ``int``, is the EDNS payload size to use when
|
||||||
@param options: The EDNS options
|
sending this message. If not specified, defaults to the value of
|
||||||
@type options: None or list of dns.edns.Option objects
|
*payload*.
|
||||||
@see: RFC 2671
|
|
||||||
|
*options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS
|
||||||
|
options.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if edns is None or edns is False:
|
if edns is None or edns is False:
|
||||||
edns = -1
|
edns = -1
|
||||||
if edns is True:
|
if edns is True:
|
||||||
@@ -525,11 +532,13 @@ class Message(object):
|
|||||||
|
|
||||||
def want_dnssec(self, wanted=True):
|
def want_dnssec(self, wanted=True):
|
||||||
"""Enable or disable 'DNSSEC desired' flag in requests.
|
"""Enable or disable 'DNSSEC desired' flag in requests.
|
||||||
@param wanted: Is DNSSEC desired? If True, EDNS is enabled if
|
|
||||||
required, and then the DO bit is set. If False, the DO bit is
|
*wanted*, a ``bool``. If ``True``, then DNSSEC data is
|
||||||
cleared if EDNS is enabled.
|
desired in the response, EDNS is enabled if required, and then
|
||||||
@type wanted: bool
|
the DO bit is set. If ``False``, the DO bit is cleared if
|
||||||
|
EDNS is enabled.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if wanted:
|
if wanted:
|
||||||
if self.edns < 0:
|
if self.edns < 0:
|
||||||
self.use_edns()
|
self.use_edns()
|
||||||
@@ -539,14 +548,15 @@ class Message(object):
|
|||||||
|
|
||||||
def rcode(self):
|
def rcode(self):
|
||||||
"""Return the rcode.
|
"""Return the rcode.
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
return dns.rcode.from_flags(self.flags, self.ednsflags)
|
return dns.rcode.from_flags(self.flags, self.ednsflags)
|
||||||
|
|
||||||
def set_rcode(self, rcode):
|
def set_rcode(self, rcode):
|
||||||
"""Set the rcode.
|
"""Set the rcode.
|
||||||
@param rcode: the rcode
|
|
||||||
@type rcode: int
|
*rcode*, an ``int``, is the rcode to set.
|
||||||
"""
|
"""
|
||||||
(value, evalue) = dns.rcode.to_flags(rcode)
|
(value, evalue) = dns.rcode.to_flags(rcode)
|
||||||
self.flags &= 0xFFF0
|
self.flags &= 0xFFF0
|
||||||
@@ -558,14 +568,15 @@ class Message(object):
|
|||||||
|
|
||||||
def opcode(self):
|
def opcode(self):
|
||||||
"""Return the opcode.
|
"""Return the opcode.
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
return dns.opcode.from_flags(self.flags)
|
return dns.opcode.from_flags(self.flags)
|
||||||
|
|
||||||
def set_opcode(self, opcode):
|
def set_opcode(self, opcode):
|
||||||
"""Set the opcode.
|
"""Set the opcode.
|
||||||
@param opcode: the opcode
|
|
||||||
@type opcode: int
|
*opcode*, an ``int``, is the opcode to set.
|
||||||
"""
|
"""
|
||||||
self.flags &= 0x87FF
|
self.flags &= 0x87FF
|
||||||
self.flags |= dns.opcode.to_flags(opcode)
|
self.flags |= dns.opcode.to_flags(opcode)
|
||||||
@@ -575,23 +586,16 @@ class _WireReader(object):
|
|||||||
|
|
||||||
"""Wire format reader.
|
"""Wire format reader.
|
||||||
|
|
||||||
@ivar wire: the wire-format message.
|
wire: a binary, is the wire-format message.
|
||||||
@type wire: string
|
message: The message object being built
|
||||||
@ivar message: The message object being built
|
current: When building a message object from wire format, this
|
||||||
@type message: dns.message.Message object
|
|
||||||
@ivar current: When building a message object from wire format, this
|
|
||||||
variable contains the offset from the beginning of wire of the next octet
|
variable contains the offset from the beginning of wire of the next octet
|
||||||
to be read.
|
to be read.
|
||||||
@type current: int
|
updating: Is the message a dynamic update?
|
||||||
@ivar updating: Is the message a dynamic update?
|
one_rr_per_rrset: Put each RR into its own RRset?
|
||||||
@type updating: bool
|
ignore_trailing: Ignore trailing junk at end of request?
|
||||||
@ivar one_rr_per_rrset: Put each RR into its own RRset?
|
zone_rdclass: The class of the zone in messages which are
|
||||||
@type one_rr_per_rrset: bool
|
|
||||||
@ivar ignore_trailing: Ignore trailing junk at end of request?
|
|
||||||
@type ignore_trailing: bool
|
|
||||||
@ivar zone_rdclass: The class of the zone in messages which are
|
|
||||||
DNS dynamic updates.
|
DNS dynamic updates.
|
||||||
@type zone_rdclass: int
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, wire, message, question_only=False,
|
def __init__(self, wire, message, question_only=False,
|
||||||
@@ -606,10 +610,9 @@ class _WireReader(object):
|
|||||||
self.ignore_trailing = ignore_trailing
|
self.ignore_trailing = ignore_trailing
|
||||||
|
|
||||||
def _get_question(self, qcount):
|
def _get_question(self, qcount):
|
||||||
"""Read the next I{qcount} records from the wire data and add them to
|
"""Read the next *qcount* records from the wire data and add them to
|
||||||
the question section.
|
the question section.
|
||||||
@param qcount: the number of questions in the message
|
"""
|
||||||
@type qcount: int"""
|
|
||||||
|
|
||||||
if self.updating and qcount > 1:
|
if self.updating and qcount > 1:
|
||||||
raise dns.exception.FormError
|
raise dns.exception.FormError
|
||||||
@@ -632,10 +635,10 @@ class _WireReader(object):
|
|||||||
def _get_section(self, section, count):
|
def _get_section(self, section, count):
|
||||||
"""Read the next I{count} records from the wire data and add them to
|
"""Read the next I{count} records from the wire data and add them to
|
||||||
the specified section.
|
the specified section.
|
||||||
@param section: the section of the message to which to add records
|
|
||||||
@type section: list of dns.rrset.RRset objects
|
section: the section of the message to which to add records
|
||||||
@param count: the number of records to read
|
count: the number of records to read
|
||||||
@type count: int"""
|
"""
|
||||||
|
|
||||||
if self.updating or self.one_rr_per_rrset:
|
if self.updating or self.one_rr_per_rrset:
|
||||||
force_unique = True
|
force_unique = True
|
||||||
@@ -753,45 +756,58 @@ class _WireReader(object):
|
|||||||
self.message.tsig_ctx.update(self.wire)
|
self.message.tsig_ctx.update(self.wire)
|
||||||
|
|
||||||
|
|
||||||
def from_wire(wire, keyring=None, request_mac='', xfr=False, origin=None,
|
def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None,
|
||||||
tsig_ctx=None, multi=False, first=True,
|
tsig_ctx=None, multi=False, first=True,
|
||||||
question_only=False, one_rr_per_rrset=False,
|
question_only=False, one_rr_per_rrset=False,
|
||||||
ignore_trailing=False):
|
ignore_trailing=False):
|
||||||
"""Convert a DNS wire format message into a message
|
"""Convert a DNS wire format message into a message
|
||||||
object.
|
object.
|
||||||
|
|
||||||
@param keyring: The keyring to use if the message is signed.
|
*keyring*, a ``dict``, the keyring to use if the message is signed.
|
||||||
@type keyring: dict
|
|
||||||
@param request_mac: If the message is a response to a TSIG-signed request,
|
*request_mac*, a ``binary``. If the message is a response to a
|
||||||
I{request_mac} should be set to the MAC of that request.
|
TSIG-signed request, *request_mac* should be set to the MAC of
|
||||||
@type request_mac: string
|
that request.
|
||||||
@param xfr: Is this message part of a zone transfer?
|
|
||||||
@type xfr: bool
|
*xfr*, a ``bool``, should be set to ``True`` if this message is part of
|
||||||
@param origin: If the message is part of a zone transfer, I{origin}
|
a zone transfer.
|
||||||
should be the origin name of the zone.
|
|
||||||
@type origin: dns.name.Name object
|
*origin*, a ``dns.name.Name`` or ``None``. If the message is part
|
||||||
@param tsig_ctx: The ongoing TSIG context, used when validating zone
|
of a zone transfer, *origin* should be the origin name of the
|
||||||
transfers.
|
zone.
|
||||||
@type tsig_ctx: hmac.HMAC object
|
|
||||||
@param multi: Is this message part of a multiple message sequence?
|
*tsig_ctx*, a ``hmac.HMAC`` objext, the ongoing TSIG context, used
|
||||||
@type multi: bool
|
when validating zone transfers.
|
||||||
@param first: Is this message standalone, or the first of a multi
|
|
||||||
message sequence?
|
*multi*, a ``bool``, should be set to ``True`` if this message
|
||||||
@type first: bool
|
part of a multiple message sequence.
|
||||||
@param question_only: Read only up to the end of the question section?
|
|
||||||
@type question_only: bool
|
*first*, a ``bool``, should be set to ``True`` if this message is
|
||||||
@param one_rr_per_rrset: Put each RR into its own RRset
|
stand-alone, or the first message in a multi-message sequence.
|
||||||
@type one_rr_per_rrset: bool
|
|
||||||
@param ignore_trailing: Ignore trailing junk at end of request?
|
*question_only*, a ``bool``. If ``True``, read only up to
|
||||||
@type ignore_trailing: bool
|
the end of the question section.
|
||||||
@raises ShortHeader: The message is less than 12 octets long.
|
|
||||||
@raises TrailingJunk: There were octets in the message past the end
|
*one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its
|
||||||
of the proper DNS message.
|
own RRset.
|
||||||
@raises BadEDNS: An OPT record was in the wrong section, or occurred more
|
|
||||||
than once.
|
*ignore_trailing*, a ``bool``. If ``True``, ignore trailing
|
||||||
@raises BadTSIG: A TSIG record was not the last record of the additional
|
junk at end of the message.
|
||||||
data section.
|
|
||||||
@rtype: dns.message.Message object"""
|
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 = Message(id=0)
|
||||||
m.keyring = keyring
|
m.keyring = keyring
|
||||||
@@ -813,18 +829,12 @@ class _TextReader(object):
|
|||||||
|
|
||||||
"""Text format reader.
|
"""Text format reader.
|
||||||
|
|
||||||
@ivar tok: the tokenizer
|
tok: the tokenizer.
|
||||||
@type tok: dns.tokenizer.Tokenizer object
|
message: The message object being built.
|
||||||
@ivar message: The message object being built
|
updating: Is the message a dynamic update?
|
||||||
@type message: dns.message.Message object
|
zone_rdclass: The class of the zone in messages which are
|
||||||
@ivar updating: Is the message a dynamic update?
|
|
||||||
@type updating: bool
|
|
||||||
@ivar zone_rdclass: The class of the zone in messages which are
|
|
||||||
DNS dynamic updates.
|
DNS dynamic updates.
|
||||||
@type zone_rdclass: int
|
last_name: The most recently read name when building a message object.
|
||||||
@ivar last_name: The most recently read name when building a message object
|
|
||||||
from text format.
|
|
||||||
@type last_name: dns.name.Name object
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, text, message):
|
def __init__(self, text, message):
|
||||||
@@ -997,11 +1007,14 @@ class _TextReader(object):
|
|||||||
def from_text(text):
|
def from_text(text):
|
||||||
"""Convert the text format message into a message object.
|
"""Convert the text format message into a message object.
|
||||||
|
|
||||||
@param text: The text format message.
|
*text*, a ``text``, the text format message.
|
||||||
@type text: string
|
|
||||||
@raises UnknownHeaderField:
|
Raises ``dns.message.UnknownHeaderField`` if a header is unknown.
|
||||||
@raises dns.exception.SyntaxError:
|
|
||||||
@rtype: dns.message.Message object"""
|
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
|
# 'text' can also be a file, but we don't publish that fact
|
||||||
# since it's an implementation detail. The official file
|
# since it's an implementation detail. The official file
|
||||||
@@ -1018,11 +1031,15 @@ def from_text(text):
|
|||||||
def from_file(f):
|
def from_file(f):
|
||||||
"""Read the next text format message from the specified file.
|
"""Read the next text format message from the specified file.
|
||||||
|
|
||||||
@param f: file or string. If I{f} is a string, it is treated
|
*f*, a ``file`` or ``text``. If *f* is text, it is treated as the
|
||||||
as the name of a file to open.
|
pathname of a file to open.
|
||||||
@raises UnknownHeaderField:
|
|
||||||
@raises dns.exception.SyntaxError:
|
Raises ``dns.message.UnknownHeaderField`` if a header is unknown.
|
||||||
@rtype: dns.message.Message object"""
|
|
||||||
|
Raises ``dns.exception.SyntaxError`` if the text is badly formed.
|
||||||
|
|
||||||
|
Returns a ``dns.message.Message object``
|
||||||
|
"""
|
||||||
|
|
||||||
str_type = string_types
|
str_type = string_types
|
||||||
opts = 'rU'
|
opts = 'rU'
|
||||||
@@ -1052,30 +1069,35 @@ def make_query(qname, rdtype, rdclass=dns.rdataclass.IN, use_edns=None,
|
|||||||
The query will have a randomly chosen query id, and its DNS flags
|
The query will have a randomly chosen query id, and its DNS flags
|
||||||
will be set to dns.flags.RD.
|
will be set to dns.flags.RD.
|
||||||
|
|
||||||
@param qname: The query name.
|
qname, a ``dns.name.Name`` or ``text``, the query name.
|
||||||
@type qname: dns.name.Name object or string
|
|
||||||
@param rdtype: The desired rdata type.
|
*rdtype*, an ``int`` or ``text``, the desired rdata type.
|
||||||
@type rdtype: int
|
|
||||||
@param rdclass: The desired rdata class; the default is class IN.
|
*rdclass*, an ``int`` or ``text``, the desired rdata class; the default
|
||||||
@type rdclass: int
|
is class IN.
|
||||||
@param use_edns: The EDNS level to use; the default is None (no EDNS).
|
|
||||||
|
*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
|
See the description of dns.message.Message.use_edns() for the possible
|
||||||
values for use_edns and their meanings.
|
values for use_edns and their meanings.
|
||||||
@type use_edns: int or bool or None
|
|
||||||
@param want_dnssec: Should the query indicate that DNSSEC is desired?
|
*want_dnssec*, a ``bool``. If ``True``, DNSSEC data is desired.
|
||||||
@type want_dnssec: bool
|
|
||||||
@param ednsflags: EDNS flag values.
|
*ednsflags*, an ``int``, the EDNS flag values.
|
||||||
@type ednsflags: int
|
|
||||||
@param payload: The EDNS sender's payload field, which is the maximum
|
*payload*, an ``int``, is the EDNS sender's payload field, which is the
|
||||||
size of UDP datagram the sender can handle.
|
maximum size of UDP datagram the sender can handle. I.e. how big
|
||||||
@type payload: int
|
a response to this message can be.
|
||||||
@param request_payload: The EDNS payload size to use when sending
|
|
||||||
this message. If not specified, defaults to the value of payload.
|
*request_payload*, an ``int``, is the EDNS payload size to use when
|
||||||
@type request_payload: int or None
|
sending this message. If not specified, defaults to the value of
|
||||||
@param options: The EDNS options
|
*payload*.
|
||||||
@type options: None or list of dns.edns.Option objects
|
|
||||||
@see: RFC 2671
|
*options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS
|
||||||
@rtype: dns.message.Message object"""
|
options.
|
||||||
|
|
||||||
|
Returns a ``dns.message.Message``
|
||||||
|
"""
|
||||||
|
|
||||||
if isinstance(qname, string_types):
|
if isinstance(qname, string_types):
|
||||||
qname = dns.name.from_text(qname)
|
qname = dns.name.from_text(qname)
|
||||||
@@ -1124,16 +1146,17 @@ def make_response(query, recursion_available=False, our_payload=8192,
|
|||||||
question section, so the query's question RRsets should not be
|
question section, so the query's question RRsets should not be
|
||||||
changed.
|
changed.
|
||||||
|
|
||||||
@param query: the query to respond to
|
*query*, a ``dns.message.Message``, the query to respond to.
|
||||||
@type query: dns.message.Message object
|
|
||||||
@param recursion_available: should RA be set in the response?
|
*recursion_available*, a ``bool``, should RA be set in the response?
|
||||||
@type recursion_available: bool
|
|
||||||
@param our_payload: payload size to advertise in EDNS responses; default
|
*our_payload*, an ``int``, the payload size to advertise in EDNS
|
||||||
is 8192.
|
responses.
|
||||||
@type our_payload: int
|
|
||||||
@param fudge: TSIG time fudge; default is 300 seconds.
|
*fudge*, an ``int``, the TSIG time fudge.
|
||||||
@type fudge: int
|
|
||||||
@rtype: dns.message.Message object"""
|
Returns a ``dns.message.Message`` object.
|
||||||
|
"""
|
||||||
|
|
||||||
if query.flags & dns.flags.QR:
|
if query.flags & dns.flags.QR:
|
||||||
raise dns.exception.FormError('specified query message is not a query')
|
raise dns.exception.FormError('specified query message is not a query')
|
||||||
@@ -1146,7 +1169,7 @@ def make_response(query, recursion_available=False, our_payload=8192,
|
|||||||
if query.edns >= 0:
|
if query.edns >= 0:
|
||||||
response.use_edns(0, 0, our_payload, query.payload)
|
response.use_edns(0, 0, our_payload, query.payload)
|
||||||
if query.had_tsig:
|
if query.had_tsig:
|
||||||
response.use_tsig(query.keyring, query.keyname, fudge, None, 0, '',
|
response.use_tsig(query.keyring, query.keyname, fudge, None, 0, b'',
|
||||||
query.keyalgorithm)
|
query.keyalgorithm)
|
||||||
response.request_mac = query.mac
|
response.request_mac = query.mac
|
||||||
return response
|
return response
|
||||||
|
|||||||
55
src/dns/message.pyi
Normal file
55
src/dns/message.pyi
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
from 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:
|
||||||
|
...
|
||||||
402
src/dns/name.py
402
src/dns/name.py
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -14,11 +16,6 @@
|
|||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
"""DNS Names.
|
"""DNS Names.
|
||||||
|
|
||||||
@var root: The DNS root name.
|
|
||||||
@type root: dns.name.Name object
|
|
||||||
@var empty: The empty DNS name.
|
|
||||||
@type empty: dns.name.Name object
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@@ -38,79 +35,76 @@ import dns.wiredata
|
|||||||
from ._compat import long, binary_type, text_type, unichr, maybe_decode
|
from ._compat import long, binary_type, text_type, unichr, maybe_decode
|
||||||
|
|
||||||
try:
|
try:
|
||||||
maxint = sys.maxint
|
maxint = sys.maxint # pylint: disable=sys-max-int
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1
|
maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1
|
||||||
|
|
||||||
|
|
||||||
|
# fullcompare() result values
|
||||||
|
|
||||||
|
#: The compared names have no relationship to each other.
|
||||||
NAMERELN_NONE = 0
|
NAMERELN_NONE = 0
|
||||||
|
#: the first name is a superdomain of the second.
|
||||||
NAMERELN_SUPERDOMAIN = 1
|
NAMERELN_SUPERDOMAIN = 1
|
||||||
|
#: The first name is a subdomain of the second.
|
||||||
NAMERELN_SUBDOMAIN = 2
|
NAMERELN_SUBDOMAIN = 2
|
||||||
|
#: The compared names are equal.
|
||||||
NAMERELN_EQUAL = 3
|
NAMERELN_EQUAL = 3
|
||||||
|
#: The compared names have a common ancestor.
|
||||||
NAMERELN_COMMONANCESTOR = 4
|
NAMERELN_COMMONANCESTOR = 4
|
||||||
|
|
||||||
|
|
||||||
class EmptyLabel(dns.exception.SyntaxError):
|
class EmptyLabel(dns.exception.SyntaxError):
|
||||||
|
|
||||||
"""A DNS label is empty."""
|
"""A DNS label is empty."""
|
||||||
|
|
||||||
|
|
||||||
class BadEscape(dns.exception.SyntaxError):
|
class BadEscape(dns.exception.SyntaxError):
|
||||||
|
|
||||||
"""An escaped code in a text format of DNS name is invalid."""
|
"""An escaped code in a text format of DNS name is invalid."""
|
||||||
|
|
||||||
|
|
||||||
class BadPointer(dns.exception.FormError):
|
class BadPointer(dns.exception.FormError):
|
||||||
|
|
||||||
"""A DNS compression pointer points forward instead of backward."""
|
"""A DNS compression pointer points forward instead of backward."""
|
||||||
|
|
||||||
|
|
||||||
class BadLabelType(dns.exception.FormError):
|
class BadLabelType(dns.exception.FormError):
|
||||||
|
|
||||||
"""The label type in DNS name wire format is unknown."""
|
"""The label type in DNS name wire format is unknown."""
|
||||||
|
|
||||||
|
|
||||||
class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
|
class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
|
||||||
|
|
||||||
"""An attempt was made to convert a non-absolute name to
|
"""An attempt was made to convert a non-absolute name to
|
||||||
wire when there was also a non-absolute (or missing) origin."""
|
wire when there was also a non-absolute (or missing) origin."""
|
||||||
|
|
||||||
|
|
||||||
class NameTooLong(dns.exception.FormError):
|
class NameTooLong(dns.exception.FormError):
|
||||||
|
|
||||||
"""A DNS name is > 255 octets long."""
|
"""A DNS name is > 255 octets long."""
|
||||||
|
|
||||||
|
|
||||||
class LabelTooLong(dns.exception.SyntaxError):
|
class LabelTooLong(dns.exception.SyntaxError):
|
||||||
|
|
||||||
"""A DNS label is > 63 octets long."""
|
"""A DNS label is > 63 octets long."""
|
||||||
|
|
||||||
|
|
||||||
class AbsoluteConcatenation(dns.exception.DNSException):
|
class AbsoluteConcatenation(dns.exception.DNSException):
|
||||||
|
|
||||||
"""An attempt was made to append anything other than the
|
"""An attempt was made to append anything other than the
|
||||||
empty name to an absolute DNS name."""
|
empty name to an absolute DNS name."""
|
||||||
|
|
||||||
|
|
||||||
class NoParent(dns.exception.DNSException):
|
class NoParent(dns.exception.DNSException):
|
||||||
|
|
||||||
"""An attempt was made to get the parent of the root name
|
"""An attempt was made to get the parent of the root name
|
||||||
or the empty name."""
|
or the empty name."""
|
||||||
|
|
||||||
class NoIDNA2008(dns.exception.DNSException):
|
class NoIDNA2008(dns.exception.DNSException):
|
||||||
|
|
||||||
"""IDNA 2008 processing was requested but the idna module is not
|
"""IDNA 2008 processing was requested but the idna module is not
|
||||||
available."""
|
available."""
|
||||||
|
|
||||||
|
|
||||||
class IDNAException(dns.exception.DNSException):
|
class IDNAException(dns.exception.DNSException):
|
||||||
|
|
||||||
"""IDNA processing raised an exception."""
|
"""IDNA processing raised an exception."""
|
||||||
|
|
||||||
supp_kwargs = set(['idna_exception'])
|
supp_kwargs = {'idna_exception'}
|
||||||
fmt = "IDNA processing exception: {idna_exception}"
|
fmt = "IDNA processing exception: {idna_exception}"
|
||||||
|
|
||||||
class IDNACodec(object):
|
|
||||||
|
|
||||||
|
class IDNACodec(object):
|
||||||
"""Abstract base class for IDNA encoder/decoders."""
|
"""Abstract base class for IDNA encoder/decoders."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -131,21 +125,24 @@ class IDNACodec(object):
|
|||||||
label = maybe_decode(label)
|
label = maybe_decode(label)
|
||||||
return _escapify(label, True)
|
return _escapify(label, True)
|
||||||
|
|
||||||
class IDNA2003Codec(IDNACodec):
|
|
||||||
|
|
||||||
|
class IDNA2003Codec(IDNACodec):
|
||||||
"""IDNA 2003 encoder/decoder."""
|
"""IDNA 2003 encoder/decoder."""
|
||||||
|
|
||||||
def __init__(self, strict_decode=False):
|
def __init__(self, strict_decode=False):
|
||||||
"""Initialize the IDNA 2003 encoder/decoder.
|
"""Initialize the IDNA 2003 encoder/decoder.
|
||||||
@param strict_decode: If True, then IDNA2003 checking is done when
|
|
||||||
decoding. This can cause failures if the name was encoded with
|
*strict_decode* is a ``bool``. If `True`, then IDNA2003 checking
|
||||||
IDNA2008. The default is False.
|
is done when decoding. This can cause failures if the name
|
||||||
@type strict_decode: bool
|
was encoded with IDNA2008. The default is `False`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super(IDNA2003Codec, self).__init__()
|
super(IDNA2003Codec, self).__init__()
|
||||||
self.strict_decode = strict_decode
|
self.strict_decode = strict_decode
|
||||||
|
|
||||||
def encode(self, label):
|
def encode(self, label):
|
||||||
|
"""Encode *label*."""
|
||||||
|
|
||||||
if label == '':
|
if label == '':
|
||||||
return b''
|
return b''
|
||||||
try:
|
try:
|
||||||
@@ -154,6 +151,7 @@ class IDNA2003Codec(IDNACodec):
|
|||||||
raise LabelTooLong
|
raise LabelTooLong
|
||||||
|
|
||||||
def decode(self, label):
|
def decode(self, label):
|
||||||
|
"""Decode *label*."""
|
||||||
if not self.strict_decode:
|
if not self.strict_decode:
|
||||||
return super(IDNA2003Codec, self).decode(label)
|
return super(IDNA2003Codec, self).decode(label)
|
||||||
if label == b'':
|
if label == b'':
|
||||||
@@ -163,34 +161,34 @@ class IDNA2003Codec(IDNACodec):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise IDNAException(idna_exception=e)
|
raise IDNAException(idna_exception=e)
|
||||||
|
|
||||||
class IDNA2008Codec(IDNACodec):
|
|
||||||
|
|
||||||
"""IDNA 2008 encoder/decoder."""
|
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,
|
def __init__(self, uts_46=False, transitional=False,
|
||||||
allow_pure_ascii=False, strict_decode=False):
|
allow_pure_ascii=False, strict_decode=False):
|
||||||
"""Initialize the IDNA 2008 encoder/decoder.
|
"""Initialize the IDNA 2008 encoder/decoder."""
|
||||||
@param uts_46: If True, apply Unicode IDNA compatibility processing
|
|
||||||
as described in Unicode Technical Standard #46
|
|
||||||
(U{http://unicode.org/reports/tr46/}). This parameter is only
|
|
||||||
meaningful if IDNA 2008 is in use. If False, do not apply
|
|
||||||
the mapping. The default is False
|
|
||||||
@type uts_46: bool
|
|
||||||
@param transitional: If True, use the "transitional" mode described
|
|
||||||
in Unicode Technical Standard #46. This parameter is only
|
|
||||||
meaningful if IDNA 2008 is in use. The default is False.
|
|
||||||
@type transitional: bool
|
|
||||||
@param allow_pure_ascii: 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
|
|
||||||
suffixm which would otherwise be disallowed. The default is False
|
|
||||||
@type allow_pure_ascii: bool
|
|
||||||
@param strict_decode: If True, then IDNA2008 checking is done when
|
|
||||||
decoding. This can cause failures if the name was encoded with
|
|
||||||
IDNA2003. The default is False.
|
|
||||||
@type strict_decode: bool
|
|
||||||
"""
|
|
||||||
super(IDNA2008Codec, self).__init__()
|
super(IDNA2008Codec, self).__init__()
|
||||||
self.uts_46 = uts_46
|
self.uts_46 = uts_46
|
||||||
self.transitional = transitional
|
self.transitional = transitional
|
||||||
@@ -277,9 +275,14 @@ def _escapify(label, unicode_mode=False):
|
|||||||
def _validate_labels(labels):
|
def _validate_labels(labels):
|
||||||
"""Check for empty labels in the middle of a label sequence,
|
"""Check for empty labels in the middle of a label sequence,
|
||||||
labels that are too long, and for too many labels.
|
labels that are too long, and for too many labels.
|
||||||
@raises NameTooLong: the name as a whole is too long
|
|
||||||
@raises EmptyLabel: a label is empty (i.e. the root label) and appears
|
Raises ``dns.name.NameTooLong`` if the name as a whole is too long.
|
||||||
in a position other than the end of the label sequence"""
|
|
||||||
|
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)
|
l = len(labels)
|
||||||
total = 0
|
total = 0
|
||||||
@@ -299,7 +302,12 @@ def _validate_labels(labels):
|
|||||||
raise EmptyLabel
|
raise EmptyLabel
|
||||||
|
|
||||||
|
|
||||||
def _ensure_bytes(label):
|
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):
|
if isinstance(label, binary_type):
|
||||||
return label
|
return label
|
||||||
if isinstance(label, text_type):
|
if isinstance(label, text_type):
|
||||||
@@ -311,24 +319,23 @@ class Name(object):
|
|||||||
|
|
||||||
"""A DNS name.
|
"""A DNS name.
|
||||||
|
|
||||||
The dns.name.Name class represents a DNS name as a tuple of labels.
|
The dns.name.Name class represents a DNS name as a tuple of
|
||||||
Instances of the class are immutable.
|
labels. Each label is a `binary` in DNS wire format. Instances
|
||||||
|
of the class are immutable.
|
||||||
@ivar labels: The tuple of labels in the name. Each label is a string of
|
"""
|
||||||
up to 63 octets."""
|
|
||||||
|
|
||||||
__slots__ = ['labels']
|
__slots__ = ['labels']
|
||||||
|
|
||||||
def __init__(self, labels):
|
def __init__(self, labels):
|
||||||
"""Initialize a domain name from a list of labels.
|
"""*labels* is any iterable whose values are ``text`` or ``binary``.
|
||||||
@param labels: the labels
|
|
||||||
@type labels: any iterable whose values are strings
|
|
||||||
"""
|
"""
|
||||||
labels = [_ensure_bytes(x) for x in labels]
|
|
||||||
|
labels = [_maybe_convert_to_binary(x) for x in labels]
|
||||||
super(Name, self).__setattr__('labels', tuple(labels))
|
super(Name, self).__setattr__('labels', tuple(labels))
|
||||||
_validate_labels(self.labels)
|
_validate_labels(self.labels)
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
|
# Names are immutable
|
||||||
raise TypeError("object doesn't support attribute assignment")
|
raise TypeError("object doesn't support attribute assignment")
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
@@ -338,6 +345,7 @@ class Name(object):
|
|||||||
return Name(copy.deepcopy(self.labels, memo))
|
return Name(copy.deepcopy(self.labels, memo))
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
|
# Names can be pickled
|
||||||
return {'labels': self.labels}
|
return {'labels': self.labels}
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
@@ -346,21 +354,24 @@ class Name(object):
|
|||||||
|
|
||||||
def is_absolute(self):
|
def is_absolute(self):
|
||||||
"""Is the most significant label of this name the root label?
|
"""Is the most significant label of this name the root label?
|
||||||
@rtype: bool
|
|
||||||
|
Returns a ``bool``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return len(self.labels) > 0 and self.labels[-1] == b''
|
return len(self.labels) > 0 and self.labels[-1] == b''
|
||||||
|
|
||||||
def is_wild(self):
|
def is_wild(self):
|
||||||
"""Is this name wild? (I.e. Is the least significant label '*'?)
|
"""Is this name wild? (I.e. Is the least significant label '*'?)
|
||||||
@rtype: bool
|
|
||||||
|
Returns a ``bool``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return len(self.labels) > 0 and self.labels[0] == b'*'
|
return len(self.labels) > 0 and self.labels[0] == b'*'
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
"""Return a case-insensitive hash of the name.
|
"""Return a case-insensitive hash of the name.
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
h = long(0)
|
h = long(0)
|
||||||
@@ -370,20 +381,35 @@ class Name(object):
|
|||||||
return int(h % maxint)
|
return int(h % maxint)
|
||||||
|
|
||||||
def fullcompare(self, other):
|
def fullcompare(self, other):
|
||||||
"""Compare two names, returning a 3-tuple (relation, order, nlabels).
|
"""Compare two names, returning a 3-tuple
|
||||||
|
``(relation, order, nlabels)``.
|
||||||
|
|
||||||
I{relation} describes the relation ship between the names,
|
*relation* describes the relation ship between the names,
|
||||||
and is one of: dns.name.NAMERELN_NONE,
|
and is one of: ``dns.name.NAMERELN_NONE``,
|
||||||
dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN,
|
``dns.name.NAMERELN_SUPERDOMAIN``, ``dns.name.NAMERELN_SUBDOMAIN``,
|
||||||
dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR
|
``dns.name.NAMERELN_EQUAL``, or ``dns.name.NAMERELN_COMMONANCESTOR``.
|
||||||
|
|
||||||
I{order} is < 0 if self < other, > 0 if self > other, and ==
|
*order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and ==
|
||||||
0 if self == other. A relative name is always less than an
|
0 if *self* == *other*. A relative name is always less than an
|
||||||
absolute name. If both names have the same relativity, then
|
absolute name. If both names have the same relativity, then
|
||||||
the DNSSEC order relation is used to order them.
|
the DNSSEC order relation is used to order them.
|
||||||
|
|
||||||
I{nlabels} is the number of significant labels that the two names
|
*nlabels* is the number of significant labels that the two names
|
||||||
have in common.
|
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()
|
sabs = self.is_absolute()
|
||||||
@@ -433,8 +459,10 @@ class Name(object):
|
|||||||
def is_subdomain(self, other):
|
def is_subdomain(self, other):
|
||||||
"""Is self a subdomain of other?
|
"""Is self a subdomain of other?
|
||||||
|
|
||||||
The notion of subdomain includes equality.
|
Note that the notion of subdomain includes equality, e.g.
|
||||||
@rtype: bool
|
"dnpython.org" is a subdomain of itself.
|
||||||
|
|
||||||
|
Returns a ``bool``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
(nr, o, nl) = self.fullcompare(other)
|
(nr, o, nl) = self.fullcompare(other)
|
||||||
@@ -445,8 +473,10 @@ class Name(object):
|
|||||||
def is_superdomain(self, other):
|
def is_superdomain(self, other):
|
||||||
"""Is self a superdomain of other?
|
"""Is self a superdomain of other?
|
||||||
|
|
||||||
The notion of subdomain includes equality.
|
Note that the notion of superdomain includes equality, e.g.
|
||||||
@rtype: bool
|
"dnpython.org" is a superdomain of itself.
|
||||||
|
|
||||||
|
Returns a ``bool``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
(nr, o, nl) = self.fullcompare(other)
|
(nr, o, nl) = self.fullcompare(other)
|
||||||
@@ -457,7 +487,6 @@ class Name(object):
|
|||||||
def canonicalize(self):
|
def canonicalize(self):
|
||||||
"""Return a name which is equal to the current name, but is in
|
"""Return a name which is equal to the current name, but is in
|
||||||
DNSSEC canonical form.
|
DNSSEC canonical form.
|
||||||
@rtype: dns.name.Name object
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return Name([x.lower() for x in self.labels])
|
return Name([x.lower() for x in self.labels])
|
||||||
@@ -505,10 +534,13 @@ class Name(object):
|
|||||||
return self.to_text(False)
|
return self.to_text(False)
|
||||||
|
|
||||||
def to_text(self, omit_final_dot=False):
|
def to_text(self, omit_final_dot=False):
|
||||||
"""Convert name to text format.
|
"""Convert name to DNS text format.
|
||||||
@param omit_final_dot: If True, don't emit the final dot (denoting the
|
|
||||||
root label) for absolute names. The default is False.
|
*omit_final_dot* is a ``bool``. If True, don't emit the final
|
||||||
@rtype: string
|
dot (denoting the root label) for absolute names. The default
|
||||||
|
is False.
|
||||||
|
|
||||||
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if len(self.labels) == 0:
|
if len(self.labels) == 0:
|
||||||
@@ -527,16 +559,17 @@ class Name(object):
|
|||||||
|
|
||||||
IDN ACE labels are converted to Unicode.
|
IDN ACE labels are converted to Unicode.
|
||||||
|
|
||||||
@param omit_final_dot: If True, don't emit the final dot (denoting the
|
*omit_final_dot* is a ``bool``. If True, don't emit the final
|
||||||
root label) for absolute names. The default is False.
|
dot (denoting the root label) for absolute names. The default
|
||||||
@type omit_final_dot: bool
|
is False.
|
||||||
@param idna_codec: IDNA encoder/decoder. If None, the
|
*idna_codec* specifies the IDNA encoder/decoder. If None, the
|
||||||
IDNA_2003_Practical encoder/decoder is used. The IDNA_2003_Practical
|
dns.name.IDNA_2003_Practical encoder/decoder is used.
|
||||||
decoder does not impose any policy, it just decodes punycode, so if
|
The IDNA_2003_Practical decoder does
|
||||||
you don't want checking for compliance, you can use this decoder for
|
not impose any policy, it just decodes punycode, so if you
|
||||||
IDNA2008 as well.
|
don't want checking for compliance, you can use this decoder
|
||||||
@type idna_codec: dns.name.IDNA
|
for IDNA2008 as well.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if len(self.labels) == 0:
|
if len(self.labels) == 0:
|
||||||
@@ -554,15 +587,18 @@ class Name(object):
|
|||||||
def to_digestable(self, origin=None):
|
def to_digestable(self, origin=None):
|
||||||
"""Convert name to a format suitable for digesting in hashes.
|
"""Convert name to a format suitable for digesting in hashes.
|
||||||
|
|
||||||
The name is canonicalized and converted to uncompressed wire format.
|
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.
|
||||||
|
|
||||||
@param origin: If the name is relative and origin is not None, then
|
*origin* is a ``dns.name.Name`` or ``None``. If the name is
|
||||||
origin will be appended to it.
|
relative and origin is not ``None``, then origin will be appended
|
||||||
@type origin: dns.name.Name object
|
to the name.
|
||||||
@raises NeedAbsoluteNameOrOrigin: All names in wire format are
|
|
||||||
absolute. If self is a relative name, then an origin must be supplied;
|
Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
|
||||||
if it is missing, then this exception is raised
|
relative and no origin was provided.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``binary``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.is_absolute():
|
if not self.is_absolute():
|
||||||
@@ -579,19 +615,21 @@ class Name(object):
|
|||||||
def to_wire(self, file=None, compress=None, origin=None):
|
def to_wire(self, file=None, compress=None, origin=None):
|
||||||
"""Convert name to wire format, possibly compressing it.
|
"""Convert name to wire format, possibly compressing it.
|
||||||
|
|
||||||
@param file: the file where the name is emitted (typically
|
*file* is the file where the name is emitted (typically a
|
||||||
a BytesIO file). If None, a string containing the wire name
|
BytesIO file). If ``None`` (the default), a ``binary``
|
||||||
will be returned.
|
containing the wire name will be returned.
|
||||||
@type file: file or None
|
|
||||||
@param compress: The compression table. If None (the default) names
|
*compress*, a ``dict``, is the compression table to use. If
|
||||||
will not be compressed.
|
``None`` (the default), names will not be compressed.
|
||||||
@type compress: dict
|
|
||||||
@param origin: If the name is relative and origin is not None, then
|
*origin* is a ``dns.name.Name`` or ``None``. If the name is
|
||||||
origin will be appended to it.
|
relative and origin is not ``None``, then *origin* will be appended
|
||||||
@type origin: dns.name.Name object
|
to it.
|
||||||
@raises NeedAbsoluteNameOrOrigin: All names in wire format are
|
|
||||||
absolute. If self is a relative name, then an origin must be supplied;
|
Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
|
||||||
if it is missing, then this exception is raised
|
relative and no origin was provided.
|
||||||
|
|
||||||
|
Returns a ``binary`` or ``None``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if file is None:
|
if file is None:
|
||||||
@@ -634,7 +672,8 @@ class Name(object):
|
|||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
"""The length of the name (in labels).
|
"""The length of the name (in labels).
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return len(self.labels)
|
return len(self.labels)
|
||||||
@@ -649,14 +688,14 @@ class Name(object):
|
|||||||
return self.relativize(other)
|
return self.relativize(other)
|
||||||
|
|
||||||
def split(self, depth):
|
def split(self, depth):
|
||||||
"""Split a name into a prefix and suffix at depth.
|
"""Split a name into a prefix and suffix names at the specified depth.
|
||||||
|
|
||||||
@param depth: the number of labels in the suffix
|
*depth* is an ``int`` specifying the number of labels in the suffix
|
||||||
@type depth: int
|
|
||||||
@raises ValueError: the depth was not >= 0 and <= the length of the
|
Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the
|
||||||
name.
|
name.
|
||||||
@returns: the tuple (prefix, suffix)
|
|
||||||
@rtype: tuple
|
Returns the tuple ``(prefix, suffix)``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
l = len(self.labels)
|
l = len(self.labels)
|
||||||
@@ -671,9 +710,11 @@ class Name(object):
|
|||||||
|
|
||||||
def concatenate(self, other):
|
def concatenate(self, other):
|
||||||
"""Return a new name which is the concatenation of self and other.
|
"""Return a new name which is the concatenation of self and other.
|
||||||
@rtype: dns.name.Name object
|
|
||||||
@raises AbsoluteConcatenation: self is absolute and other is
|
Raises ``dns.name.AbsoluteConcatenation`` if the name is
|
||||||
not the empty name
|
absolute and *other* is not the empty name.
|
||||||
|
|
||||||
|
Returns a ``dns.name.Name``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.is_absolute() and len(other) > 0:
|
if self.is_absolute() and len(other) > 0:
|
||||||
@@ -683,9 +724,14 @@ class Name(object):
|
|||||||
return Name(labels)
|
return Name(labels)
|
||||||
|
|
||||||
def relativize(self, origin):
|
def relativize(self, origin):
|
||||||
"""If self is a subdomain of origin, return a new name which is self
|
"""If the name is a subdomain of *origin*, return a new name which is
|
||||||
relative to origin. Otherwise return self.
|
the name relative to origin. Otherwise return the name.
|
||||||
@rtype: dns.name.Name object
|
|
||||||
|
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):
|
if origin is not None and self.is_subdomain(origin):
|
||||||
@@ -694,9 +740,14 @@ class Name(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def derelativize(self, origin):
|
def derelativize(self, origin):
|
||||||
"""If self is a relative name, return a new name which is the
|
"""If the name is a relative name, return a new name which is the
|
||||||
concatenation of self and origin. Otherwise return self.
|
concatenation of the name and origin. Otherwise return the name.
|
||||||
@rtype: dns.name.Name object
|
|
||||||
|
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():
|
if not self.is_absolute():
|
||||||
@@ -705,11 +756,14 @@ class Name(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def choose_relativity(self, origin=None, relativize=True):
|
def choose_relativity(self, origin=None, relativize=True):
|
||||||
"""Return a name with the relativity desired by the caller. If
|
"""Return a name with the relativity desired by the caller.
|
||||||
origin is None, then self is returned. Otherwise, if
|
|
||||||
relativize is true the name is relativized, and if relativize is
|
If *origin* is ``None``, then the name is returned.
|
||||||
false the name is derelativized.
|
Otherwise, if *relativize* is ``True`` the name is
|
||||||
@rtype: dns.name.Name object
|
relativized, and if *relativize* is ``False`` the name is
|
||||||
|
derelativized.
|
||||||
|
|
||||||
|
Returns a ``dns.name.Name``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if origin:
|
if origin:
|
||||||
@@ -722,31 +776,41 @@ class Name(object):
|
|||||||
|
|
||||||
def parent(self):
|
def parent(self):
|
||||||
"""Return the parent of the name.
|
"""Return the parent of the name.
|
||||||
@rtype: dns.name.Name object
|
|
||||||
@raises NoParent: the name is either the root name or the empty name,
|
For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``.
|
||||||
and thus has no parent.
|
|
||||||
|
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:
|
if self == root or self == empty:
|
||||||
raise NoParent
|
raise NoParent
|
||||||
return Name(self.labels[1:])
|
return Name(self.labels[1:])
|
||||||
|
|
||||||
|
#: The root name, '.'
|
||||||
root = Name([b''])
|
root = Name([b''])
|
||||||
empty = Name([])
|
|
||||||
|
|
||||||
|
#: The empty name.
|
||||||
|
empty = Name([])
|
||||||
|
|
||||||
def from_unicode(text, origin=root, idna_codec=None):
|
def from_unicode(text, origin=root, idna_codec=None):
|
||||||
"""Convert unicode text into a Name object.
|
"""Convert unicode text into a Name object.
|
||||||
|
|
||||||
Labels are encoded in IDN ACE form.
|
Labels are encoded in IDN ACE form according to rules specified by
|
||||||
|
the IDNA codec.
|
||||||
|
|
||||||
@param text: The text to convert into a name.
|
*text*, a ``text``, is the text to convert into a name.
|
||||||
@type text: Unicode string
|
|
||||||
@param origin: The origin to append to non-absolute names.
|
*origin*, a ``dns.name.Name``, specifies the origin to
|
||||||
@type origin: dns.name.Name
|
append to non-absolute names. The default is the root name.
|
||||||
@param idna_codec: IDNA encoder/decoder. If None, the default IDNA 2003
|
|
||||||
encoder/decoder is used.
|
*idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
|
||||||
@type idna_codec: dns.name.IDNA
|
encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
|
||||||
@rtype: dns.name.Name object
|
is used.
|
||||||
|
|
||||||
|
Returns a ``dns.name.Name``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(text, text_type):
|
if not isinstance(text, text_type):
|
||||||
@@ -809,14 +873,16 @@ def from_unicode(text, origin=root, idna_codec=None):
|
|||||||
def from_text(text, origin=root, idna_codec=None):
|
def from_text(text, origin=root, idna_codec=None):
|
||||||
"""Convert text into a Name object.
|
"""Convert text into a Name object.
|
||||||
|
|
||||||
@param text: The text to convert into a name.
|
*text*, a ``text``, is the text to convert into a name.
|
||||||
@type text: string
|
|
||||||
@param origin: The origin to append to non-absolute names.
|
*origin*, a ``dns.name.Name``, specifies the origin to
|
||||||
@type origin: dns.name.Name
|
append to non-absolute names. The default is the root name.
|
||||||
@param idna_codec: IDNA encoder/decoder. If None, the default IDNA 2003
|
|
||||||
encoder/decoder is used.
|
*idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
|
||||||
@type idna_codec: dns.name.IDNA
|
encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
|
||||||
@rtype: dns.name.Name object
|
is used.
|
||||||
|
|
||||||
|
Returns a ``dns.name.Name``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(text, text_type):
|
if isinstance(text, text_type):
|
||||||
@@ -878,17 +944,21 @@ def from_text(text, origin=root, idna_codec=None):
|
|||||||
|
|
||||||
def from_wire(message, current):
|
def from_wire(message, current):
|
||||||
"""Convert possibly compressed wire format into a Name.
|
"""Convert possibly compressed wire format into a Name.
|
||||||
@param message: the entire DNS message
|
|
||||||
@type message: string
|
*message* is a ``binary`` containing an entire DNS message in DNS
|
||||||
@param current: the offset of the beginning of the name from the start
|
wire form.
|
||||||
of the message
|
|
||||||
@type current: int
|
*current*, an ``int``, is the offset of the beginning of the name
|
||||||
@raises dns.name.BadPointer: a compression pointer did not point backwards
|
from the start of the message
|
||||||
in the message
|
|
||||||
@raises dns.name.BadLabelType: an invalid label type was encountered.
|
Raises ``dns.name.BadPointer`` if a compression pointer did not
|
||||||
@returns: a tuple consisting of the name that was read and the number
|
point backwards in the message.
|
||||||
of bytes of the wire format message which were consumed reading it
|
|
||||||
@rtype: (dns.name.Name object, int) tuple
|
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):
|
if not isinstance(message, binary_type):
|
||||||
|
|||||||
35
src/dns/name.pyi
Normal file
35
src/dns/name.pyi
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
from 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
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
|
# Copyright (C) 2003-2017 Nominum, Inc.
|
||||||
# Copyright (C) 2016 Coresec Systems AB
|
# Copyright (C) 2016 Coresec Systems AB
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -31,20 +33,20 @@ from ._compat import xrange
|
|||||||
|
|
||||||
|
|
||||||
class NameDict(collections.MutableMapping):
|
class NameDict(collections.MutableMapping):
|
||||||
|
|
||||||
"""A dictionary whose keys are dns.name.Name objects.
|
"""A dictionary whose keys are dns.name.Name objects.
|
||||||
@ivar max_depth: the maximum depth of the keys that have ever been
|
|
||||||
added to the dictionary.
|
In addition to being like a regular Python dictionary, this
|
||||||
@type max_depth: int
|
dictionary can also get the deepest match for a given key.
|
||||||
@ivar max_depth_items: the number of items of maximum depth
|
|
||||||
@type max_depth_items: int
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ["max_depth", "max_depth_items", "__store"]
|
__slots__ = ["max_depth", "max_depth_items", "__store"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(NameDict, self).__init__()
|
||||||
self.__store = dict()
|
self.__store = dict()
|
||||||
|
#: the maximum depth of the keys that have ever been added
|
||||||
self.max_depth = 0
|
self.max_depth = 0
|
||||||
|
#: the number of items of maximum depth
|
||||||
self.max_depth_items = 0
|
self.max_depth_items = 0
|
||||||
self.update(dict(*args, **kwargs))
|
self.update(dict(*args, **kwargs))
|
||||||
|
|
||||||
@@ -83,14 +85,16 @@ class NameDict(collections.MutableMapping):
|
|||||||
return key in self.__store
|
return key in self.__store
|
||||||
|
|
||||||
def get_deepest_match(self, name):
|
def get_deepest_match(self, name):
|
||||||
"""Find the deepest match to I{name} in the dictionary.
|
"""Find the deepest match to *fname* in the dictionary.
|
||||||
|
|
||||||
The deepest match is the longest name in the dictionary which is
|
The deepest match is the longest name in the dictionary which is
|
||||||
a superdomain of I{name}.
|
a superdomain of *name*. Note that *superdomain* includes matching
|
||||||
|
*name* itself.
|
||||||
|
|
||||||
@param name: the name
|
*name*, a ``dns.name.Name``, the name to find.
|
||||||
@type name: dns.name.Name object
|
|
||||||
@rtype: (key, value) tuple
|
Returns a ``(key, value)`` where *key* is the deepest
|
||||||
|
``dns.name.Name``, and *value* is the value associated with *key*.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
depth = len(name)
|
depth = len(name)
|
||||||
|
|||||||
100
src/dns/node.py
100
src/dns/node.py
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -24,19 +26,12 @@ import dns.renderer
|
|||||||
|
|
||||||
class Node(object):
|
class Node(object):
|
||||||
|
|
||||||
"""A DNS node.
|
"""A Node is a set of rdatasets."""
|
||||||
|
|
||||||
A node is a set of rdatasets
|
|
||||||
|
|
||||||
@ivar rdatasets: the node's rdatasets
|
|
||||||
@type rdatasets: list of dns.rdataset.Rdataset objects"""
|
|
||||||
|
|
||||||
__slots__ = ['rdatasets']
|
__slots__ = ['rdatasets']
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize a DNS node.
|
#: the set of rdatsets, represented as a list.
|
||||||
"""
|
|
||||||
|
|
||||||
self.rdatasets = []
|
self.rdatasets = []
|
||||||
|
|
||||||
def to_text(self, name, **kw):
|
def to_text(self, name, **kw):
|
||||||
@@ -44,9 +39,10 @@ class Node(object):
|
|||||||
|
|
||||||
Each rdataset at the node is printed. Any keyword arguments
|
Each rdataset at the node is printed. Any keyword arguments
|
||||||
to this method are passed on to the rdataset's to_text() method.
|
to this method are passed on to the rdataset's to_text() method.
|
||||||
@param name: the owner name of the rdatasets
|
|
||||||
@type name: dns.name.Name object
|
*name*, a ``dns.name.Name`` or ``text``, the owner name of the rdatasets.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
s = StringIO()
|
s = StringIO()
|
||||||
@@ -60,10 +56,6 @@ class Node(object):
|
|||||||
return '<DNS node ' + str(id(self)) + '>'
|
return '<DNS node ' + str(id(self)) + '>'
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Two nodes are equal if they have the same rdatasets.
|
|
||||||
|
|
||||||
@rtype: bool
|
|
||||||
"""
|
|
||||||
#
|
#
|
||||||
# This is inefficient. Good thing we don't need to do it much.
|
# This is inefficient. Good thing we don't need to do it much.
|
||||||
#
|
#
|
||||||
@@ -89,11 +81,11 @@ class Node(object):
|
|||||||
"""Find an rdataset matching the specified properties in the
|
"""Find an rdataset matching the specified properties in the
|
||||||
current node.
|
current node.
|
||||||
|
|
||||||
@param rdclass: The class of the rdataset
|
*rdclass*, an ``int``, the class of the rdataset.
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: The type of the rdataset
|
*rdtype*, an ``int``, the type of the rdataset.
|
||||||
@type rdtype: int
|
|
||||||
@param covers: The covered type. Usually this value is
|
*covers*, an ``int``, the covered type. Usually this value is
|
||||||
dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
|
dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
|
||||||
dns.rdatatype.RRSIG, then the covers value will be the rdata
|
dns.rdatatype.RRSIG, then the covers value will be the rdata
|
||||||
type the SIG/RRSIG covers. The library treats the SIG and RRSIG
|
type the SIG/RRSIG covers. The library treats the SIG and RRSIG
|
||||||
@@ -101,12 +93,13 @@ class Node(object):
|
|||||||
types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much
|
types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much
|
||||||
easier to work with than if RRSIGs covering different rdata
|
easier to work with than if RRSIGs covering different rdata
|
||||||
types were aggregated into a single RRSIG rdataset.
|
types were aggregated into a single RRSIG rdataset.
|
||||||
@type covers: int
|
|
||||||
@param create: If True, create the rdataset if it is not found.
|
*create*, a ``bool``. If True, create the rdataset if it is not found.
|
||||||
@type create: bool
|
|
||||||
@raises KeyError: An rdataset of the desired type and class does
|
Raises ``KeyError`` if an rdataset of the desired type and class does
|
||||||
not exist and I{create} is not True.
|
not exist and *create* is not ``True``.
|
||||||
@rtype: dns.rdataset.Rdataset object
|
|
||||||
|
Returns a ``dns.rdataset.Rdataset``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for rds in self.rdatasets:
|
for rds in self.rdatasets:
|
||||||
@@ -124,17 +117,24 @@ class Node(object):
|
|||||||
current node.
|
current node.
|
||||||
|
|
||||||
None is returned if an rdataset of the specified type and
|
None is returned if an rdataset of the specified type and
|
||||||
class does not exist and I{create} is not True.
|
class does not exist and *create* is not ``True``.
|
||||||
|
|
||||||
@param rdclass: The class of the rdataset
|
*rdclass*, an ``int``, the class of the rdataset.
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: The type of the rdataset
|
*rdtype*, an ``int``, the type of the rdataset.
|
||||||
@type rdtype: int
|
|
||||||
@param covers: The covered type.
|
*covers*, an ``int``, the covered type. Usually this value is
|
||||||
@type covers: int
|
dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or
|
||||||
@param create: If True, create the rdataset if it is not found.
|
dns.rdatatype.RRSIG, then the covers value will be the rdata
|
||||||
@type create: bool
|
type the SIG/RRSIG covers. The library treats the SIG and RRSIG
|
||||||
@rtype: dns.rdataset.Rdataset object or None
|
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:
|
try:
|
||||||
@@ -149,12 +149,11 @@ class Node(object):
|
|||||||
|
|
||||||
If a matching rdataset does not exist, it is not an error.
|
If a matching rdataset does not exist, it is not an error.
|
||||||
|
|
||||||
@param rdclass: The class of the rdataset
|
*rdclass*, an ``int``, the class of the rdataset.
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: The type of the rdataset
|
*rdtype*, an ``int``, the type of the rdataset.
|
||||||
@type rdtype: int
|
|
||||||
@param covers: The covered type.
|
*covers*, an ``int``, the covered type.
|
||||||
@type covers: int
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rds = self.get_rdataset(rdclass, rdtype, covers)
|
rds = self.get_rdataset(rdclass, rdtype, covers)
|
||||||
@@ -164,11 +163,16 @@ class Node(object):
|
|||||||
def replace_rdataset(self, replacement):
|
def replace_rdataset(self, replacement):
|
||||||
"""Replace an rdataset.
|
"""Replace an rdataset.
|
||||||
|
|
||||||
It is not an error if there is no rdataset matching I{replacement}.
|
It is not an error if there is no rdataset matching *replacement*.
|
||||||
|
|
||||||
Ownership of the I{replacement} object is transferred to the node;
|
Ownership of the *replacement* object is transferred to the node;
|
||||||
in other words, this method does not store a copy of I{replacement}
|
in other words, this method does not store a copy of *replacement*
|
||||||
at the node, it stores I{replacement} itself.
|
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):
|
if not isinstance(replacement, dns.rdataset.Rdataset):
|
||||||
|
|||||||
17
src/dns/node.pyi
Normal file
17
src/dns/node.pyi
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from 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:
|
||||||
|
...
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -17,10 +19,15 @@
|
|||||||
|
|
||||||
import dns.exception
|
import dns.exception
|
||||||
|
|
||||||
|
#: Query
|
||||||
QUERY = 0
|
QUERY = 0
|
||||||
|
#: Inverse Query (historical)
|
||||||
IQUERY = 1
|
IQUERY = 1
|
||||||
|
#: Server Status (unspecified and unimplemented anywhere)
|
||||||
STATUS = 2
|
STATUS = 2
|
||||||
|
#: Notify
|
||||||
NOTIFY = 4
|
NOTIFY = 4
|
||||||
|
#: Dynamic Update
|
||||||
UPDATE = 5
|
UPDATE = 5
|
||||||
|
|
||||||
_by_text = {
|
_by_text = {
|
||||||
@@ -35,21 +42,21 @@ _by_text = {
|
|||||||
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
||||||
# would cause the mapping not to be true inverse.
|
# would cause the mapping not to be true inverse.
|
||||||
|
|
||||||
_by_value = dict((y, x) for x, y in _by_text.items())
|
_by_value = {y: x for x, y in _by_text.items()}
|
||||||
|
|
||||||
|
|
||||||
class UnknownOpcode(dns.exception.DNSException):
|
class UnknownOpcode(dns.exception.DNSException):
|
||||||
|
|
||||||
"""An DNS opcode is unknown."""
|
"""An DNS opcode is unknown."""
|
||||||
|
|
||||||
|
|
||||||
def from_text(text):
|
def from_text(text):
|
||||||
"""Convert text into an opcode.
|
"""Convert text into an opcode.
|
||||||
|
|
||||||
@param text: the textual opcode
|
*text*, a ``text``, the textual opcode
|
||||||
@type text: string
|
|
||||||
@raises UnknownOpcode: the opcode is unknown
|
Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if text.isdigit():
|
if text.isdigit():
|
||||||
@@ -65,8 +72,9 @@ def from_text(text):
|
|||||||
def from_flags(flags):
|
def from_flags(flags):
|
||||||
"""Extract an opcode from DNS message flags.
|
"""Extract an opcode from DNS message flags.
|
||||||
|
|
||||||
@param flags: int
|
*flags*, an ``int``, the DNS flags.
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (flags & 0x7800) >> 11
|
return (flags & 0x7800) >> 11
|
||||||
@@ -75,7 +83,10 @@ def from_flags(flags):
|
|||||||
def to_flags(value):
|
def to_flags(value):
|
||||||
"""Convert an opcode to a value suitable for ORing into DNS message
|
"""Convert an opcode to a value suitable for ORing into DNS message
|
||||||
flags.
|
flags.
|
||||||
@rtype: int
|
|
||||||
|
*value*, an ``int``, the DNS opcode value.
|
||||||
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (value << 11) & 0x7800
|
return (value << 11) & 0x7800
|
||||||
@@ -84,10 +95,11 @@ def to_flags(value):
|
|||||||
def to_text(value):
|
def to_text(value):
|
||||||
"""Convert an opcode to text.
|
"""Convert an opcode to text.
|
||||||
|
|
||||||
@param value: the opcdoe
|
*value*, an ``int`` the opcode value,
|
||||||
@type value: int
|
|
||||||
@raises UnknownOpcode: the opcode is unknown
|
Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
text = _by_value.get(value)
|
text = _by_value.get(value)
|
||||||
@@ -97,11 +109,11 @@ def to_text(value):
|
|||||||
|
|
||||||
|
|
||||||
def is_update(flags):
|
def is_update(flags):
|
||||||
"""True if the opcode in flags is UPDATE.
|
"""Is the opcode in flags UPDATE?
|
||||||
|
|
||||||
@param flags: DNS flags
|
*flags*, an ``int``, the DNS message flags.
|
||||||
@type flags: int
|
|
||||||
@rtype: bool
|
Returns a ``bool``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return from_flags(flags) == UPDATE
|
return from_flags(flags) == UPDATE
|
||||||
|
|||||||
0
src/dns/py.typed
Normal file
0
src/dns/py.typed
Normal file
460
src/dns/query.py
460
src/dns/query.py
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -28,11 +30,12 @@ import dns.exception
|
|||||||
import dns.inet
|
import dns.inet
|
||||||
import dns.name
|
import dns.name
|
||||||
import dns.message
|
import dns.message
|
||||||
|
import dns.rcode
|
||||||
import dns.rdataclass
|
import dns.rdataclass
|
||||||
import dns.rdatatype
|
import dns.rdatatype
|
||||||
from ._compat import long, string_types
|
from ._compat import long, string_types, PY3
|
||||||
|
|
||||||
if sys.version_info > (3,):
|
if PY3:
|
||||||
select_error = OSError
|
select_error = OSError
|
||||||
else:
|
else:
|
||||||
select_error = select.error
|
select_error = select.error
|
||||||
@@ -42,34 +45,36 @@ else:
|
|||||||
socket_factory = socket.socket
|
socket_factory = socket.socket
|
||||||
|
|
||||||
class UnexpectedSource(dns.exception.DNSException):
|
class UnexpectedSource(dns.exception.DNSException):
|
||||||
|
|
||||||
"""A DNS query response came from an unexpected address or port."""
|
"""A DNS query response came from an unexpected address or port."""
|
||||||
|
|
||||||
|
|
||||||
class BadResponse(dns.exception.FormError):
|
class BadResponse(dns.exception.FormError):
|
||||||
|
|
||||||
"""A DNS query response does not respond to the question asked."""
|
"""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):
|
def _compute_expiration(timeout):
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return time.time() + timeout
|
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):
|
def _poll_for(fd, readable, writable, error, timeout):
|
||||||
"""Poll polling backend.
|
"""Poll polling backend."""
|
||||||
@param fd: File descriptor
|
|
||||||
@type fd: int
|
|
||||||
@param readable: Whether to wait for readability
|
|
||||||
@type readable: bool
|
|
||||||
@param writable: Whether to wait for writability
|
|
||||||
@type writable: bool
|
|
||||||
@param timeout: Deadline timeout (expiration time, in seconds)
|
|
||||||
@type timeout: float
|
|
||||||
@return True on success, False on timeout
|
|
||||||
"""
|
|
||||||
event_mask = 0
|
event_mask = 0
|
||||||
if readable:
|
if readable:
|
||||||
event_mask |= select.POLLIN
|
event_mask |= select.POLLIN
|
||||||
@@ -90,17 +95,8 @@ def _poll_for(fd, readable, writable, error, timeout):
|
|||||||
|
|
||||||
|
|
||||||
def _select_for(fd, readable, writable, error, timeout):
|
def _select_for(fd, readable, writable, error, timeout):
|
||||||
"""Select polling backend.
|
"""Select polling backend."""
|
||||||
@param fd: File descriptor
|
|
||||||
@type fd: int
|
|
||||||
@param readable: Whether to wait for readability
|
|
||||||
@type readable: bool
|
|
||||||
@param writable: Whether to wait for writability
|
|
||||||
@type writable: bool
|
|
||||||
@param timeout: Deadline timeout (expiration time, in seconds)
|
|
||||||
@type timeout: float
|
|
||||||
@return True on success, False on timeout
|
|
||||||
"""
|
|
||||||
rset, wset, xset = [], [], []
|
rset, wset, xset = [], [], []
|
||||||
|
|
||||||
if readable:
|
if readable:
|
||||||
@@ -119,6 +115,10 @@ def _select_for(fd, readable, writable, error, timeout):
|
|||||||
|
|
||||||
|
|
||||||
def _wait_for(fd, readable, writable, error, expiration):
|
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
|
done = False
|
||||||
while not done:
|
while not done:
|
||||||
if expiration is None:
|
if expiration is None:
|
||||||
@@ -137,9 +137,8 @@ def _wait_for(fd, readable, writable, error, expiration):
|
|||||||
|
|
||||||
|
|
||||||
def _set_polling_backend(fn):
|
def _set_polling_backend(fn):
|
||||||
"""
|
# Internal API. Do not use.
|
||||||
Internal API. Do not use.
|
|
||||||
"""
|
|
||||||
global _polling_backend
|
global _polling_backend
|
||||||
|
|
||||||
_polling_backend = fn
|
_polling_backend = fn
|
||||||
@@ -165,8 +164,11 @@ def _addresses_equal(af, a1, a2):
|
|||||||
# Convert the first value of the tuple, which is a textual format
|
# Convert the first value of the tuple, which is a textual format
|
||||||
# address into binary form, so that we are not confused by different
|
# address into binary form, so that we are not confused by different
|
||||||
# textual representations of the same address
|
# textual representations of the same address
|
||||||
|
try:
|
||||||
n1 = dns.inet.inet_pton(af, a1[0])
|
n1 = dns.inet.inet_pton(af, a1[0])
|
||||||
n2 = dns.inet.inet_pton(af, a2[0])
|
n2 = dns.inet.inet_pton(af, a2[0])
|
||||||
|
except dns.exception.SyntaxError:
|
||||||
|
return False
|
||||||
return n1 == n2 and a1[1:] == a2[1:]
|
return n1 == n2 and a1[1:] == a2[1:]
|
||||||
|
|
||||||
|
|
||||||
@@ -193,68 +195,140 @@ def _destination_and_source(af, where, port, source, source_port):
|
|||||||
return (af, destination, source)
|
return (af, destination, source)
|
||||||
|
|
||||||
|
|
||||||
def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
|
def send_udp(sock, what, destination, expiration=None):
|
||||||
ignore_unexpected=False, one_rr_per_rrset=False):
|
"""Send a DNS message to the specified UDP socket.
|
||||||
"""Return the response obtained after sending a query via UDP.
|
|
||||||
|
|
||||||
@param q: the query
|
*sock*, a ``socket``.
|
||||||
@type q: dns.message.Message
|
|
||||||
@param where: where to send the message
|
*what*, a ``binary`` or ``dns.message.Message``, the message to send.
|
||||||
@type where: string containing an IPv4 or IPv6 address
|
|
||||||
@param timeout: The number of seconds to wait before the query times out.
|
*destination*, a destination tuple appropriate for the address family
|
||||||
If None, the default, wait forever.
|
of the socket, specifying where to send the query.
|
||||||
@type timeout: float
|
|
||||||
@param port: The port to which to send the message. The default is 53.
|
*expiration*, a ``float`` or ``None``, the absolute time at which
|
||||||
@type port: int
|
a timeout exception should be raised. If ``None``, no timeout will
|
||||||
@param af: the address family to use. The default is None, which
|
occur.
|
||||||
causes the address family to use to be inferred from the form of where.
|
|
||||||
If the inference attempt fails, AF_INET is used.
|
Returns an ``(int, float)`` tuple of bytes sent and the sent time.
|
||||||
@type af: int
|
|
||||||
@rtype: dns.message.Message object
|
|
||||||
@param source: source address. The default is the wildcard address.
|
|
||||||
@type source: string
|
|
||||||
@param source_port: The port from which to send the message.
|
|
||||||
The default is 0.
|
|
||||||
@type source_port: int
|
|
||||||
@param ignore_unexpected: If True, ignore responses from unexpected
|
|
||||||
sources. The default is False.
|
|
||||||
@type ignore_unexpected: bool
|
|
||||||
@param one_rr_per_rrset: Put each RR into its own RRset
|
|
||||||
@type one_rr_per_rrset: bool
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
wire = q.to_wire()
|
if isinstance(what, dns.message.Message):
|
||||||
(af, destination, source) = _destination_and_source(af, where, port,
|
what = what.to_wire()
|
||||||
source, source_port)
|
_wait_for_writable(sock, expiration)
|
||||||
s = socket_factory(af, socket.SOCK_DGRAM, 0)
|
sent_time = time.time()
|
||||||
begin_time = None
|
n = sock.sendto(what, destination)
|
||||||
try:
|
return (n, sent_time)
|
||||||
expiration = _compute_expiration(timeout)
|
|
||||||
s.setblocking(0)
|
|
||||||
if source is not None:
|
def receive_udp(sock, destination, expiration=None,
|
||||||
s.bind(source)
|
ignore_unexpected=False, one_rr_per_rrset=False,
|
||||||
_wait_for_writable(s, expiration)
|
keyring=None, request_mac=b'', ignore_trailing=False):
|
||||||
begin_time = time.time()
|
"""Read a DNS message from a UDP socket.
|
||||||
s.sendto(wire, destination)
|
|
||||||
|
*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:
|
while 1:
|
||||||
_wait_for_readable(s, expiration)
|
_wait_for_readable(sock, expiration)
|
||||||
(wire, from_address) = s.recvfrom(65535)
|
(wire, from_address) = sock.recvfrom(65535)
|
||||||
if _addresses_equal(af, from_address, destination) or \
|
if _addresses_equal(sock.family, from_address, destination) or \
|
||||||
(dns.inet.is_multicast(where) and
|
(dns.inet.is_multicast(destination[0]) and
|
||||||
from_address[1:] == destination[1:]):
|
from_address[1:] == destination[1:]):
|
||||||
break
|
break
|
||||||
if not ignore_unexpected:
|
if not ignore_unexpected:
|
||||||
raise UnexpectedSource('got a response from '
|
raise UnexpectedSource('got a response from '
|
||||||
'%s instead of %s' % (from_address,
|
'%s instead of %s' % (from_address,
|
||||||
destination))
|
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:
|
finally:
|
||||||
if begin_time is None:
|
if sent_time is None or received_time is None:
|
||||||
response_time = 0
|
response_time = 0
|
||||||
else:
|
else:
|
||||||
response_time = time.time() - begin_time
|
response_time = received_time - sent_time
|
||||||
s.close()
|
s.close()
|
||||||
r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
|
|
||||||
one_rr_per_rrset=one_rr_per_rrset)
|
|
||||||
r.time = response_time
|
r.time = response_time
|
||||||
if not q.is_response(r):
|
if not q.is_response(r):
|
||||||
raise BadResponse
|
raise BadResponse
|
||||||
@@ -290,6 +364,67 @@ def _net_write(sock, data, expiration):
|
|||||||
current += sock.send(data[current:])
|
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):
|
def _connect(s, address):
|
||||||
try:
|
try:
|
||||||
s.connect(address)
|
s.connect(address)
|
||||||
@@ -305,30 +440,37 @@ def _connect(s, address):
|
|||||||
|
|
||||||
|
|
||||||
def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
|
def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
|
||||||
one_rr_per_rrset=False):
|
one_rr_per_rrset=False, ignore_trailing=False):
|
||||||
"""Return the response obtained after sending a query via TCP.
|
"""Return the response obtained after sending a query via TCP.
|
||||||
|
|
||||||
@param q: the query
|
*q*, a ``dns.message.Message``, the query to send
|
||||||
@type q: dns.message.Message object
|
|
||||||
@param where: where to send the message
|
*where*, a ``text`` containing an IPv4 or IPv6 address, where
|
||||||
@type where: string containing an IPv4 or IPv6 address
|
to send the message.
|
||||||
@param timeout: The number of seconds to wait before the query times out.
|
|
||||||
If None, the default, wait forever.
|
*timeout*, a ``float`` or ``None``, the number of seconds to wait before the
|
||||||
@type timeout: float
|
query times out. If ``None``, the default, wait forever.
|
||||||
@param port: The port to which to send the message. The default is 53.
|
|
||||||
@type port: int
|
*port*, an ``int``, the port send the message to. The default is 53.
|
||||||
@param af: the address family to use. The default is None, which
|
|
||||||
causes the address family to use to be inferred from the form of where.
|
*af*, an ``int``, the address family to use. The default is ``None``,
|
||||||
If the inference attempt fails, AF_INET is used.
|
which causes the address family to use to be inferred from the form of
|
||||||
@type af: int
|
*where*. If the inference attempt fails, AF_INET is used. This
|
||||||
@rtype: dns.message.Message object
|
parameter is historical; you need never set it.
|
||||||
@param source: source address. The default is the wildcard address.
|
|
||||||
@type source: string
|
*source*, a ``text`` containing an IPv4 or IPv6 address, specifying
|
||||||
@param source_port: The port from which to send the message.
|
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.
|
The default is 0.
|
||||||
@type source_port: int
|
|
||||||
@param one_rr_per_rrset: Put each RR into its own RRset
|
*one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own
|
||||||
@type one_rr_per_rrset: bool
|
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()
|
wire = q.to_wire()
|
||||||
@@ -336,6 +478,7 @@ def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
|
|||||||
source, source_port)
|
source, source_port)
|
||||||
s = socket_factory(af, socket.SOCK_STREAM, 0)
|
s = socket_factory(af, socket.SOCK_STREAM, 0)
|
||||||
begin_time = None
|
begin_time = None
|
||||||
|
received_time = None
|
||||||
try:
|
try:
|
||||||
expiration = _compute_expiration(timeout)
|
expiration = _compute_expiration(timeout)
|
||||||
s.setblocking(0)
|
s.setblocking(0)
|
||||||
@@ -343,25 +486,15 @@ def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0,
|
|||||||
if source is not None:
|
if source is not None:
|
||||||
s.bind(source)
|
s.bind(source)
|
||||||
_connect(s, destination)
|
_connect(s, destination)
|
||||||
|
send_tcp(s, wire, expiration)
|
||||||
l = len(wire)
|
(r, received_time) = receive_tcp(s, expiration, one_rr_per_rrset,
|
||||||
|
q.keyring, q.mac, ignore_trailing)
|
||||||
# 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) + wire
|
|
||||||
_net_write(s, tcpmsg, expiration)
|
|
||||||
ldata = _net_read(s, 2, expiration)
|
|
||||||
(l,) = struct.unpack("!H", ldata)
|
|
||||||
wire = _net_read(s, l, expiration)
|
|
||||||
finally:
|
finally:
|
||||||
if begin_time is None:
|
if begin_time is None or received_time is None:
|
||||||
response_time = 0
|
response_time = 0
|
||||||
else:
|
else:
|
||||||
response_time = time.time() - begin_time
|
response_time = received_time - begin_time
|
||||||
s.close()
|
s.close()
|
||||||
r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
|
|
||||||
one_rr_per_rrset=one_rr_per_rrset)
|
|
||||||
r.time = response_time
|
r.time = response_time
|
||||||
if not q.is_response(r):
|
if not q.is_response(r):
|
||||||
raise BadResponse
|
raise BadResponse
|
||||||
@@ -374,51 +507,59 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
|
|||||||
use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
|
use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
|
||||||
"""Return a generator for the responses to a zone transfer.
|
"""Return a generator for the responses to a zone transfer.
|
||||||
|
|
||||||
@param where: where to send the message
|
*where*. If the inference attempt fails, AF_INET is used. This
|
||||||
@type where: string containing an IPv4 or IPv6 address
|
parameter is historical; you need never set it.
|
||||||
@param zone: The name of the zone to transfer
|
|
||||||
@type zone: dns.name.Name object or string
|
*zone*, a ``dns.name.Name`` or ``text``, the name of the zone to transfer.
|
||||||
@param rdtype: The type of zone transfer. The default is
|
|
||||||
dns.rdatatype.AXFR.
|
*rdtype*, an ``int`` or ``text``, the type of zone transfer. The
|
||||||
@type rdtype: int or string
|
default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be
|
||||||
@param rdclass: The class of the zone transfer. The default is
|
used to do an incremental transfer instead.
|
||||||
dns.rdataclass.IN.
|
|
||||||
@type rdclass: int or string
|
*rdclass*, an ``int`` or ``text``, the class of the zone transfer.
|
||||||
@param timeout: The number of seconds to wait for each response message.
|
The default is ``dns.rdataclass.IN``.
|
||||||
If None, the default, wait forever.
|
|
||||||
@type timeout: float
|
*timeout*, a ``float``, the number of seconds to wait for each
|
||||||
@param port: The port to which to send the message. The default is 53.
|
response message. If None, the default, wait forever.
|
||||||
@type port: int
|
|
||||||
@param keyring: The TSIG keyring to use
|
*port*, an ``int``, the port send the message to. The default is 53.
|
||||||
@type keyring: dict
|
|
||||||
@param keyname: The name of the TSIG key to use
|
*keyring*, a ``dict``, the keyring to use for TSIG.
|
||||||
@type keyname: dns.name.Name object or string
|
|
||||||
@param relativize: If True, all names in the zone will be relativized to
|
*keyname*, a ``dns.name.Name`` or ``text``, the name of the TSIG
|
||||||
the zone origin. It is essential that the relativize setting matches
|
key to use.
|
||||||
the one specified to dns.zone.from_xfr().
|
|
||||||
@type relativize: bool
|
*relativize*, a ``bool``. If ``True``, all names in the zone will be
|
||||||
@param af: the address family to use. The default is None, which
|
relativized to the zone origin. It is essential that the
|
||||||
causes the address family to use to be inferred from the form of where.
|
relativize setting matches the one specified to
|
||||||
If the inference attempt fails, AF_INET is used.
|
``dns.zone.from_xfr()`` if using this generator to make a zone.
|
||||||
@type af: int
|
|
||||||
@param lifetime: The total number of seconds to spend doing the transfer.
|
*af*, an ``int``, the address family to use. The default is ``None``,
|
||||||
If None, the default, then there is no limit on the time the transfer may
|
which causes the address family to use to be inferred from the form of
|
||||||
take.
|
*where*. If the inference attempt fails, AF_INET is used. This
|
||||||
@type lifetime: float
|
parameter is historical; you need never set it.
|
||||||
@rtype: generator of dns.message.Message objects.
|
|
||||||
@param source: source address. The default is the wildcard address.
|
*lifetime*, a ``float``, the total number of seconds to spend
|
||||||
@type source: string
|
doing the transfer. If ``None``, the default, then there is no
|
||||||
@param source_port: The port from which to send the message.
|
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.
|
The default is 0.
|
||||||
@type source_port: int
|
|
||||||
@param serial: The SOA serial number to use as the base for an IXFR diff
|
*serial*, an ``int``, the SOA serial number to use as the base for
|
||||||
sequence (only meaningful if rdtype == dns.rdatatype.IXFR).
|
an IXFR diff sequence (only meaningful if *rdtype* is
|
||||||
@type serial: int
|
``dns.rdatatype.IXFR``).
|
||||||
@param use_udp: Use UDP (only meaningful for IXFR)
|
|
||||||
@type use_udp: bool
|
*use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR).
|
||||||
@param keyalgorithm: The TSIG algorithm to use; defaults to
|
|
||||||
dns.tsig.default_algorithm
|
*keyalgorithm*, a ``dns.name.Name`` or ``text``, the TSIG algorithm to use.
|
||||||
@type keyalgorithm: string
|
|
||||||
|
Raises on errors, and so does the generator.
|
||||||
|
|
||||||
|
Returns a generator of ``dns.message.Message`` objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(zone, string_types):
|
if isinstance(zone, string_types):
|
||||||
@@ -481,6 +622,9 @@ def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN,
|
|||||||
xfr=True, origin=origin, tsig_ctx=tsig_ctx,
|
xfr=True, origin=origin, tsig_ctx=tsig_ctx,
|
||||||
multi=True, first=first,
|
multi=True, first=first,
|
||||||
one_rr_per_rrset=is_ixfr)
|
one_rr_per_rrset=is_ixfr)
|
||||||
|
rcode = r.rcode()
|
||||||
|
if rcode != dns.rcode.NOERROR:
|
||||||
|
raise TransferError(rcode)
|
||||||
tsig_ctx = r.tsig_ctx
|
tsig_ctx = r.tsig_ctx
|
||||||
first = False
|
first = False
|
||||||
answer_index = 0
|
answer_index = 0
|
||||||
|
|||||||
15
src/dns/query.pyi
Normal file
15
src/dns/query.pyi
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from 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:
|
||||||
|
...
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -18,18 +20,29 @@
|
|||||||
import dns.exception
|
import dns.exception
|
||||||
from ._compat import long
|
from ._compat import long
|
||||||
|
|
||||||
|
#: No error
|
||||||
NOERROR = 0
|
NOERROR = 0
|
||||||
|
#: Form error
|
||||||
FORMERR = 1
|
FORMERR = 1
|
||||||
|
#: Server failure
|
||||||
SERVFAIL = 2
|
SERVFAIL = 2
|
||||||
|
#: Name does not exist ("Name Error" in RFC 1025 terminology).
|
||||||
NXDOMAIN = 3
|
NXDOMAIN = 3
|
||||||
|
#: Not implemented
|
||||||
NOTIMP = 4
|
NOTIMP = 4
|
||||||
|
#: Refused
|
||||||
REFUSED = 5
|
REFUSED = 5
|
||||||
|
#: Name exists.
|
||||||
YXDOMAIN = 6
|
YXDOMAIN = 6
|
||||||
|
#: RRset exists.
|
||||||
YXRRSET = 7
|
YXRRSET = 7
|
||||||
|
#: RRset does not exist.
|
||||||
NXRRSET = 8
|
NXRRSET = 8
|
||||||
|
#: Not authoritative.
|
||||||
NOTAUTH = 9
|
NOTAUTH = 9
|
||||||
|
#: Name not in zone.
|
||||||
NOTZONE = 10
|
NOTZONE = 10
|
||||||
|
#: Bad EDNS version.
|
||||||
BADVERS = 16
|
BADVERS = 16
|
||||||
|
|
||||||
_by_text = {
|
_by_text = {
|
||||||
@@ -51,21 +64,21 @@ _by_text = {
|
|||||||
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
||||||
# would cause the mapping not to be a true inverse.
|
# would cause the mapping not to be a true inverse.
|
||||||
|
|
||||||
_by_value = dict((y, x) for x, y in _by_text.items())
|
_by_value = {y: x for x, y in _by_text.items()}
|
||||||
|
|
||||||
|
|
||||||
class UnknownRcode(dns.exception.DNSException):
|
class UnknownRcode(dns.exception.DNSException):
|
||||||
|
|
||||||
"""A DNS rcode is unknown."""
|
"""A DNS rcode is unknown."""
|
||||||
|
|
||||||
|
|
||||||
def from_text(text):
|
def from_text(text):
|
||||||
"""Convert text into an rcode.
|
"""Convert text into an rcode.
|
||||||
|
|
||||||
@param text: the textual rcode
|
*text*, a ``text``, the textual rcode or an integer in textual form.
|
||||||
@type text: string
|
|
||||||
@raises UnknownRcode: the rcode is unknown
|
Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown.
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if text.isdigit():
|
if text.isdigit():
|
||||||
@@ -81,12 +94,13 @@ def from_text(text):
|
|||||||
def from_flags(flags, ednsflags):
|
def from_flags(flags, ednsflags):
|
||||||
"""Return the rcode value encoded by flags and ednsflags.
|
"""Return the rcode value encoded by flags and ednsflags.
|
||||||
|
|
||||||
@param flags: the DNS flags
|
*flags*, an ``int``, the DNS flags field.
|
||||||
@type flags: int
|
|
||||||
@param ednsflags: the EDNS flags
|
*ednsflags*, an ``int``, the EDNS flags field.
|
||||||
@type ednsflags: int
|
|
||||||
@raises ValueError: rcode is < 0 or > 4095
|
Raises ``ValueError`` if rcode is < 0 or > 4095
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0)
|
value = (flags & 0x000f) | ((ednsflags >> 20) & 0xff0)
|
||||||
@@ -98,10 +112,11 @@ def from_flags(flags, ednsflags):
|
|||||||
def to_flags(value):
|
def to_flags(value):
|
||||||
"""Return a (flags, ednsflags) tuple which encodes the rcode.
|
"""Return a (flags, ednsflags) tuple which encodes the rcode.
|
||||||
|
|
||||||
@param value: the rcode
|
*value*, an ``int``, the rcode.
|
||||||
@type value: int
|
|
||||||
@raises ValueError: rcode is < 0 or > 4095
|
Raises ``ValueError`` if rcode is < 0 or > 4095.
|
||||||
@rtype: (int, int) tuple
|
|
||||||
|
Returns an ``(int, int)`` tuple.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if value < 0 or value > 4095:
|
if value < 0 or value > 4095:
|
||||||
@@ -114,11 +129,15 @@ def to_flags(value):
|
|||||||
def to_text(value):
|
def to_text(value):
|
||||||
"""Convert rcode into text.
|
"""Convert rcode into text.
|
||||||
|
|
||||||
@param value: the rcode
|
*value*, and ``int``, the rcode.
|
||||||
@type value: int
|
|
||||||
@rtype: string
|
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)
|
text = _by_value.get(value)
|
||||||
if text is None:
|
if text is None:
|
||||||
text = str(value)
|
text = str(value)
|
||||||
|
|||||||
232
src/dns/rdata.py
232
src/dns/rdata.py
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -13,17 +15,7 @@
|
|||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
"""DNS rdata.
|
"""DNS rdata."""
|
||||||
|
|
||||||
@var _rdata_modules: A dictionary mapping a (rdclass, rdtype) tuple to
|
|
||||||
the module which implements that type.
|
|
||||||
@type _rdata_modules: dict
|
|
||||||
@var _module_prefix: The prefix to use when forming modules names. The
|
|
||||||
default is 'dns.rdtypes'. Changing this value will break the library.
|
|
||||||
@type _module_prefix: string
|
|
||||||
@var _hex_chunk: At most this many octets that will be represented in each
|
|
||||||
chunk of hexstring that _hexify() produces before whitespace occurs.
|
|
||||||
@type _hex_chunk: int"""
|
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import base64
|
import base64
|
||||||
@@ -37,17 +29,17 @@ import dns.tokenizer
|
|||||||
import dns.wiredata
|
import dns.wiredata
|
||||||
from ._compat import xrange, string_types, text_type
|
from ._compat import xrange, string_types, text_type
|
||||||
|
|
||||||
|
try:
|
||||||
|
import threading as _threading
|
||||||
|
except ImportError:
|
||||||
|
import dummy_threading as _threading
|
||||||
|
|
||||||
_hex_chunksize = 32
|
_hex_chunksize = 32
|
||||||
|
|
||||||
|
|
||||||
def _hexify(data, chunksize=_hex_chunksize):
|
def _hexify(data, chunksize=_hex_chunksize):
|
||||||
"""Convert a binary string into its hex encoding, broken up into chunks
|
"""Convert a binary string into its hex encoding, broken up into chunks
|
||||||
of I{chunksize} characters separated by a space.
|
of chunksize characters separated by a space.
|
||||||
|
|
||||||
@param data: the binary string
|
|
||||||
@type data: string
|
|
||||||
@param chunksize: the chunk size. Default is L{dns.rdata._hex_chunksize}
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
line = binascii.hexlify(data)
|
line = binascii.hexlify(data)
|
||||||
@@ -60,13 +52,7 @@ _base64_chunksize = 32
|
|||||||
|
|
||||||
def _base64ify(data, chunksize=_base64_chunksize):
|
def _base64ify(data, chunksize=_base64_chunksize):
|
||||||
"""Convert a binary string into its base64 encoding, broken up into chunks
|
"""Convert a binary string into its base64 encoding, broken up into chunks
|
||||||
of I{chunksize} characters separated by a space.
|
of chunksize characters separated by a space.
|
||||||
|
|
||||||
@param data: the binary string
|
|
||||||
@type data: string
|
|
||||||
@param chunksize: the chunk size. Default is
|
|
||||||
L{dns.rdata._base64_chunksize}
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
line = base64.b64encode(data)
|
line = base64.b64encode(data)
|
||||||
@@ -77,13 +63,7 @@ def _base64ify(data, chunksize=_base64_chunksize):
|
|||||||
__escaped = bytearray(b'"\\')
|
__escaped = bytearray(b'"\\')
|
||||||
|
|
||||||
def _escapify(qstring):
|
def _escapify(qstring):
|
||||||
"""Escape the characters in a quoted string which need it.
|
"""Escape the characters in a quoted string which need it."""
|
||||||
|
|
||||||
@param qstring: the string
|
|
||||||
@type qstring: string
|
|
||||||
@returns: the escaped string
|
|
||||||
@rtype: string
|
|
||||||
"""
|
|
||||||
|
|
||||||
if isinstance(qstring, text_type):
|
if isinstance(qstring, text_type):
|
||||||
qstring = qstring.encode()
|
qstring = qstring.encode()
|
||||||
@@ -104,10 +84,6 @@ def _escapify(qstring):
|
|||||||
def _truncate_bitmap(what):
|
def _truncate_bitmap(what):
|
||||||
"""Determine the index of greatest byte that isn't all zeros, and
|
"""Determine the index of greatest byte that isn't all zeros, and
|
||||||
return the bitmap that contains all the bytes less than that index.
|
return the bitmap that contains all the bytes less than that index.
|
||||||
|
|
||||||
@param what: a string of octets representing a bitmap.
|
|
||||||
@type what: string
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for i in xrange(len(what) - 1, -1, -1):
|
for i in xrange(len(what) - 1, -1, -1):
|
||||||
@@ -117,30 +93,30 @@ def _truncate_bitmap(what):
|
|||||||
|
|
||||||
|
|
||||||
class Rdata(object):
|
class Rdata(object):
|
||||||
|
"""Base class for all DNS rdata types."""
|
||||||
"""Base class for all DNS rdata types.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ['rdclass', 'rdtype']
|
__slots__ = ['rdclass', 'rdtype']
|
||||||
|
|
||||||
def __init__(self, rdclass, rdtype):
|
def __init__(self, rdclass, rdtype):
|
||||||
"""Initialize an rdata.
|
"""Initialize an rdata.
|
||||||
@param rdclass: The rdata class
|
|
||||||
@type rdclass: int
|
*rdclass*, an ``int`` is the rdataclass of the Rdata.
|
||||||
@param rdtype: The rdata type
|
*rdtype*, an ``int`` is the rdatatype of the Rdata.
|
||||||
@type rdtype: int
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.rdclass = rdclass
|
self.rdclass = rdclass
|
||||||
self.rdtype = rdtype
|
self.rdtype = rdtype
|
||||||
|
|
||||||
def covers(self):
|
def covers(self):
|
||||||
"""DNS SIG/RRSIG rdatas apply to a specific type; this type is
|
"""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
|
returned by the covers() function. If the rdata type is not
|
||||||
SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when
|
SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when
|
||||||
creating rdatasets, allowing the rdataset to contain only RRSIGs
|
creating rdatasets, allowing the rdataset to contain only RRSIGs
|
||||||
of a particular type, e.g. RRSIG(NS).
|
of a particular type, e.g. RRSIG(NS).
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return dns.rdatatype.NONE
|
return dns.rdatatype.NONE
|
||||||
@@ -149,37 +125,52 @@ class Rdata(object):
|
|||||||
"""Return a 32-bit type value, the least significant 16 bits of
|
"""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
|
which are the ordinary DNS type, and the upper 16 bits of which are
|
||||||
the "covered" type, if any.
|
the "covered" type, if any.
|
||||||
@rtype: int
|
|
||||||
|
Returns an ``int``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.covers() << 16 | self.rdtype
|
return self.covers() << 16 | self.rdtype
|
||||||
|
|
||||||
def to_text(self, origin=None, relativize=True, **kw):
|
def to_text(self, origin=None, relativize=True, **kw):
|
||||||
"""Convert an rdata to text format.
|
"""Convert an rdata to text format.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``text``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def to_wire(self, file, compress=None, origin=None):
|
def to_wire(self, file, compress=None, origin=None):
|
||||||
"""Convert an rdata to wire format.
|
"""Convert an rdata to wire format.
|
||||||
@rtype: string
|
|
||||||
|
Returns a ``binary``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def to_digestable(self, origin=None):
|
def to_digestable(self, origin=None):
|
||||||
"""Convert rdata to a format suitable for digesting in hashes. This
|
"""Convert rdata to a format suitable for digesting in hashes. This
|
||||||
is also the DNSSEC canonical form."""
|
is also the DNSSEC canonical form.
|
||||||
|
|
||||||
|
Returns a ``binary``.
|
||||||
|
"""
|
||||||
|
|
||||||
f = BytesIO()
|
f = BytesIO()
|
||||||
self.to_wire(f, None, origin)
|
self.to_wire(f, None, origin)
|
||||||
return f.getvalue()
|
return f.getvalue()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
"""Check that the current contents of the rdata's fields are
|
"""Check that the current contents of the rdata's fields are
|
||||||
valid. If you change an rdata by assigning to its fields,
|
valid.
|
||||||
|
|
||||||
|
If you change an rdata by assigning to its fields,
|
||||||
it is a good idea to call validate() when you are done making
|
it is a good idea to call validate() when you are done making
|
||||||
changes.
|
changes.
|
||||||
|
|
||||||
|
Raises various exceptions if there are problems.
|
||||||
|
|
||||||
|
Returns ``None``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text())
|
dns.rdata.from_text(self.rdclass, self.rdtype, self.to_text())
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -197,16 +188,19 @@ class Rdata(object):
|
|||||||
|
|
||||||
def _cmp(self, other):
|
def _cmp(self, other):
|
||||||
"""Compare an rdata with another rdata of the same rdtype and
|
"""Compare an rdata with another rdata of the same rdtype and
|
||||||
rdclass. Return < 0 if self < other in the DNSSEC ordering,
|
rdclass.
|
||||||
0 if self == other, and > 0 if self > other.
|
|
||||||
|
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)
|
our = self.to_digestable(dns.name.root)
|
||||||
their = other.to_digestable(dns.name.root)
|
their = other.to_digestable(dns.name.root)
|
||||||
if our == their:
|
if our == their:
|
||||||
return 0
|
return 0
|
||||||
if our > their:
|
elif our > their:
|
||||||
return 1
|
return 1
|
||||||
|
else:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
@@ -253,42 +247,10 @@ class Rdata(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
||||||
"""Build an rdata object from text format.
|
|
||||||
|
|
||||||
@param rdclass: The rdata class
|
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: The rdata type
|
|
||||||
@type rdtype: int
|
|
||||||
@param tok: The tokenizer
|
|
||||||
@type tok: dns.tokenizer.Tokenizer
|
|
||||||
@param origin: The origin to use for relative names
|
|
||||||
@type origin: dns.name.Name
|
|
||||||
@param relativize: should names be relativized?
|
|
||||||
@type relativize: bool
|
|
||||||
@rtype: dns.rdata.Rdata instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
|
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
|
||||||
"""Build an rdata object from wire format
|
|
||||||
|
|
||||||
@param rdclass: The rdata class
|
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: The rdata type
|
|
||||||
@type rdtype: int
|
|
||||||
@param wire: The wire-format message
|
|
||||||
@type wire: string
|
|
||||||
@param current: The offset in wire of the beginning of the rdata.
|
|
||||||
@type current: int
|
|
||||||
@param rdlen: The length of the wire-format rdata
|
|
||||||
@type rdlen: int
|
|
||||||
@param origin: The origin to use for relative names
|
|
||||||
@type origin: dns.name.Name
|
|
||||||
@rtype: dns.rdata.Rdata instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def choose_relativity(self, origin=None, relativize=True):
|
def choose_relativity(self, origin=None, relativize=True):
|
||||||
@@ -296,12 +258,9 @@ class Rdata(object):
|
|||||||
relativization.
|
relativization.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class GenericRdata(Rdata):
|
class GenericRdata(Rdata):
|
||||||
|
|
||||||
"""Generate Rdata Class
|
"""Generic Rdata Class
|
||||||
|
|
||||||
This class is used for rdata types for which we have no better
|
This class is used for rdata types for which we have no better
|
||||||
implementation. It implements the DNS "unknown RRs" scheme.
|
implementation. It implements the DNS "unknown RRs" scheme.
|
||||||
@@ -319,7 +278,7 @@ class GenericRdata(Rdata):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
||||||
token = tok.get()
|
token = tok.get()
|
||||||
if not token.is_identifier() or token.value != '\#':
|
if not token.is_identifier() or token.value != r'\#':
|
||||||
raise dns.exception.SyntaxError(
|
raise dns.exception.SyntaxError(
|
||||||
r'generic rdata does not start with \#')
|
r'generic rdata does not start with \#')
|
||||||
length = tok.get_int()
|
length = tok.get_int()
|
||||||
@@ -345,11 +304,12 @@ class GenericRdata(Rdata):
|
|||||||
|
|
||||||
_rdata_modules = {}
|
_rdata_modules = {}
|
||||||
_module_prefix = 'dns.rdtypes'
|
_module_prefix = 'dns.rdtypes'
|
||||||
|
_import_lock = _threading.Lock()
|
||||||
|
|
||||||
def get_rdata_class(rdclass, rdtype):
|
def get_rdata_class(rdclass, rdtype):
|
||||||
|
|
||||||
def import_module(name):
|
def import_module(name):
|
||||||
|
with _import_lock:
|
||||||
mod = __import__(name)
|
mod = __import__(name)
|
||||||
components = name.split('.')
|
components = name.split('.')
|
||||||
for comp in components[1:]:
|
for comp in components[1:]:
|
||||||
@@ -392,20 +352,23 @@ def from_text(rdclass, rdtype, tok, origin=None, relativize=True):
|
|||||||
Once a class is chosen, its from_text() class method is called
|
Once a class is chosen, its from_text() class method is called
|
||||||
with the parameters to this function.
|
with the parameters to this function.
|
||||||
|
|
||||||
If I{tok} is a string, then a tokenizer is created and the string
|
If *tok* is a ``text``, then a tokenizer is created and the string
|
||||||
is used as its input.
|
is used as its input.
|
||||||
|
|
||||||
@param rdclass: The rdata class
|
*rdclass*, an ``int``, the rdataclass.
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: The rdata type
|
*rdtype*, an ``int``, the rdatatype.
|
||||||
@type rdtype: int
|
|
||||||
@param tok: The tokenizer or input text
|
*tok*, a ``dns.tokenizer.Tokenizer`` or a ``text``.
|
||||||
@type tok: dns.tokenizer.Tokenizer or string
|
|
||||||
@param origin: The origin to use for relative names
|
*origin*, a ``dns.name.Name`` (or ``None``), the
|
||||||
@type origin: dns.name.Name
|
origin to use for relative names.
|
||||||
@param relativize: Should names be relativized?
|
|
||||||
@type relativize: bool
|
*relativize*, a ``bool``. If true, name will be relativized to
|
||||||
@rtype: dns.rdata.Rdata instance"""
|
the specified origin.
|
||||||
|
|
||||||
|
Returns an instance of the chosen Rdata subclass.
|
||||||
|
"""
|
||||||
|
|
||||||
if isinstance(tok, string_types):
|
if isinstance(tok, string_types):
|
||||||
tok = dns.tokenizer.Tokenizer(tok)
|
tok = dns.tokenizer.Tokenizer(tok)
|
||||||
@@ -439,20 +402,55 @@ def from_wire(rdclass, rdtype, wire, current, rdlen, origin=None):
|
|||||||
Once a class is chosen, its from_wire() class method is called
|
Once a class is chosen, its from_wire() class method is called
|
||||||
with the parameters to this function.
|
with the parameters to this function.
|
||||||
|
|
||||||
@param rdclass: The rdata class
|
*rdclass*, an ``int``, the rdataclass.
|
||||||
@type rdclass: int
|
|
||||||
@param rdtype: The rdata type
|
*rdtype*, an ``int``, the rdatatype.
|
||||||
@type rdtype: int
|
|
||||||
@param wire: The wire-format message
|
*wire*, a ``binary``, the wire-format message.
|
||||||
@type wire: string
|
|
||||||
@param current: The offset in wire of the beginning of the rdata.
|
*current*, an ``int``, the offset in wire of the beginning of
|
||||||
@type current: int
|
the rdata.
|
||||||
@param rdlen: The length of the wire-format rdata
|
|
||||||
@type rdlen: int
|
*rdlen*, an ``int``, the length of the wire-format rdata
|
||||||
@param origin: The origin to use for relative names
|
|
||||||
@type origin: dns.name.Name
|
*origin*, a ``dns.name.Name`` (or ``None``). If not ``None``,
|
||||||
@rtype: dns.rdata.Rdata instance"""
|
then names will be relativized to this origin.
|
||||||
|
|
||||||
|
Returns an instance of the chosen Rdata subclass.
|
||||||
|
"""
|
||||||
|
|
||||||
wire = dns.wiredata.maybe_wrap(wire)
|
wire = dns.wiredata.maybe_wrap(wire)
|
||||||
cls = get_rdata_class(rdclass, rdtype)
|
cls = get_rdata_class(rdclass, rdtype)
|
||||||
return cls.from_wire(rdclass, rdtype, wire, current, rdlen, origin)
|
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)
|
||||||
|
|||||||
17
src/dns/rdata.pyi
Normal file
17
src/dns/rdata.pyi
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from 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):
|
||||||
|
...
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -13,15 +15,7 @@
|
|||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
"""DNS Rdata Classes.
|
"""DNS Rdata Classes."""
|
||||||
|
|
||||||
@var _by_text: The rdata class textual name to value mapping
|
|
||||||
@type _by_text: dict
|
|
||||||
@var _by_value: The rdata class value to textual name mapping
|
|
||||||
@type _by_value: dict
|
|
||||||
@var _metaclasses: If an rdataclass is a metaclass, there will be a mapping
|
|
||||||
whose key is the rdatatype value and whose value is True in this dictionary.
|
|
||||||
@type _metaclasses: dict"""
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -47,7 +41,7 @@ _by_text = {
|
|||||||
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
||||||
# would cause the mapping not to be true inverse.
|
# would cause the mapping not to be true inverse.
|
||||||
|
|
||||||
_by_value = dict((y, x) for x, y in _by_text.items())
|
_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
|
# Now that we've built the inverse map, we can add class aliases to
|
||||||
# the _by_text mapping.
|
# the _by_text mapping.
|
||||||
@@ -67,17 +61,22 @@ _unknown_class_pattern = re.compile('CLASS([0-9]+)$', re.I)
|
|||||||
|
|
||||||
|
|
||||||
class UnknownRdataclass(dns.exception.DNSException):
|
class UnknownRdataclass(dns.exception.DNSException):
|
||||||
|
|
||||||
"""A DNS class is unknown."""
|
"""A DNS class is unknown."""
|
||||||
|
|
||||||
|
|
||||||
def from_text(text):
|
def from_text(text):
|
||||||
"""Convert text into a DNS rdata class value.
|
"""Convert text into a DNS rdata class value.
|
||||||
@param text: the text
|
|
||||||
@type text: string
|
The input text can be a defined DNS RR class mnemonic or
|
||||||
@rtype: int
|
instance of the DNS generic class syntax.
|
||||||
@raises dns.rdataclass.UnknownRdataclass: the class is unknown
|
|
||||||
@raises ValueError: the rdata class value is not >= 0 and <= 65535
|
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())
|
value = _by_text.get(text.upper())
|
||||||
@@ -92,11 +91,14 @@ def from_text(text):
|
|||||||
|
|
||||||
|
|
||||||
def to_text(value):
|
def to_text(value):
|
||||||
"""Convert a DNS rdata class to text.
|
"""Convert a DNS rdata type value to text.
|
||||||
@param value: the rdata class value
|
|
||||||
@type value: int
|
If the value has a known mnemonic, it will be used, otherwise the
|
||||||
@rtype: string
|
DNS generic class syntax will be used.
|
||||||
@raises ValueError: the rdata class value is not >= 0 and <= 65535
|
|
||||||
|
Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535.
|
||||||
|
|
||||||
|
Returns a ``str``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if value < 0 or value > 65535:
|
if value < 0 or value > 65535:
|
||||||
@@ -108,10 +110,12 @@ def to_text(value):
|
|||||||
|
|
||||||
|
|
||||||
def is_metaclass(rdclass):
|
def is_metaclass(rdclass):
|
||||||
"""True if the class is a metaclass.
|
"""True if the specified class is a metaclass.
|
||||||
@param rdclass: the rdata class
|
|
||||||
@type rdclass: int
|
The currently defined metaclasses are ANY and NONE.
|
||||||
@rtype: bool"""
|
|
||||||
|
*rdclass* is an ``int``.
|
||||||
|
"""
|
||||||
|
|
||||||
if rdclass in _metaclasses:
|
if rdclass in _metaclasses:
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -31,50 +33,37 @@ SimpleSet = dns.set.Set
|
|||||||
|
|
||||||
|
|
||||||
class DifferingCovers(dns.exception.DNSException):
|
class DifferingCovers(dns.exception.DNSException):
|
||||||
|
|
||||||
"""An attempt was made to add a DNS SIG/RRSIG whose covered type
|
"""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."""
|
is not the same as that of the other rdatas in the rdataset."""
|
||||||
|
|
||||||
|
|
||||||
class IncompatibleTypes(dns.exception.DNSException):
|
class IncompatibleTypes(dns.exception.DNSException):
|
||||||
|
|
||||||
"""An attempt was made to add DNS RR data of an incompatible type."""
|
"""An attempt was made to add DNS RR data of an incompatible type."""
|
||||||
|
|
||||||
|
|
||||||
class Rdataset(dns.set.Set):
|
class Rdataset(dns.set.Set):
|
||||||
|
|
||||||
"""A DNS rdataset.
|
"""A DNS rdataset."""
|
||||||
|
|
||||||
@ivar rdclass: The class of the rdataset
|
|
||||||
@type rdclass: int
|
|
||||||
@ivar rdtype: The type of the rdataset
|
|
||||||
@type rdtype: int
|
|
||||||
@ivar covers: 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.
|
|
||||||
@type covers: int
|
|
||||||
@ivar ttl: The DNS TTL (Time To Live) value
|
|
||||||
@type ttl: int
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
|
__slots__ = ['rdclass', 'rdtype', 'covers', 'ttl']
|
||||||
|
|
||||||
def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE):
|
def __init__(self, rdclass, rdtype, covers=dns.rdatatype.NONE, ttl=0):
|
||||||
"""Create a new rdataset of the specified class and type.
|
"""Create a new rdataset of the specified class and type.
|
||||||
|
|
||||||
@see: the description of the class instance variables for the
|
*rdclass*, an ``int``, the rdataclass.
|
||||||
meaning of I{rdclass} and I{rdtype}"""
|
|
||||||
|
*rdtype*, an ``int``, the rdatatype.
|
||||||
|
|
||||||
|
*covers*, an ``int``, the covered rdatatype.
|
||||||
|
|
||||||
|
*ttl*, an ``int``, the TTL.
|
||||||
|
"""
|
||||||
|
|
||||||
super(Rdataset, self).__init__()
|
super(Rdataset, self).__init__()
|
||||||
self.rdclass = rdclass
|
self.rdclass = rdclass
|
||||||
self.rdtype = rdtype
|
self.rdtype = rdtype
|
||||||
self.covers = covers
|
self.covers = covers
|
||||||
self.ttl = 0
|
self.ttl = ttl
|
||||||
|
|
||||||
def _clone(self):
|
def _clone(self):
|
||||||
obj = super(Rdataset, self)._clone()
|
obj = super(Rdataset, self)._clone()
|
||||||
@@ -85,11 +74,14 @@ class Rdataset(dns.set.Set):
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
def update_ttl(self, ttl):
|
def update_ttl(self, ttl):
|
||||||
"""Set the TTL of the rdataset to be the lesser of the set's current
|
"""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
|
TTL or the specified TTL. If the set contains no rdatas, set the TTL
|
||||||
to the specified TTL.
|
to the specified TTL.
|
||||||
@param ttl: The TTL
|
|
||||||
@type ttl: int"""
|
*ttl*, an ``int``.
|
||||||
|
"""
|
||||||
|
|
||||||
if len(self) == 0:
|
if len(self) == 0:
|
||||||
self.ttl = ttl
|
self.ttl = ttl
|
||||||
@@ -99,13 +91,19 @@ class Rdataset(dns.set.Set):
|
|||||||
def add(self, rd, ttl=None):
|
def add(self, rd, ttl=None):
|
||||||
"""Add the specified rdata to the rdataset.
|
"""Add the specified rdata to the rdataset.
|
||||||
|
|
||||||
If the optional I{ttl} parameter is supplied, then
|
If the optional *ttl* parameter is supplied, then
|
||||||
self.update_ttl(ttl) will be called prior to adding the rdata.
|
``self.update_ttl(ttl)`` will be called prior to adding the rdata.
|
||||||
|
|
||||||
@param rd: The rdata
|
*rd*, a ``dns.rdata.Rdata``, the rdata
|
||||||
@type rd: dns.rdata.Rdata object
|
|
||||||
@param ttl: The TTL
|
*ttl*, an ``int``, the TTL.
|
||||||
@type ttl: int"""
|
|
||||||
|
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
|
# If we're adding a signature, do some special handling to
|
||||||
@@ -139,8 +137,9 @@ class Rdataset(dns.set.Set):
|
|||||||
def update(self, other):
|
def update(self, other):
|
||||||
"""Add all rdatas in other to self.
|
"""Add all rdatas in other to self.
|
||||||
|
|
||||||
@param other: The rdataset from which to update
|
*other*, a ``dns.rdataset.Rdataset``, the rdataset from which
|
||||||
@type other: dns.rdataset.Rdataset object"""
|
to update.
|
||||||
|
"""
|
||||||
|
|
||||||
self.update_ttl(other.ttl)
|
self.update_ttl(other.ttl)
|
||||||
super(Rdataset, self).update(other)
|
super(Rdataset, self).update(other)
|
||||||
@@ -157,10 +156,6 @@ class Rdataset(dns.set.Set):
|
|||||||
return self.to_text()
|
return self.to_text()
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Two rdatasets are equal if they have the same class, type, and
|
|
||||||
covers, and contain the same rdata.
|
|
||||||
@rtype: bool"""
|
|
||||||
|
|
||||||
if not isinstance(other, Rdataset):
|
if not isinstance(other, Rdataset):
|
||||||
return False
|
return False
|
||||||
if self.rdclass != other.rdclass or \
|
if self.rdclass != other.rdclass or \
|
||||||
@@ -176,20 +171,23 @@ class Rdataset(dns.set.Set):
|
|||||||
override_rdclass=None, **kw):
|
override_rdclass=None, **kw):
|
||||||
"""Convert the rdataset into DNS master file format.
|
"""Convert the rdataset into DNS master file format.
|
||||||
|
|
||||||
@see: L{dns.name.Name.choose_relativity} for more information
|
See ``dns.name.Name.choose_relativity`` for more information
|
||||||
on how I{origin} and I{relativize} determine the way names
|
on how *origin* and *relativize* determine the way names
|
||||||
are emitted.
|
are emitted.
|
||||||
|
|
||||||
Any additional keyword arguments are passed on to the rdata
|
Any additional keyword arguments are passed on to the rdata
|
||||||
to_text() method.
|
``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*.
|
||||||
|
"""
|
||||||
|
|
||||||
@param name: If name is not None, emit a RRs with I{name} as
|
|
||||||
the owner name.
|
|
||||||
@type name: dns.name.Name object
|
|
||||||
@param origin: The origin for relative names, or None.
|
|
||||||
@type origin: dns.name.Name object
|
|
||||||
@param relativize: True if names should names be relativized
|
|
||||||
@type relativize: bool"""
|
|
||||||
if name is not None:
|
if name is not None:
|
||||||
name = name.choose_relativity(origin, relativize)
|
name = name.choose_relativity(origin, relativize)
|
||||||
ntext = str(name)
|
ntext = str(name)
|
||||||
@@ -208,7 +206,7 @@ class Rdataset(dns.set.Set):
|
|||||||
# some dynamic updates, so we don't need to print out the TTL
|
# some dynamic updates, so we don't need to print out the TTL
|
||||||
# (which is meaningless anyway).
|
# (which is meaningless anyway).
|
||||||
#
|
#
|
||||||
s.write(u'%s%s%s %s\n' % (ntext, pad,
|
s.write(u'{}{}{} {}\n'.format(ntext, pad,
|
||||||
dns.rdataclass.to_text(rdclass),
|
dns.rdataclass.to_text(rdclass),
|
||||||
dns.rdatatype.to_text(self.rdtype)))
|
dns.rdatatype.to_text(self.rdtype)))
|
||||||
else:
|
else:
|
||||||
@@ -227,16 +225,26 @@ class Rdataset(dns.set.Set):
|
|||||||
override_rdclass=None, want_shuffle=True):
|
override_rdclass=None, want_shuffle=True):
|
||||||
"""Convert the rdataset to wire format.
|
"""Convert the rdataset to wire format.
|
||||||
|
|
||||||
@param name: The owner name of the RRset that will be emitted
|
*name*, a ``dns.name.Name`` is the owner name to use.
|
||||||
@type name: dns.name.Name object
|
|
||||||
@param file: The file to which the wire format data will be appended
|
*file* is the file where the name is emitted (typically a
|
||||||
@type file: file
|
BytesIO file).
|
||||||
@param compress: The compression table to use; the default is None.
|
|
||||||
@type compress: dict
|
*compress*, a ``dict``, is the compression table to use. If
|
||||||
@param origin: The origin to be appended to any relative names when
|
``None`` (the default), names will not be compressed.
|
||||||
they are emitted. The default is None.
|
|
||||||
@returns: the number of records emitted
|
*origin* is a ``dns.name.Name`` or ``None``. If the name is
|
||||||
@rtype: int
|
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:
|
if override_rdclass is not None:
|
||||||
@@ -272,8 +280,9 @@ class Rdataset(dns.set.Set):
|
|||||||
return len(self)
|
return len(self)
|
||||||
|
|
||||||
def match(self, rdclass, rdtype, covers):
|
def match(self, rdclass, rdtype, covers):
|
||||||
"""Returns True if this rdataset matches the specified class, type,
|
"""Returns ``True`` if this rdataset matches the specified class,
|
||||||
and covers"""
|
type, and covers.
|
||||||
|
"""
|
||||||
if self.rdclass == rdclass and \
|
if self.rdclass == rdclass and \
|
||||||
self.rdtype == rdtype and \
|
self.rdtype == rdtype and \
|
||||||
self.covers == covers:
|
self.covers == covers:
|
||||||
@@ -285,7 +294,7 @@ def from_text_list(rdclass, rdtype, ttl, text_rdatas):
|
|||||||
"""Create an rdataset with the specified class, type, and TTL, and with
|
"""Create an rdataset with the specified class, type, and TTL, and with
|
||||||
the specified list of rdatas in text format.
|
the specified list of rdatas in text format.
|
||||||
|
|
||||||
@rtype: dns.rdataset.Rdataset object
|
Returns a ``dns.rdataset.Rdataset`` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(rdclass, string_types):
|
if isinstance(rdclass, string_types):
|
||||||
@@ -304,7 +313,7 @@ def from_text(rdclass, rdtype, ttl, *text_rdatas):
|
|||||||
"""Create an rdataset with the specified class, type, and TTL, and with
|
"""Create an rdataset with the specified class, type, and TTL, and with
|
||||||
the specified rdatas in text format.
|
the specified rdatas in text format.
|
||||||
|
|
||||||
@rtype: dns.rdataset.Rdataset object
|
Returns a ``dns.rdataset.Rdataset`` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return from_text_list(rdclass, rdtype, ttl, text_rdatas)
|
return from_text_list(rdclass, rdtype, ttl, text_rdatas)
|
||||||
@@ -314,7 +323,7 @@ def from_rdata_list(ttl, rdatas):
|
|||||||
"""Create an rdataset with the specified TTL, and with
|
"""Create an rdataset with the specified TTL, and with
|
||||||
the specified list of rdata objects.
|
the specified list of rdata objects.
|
||||||
|
|
||||||
@rtype: dns.rdataset.Rdataset object
|
Returns a ``dns.rdataset.Rdataset`` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if len(rdatas) == 0:
|
if len(rdatas) == 0:
|
||||||
@@ -332,7 +341,7 @@ def from_rdata(ttl, *rdatas):
|
|||||||
"""Create an rdataset with the specified TTL, and with
|
"""Create an rdataset with the specified TTL, and with
|
||||||
the specified rdata objects.
|
the specified rdata objects.
|
||||||
|
|
||||||
@rtype: dns.rdataset.Rdataset object
|
Returns a ``dns.rdataset.Rdataset`` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return from_rdata_list(ttl, rdatas)
|
return from_rdata_list(ttl, rdatas)
|
||||||
|
|||||||
58
src/dns/rdataset.pyi
Normal file
58
src/dns/rdataset.pyi
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
from 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:
|
||||||
|
...
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -13,18 +15,7 @@
|
|||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
"""DNS Rdata Types.
|
"""DNS Rdata Types."""
|
||||||
|
|
||||||
@var _by_text: The rdata type textual name to value mapping
|
|
||||||
@type _by_text: dict
|
|
||||||
@var _by_value: The rdata type value to textual name mapping
|
|
||||||
@type _by_value: dict
|
|
||||||
@var _metatypes: If an rdatatype is a metatype, there will be a mapping
|
|
||||||
whose key is the rdatatype value and whose value is True in this dictionary.
|
|
||||||
@type _metatypes: dict
|
|
||||||
@var _singletons: If an rdatatype is a singleton, there will be a mapping
|
|
||||||
whose key is the rdatatype value and whose value is True in this dictionary.
|
|
||||||
@type _singletons: dict"""
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -82,6 +73,7 @@ TLSA = 52
|
|||||||
HIP = 55
|
HIP = 55
|
||||||
CDS = 59
|
CDS = 59
|
||||||
CDNSKEY = 60
|
CDNSKEY = 60
|
||||||
|
OPENPGPKEY = 61
|
||||||
CSYNC = 62
|
CSYNC = 62
|
||||||
SPF = 99
|
SPF = 99
|
||||||
UNSPEC = 103
|
UNSPEC = 103
|
||||||
@@ -153,6 +145,7 @@ _by_text = {
|
|||||||
'HIP': HIP,
|
'HIP': HIP,
|
||||||
'CDS': CDS,
|
'CDS': CDS,
|
||||||
'CDNSKEY': CDNSKEY,
|
'CDNSKEY': CDNSKEY,
|
||||||
|
'OPENPGPKEY': OPENPGPKEY,
|
||||||
'CSYNC': CSYNC,
|
'CSYNC': CSYNC,
|
||||||
'SPF': SPF,
|
'SPF': SPF,
|
||||||
'UNSPEC': UNSPEC,
|
'UNSPEC': UNSPEC,
|
||||||
@@ -176,8 +169,7 @@ _by_text = {
|
|||||||
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
||||||
# would cause the mapping not to be true inverse.
|
# would cause the mapping not to be true inverse.
|
||||||
|
|
||||||
_by_value = dict((y, x) for x, y in _by_text.items())
|
_by_value = {y: x for x, y in _by_text.items()}
|
||||||
|
|
||||||
|
|
||||||
_metatypes = {
|
_metatypes = {
|
||||||
OPT: True
|
OPT: True
|
||||||
@@ -188,24 +180,30 @@ _singletons = {
|
|||||||
NXT: True,
|
NXT: True,
|
||||||
DNAME: True,
|
DNAME: True,
|
||||||
NSEC: True,
|
NSEC: True,
|
||||||
# CNAME is technically a singleton, but we allow multiple CNAMEs.
|
CNAME: True,
|
||||||
}
|
}
|
||||||
|
|
||||||
_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I)
|
_unknown_type_pattern = re.compile('TYPE([0-9]+)$', re.I)
|
||||||
|
|
||||||
|
|
||||||
class UnknownRdatatype(dns.exception.DNSException):
|
class UnknownRdatatype(dns.exception.DNSException):
|
||||||
|
|
||||||
"""DNS resource record type is unknown."""
|
"""DNS resource record type is unknown."""
|
||||||
|
|
||||||
|
|
||||||
def from_text(text):
|
def from_text(text):
|
||||||
"""Convert text into a DNS rdata type value.
|
"""Convert text into a DNS rdata type value.
|
||||||
@param text: the text
|
|
||||||
@type text: string
|
The input text can be a defined DNS RR type mnemonic or
|
||||||
@raises dns.rdatatype.UnknownRdatatype: the type is unknown
|
instance of the DNS generic type syntax.
|
||||||
@raises ValueError: the rdata type value is not >= 0 and <= 65535
|
|
||||||
@rtype: int"""
|
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())
|
value = _by_text.get(text.upper())
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -219,11 +217,15 @@ def from_text(text):
|
|||||||
|
|
||||||
|
|
||||||
def to_text(value):
|
def to_text(value):
|
||||||
"""Convert a DNS rdata type to text.
|
"""Convert a DNS rdata type value to text.
|
||||||
@param value: the rdata type value
|
|
||||||
@type value: int
|
If the value has a known mnemonic, it will be used, otherwise the
|
||||||
@raises ValueError: the rdata type value is not >= 0 and <= 65535
|
DNS generic type syntax will be used.
|
||||||
@rtype: string"""
|
|
||||||
|
Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
|
||||||
|
|
||||||
|
Returns a ``str``.
|
||||||
|
"""
|
||||||
|
|
||||||
if value < 0 or value > 65535:
|
if value < 0 or value > 65535:
|
||||||
raise ValueError("type must be between >= 0 and <= 65535")
|
raise ValueError("type must be between >= 0 and <= 65535")
|
||||||
@@ -234,10 +236,15 @@ def to_text(value):
|
|||||||
|
|
||||||
|
|
||||||
def is_metatype(rdtype):
|
def is_metatype(rdtype):
|
||||||
"""True if the type is a metatype.
|
"""True if the specified type is a metatype.
|
||||||
@param rdtype: the type
|
|
||||||
@type rdtype: int
|
*rdtype* is an ``int``.
|
||||||
@rtype: bool"""
|
|
||||||
|
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:
|
if rdtype >= TKEY and rdtype <= ANY or rdtype in _metatypes:
|
||||||
return True
|
return True
|
||||||
@@ -245,11 +252,36 @@ def is_metatype(rdtype):
|
|||||||
|
|
||||||
|
|
||||||
def is_singleton(rdtype):
|
def is_singleton(rdtype):
|
||||||
"""True if the type is a singleton.
|
"""Is the specified type a singleton type?
|
||||||
@param rdtype: the type
|
|
||||||
@type rdtype: int
|
Singleton types can only have a single rdata in an rdataset, or a single
|
||||||
@rtype: bool"""
|
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:
|
if rdtype in _singletons:
|
||||||
return True
|
return True
|
||||||
return False
|
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
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2016 Nominum, Inc.
|
# Copyright (C) 2016 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc.
|
# Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2009-2011 Nominum, Inc.
|
# Copyright (C) 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -80,7 +82,7 @@ class GPOS(dns.rdata.Rdata):
|
|||||||
self.altitude = altitude
|
self.altitude = altitude
|
||||||
|
|
||||||
def to_text(self, origin=None, relativize=True, **kw):
|
def to_text(self, origin=None, relativize=True, **kw):
|
||||||
return '%s %s %s' % (self.latitude.decode(),
|
return '{} {} {}'.format(self.latitude.decode(),
|
||||||
self.longitude.decode(),
|
self.longitude.decode(),
|
||||||
self.altitude.decode())
|
self.altitude.decode())
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -45,7 +47,7 @@ class HINFO(dns.rdata.Rdata):
|
|||||||
self.os = os
|
self.os = os
|
||||||
|
|
||||||
def to_text(self, origin=None, relativize=True, **kw):
|
def to_text(self, origin=None, relativize=True, **kw):
|
||||||
return '"%s" "%s"' % (dns.rdata._escapify(self.cpu),
|
return '"{}" "{}"'.format(dns.rdata._escapify(self.cpu),
|
||||||
dns.rdata._escapify(self.os))
|
dns.rdata._escapify(self.os))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2010, 2011 Nominum, Inc.
|
# Copyright (C) 2010, 2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -46,7 +48,7 @@ class ISDN(dns.rdata.Rdata):
|
|||||||
|
|
||||||
def to_text(self, origin=None, relativize=True, **kw):
|
def to_text(self, origin=None, relativize=True, **kw):
|
||||||
if self.subaddress:
|
if self.subaddress:
|
||||||
return '"%s" "%s"' % (dns.rdata._escapify(self.address),
|
return '"{}" "{}"'.format(dns.rdata._escapify(self.address),
|
||||||
dns.rdata._escapify(self.subaddress))
|
dns.rdata._escapify(self.subaddress))
|
||||||
else:
|
else:
|
||||||
return '"%s"' % dns.rdata._escapify(self.address)
|
return '"%s"' % dns.rdata._escapify(self.address)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -156,7 +158,7 @@ class LOC(dns.rdata.Rdata):
|
|||||||
if self.size != _default_size or \
|
if self.size != _default_size or \
|
||||||
self.horizontal_precision != _default_hprec or \
|
self.horizontal_precision != _default_hprec or \
|
||||||
self.vertical_precision != _default_vprec:
|
self.vertical_precision != _default_vprec:
|
||||||
text += " %0.2fm %0.2fm %0.2fm" % (
|
text += " {:0.2f}m {:0.2f}m {:0.2f}m".format(
|
||||||
self.size / 100.0, self.horizontal_precision / 100.0,
|
self.size / 100.0, self.horizontal_precision / 100.0,
|
||||||
self.vertical_precision / 100.0
|
self.vertical_precision / 100.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -50,7 +52,7 @@ class NSEC(dns.rdata.Rdata):
|
|||||||
bits.append(dns.rdatatype.to_text(window * 256 +
|
bits.append(dns.rdatatype.to_text(window * 256 +
|
||||||
i * 8 + j))
|
i * 8 + j))
|
||||||
text += (' ' + ' '.join(bits))
|
text += (' ' + ' '.join(bits))
|
||||||
return '%s%s' % (next, text)
|
return '{}{}'.format(next, text)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -21,18 +23,21 @@ import struct
|
|||||||
import dns.exception
|
import dns.exception
|
||||||
import dns.rdata
|
import dns.rdata
|
||||||
import dns.rdatatype
|
import dns.rdatatype
|
||||||
from dns._compat import xrange, text_type
|
from dns._compat import xrange, text_type, PY3
|
||||||
|
|
||||||
try:
|
# pylint: disable=deprecated-string-function
|
||||||
b32_hex_to_normal = string.maketrans('0123456789ABCDEFGHIJKLMNOPQRSTUV',
|
if PY3:
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
|
|
||||||
b32_normal_to_hex = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
|
||||||
'0123456789ABCDEFGHIJKLMNOPQRSTUV')
|
|
||||||
except AttributeError:
|
|
||||||
b32_hex_to_normal = bytes.maketrans(b'0123456789ABCDEFGHIJKLMNOPQRSTUV',
|
b32_hex_to_normal = bytes.maketrans(b'0123456789ABCDEFGHIJKLMNOPQRSTUV',
|
||||||
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
|
b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
|
||||||
b32_normal_to_hex = bytes.maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
b32_normal_to_hex = bytes.maketrans(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
||||||
b'0123456789ABCDEFGHIJKLMNOPQRSTUV')
|
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
|
# hash algorithm constants
|
||||||
SHA1 = 1
|
SHA1 = 1
|
||||||
@@ -130,7 +135,7 @@ class NSEC3(dns.rdata.Rdata):
|
|||||||
new_window = nrdtype // 256
|
new_window = nrdtype // 256
|
||||||
if new_window != window:
|
if new_window != window:
|
||||||
if octets != 0:
|
if octets != 0:
|
||||||
windows.append((window, ''.join(bitmap[0:octets])))
|
windows.append((window, bitmap[0:octets]))
|
||||||
bitmap = bytearray(b'\0' * 32)
|
bitmap = bytearray(b'\0' * 32)
|
||||||
window = new_window
|
window = new_window
|
||||||
offset = nrdtype % 256
|
offset = nrdtype % 256
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
60
src/dns/rdtypes/ANY/OPENPGPKEY.py
Normal file
60
src/dns/rdtypes/ANY/OPENPGPKEY.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# 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)
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -39,7 +41,7 @@ class RP(dns.rdata.Rdata):
|
|||||||
def to_text(self, origin=None, relativize=True, **kw):
|
def to_text(self, origin=None, relativize=True, **kw):
|
||||||
mbox = self.mbox.choose_relativity(origin, relativize)
|
mbox = self.mbox.choose_relativity(origin, relativize)
|
||||||
txt = self.txt.choose_relativity(origin, relativize)
|
txt = self.txt.choose_relativity(origin, relativize)
|
||||||
return "%s %s" % (str(mbox), str(txt))
|
return "{} {}".format(str(mbox), str(txt))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
# Copyright (C) 2015 Red Hat, Inc.
|
# Copyright (C) 2015 Red Hat, Inc.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -17,10 +19,13 @@
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AFSDB',
|
'AFSDB',
|
||||||
|
'AVC',
|
||||||
|
'CAA',
|
||||||
'CDNSKEY',
|
'CDNSKEY',
|
||||||
'CDS',
|
'CDS',
|
||||||
'CERT',
|
'CERT',
|
||||||
'CNAME',
|
'CNAME',
|
||||||
|
'CSYNC',
|
||||||
'DLV',
|
'DLV',
|
||||||
'DNAME',
|
'DNAME',
|
||||||
'DNSKEY',
|
'DNSKEY',
|
||||||
@@ -37,7 +42,7 @@ __all__ = [
|
|||||||
'NSEC',
|
'NSEC',
|
||||||
'NSEC3',
|
'NSEC3',
|
||||||
'NSEC3PARAM',
|
'NSEC3PARAM',
|
||||||
'TLSA',
|
'OPENPGPKEY',
|
||||||
'PTR',
|
'PTR',
|
||||||
'RP',
|
'RP',
|
||||||
'RRSIG',
|
'RRSIG',
|
||||||
@@ -45,6 +50,8 @@ __all__ = [
|
|||||||
'SOA',
|
'SOA',
|
||||||
'SPF',
|
'SPF',
|
||||||
'SSHFP',
|
'SSHFP',
|
||||||
|
'TLSA',
|
||||||
'TXT',
|
'TXT',
|
||||||
|
'URI',
|
||||||
'X25',
|
'X25',
|
||||||
]
|
]
|
||||||
|
|||||||
70
src/dns/rdtypes/CH/A.py
Normal file
70
src/dns/rdtypes/CH/A.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# 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)
|
||||||
22
src/dns/rdtypes/CH/__init__.py
Normal file
22
src/dns/rdtypes/CH/__init__.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# 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',
|
||||||
|
]
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -48,5 +50,5 @@ class A(dns.rdata.Rdata):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
|
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
|
||||||
address = dns.ipv4.inet_ntoa(wire[current: current + rdlen]).decode()
|
address = dns.ipv4.inet_ntoa(wire[current: current + rdlen])
|
||||||
return cls(rdclass, rdtype, address)
|
return cls(rdclass, rdtype, address)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# 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
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose with or without fee is hereby granted,
|
# documentation for any purpose with or without fee is hereby granted,
|
||||||
@@ -13,14 +15,15 @@
|
|||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
import struct
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import codecs
|
||||||
|
import struct
|
||||||
|
|
||||||
import dns.exception
|
import dns.exception
|
||||||
import dns.inet
|
import dns.inet
|
||||||
import dns.rdata
|
import dns.rdata
|
||||||
import dns.tokenizer
|
import dns.tokenizer
|
||||||
from dns._compat import xrange
|
from dns._compat import xrange, maybe_chr
|
||||||
|
|
||||||
|
|
||||||
class APLItem(object):
|
class APLItem(object):
|
||||||
@@ -63,7 +66,7 @@ class APLItem(object):
|
|||||||
#
|
#
|
||||||
last = 0
|
last = 0
|
||||||
for i in xrange(len(address) - 1, -1, -1):
|
for i in xrange(len(address) - 1, -1, -1):
|
||||||
if address[i] != chr(0):
|
if address[i] != maybe_chr(0):
|
||||||
last = i + 1
|
last = i + 1
|
||||||
break
|
break
|
||||||
address = address[0: last]
|
address = address[0: last]
|
||||||
@@ -121,6 +124,7 @@ class APL(dns.rdata.Rdata):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
|
def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
while 1:
|
while 1:
|
||||||
if rdlen == 0:
|
if rdlen == 0:
|
||||||
@@ -142,18 +146,18 @@ class APL(dns.rdata.Rdata):
|
|||||||
l = len(address)
|
l = len(address)
|
||||||
if header[0] == 1:
|
if header[0] == 1:
|
||||||
if l < 4:
|
if l < 4:
|
||||||
address += '\x00' * (4 - l)
|
address += b'\x00' * (4 - l)
|
||||||
address = dns.inet.inet_ntop(dns.inet.AF_INET, address)
|
address = dns.inet.inet_ntop(dns.inet.AF_INET, address)
|
||||||
elif header[0] == 2:
|
elif header[0] == 2:
|
||||||
if l < 16:
|
if l < 16:
|
||||||
address += '\x00' * (16 - l)
|
address += b'\x00' * (16 - l)
|
||||||
address = dns.inet.inet_ntop(dns.inet.AF_INET6, address)
|
address = dns.inet.inet_ntop(dns.inet.AF_INET6, address)
|
||||||
else:
|
else:
|
||||||
#
|
#
|
||||||
# This isn't really right according to the RFC, but it
|
# This isn't really right according to the RFC, but it
|
||||||
# seems better than throwing an exception
|
# seems better than throwing an exception
|
||||||
#
|
#
|
||||||
address = address.encode('hex_codec')
|
address = codecs.encode(address, 'hex_codec')
|
||||||
current += afdlen
|
current += afdlen
|
||||||
rdlen -= afdlen
|
rdlen -= afdlen
|
||||||
item = APLItem(header[0], negation, address, header[1])
|
item = APLItem(header[0], negation, address, header[1])
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -20,6 +22,7 @@ __all__ = [
|
|||||||
'AAAA',
|
'AAAA',
|
||||||
'APL',
|
'APL',
|
||||||
'DHCID',
|
'DHCID',
|
||||||
|
'IPSECKEY',
|
||||||
'KX',
|
'KX',
|
||||||
'NAPTR',
|
'NAPTR',
|
||||||
'NSAP',
|
'NSAP',
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -18,6 +20,7 @@
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
'ANY',
|
'ANY',
|
||||||
'IN',
|
'IN',
|
||||||
|
'CH',
|
||||||
'euibase',
|
'euibase',
|
||||||
'mxbase',
|
'mxbase',
|
||||||
'nsbase',
|
'nsbase',
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
@@ -38,7 +40,7 @@ _flag_by_text = {
|
|||||||
# We construct the inverse mapping programmatically to ensure that we
|
# We construct the inverse mapping programmatically to ensure that we
|
||||||
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
# cannot make any mistakes (e.g. omissions, cut-and-paste errors) that
|
||||||
# would cause the mapping not to be true inverse.
|
# would cause the mapping not to be true inverse.
|
||||||
_flag_by_value = dict((y, x) for x, y in _flag_by_text.items())
|
_flag_by_value = {y: x for x, y in _flag_by_text.items()}
|
||||||
|
|
||||||
|
|
||||||
def flags_to_text_set(flags):
|
def flags_to_text_set(flags):
|
||||||
|
|||||||
37
src/dns/rdtypes/dnskeybase.pyi
Normal file
37
src/dns/rdtypes/dnskeybase.pyi
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from 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]:
|
||||||
|
...
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2010, 2011 Nominum, Inc.
|
# Copyright (C) 2010, 2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
|
||||||
|
|
||||||
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user