1 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
3 # Permission to use, copy, modify, and distribute this software and its
4 # documentation for any purpose with or without fee is hereby granted,
5 # provided that the above copyright notice and this permission notice
6 # appear in all copies.
8 # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
9 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
11 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 @var default_resolver: The default resolver object
19 @type default_resolver: dns.resolver.Resolver object"""
33 if sys
.platform
== 'win32':
36 class NXDOMAIN(dns
.exception
.DNSException
):
37 """The query name does not exist."""
40 # The definition of the Timeout exception has moved from here to the
41 # dns.exception module. We keep dns.resolver.Timeout defined for
42 # backwards compatibility.
44 Timeout
= dns
.exception
.Timeout
46 class NoAnswer(dns
.exception
.DNSException
):
47 """The response did not contain an answer to the question."""
50 class NoNameservers(dns
.exception
.DNSException
):
51 """No non-broken nameservers are available to answer the query."""
54 class NotAbsolute(dns
.exception
.DNSException
):
55 """Raised if an absolute domain name is required but a relative name
59 class NoRootSOA(dns
.exception
.DNSException
):
60 """Raised if for some reason there is no SOA at the root name.
61 This should never happen!"""
64 class NoMetaqueries(dns
.exception
.DNSException
):
65 """Metaqueries are not allowed."""
70 """DNS stub resolver answer
72 Instances of this class bundle up the result of a successful DNS
75 For convenience, the answer object implements much of the sequence
76 protocol, forwarding to its rrset. E.g. "for a in answer" is
77 equivalent to "for a in answer.rrset", "answer[i]" is equivalent
78 to "answer.rrset[i]", and "answer[i:j]" is equivalent to
81 Note that CNAMEs or DNAMEs in the response may mean that answer
82 node's name might not be the query name.
84 @ivar qname: The query name
85 @type qname: dns.name.Name object
86 @ivar rdtype: The query type
88 @ivar rdclass: The query class
90 @ivar response: The response message
91 @type response: dns.message.Message object
92 @ivar rrset: The answer
93 @type rrset: dns.rrset.RRset object
94 @ivar expiration: The time when the answer expires
95 @type expiration: float (seconds since the epoch)
97 def __init__(self
, qname
, rdtype
, rdclass
, response
):
100 self
.rdclass
= rdclass
101 self
.response
= response
104 for count
in xrange(0, 15):
106 rrset
= response
.find_rrset(response
.answer
, qname
,
108 if min_ttl
== -1 or rrset
.ttl
< min_ttl
:
112 if rdtype
!= dns
.rdatatype
.CNAME
:
114 crrset
= response
.find_rrset(response
.answer
,
118 if min_ttl
== -1 or crrset
.ttl
< min_ttl
:
130 self
.expiration
= time
.time() + min_ttl
132 def __getattr__(self
, attr
):
134 return self
.rrset
.name
136 return self
.rrset
.ttl
137 elif attr
== 'covers':
138 return self
.rrset
.covers
139 elif attr
== 'rdclass':
140 return self
.rrset
.rdclass
141 elif attr
== 'rdtype':
142 return self
.rrset
.rdtype
144 raise AttributeError(attr
)
147 return len(self
.rrset
)
150 return iter(self
.rrset
)
152 def __getitem__(self
, i
):
155 def __delitem__(self
, i
):
158 def __getslice__(self
, i
, j
):
159 return self
.rrset
[i
:j
]
161 def __delslice__(self
, i
, j
):
165 """Simple DNS answer cache.
167 @ivar data: A dictionary of cached data
169 @ivar cleaning_interval: The number of seconds between cleanings. The
170 default is 300 (5 minutes).
171 @type cleaning_interval: float
172 @ivar next_cleaning: The time the cache should next be cleaned (in seconds
174 @type next_cleaning: float
177 def __init__(self
, cleaning_interval
=300.0):
178 """Initialize a DNS cache.
180 @param cleaning_interval: the number of seconds between periodic
181 cleanings. The default is 300.0
182 @type cleaning_interval: float.
186 self
.cleaning_interval
= cleaning_interval
187 self
.next_cleaning
= time
.time() + self
.cleaning_interval
189 def maybe_clean(self
):
190 """Clean the cache if it's time to do so."""
193 if self
.next_cleaning
<= now
:
195 for (k
, v
) in self
.data
.iteritems():
196 if v
.expiration
<= now
:
197 keys_to_delete
.append(k
)
198 for k
in keys_to_delete
:
201 self
.next_cleaning
= now
+ self
.cleaning_interval
204 """Get the answer associated with I{key}. Returns None if
205 no answer is cached for the key.
207 @type key: (dns.name.Name, int, int) tuple whose values are the
208 query name, rdtype, and rdclass.
209 @rtype: dns.resolver.Answer object or None
213 v
= self
.data
.get(key
)
214 if v
is None or v
.expiration
<= time
.time():
218 def put(self
, key
, value
):
219 """Associate key and value in the cache.
221 @type key: (dns.name.Name, int, int) tuple whose values are the
222 query name, rdtype, and rdclass.
223 @param value: The answer being cached
224 @type value: dns.resolver.Answer object
228 self
.data
[key
] = value
230 def flush(self
, key
=None):
233 If I{key} is specified, only that item is flushed. Otherwise
234 the entire cache is flushed.
236 @param key: the key to flush
237 @type key: (dns.name.Name, int, int) tuple or None
241 if self
.data
.has_key(key
):
245 self
.next_cleaning
= time
.time() + self
.cleaning_interval
247 class Resolver(object):
250 @ivar domain: The domain of this host
251 @type domain: dns.name.Name object
252 @ivar nameservers: A list of nameservers to query. Each nameserver is
253 a string which contains the IP address of a nameserver.
254 @type nameservers: list of strings
255 @ivar search: The search list. If the query name is a relative name,
256 the resolver will construct an absolute query name by appending the search
257 names one by one to the query name.
258 @type search: list of dns.name.Name objects
259 @ivar port: The port to which to send queries. The default is 53.
261 @ivar timeout: The number of seconds to wait for a response from a
262 server, before timing out.
264 @ivar lifetime: The total number of seconds to spend trying to get an
265 answer to the question. If the lifetime expires, a Timeout exception
267 @type lifetime: float
268 @ivar keyring: The TSIG keyring to use. The default is None.
270 @ivar keyname: The TSIG keyname to use. The default is None.
271 @type keyname: dns.name.Name object
272 @ivar keyalgorithm: The TSIG key algorithm to use. The default is
273 dns.tsig.default_algorithm.
274 @type keyalgorithm: string
275 @ivar edns: The EDNS level to use. The default is -1, no Edns.
277 @ivar ednsflags: The EDNS flags
279 @ivar payload: The EDNS payload size. The default is 0.
281 @ivar cache: The cache to use. The default is None.
282 @type cache: dns.resolver.Cache object
284 def __init__(self
, filename
='/etc/resolv.conf', configure
=True):
285 """Initialize a resolver instance.
287 @param filename: The filename of a configuration file in
288 standard /etc/resolv.conf format. This parameter is meaningful
289 only when I{configure} is true and the platform is POSIX.
290 @type filename: string or file object
291 @param configure: If True (the default), the resolver instance
292 is configured in the normal fashion for the operating system
293 the resolver is running on. (I.e. a /etc/resolv.conf file on
294 POSIX systems and from the registry on Windows systems.)
295 @type configure: bool"""
299 if sys
.platform
== 'win32':
302 self
.read_resolv_conf(filename
)
305 """Reset all resolver configuration to the defaults."""
307 dns
.name
.Name(dns
.name
.from_text(socket
.gethostname())[1:])
308 if len(self
.domain
) == 0:
309 self
.domain
= dns
.name
.root
310 self
.nameservers
= []
317 self
.keyalgorithm
= dns
.tsig
.default_algorithm
323 def read_resolv_conf(self
, f
):
324 """Process f as a file in the /etc/resolv.conf format. If f is
325 a string, it is used as the name of the file to open; otherwise it
326 is treated as the file itself."""
327 if isinstance(f
, str) or isinstance(f
, unicode):
331 # /etc/resolv.conf doesn't exist, can't be read, etc.
332 # We'll just use the default resolver configuration.
333 self
.nameservers
= ['127.0.0.1']
340 if len(l
) == 0 or l
[0] == '#' or l
[0] == ';':
345 if tokens
[0] == 'nameserver':
346 self
.nameservers
.append(tokens
[1])
347 elif tokens
[0] == 'domain':
348 self
.domain
= dns
.name
.from_text(tokens
[1])
349 elif tokens
[0] == 'search':
350 for suffix
in tokens
[1:]:
351 self
.search
.append(dns
.name
.from_text(suffix
))
355 if len(self
.nameservers
) == 0:
356 self
.nameservers
.append('127.0.0.1')
358 def _determine_split_char(self
, entry
):
360 # The windows registry irritatingly changes the list element
361 # delimiter in between ' ' and ',' (and vice-versa) in various
362 # versions of windows.
364 if entry
.find(' ') >= 0:
366 elif entry
.find(',') >= 0:
369 # probably a singleton; treat as a space-separated list.
373 def _config_win32_nameservers(self
, nameservers
):
374 """Configure a NameServer registry entry."""
375 # we call str() on nameservers to convert it from unicode to ascii
376 nameservers
= str(nameservers
)
377 split_char
= self
._determine
_split
_char
(nameservers
)
378 ns_list
= nameservers
.split(split_char
)
380 if not ns
in self
.nameservers
:
381 self
.nameservers
.append(ns
)
383 def _config_win32_domain(self
, domain
):
384 """Configure a Domain registry entry."""
385 # we call str() on domain to convert it from unicode to ascii
386 self
.domain
= dns
.name
.from_text(str(domain
))
388 def _config_win32_search(self
, search
):
389 """Configure a Search registry entry."""
390 # we call str() on search to convert it from unicode to ascii
392 split_char
= self
._determine
_split
_char
(search
)
393 search_list
= search
.split(split_char
)
394 for s
in search_list
:
395 if not s
in self
.search
:
396 self
.search
.append(dns
.name
.from_text(s
))
398 def _config_win32_fromkey(self
, key
):
399 """Extract DNS info from a registry key."""
401 servers
, rtype
= _winreg
.QueryValueEx(key
, 'NameServer')
405 self
._config
_win
32_nameservers
(servers
)
407 dom
, rtype
= _winreg
.QueryValueEx(key
, 'Domain')
409 self
._config
_win
32_domain
(dom
)
414 servers
, rtype
= _winreg
.QueryValueEx(key
, 'DhcpNameServer')
418 self
._config
_win
32_nameservers
(servers
)
420 dom
, rtype
= _winreg
.QueryValueEx(key
, 'DhcpDomain')
422 self
._config
_win
32_domain
(dom
)
426 search
, rtype
= _winreg
.QueryValueEx(key
, 'SearchList')
430 self
._config
_win
32_search
(search
)
432 def read_registry(self
):
433 """Extract resolver configuration from the Windows registry."""
434 lm
= _winreg
.ConnectRegistry(None, _winreg
.HKEY_LOCAL_MACHINE
)
439 tcp_params
= _winreg
.OpenKey(lm
,
440 r
'SYSTEM\CurrentControlSet'
441 r
'\Services\Tcpip\Parameters')
443 except EnvironmentError:
445 tcp_params
= _winreg
.OpenKey(lm
,
446 r
'SYSTEM\CurrentControlSet'
447 r
'\Services\VxD\MSTCP')
449 self
._config
_win
32_fromkey
(tcp_params
)
453 interfaces
= _winreg
.OpenKey(lm
,
454 r
'SYSTEM\CurrentControlSet'
455 r
'\Services\Tcpip\Parameters'
461 guid
= _winreg
.EnumKey(interfaces
, i
)
463 key
= _winreg
.OpenKey(interfaces
, guid
)
464 if not self
._win
32_is
_nic
_enabled
(lm
, guid
, key
):
467 self
._config
_win
32_fromkey
(key
)
470 except EnvironmentError:
477 def _win32_is_nic_enabled(self
, lm
, guid
, interface_key
):
478 # Look in the Windows Registry to determine whether the network
479 # interface corresponding to the given guid is enabled.
481 # (Code contributed by Paul Marks, thanks!)
484 # This hard-coded location seems to be consistent, at least
485 # from Windows 2000 through Vista.
486 connection_key
= _winreg
.OpenKey(
488 r
'SYSTEM\CurrentControlSet\Control\Network'
489 r
'\{4D36E972-E325-11CE-BFC1-08002BE10318}'
490 r
'\%s\Connection' % guid
)
493 # The PnpInstanceID points to a key inside Enum
494 (pnp_id
, ttype
) = _winreg
.QueryValueEx(
495 connection_key
, 'PnpInstanceID')
497 if ttype
!= _winreg
.REG_SZ
:
500 device_key
= _winreg
.OpenKey(
501 lm
, r
'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id
)
504 # Get ConfigFlags for this device
505 (flags
, ttype
) = _winreg
.QueryValueEx(
506 device_key
, 'ConfigFlags')
508 if ttype
!= _winreg
.REG_DWORD
:
511 # Based on experimentation, bit 0x1 indicates that the
512 # device is disabled.
513 return not (flags
& 0x1)
518 connection_key
.Close()
519 except (EnvironmentError, ValueError):
520 # Pre-vista, enabled interfaces seem to have a non-empty
521 # NTEContextList; this was how dnspython detected enabled
522 # nics before the code above was contributed. We've retained
523 # the old method since we don't know if the code above works
524 # on Windows 95/98/ME.
526 (nte
, ttype
) = _winreg
.QueryValueEx(interface_key
,
528 return nte
is not None
532 def _compute_timeout(self
, start
):
536 # Time going backwards is bad. Just give up.
539 # Time went backwards, but only a little. This can
540 # happen, e.g. under vmware with older linux kernels.
541 # Pretend it didn't happen.
543 duration
= now
- start
544 if duration
>= self
.lifetime
:
546 return min(self
.lifetime
- duration
, self
.timeout
)
548 def query(self
, qname
, rdtype
=dns
.rdatatype
.A
, rdclass
=dns
.rdataclass
.IN
,
549 tcp
=False, source
=None):
550 """Query nameservers to find the answer to the question.
552 The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects
553 of the appropriate type, or strings that can be converted into objects
554 of the appropriate type. E.g. For I{rdtype} the integer 2 and the
555 the string 'NS' both mean to query for records with DNS rdata type NS.
557 @param qname: the query name
558 @type qname: dns.name.Name object or string
559 @param rdtype: the query type
560 @type rdtype: int or string
561 @param rdclass: the query class
562 @type rdclass: int or string
563 @param tcp: use TCP to make the query (default is False).
565 @param source: bind to this IP address (defaults to machine default IP).
566 @type source: IP address in dotted quad notation
567 @rtype: dns.resolver.Answer instance
568 @raises Timeout: no answers could be found in the specified lifetime
569 @raises NXDOMAIN: the query name does not exist
570 @raises NoAnswer: the response did not contain an answer
571 @raises NoNameservers: no non-broken nameservers are available to
572 answer the question."""
574 if isinstance(qname
, (str, unicode)):
575 qname
= dns
.name
.from_text(qname
, None)
576 if isinstance(rdtype
, (str, unicode)):
577 rdtype
= dns
.rdatatype
.from_text(rdtype
)
578 if dns
.rdatatype
.is_metatype(rdtype
):
580 if isinstance(rdclass
, (str, unicode)):
581 rdclass
= dns
.rdataclass
.from_text(rdclass
)
582 if dns
.rdataclass
.is_metaclass(rdclass
):
585 if qname
.is_absolute():
586 qnames_to_try
.append(qname
)
589 qnames_to_try
.append(qname
.concatenate(dns
.name
.root
))
591 for suffix
in self
.search
:
592 qnames_to_try
.append(qname
.concatenate(suffix
))
594 qnames_to_try
.append(qname
.concatenate(self
.domain
))
597 for qname
in qnames_to_try
:
599 answer
= self
.cache
.get((qname
, rdtype
, rdclass
))
602 request
= dns
.message
.make_query(qname
, rdtype
, rdclass
)
603 if not self
.keyname
is None:
604 request
.use_tsig(self
.keyring
, self
.keyname
,
605 algorithm
=self
.keyalgorithm
)
606 request
.use_edns(self
.edns
, self
.ednsflags
, self
.payload
)
609 # make a copy of the servers list so we can alter it later.
611 nameservers
= self
.nameservers
[:]
613 while response
is None:
614 if len(nameservers
) == 0:
616 for nameserver
in nameservers
[:]:
617 timeout
= self
._compute
_timeout
(start
)
620 response
= dns
.query
.tcp(request
, nameserver
,
624 response
= dns
.query
.udp(request
, nameserver
,
627 except (socket
.error
, dns
.exception
.Timeout
):
629 # Communication failure or timeout. Go to the
634 except dns
.query
.UnexpectedSource
:
636 # Who knows? Keep going.
640 except dns
.exception
.FormError
:
642 # We don't understand what this server is
643 # saying. Take it out of the mix and
646 nameservers
.remove(nameserver
)
649 rcode
= response
.rcode()
650 if rcode
== dns
.rcode
.NOERROR
or \
651 rcode
== dns
.rcode
.NXDOMAIN
:
654 # We got a response, but we're not happy with the
655 # rcode in it. Remove the server from the mix if
656 # the rcode isn't SERVFAIL.
658 if rcode
!= dns
.rcode
.SERVFAIL
:
659 nameservers
.remove(nameserver
)
661 if not response
is None:
664 # All nameservers failed!
666 if len(nameservers
) > 0:
668 # But we still have servers to try. Sleep a bit
669 # so we don't pound them!
671 timeout
= self
._compute
_timeout
(start
)
672 sleep_time
= min(timeout
, backoff
)
674 time
.sleep(sleep_time
)
675 if response
.rcode() == dns
.rcode
.NXDOMAIN
:
681 answer
= Answer(qname
, rdtype
, rdclass
, response
)
683 self
.cache
.put((qname
, rdtype
, rdclass
), answer
)
686 def use_tsig(self
, keyring
, keyname
=None,
687 algorithm
=dns
.tsig
.default_algorithm
):
688 """Add a TSIG signature to the query.
690 @param keyring: The TSIG keyring to use; defaults to None.
692 @param keyname: The name of the TSIG key to use; defaults to None.
693 The key must be defined in the keyring. If a keyring is specified
694 but a keyname is not, then the key used will be the first key in the
695 keyring. Note that the order of keys in a dictionary is not defined,
696 so applications should supply a keyname when a keyring is used, unless
697 they know the keyring contains only one key.
698 @param algorithm: The TSIG key algorithm to use. The default
699 is dns.tsig.default_algorithm.
700 @type algorithm: string"""
701 self
.keyring
= keyring
703 self
.keyname
= self
.keyring
.keys()[0]
705 self
.keyname
= keyname
706 self
.keyalgorithm
= algorithm
708 def use_edns(self
, edns
, ednsflags
, payload
):
711 @param edns: The EDNS level to use. The default is -1, no Edns.
713 @param ednsflags: The EDNS flags
715 @param payload: The EDNS payload size. The default is 0.
716 @type payload: int"""
721 self
.ednsflags
= ednsflags
722 self
.payload
= payload
724 default_resolver
= None
726 def get_default_resolver():
727 """Get the default resolver, initializing it if necessary."""
728 global default_resolver
729 if default_resolver
is None:
730 default_resolver
= Resolver()
731 return default_resolver
733 def query(qname
, rdtype
=dns
.rdatatype
.A
, rdclass
=dns
.rdataclass
.IN
,
734 tcp
=False, source
=None):
735 """Query nameservers to find the answer to the question.
737 This is a convenience function that uses the default resolver
738 object to make the query.
739 @see: L{dns.resolver.Resolver.query} for more information on the
741 return get_default_resolver().query(qname
, rdtype
, rdclass
, tcp
, source
)
743 def zone_for_name(name
, rdclass
=dns
.rdataclass
.IN
, tcp
=False, resolver
=None):
744 """Find the name of the zone which contains the specified name.
746 @param name: the query name
747 @type name: absolute dns.name.Name object or string
748 @param rdclass: The query class
750 @param tcp: use TCP to make the query (default is False).
752 @param resolver: the resolver to use
753 @type resolver: dns.resolver.Resolver object or None
754 @rtype: dns.name.Name"""
756 if isinstance(name
, (str, unicode)):
757 name
= dns
.name
.from_text(name
, dns
.name
.root
)
759 resolver
= get_default_resolver()
760 if not name
.is_absolute():
761 raise NotAbsolute(name
)
764 answer
= resolver
.query(name
, dns
.rdatatype
.SOA
, rdclass
, tcp
)
765 if answer
.rrset
.name
== name
:
767 # otherwise we were CNAMEd or DNAMEd and need to look higher
768 except (dns
.resolver
.NXDOMAIN
, dns
.resolver
.NoAnswer
):
772 except dns
.name
.NoParent
: