diff options
Diffstat (limited to 'source4/scripting/python/samba_external/dnspython/dns/resolver.py')
-rw-r--r-- | source4/scripting/python/samba_external/dnspython/dns/resolver.py | 761 |
1 files changed, 0 insertions, 761 deletions
diff --git a/source4/scripting/python/samba_external/dnspython/dns/resolver.py b/source4/scripting/python/samba_external/dnspython/dns/resolver.py deleted file mode 100644 index 372d7d8361..0000000000 --- a/source4/scripting/python/samba_external/dnspython/dns/resolver.py +++ /dev/null @@ -1,761 +0,0 @@ -# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS stub resolver. - -@var default_resolver: The default resolver object -@type default_resolver: dns.resolver.Resolver object""" - -import socket -import sys -import time - -import dns.exception -import dns.message -import dns.name -import dns.query -import dns.rcode -import dns.rdataclass -import dns.rdatatype - -if sys.platform == 'win32': - import _winreg - -class NXDOMAIN(dns.exception.DNSException): - """The query name does not exist.""" - pass - -# The definition of the Timeout exception has moved from here to the -# dns.exception module. We keep dns.resolver.Timeout defined for -# backwards compatibility. - -Timeout = dns.exception.Timeout - -class NoAnswer(dns.exception.DNSException): - """The response did not contain an answer to the question.""" - pass - -class NoNameservers(dns.exception.DNSException): - """No non-broken nameservers are available to answer the query.""" - pass - -class NotAbsolute(dns.exception.DNSException): - """Raised if an absolute domain name is required but a relative name - was provided.""" - pass - -class NoRootSOA(dns.exception.DNSException): - """Raised if for some reason there is no SOA at the root name. - This should never happen!""" - pass - - -class Answer(object): - """DNS stub resolver answer - - Instances of this class bundle up the result of a successful DNS - resolution. - - For convenience, the answer object implements much of the sequence - protocol, forwarding to its rrset. E.g. "for a in answer" is - equivalent to "for a in answer.rrset", "answer[i]" is equivalent - to "answer.rrset[i]", and "answer[i:j]" is equivalent to - "answer.rrset[i:j]". - - Note that CNAMEs or DNAMEs in the response may mean that answer - node's name might not be the query name. - - @ivar qname: The query name - @type qname: dns.name.Name object - @ivar rdtype: The query type - @type rdtype: int - @ivar rdclass: The query class - @type rdclass: int - @ivar response: The response message - @type response: dns.message.Message object - @ivar rrset: The answer - @type rrset: dns.rrset.RRset object - @ivar expiration: The time when the answer expires - @type expiration: float (seconds since the epoch) - """ - def __init__(self, qname, rdtype, rdclass, response): - self.qname = qname - self.rdtype = rdtype - self.rdclass = rdclass - self.response = response - min_ttl = -1 - rrset = None - for count in xrange(0, 15): - try: - rrset = response.find_rrset(response.answer, qname, - rdclass, rdtype) - if min_ttl == -1 or rrset.ttl < min_ttl: - min_ttl = rrset.ttl - break - except KeyError: - if rdtype != dns.rdatatype.CNAME: - try: - crrset = response.find_rrset(response.answer, - qname, - rdclass, - dns.rdatatype.CNAME) - if min_ttl == -1 or crrset.ttl < min_ttl: - min_ttl = crrset.ttl - for rd in crrset: - qname = rd.target - break - continue - except KeyError: - raise NoAnswer - raise NoAnswer - if rrset is None: - raise NoAnswer - self.rrset = rrset - self.expiration = time.time() + min_ttl - - def __getattr__(self, attr): - if attr == 'name': - return self.rrset.name - elif attr == 'ttl': - return self.rrset.ttl - elif attr == 'covers': - return self.rrset.covers - elif attr == 'rdclass': - return self.rrset.rdclass - elif attr == 'rdtype': - return self.rrset.rdtype - else: - raise AttributeError(attr) - - def __len__(self): - return len(self.rrset) - - def __iter__(self): - return iter(self.rrset) - - def __getitem__(self, i): - return self.rrset[i] - - def __delitem__(self, i): - del self.rrset[i] - - def __getslice__(self, i, j): - return self.rrset[i:j] - - def __delslice__(self, i, j): - del self.rrset[i:j] - -class Cache(object): - """Simple DNS answer cache. - - @ivar data: A dictionary of cached data - @type data: dict - @ivar cleaning_interval: The number of seconds between cleanings. The - default is 300 (5 minutes). - @type cleaning_interval: float - @ivar next_cleaning: The time the cache should next be cleaned (in seconds - since the epoch.) - @type next_cleaning: float - """ - - def __init__(self, cleaning_interval=300.0): - """Initialize a DNS cache. - - @param cleaning_interval: the number of seconds between periodic - cleanings. The default is 300.0 - @type cleaning_interval: float. - """ - - self.data = {} - self.cleaning_interval = cleaning_interval - self.next_cleaning = time.time() + self.cleaning_interval - - def maybe_clean(self): - """Clean the cache if it's time to do so.""" - - now = time.time() - if self.next_cleaning <= now: - keys_to_delete = [] - for (k, v) in self.data.iteritems(): - if v.expiration <= now: - keys_to_delete.append(k) - for k in keys_to_delete: - del self.data[k] - now = time.time() - self.next_cleaning = now + self.cleaning_interval - - def get(self, key): - """Get the answer associated with I{key}. Returns None if - no answer is cached for the key. - @param key: the key - @type key: (dns.name.Name, int, int) tuple whose values are the - query name, rdtype, and rdclass. - @rtype: dns.resolver.Answer object or None - """ - - self.maybe_clean() - v = self.data.get(key) - if v is None or v.expiration <= time.time(): - return None - return v - - def put(self, key, value): - """Associate key and value in the cache. - @param key: the key - @type key: (dns.name.Name, int, int) tuple whose values are the - query name, rdtype, and rdclass. - @param value: The answer being cached - @type value: dns.resolver.Answer object - """ - - self.maybe_clean() - self.data[key] = value - - def flush(self, key=None): - """Flush the cache. - - If I{key} is specified, only that item is flushed. Otherwise - the entire cache is flushed. - - @param key: the key to flush - @type key: (dns.name.Name, int, int) tuple or None - """ - - if not key is None: - if self.data.has_key(key): - del self.data[key] - else: - self.data = {} - self.next_cleaning = time.time() + self.cleaning_interval - -class Resolver(object): - """DNS stub resolver - - @ivar domain: The domain of this host - @type domain: dns.name.Name object - @ivar nameservers: A list of nameservers to query. Each nameserver is - a string which contains the IP address of a nameserver. - @type nameservers: list of strings - @ivar search: The search list. If the query name is a relative name, - the resolver will construct an absolute query name by appending the search - names one by one to the query name. - @type search: list of dns.name.Name objects - @ivar port: The port to which to send queries. The default is 53. - @type port: int - @ivar timeout: The number of seconds to wait for a response from a - server, before timing out. - @type timeout: float - @ivar lifetime: The total number of seconds to spend trying to get an - answer to the question. If the lifetime expires, a Timeout exception - will occur. - @type lifetime: float - @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 key algorithm to use. The default is - dns.tsig.default_algorithm. - @type keyalgorithm: string - @ivar edns: The EDNS level to use. The default is -1, no Edns. - @type edns: int - @ivar ednsflags: The EDNS flags - @type ednsflags: int - @ivar payload: The EDNS payload size. The default is 0. - @type payload: int - @ivar cache: The cache to use. The default is None. - @type cache: dns.resolver.Cache object - """ - def __init__(self, filename='/etc/resolv.conf', configure=True): - """Initialize a resolver instance. - - @param filename: The filename of a configuration file in - standard /etc/resolv.conf format. This parameter is meaningful - only when I{configure} is true and the platform is POSIX. - @type filename: string or file object - @param configure: If True (the default), the resolver instance - is configured in the normal fashion for the operating system - the resolver is running on. (I.e. a /etc/resolv.conf file on - POSIX systems and from the registry on Windows systems.) - @type configure: bool""" - - self.reset() - if configure: - if sys.platform == 'win32': - self.read_registry() - elif filename: - self.read_resolv_conf(filename) - - def reset(self): - """Reset all resolver configuration to the defaults.""" - self.domain = \ - dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) - if len(self.domain) == 0: - self.domain = dns.name.root - self.nameservers = [] - self.search = [] - self.port = 53 - self.timeout = 2.0 - self.lifetime = 30.0 - self.keyring = None - self.keyname = None - self.keyalgorithm = dns.tsig.default_algorithm - self.edns = -1 - self.ednsflags = 0 - self.payload = 0 - self.cache = None - - def read_resolv_conf(self, f): - """Process f as a file in the /etc/resolv.conf format. If f is - a string, it is used as the name of the file to open; otherwise it - is treated as the file itself.""" - if isinstance(f, str) or isinstance(f, unicode): - try: - f = open(f, 'r') - except IOError: - # /etc/resolv.conf doesn't exist, can't be read, etc. - # We'll just use the default resolver configuration. - self.nameservers = ['127.0.0.1'] - return - want_close = True - else: - want_close = False - try: - for l in f: - if len(l) == 0 or l[0] == '#' or l[0] == ';': - continue - tokens = l.split() - if len(tokens) == 0: - continue - if tokens[0] == 'nameserver': - self.nameservers.append(tokens[1]) - elif tokens[0] == 'domain': - self.domain = dns.name.from_text(tokens[1]) - elif tokens[0] == 'search': - for suffix in tokens[1:]: - self.search.append(dns.name.from_text(suffix)) - finally: - if want_close: - f.close() - if len(self.nameservers) == 0: - self.nameservers.append('127.0.0.1') - - def _determine_split_char(self, entry): - # - # The windows registry irritatingly changes the list element - # delimiter in between ' ' and ',' (and vice-versa) in various - # versions of windows. - # - if entry.find(' ') >= 0: - split_char = ' ' - elif entry.find(',') >= 0: - split_char = ',' - else: - # probably a singleton; treat as a space-separated list. - split_char = ' ' - return split_char - - def _config_win32_nameservers(self, nameservers): - """Configure a NameServer registry entry.""" - # we call str() on nameservers to convert it from unicode to ascii - nameservers = str(nameservers) - split_char = self._determine_split_char(nameservers) - ns_list = nameservers.split(split_char) - for ns in ns_list: - if not ns in self.nameservers: - self.nameservers.append(ns) - - def _config_win32_domain(self, domain): - """Configure a Domain registry entry.""" - # we call str() on domain to convert it from unicode to ascii - self.domain = dns.name.from_text(str(domain)) - - def _config_win32_search(self, search): - """Configure a Search registry entry.""" - # we call str() on search to convert it from unicode to ascii - search = str(search) - split_char = self._determine_split_char(search) - search_list = search.split(split_char) - for s in search_list: - if not s in self.search: - self.search.append(dns.name.from_text(s)) - - def _config_win32_fromkey(self, key): - """Extract DNS info from a registry key.""" - try: - servers, rtype = _winreg.QueryValueEx(key, 'NameServer') - except WindowsError: - servers = None - if servers: - self._config_win32_nameservers(servers) - try: - dom, rtype = _winreg.QueryValueEx(key, 'Domain') - if dom: - self._config_win32_domain(dom) - except WindowsError: - pass - else: - try: - servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer') - except WindowsError: - servers = None - if servers: - self._config_win32_nameservers(servers) - try: - dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain') - if dom: - self._config_win32_domain(dom) - except WindowsError: - pass - try: - search, rtype = _winreg.QueryValueEx(key, 'SearchList') - except WindowsError: - search = None - if search: - self._config_win32_search(search) - - def read_registry(self): - """Extract resolver configuration from the Windows registry.""" - lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) - want_scan = False - try: - try: - # XP, 2000 - tcp_params = _winreg.OpenKey(lm, - r'SYSTEM\CurrentControlSet' - r'\Services\Tcpip\Parameters') - want_scan = True - except EnvironmentError: - # ME - tcp_params = _winreg.OpenKey(lm, - r'SYSTEM\CurrentControlSet' - r'\Services\VxD\MSTCP') - try: - self._config_win32_fromkey(tcp_params) - finally: - tcp_params.Close() - if want_scan: - interfaces = _winreg.OpenKey(lm, - r'SYSTEM\CurrentControlSet' - r'\Services\Tcpip\Parameters' - r'\Interfaces') - try: - i = 0 - while True: - try: - guid = _winreg.EnumKey(interfaces, i) - i += 1 - key = _winreg.OpenKey(interfaces, guid) - if not self._win32_is_nic_enabled(lm, guid, key): - continue - try: - self._config_win32_fromkey(key) - finally: - key.Close() - except EnvironmentError: - break - finally: - interfaces.Close() - finally: - lm.Close() - - def _win32_is_nic_enabled(self, lm, guid, interface_key): - # Look in the Windows Registry to determine whether the network - # interface corresponding to the given guid is enabled. - # - # (Code contributed by Paul Marks, thanks!) - # - try: - # This hard-coded location seems to be consistent, at least - # from Windows 2000 through Vista. - connection_key = _winreg.OpenKey( - lm, - r'SYSTEM\CurrentControlSet\Control\Network' - r'\{4D36E972-E325-11CE-BFC1-08002BE10318}' - r'\%s\Connection' % guid) - - try: - # The PnpInstanceID points to a key inside Enum - (pnp_id, ttype) = _winreg.QueryValueEx( - connection_key, 'PnpInstanceID') - - if ttype != _winreg.REG_SZ: - raise ValueError - - device_key = _winreg.OpenKey( - lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id) - - try: - # Get ConfigFlags for this device - (flags, ttype) = _winreg.QueryValueEx( - device_key, 'ConfigFlags') - - if ttype != _winreg.REG_DWORD: - raise ValueError - - # Based on experimentation, bit 0x1 indicates that the - # device is disabled. - return not (flags & 0x1) - - finally: - device_key.Close() - finally: - connection_key.Close() - except (EnvironmentError, ValueError): - # Pre-vista, enabled interfaces seem to have a non-empty - # NTEContextList; this was how dnspython detected enabled - # nics before the code above was contributed. We've retained - # the old method since we don't know if the code above works - # on Windows 95/98/ME. - try: - (nte, ttype) = _winreg.QueryValueEx(interface_key, - 'NTEContextList') - return nte is not None - except WindowsError: - return False - - def _compute_timeout(self, start): - now = time.time() - if now < start: - if start - now > 1: - # Time going backwards is bad. Just give up. - raise Timeout - else: - # Time went backwards, but only a little. This can - # happen, e.g. under vmware with older linux kernels. - # Pretend it didn't happen. - now = start - duration = now - start - if duration >= self.lifetime: - raise Timeout - return min(self.lifetime - duration, self.timeout) - - def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, - tcp=False, source=None): - """Query nameservers to find the answer to the question. - - The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects - of the appropriate type, or strings that can be converted into objects - of the appropriate type. E.g. For I{rdtype} the integer 2 and the - the string 'NS' both mean to query for records with DNS rdata type NS. - - @param qname: the query name - @type qname: dns.name.Name object or string - @param rdtype: the query type - @type rdtype: int or string - @param rdclass: the query class - @type rdclass: int or string - @param tcp: use TCP to make the query (default is False). - @type tcp: bool - @param source: bind to this IP address (defaults to machine default IP). - @type source: IP address in dotted quad notation - @rtype: dns.resolver.Answer instance - @raises Timeout: no answers could be found in the specified lifetime - @raises NXDOMAIN: the query name does not exist - @raises NoAnswer: the response did not contain an answer - @raises NoNameservers: no non-broken nameservers are available to - answer the question.""" - - if isinstance(qname, (str, unicode)): - qname = dns.name.from_text(qname, None) - if isinstance(rdtype, str): - rdtype = dns.rdatatype.from_text(rdtype) - if isinstance(rdclass, str): - rdclass = dns.rdataclass.from_text(rdclass) - qnames_to_try = [] - if qname.is_absolute(): - qnames_to_try.append(qname) - else: - if len(qname) > 1: - qnames_to_try.append(qname.concatenate(dns.name.root)) - if self.search: - for suffix in self.search: - qnames_to_try.append(qname.concatenate(suffix)) - else: - qnames_to_try.append(qname.concatenate(self.domain)) - all_nxdomain = True - start = time.time() - for qname in qnames_to_try: - if self.cache: - answer = self.cache.get((qname, rdtype, rdclass)) - if answer: - return answer - request = dns.message.make_query(qname, rdtype, rdclass) - if not self.keyname is None: - request.use_tsig(self.keyring, self.keyname, self.keyalgorithm) - request.use_edns(self.edns, self.ednsflags, self.payload) - response = None - # - # make a copy of the servers list so we can alter it later. - # - nameservers = self.nameservers[:] - backoff = 0.10 - while response is None: - if len(nameservers) == 0: - raise NoNameservers - for nameserver in nameservers[:]: - timeout = self._compute_timeout(start) - try: - if tcp: - response = dns.query.tcp(request, nameserver, - timeout, self.port, - source=source) - else: - response = dns.query.udp(request, nameserver, - timeout, self.port, - source=source) - except (socket.error, dns.exception.Timeout): - # - # Communication failure or timeout. Go to the - # next server - # - response = None - continue - except dns.query.UnexpectedSource: - # - # Who knows? Keep going. - # - response = None - continue - except dns.exception.FormError: - # - # We don't understand what this server is - # saying. Take it out of the mix and - # continue. - # - nameservers.remove(nameserver) - response = None - continue - rcode = response.rcode() - if rcode == dns.rcode.NOERROR or \ - rcode == dns.rcode.NXDOMAIN: - break - # - # We got a response, but we're not happy with the - # rcode in it. Remove the server from the mix if - # the rcode isn't SERVFAIL. - # - if rcode != dns.rcode.SERVFAIL: - nameservers.remove(nameserver) - response = None - if not response is None: - break - # - # All nameservers failed! - # - if len(nameservers) > 0: - # - # But we still have servers to try. Sleep a bit - # so we don't pound them! - # - timeout = self._compute_timeout(start) - sleep_time = min(timeout, backoff) - backoff *= 2 - time.sleep(sleep_time) - if response.rcode() == dns.rcode.NXDOMAIN: - continue - all_nxdomain = False - break - if all_nxdomain: - raise NXDOMAIN - answer = Answer(qname, rdtype, rdclass, response) - if self.cache: - self.cache.put((qname, rdtype, rdclass), answer) - return answer - - def use_tsig(self, keyring, keyname=None, - algorithm=dns.tsig.default_algorithm): - """Add a TSIG signature to the query. - - @param keyring: The TSIG keyring to use; defaults to None. - @type keyring: dict - @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 - but a keyname is not, then the key used will be the first key in the - keyring. Note that the order of keys in a dictionary is not defined, - so applications should supply a keyname when a keyring is used, unless - they know the keyring contains only one key. - @param algorithm: The TSIG key algorithm to use. The default - is dns.tsig.default_algorithm. - @type algorithm: string""" - self.keyring = keyring - if keyname is None: - self.keyname = self.keyring.keys()[0] - else: - self.keyname = keyname - self.keyalgorithm = algorithm - - def use_edns(self, edns, ednsflags, payload): - """Configure Edns. - - @param edns: The EDNS level to use. The default is -1, no Edns. - @type edns: int - @param ednsflags: The EDNS flags - @type ednsflags: int - @param payload: The EDNS payload size. The default is 0. - @type payload: int""" - - if edns is None: - edns = -1 - self.edns = edns - self.ednsflags = ednsflags - self.payload = payload - -default_resolver = None - -def get_default_resolver(): - """Get the default resolver, initializing it if necessary.""" - global default_resolver - if default_resolver is None: - default_resolver = Resolver() - return default_resolver - -def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, - tcp=False, source=None): - """Query nameservers to find the answer to the question. - - This is a convenience function that uses the default resolver - object to make the query. - @see: L{dns.resolver.Resolver.query} for more information on the - parameters.""" - return get_default_resolver().query(qname, rdtype, rdclass, tcp, source) - -def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None): - """Find the name of the zone which contains the specified name. - - @param name: the query name - @type name: absolute dns.name.Name object or string - @param rdclass: The query class - @type rdclass: int - @param tcp: use TCP to make the query (default is False). - @type tcp: bool - @param resolver: the resolver to use - @type resolver: dns.resolver.Resolver object or None - @rtype: dns.name.Name""" - - if isinstance(name, (str, unicode)): - name = dns.name.from_text(name, dns.name.root) - if resolver is None: - resolver = get_default_resolver() - if not name.is_absolute(): - raise NotAbsolute(name) - while 1: - try: - answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp) - return name - except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - try: - name = name.parent() - except dns.name.NoParent: - raise NoRootSOA |