1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS stub resolver.
17
18 @var default_resolver: The default resolver object
19 @type default_resolver: dns.resolver.Resolver object"""
20
21 import socket
22 import sys
23 import time
24
25 import dns.exception
26 import dns.message
27 import dns.name
28 import dns.query
29 import dns.rcode
30 import dns.rdataclass
31 import dns.rdatatype
32
33 if sys.platform == 'win32':
34 import _winreg
35
36 -class NXDOMAIN(dns.exception.DNSException):
37 """The query name does not exist."""
38 pass
39
40
41
42
43
44 Timeout = dns.exception.Timeout
45
46 -class NoAnswer(dns.exception.DNSException):
47 """The response did not contain an answer to the question."""
48 pass
49
51 """No non-broken nameservers are available to answer the query."""
52 pass
53
55 """Raised if an absolute domain name is required but a relative name
56 was provided."""
57 pass
58
60 """Raised if for some reason there is no SOA at the root name.
61 This should never happen!"""
62 pass
63
64
66 """DNS stub resolver answer
67
68 Instances of this class bundle up the result of a successful DNS
69 resolution.
70
71 For convenience, the answer object implements much of the sequence
72 protocol, forwarding to its rrset. E.g. "for a in answer" is
73 equivalent to "for a in answer.rrset", "answer[i]" is equivalent
74 to "answer.rrset[i]", and "answer[i:j]" is equivalent to
75 "answer.rrset[i:j]".
76
77 Note that CNAMEs or DNAMEs in the response may mean that answer
78 node's name might not be the query name.
79
80 @ivar qname: The query name
81 @type qname: dns.name.Name object
82 @ivar rdtype: The query type
83 @type rdtype: int
84 @ivar rdclass: The query class
85 @type rdclass: int
86 @ivar response: The response message
87 @type response: dns.message.Message object
88 @ivar rrset: The answer
89 @type rrset: dns.rrset.RRset object
90 @ivar expiration: The time when the answer expires
91 @type expiration: float (seconds since the epoch)
92 """
93 - def __init__(self, qname, rdtype, rdclass, response):
127
129 if attr == 'name':
130 return self.rrset.name
131 elif attr == 'ttl':
132 return self.rrset.ttl
133 elif attr == 'covers':
134 return self.rrset.covers
135 elif attr == 'rdclass':
136 return self.rrset.rdclass
137 elif attr == 'rdtype':
138 return self.rrset.rdtype
139 else:
140 raise AttributeError, attr
141
143 return len(self.rrset)
144
146 return iter(self.rrset)
147
150
153
155 return self.rrset[i:j]
156
159
161 """Simple DNS answer cache.
162
163 @ivar data: A dictionary of cached data
164 @type data: dict
165 @ivar cleaning_interval: The number of seconds between cleanings. The
166 default is 300 (5 minutes).
167 @type cleaning_interval: float
168 @ivar next_cleaning: The time the cache should next be cleaned (in seconds
169 since the epoch.)
170 @type next_cleaning: float
171 """
172
173 - def __init__(self, cleaning_interval=300.0):
174 """Initialize a DNS cache.
175
176 @param cleaning_interval: the number of seconds between periodic
177 cleanings. The default is 300.0
178 @type cleaning_interval: float.
179 """
180
181 self.data = {}
182 self.cleaning_interval = cleaning_interval
183 self.next_cleaning = time.time() + self.cleaning_interval
184
186 """Clean the cache if it's time to do so."""
187
188 now = time.time()
189 if self.next_cleaning <= now:
190 keys_to_delete = []
191 for (k, v) in self.data.iteritems():
192 if v.expiration <= now:
193 keys_to_delete.append(k)
194 for k in keys_to_delete:
195 del self.data[k]
196 now = time.time()
197 self.next_cleaning = now + self.cleaning_interval
198
199 - def get(self, key):
200 """Get the answer associated with I{key}. Returns None if
201 no answer is cached for the key.
202 @param key: the key
203 @type key: (dns.name.Name, int, int) tuple whose values are the
204 query name, rdtype, and rdclass.
205 @rtype: dns.resolver.Answer object or None
206 """
207
208 self.maybe_clean()
209 v = self.data.get(key)
210 if v is None or v.expiration <= time.time():
211 return None
212 return v
213
214 - def put(self, key, value):
215 """Associate key and value in the cache.
216 @param key: the key
217 @type key: (dns.name.Name, int, int) tuple whose values are the
218 query name, rdtype, and rdclass.
219 @param value: The answer being cached
220 @type value: dns.resolver.Answer object
221 """
222
223 self.maybe_clean()
224 self.data[key] = value
225
226 - def flush(self, key=None):
227 """Flush the cache.
228
229 If I{key} is specified, only that item is flushed. Otherwise
230 the entire cache is flushed.
231
232 @param key: the key to flush
233 @type key: (dns.name.Name, int, int) tuple or None
234 """
235
236 if not key is None:
237 if self.data.has_key(key):
238 del self.data[key]
239 else:
240 self.data = {}
241 self.next_cleaning = time.time() + self.cleaning_interval
242
244 """DNS stub resolver
245
246 @ivar domain: The domain of this host
247 @type domain: dns.name.Name object
248 @ivar nameservers: A list of nameservers to query. Each nameserver is
249 a string which contains the IP address of a nameserver.
250 @type nameservers: list of strings
251 @ivar search: The search list. If the query name is a relative name,
252 the resolver will construct an absolute query name by appending the search
253 names one by one to the query name.
254 @type search: list of dns.name.Name objects
255 @ivar port: The port to which to send queries. The default is 53.
256 @type port: int
257 @ivar timeout: The number of seconds to wait for a response from a
258 server, before timing out.
259 @type timeout: float
260 @ivar lifetime: The total number of seconds to spend trying to get an
261 answer to the question. If the lifetime expires, a Timeout exception
262 will occur.
263 @type lifetime: float
264 @ivar keyring: The TSIG keyring to use. The default is None.
265 @type keyring: dict
266 @ivar keyname: The TSIG keyname to use. The default is None.
267 @type keyname: dns.name.Name object
268 @ivar edns: The EDNS level to use. The default is -1, no Edns.
269 @type edns: int
270 @ivar ednsflags: The EDNS flags
271 @type ednsflags: int
272 @ivar payload: The EDNS payload size. The default is 0.
273 @type payload: int
274 @ivar cache: The cache to use. The default is None.
275 @type cache: dns.resolver.Cache object
276 """
277 - def __init__(self, filename='/etc/resolv.conf', configure=True):
278 """Initialize a resolver instance.
279
280 @param filename: The filename of a configuration file in
281 standard /etc/resolv.conf format. This parameter is meaningful
282 only when I{configure} is true and the platform is POSIX.
283 @type filename: string or file object
284 @param configure: If True (the default), the resolver instance
285 is configured in the normal fashion for the operating system
286 the resolver is running on. (I.e. a /etc/resolv.conf file on
287 POSIX systems and from the registry on Windows systems.)
288 @type configure: bool"""
289
290 self.reset()
291 if configure:
292 if sys.platform == 'win32':
293 self.read_registry()
294 elif filename:
295 self.read_resolv_conf(filename)
296
298 """Reset all resolver configuration to the defaults."""
299 self.domain = \
300 dns.name.Name(dns.name.from_text(socket.gethostname())[1:])
301 if len(self.domain) == 0:
302 self.domain = dns.name.root
303 self.nameservers = []
304 self.search = []
305 self.port = 53
306 self.timeout = 2.0
307 self.lifetime = 30.0
308 self.keyring = None
309 self.keyname = None
310 self.edns = -1
311 self.ednsflags = 0
312 self.payload = 0
313 self.cache = None
314
316 """Process f as a file in the /etc/resolv.conf format. If f is
317 a string, it is used as the name of the file to open; otherwise it
318 is treated as the file itself."""
319 if isinstance(f, str) or isinstance(f, unicode):
320 try:
321 f = open(f, 'r')
322 except IOError:
323
324
325 self.nameservers = ['127.0.0.1']
326 return
327 want_close = True
328 else:
329 want_close = False
330 try:
331 for l in f:
332 if len(l) == 0 or l[0] == '#' or l[0] == ';':
333 continue
334 tokens = l.split()
335 if len(tokens) == 0:
336 continue
337 if tokens[0] == 'nameserver':
338 self.nameservers.append(tokens[1])
339 elif tokens[0] == 'domain':
340 self.domain = dns.name.from_text(tokens[1])
341 elif tokens[0] == 'search':
342 for suffix in tokens[1:]:
343 self.search.append(dns.name.from_text(suffix))
344 finally:
345 if want_close:
346 f.close()
347 if len(self.nameservers) == 0:
348 self.nameservers.append('127.0.0.1')
349
351
352
353
354
355
356 if entry.find(' ') >= 0:
357 split_char = ' '
358 elif entry.find(',') >= 0:
359 split_char = ','
360 else:
361
362 split_char = ' '
363 return split_char
364
366 """Configure a NameServer registry entry."""
367
368 nameservers = str(nameservers)
369 split_char = self._determine_split_char(nameservers)
370 ns_list = nameservers.split(split_char)
371 for ns in ns_list:
372 if not ns in self.nameservers:
373 self.nameservers.append(ns)
374
375 - def _config_win32_domain(self, domain):
376 """Configure a Domain registry entry."""
377
378 self.domain = dns.name.from_text(str(domain))
379
381 """Configure a Search registry entry."""
382
383 search = str(search)
384 split_char = self._determine_split_char(search)
385 search_list = search.split(split_char)
386 for s in search_list:
387 if not s in self.search:
388 self.search.append(dns.name.from_text(s))
389
391 """Extract DNS info from a registry key."""
392 try:
393 servers, rtype = _winreg.QueryValueEx(key, 'NameServer')
394 except WindowsError:
395 servers = None
396 if servers:
397 self._config_win32_nameservers(servers)
398 try:
399 dom, rtype = _winreg.QueryValueEx(key, 'Domain')
400 if dom:
401 self._config_win32_domain(dom)
402 except WindowsError:
403 pass
404 else:
405 try:
406 servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer')
407 except WindowsError:
408 servers = None
409 if servers:
410 self._config_win32_nameservers(servers)
411 try:
412 dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain')
413 if dom:
414 self._config_win32_domain(dom)
415 except WindowsError:
416 pass
417 try:
418 search, rtype = _winreg.QueryValueEx(key, 'SearchList')
419 except WindowsError:
420 search = None
421 if search:
422 self._config_win32_search(search)
423
425 """Extract resolver configuration from the Windows registry."""
426 lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
427 want_scan = False
428 try:
429 try:
430
431 tcp_params = _winreg.OpenKey(lm,
432 r'SYSTEM\CurrentControlSet'
433 r'\Services\Tcpip\Parameters')
434 want_scan = True
435 except EnvironmentError:
436
437 tcp_params = _winreg.OpenKey(lm,
438 r'SYSTEM\CurrentControlSet'
439 r'\Services\VxD\MSTCP')
440 try:
441 self._config_win32_fromkey(tcp_params)
442 finally:
443 tcp_params.Close()
444 if want_scan:
445 interfaces = _winreg.OpenKey(lm,
446 r'SYSTEM\CurrentControlSet'
447 r'\Services\Tcpip\Parameters'
448 r'\Interfaces')
449 try:
450 i = 0
451 while True:
452 try:
453 guid = _winreg.EnumKey(interfaces, i)
454 i += 1
455 key = _winreg.OpenKey(interfaces, guid)
456 if not self._win32_is_nic_enabled(lm, guid, key):
457 continue
458 try:
459 self._config_win32_fromkey(key)
460 finally:
461 key.Close()
462 except EnvironmentError:
463 break
464 finally:
465 interfaces.Close()
466 finally:
467 lm.Close()
468
470
471
472
473
474
475 try:
476
477
478 connection_key = _winreg.OpenKey(
479 lm,
480 r'SYSTEM\CurrentControlSet\Control\Network'
481 r'\{4D36E972-E325-11CE-BFC1-08002BE10318}'
482 r'\%s\Connection' % guid)
483
484 try:
485
486 (pnp_id, ttype) = _winreg.QueryValueEx(
487 connection_key, 'PnpInstanceID')
488
489 if ttype != _winreg.REG_SZ:
490 raise ValueError
491
492 device_key = _winreg.OpenKey(
493 lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id)
494
495 try:
496
497 (flags, ttype) = _winreg.QueryValueEx(
498 device_key, 'ConfigFlags')
499
500 if ttype != _winreg.REG_DWORD:
501 raise ValueError
502
503
504
505 return not (flags & 0x1)
506
507 finally:
508 device_key.Close()
509 finally:
510 connection_key.Close()
511 except (EnvironmentError, ValueError):
512
513
514
515
516
517 try:
518 (nte, ttype) = _winreg.QueryValueEx(interface_key,
519 'NTEContextList')
520 return nte is not None
521 except WindowsError:
522 return False
523
525 now = time.time()
526 if now < start:
527 if start - now > 1:
528
529 raise Timeout
530 else:
531
532
533
534 now = start
535 duration = now - start
536 if duration >= self.lifetime:
537 raise Timeout
538 return min(self.lifetime - duration, self.timeout)
539
542 """Query nameservers to find the answer to the question.
543
544 The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects
545 of the appropriate type, or strings that can be converted into objects
546 of the appropriate type. E.g. For I{rdtype} the integer 2 and the
547 the string 'NS' both mean to query for records with DNS rdata type NS.
548
549 @param qname: the query name
550 @type qname: dns.name.Name object or string
551 @param rdtype: the query type
552 @type rdtype: int or string
553 @param rdclass: the query class
554 @type rdclass: int or string
555 @param tcp: use TCP to make the query (default is False).
556 @type tcp: bool
557 @param source: bind to this IP address (defaults to machine default IP).
558 @type source: IP address in dotted quad notation
559 @rtype: dns.resolver.Answer instance
560 @raises Timeout: no answers could be found in the specified lifetime
561 @raises NXDOMAIN: the query name does not exist
562 @raises NoAnswer: the response did not contain an answer
563 @raises NoNameservers: no non-broken nameservers are available to
564 answer the question."""
565
566 if isinstance(qname, (str, unicode)):
567 qname = dns.name.from_text(qname, None)
568 if isinstance(rdtype, str):
569 rdtype = dns.rdatatype.from_text(rdtype)
570 if isinstance(rdclass, str):
571 rdclass = dns.rdataclass.from_text(rdclass)
572 qnames_to_try = []
573 if qname.is_absolute():
574 qnames_to_try.append(qname)
575 else:
576 if len(qname) > 1:
577 qnames_to_try.append(qname.concatenate(dns.name.root))
578 if self.search:
579 for suffix in self.search:
580 qnames_to_try.append(qname.concatenate(suffix))
581 else:
582 qnames_to_try.append(qname.concatenate(self.domain))
583 all_nxdomain = True
584 start = time.time()
585 for qname in qnames_to_try:
586 if self.cache:
587 answer = self.cache.get((qname, rdtype, rdclass))
588 if answer:
589 return answer
590 request = dns.message.make_query(qname, rdtype, rdclass)
591 if not self.keyname is None:
592 request.use_tsig(self.keyring, self.keyname)
593 request.use_edns(self.edns, self.ednsflags, self.payload)
594 response = None
595
596
597
598 nameservers = self.nameservers[:]
599 backoff = 0.10
600 while response is None:
601 if len(nameservers) == 0:
602 raise NoNameservers
603 for nameserver in nameservers[:]:
604 timeout = self._compute_timeout(start)
605 try:
606 if tcp:
607 response = dns.query.tcp(request, nameserver,
608 timeout, self.port,
609 source=source)
610 else:
611 response = dns.query.udp(request, nameserver,
612 timeout, self.port,
613 source=source)
614 except (socket.error, dns.exception.Timeout):
615
616
617
618
619 response = None
620 continue
621 except dns.query.UnexpectedSource:
622
623
624
625 response = None
626 continue
627 except dns.exception.FormError:
628
629
630
631
632
633 nameservers.remove(nameserver)
634 response = None
635 continue
636 rcode = response.rcode()
637 if rcode == dns.rcode.NOERROR or \
638 rcode == dns.rcode.NXDOMAIN:
639 break
640
641
642
643
644
645 if rcode != dns.rcode.SERVFAIL:
646 nameservers.remove(nameserver)
647 response = None
648 if not response is None:
649 break
650
651
652
653 if len(nameservers) > 0:
654
655
656
657
658 timeout = self._compute_timeout(start)
659 sleep_time = min(timeout, backoff)
660 backoff *= 2
661 time.sleep(sleep_time)
662 if response.rcode() == dns.rcode.NXDOMAIN:
663 continue
664 all_nxdomain = False
665 break
666 if all_nxdomain:
667 raise NXDOMAIN
668 answer = Answer(qname, rdtype, rdclass, response)
669 if self.cache:
670 self.cache.put((qname, rdtype, rdclass), answer)
671 return answer
672
673 - def use_tsig(self, keyring, keyname=None):
674 """Add a TSIG signature to the query.
675
676 @param keyring: The TSIG keyring to use; defaults to None.
677 @type keyring: dict
678 @param keyname: The name of the TSIG key to use; defaults to None.
679 The key must be defined in the keyring. If a keyring is specified
680 but a keyname is not, then the key used will be the first key in the
681 keyring. Note that the order of keys in a dictionary is not defined,
682 so applications should supply a keyname when a keyring is used, unless
683 they know the keyring contains only one key."""
684 self.keyring = keyring
685 if keyname is None:
686 self.keyname = self.keyring.keys()[0]
687 else:
688 self.keyname = keyname
689
690 - def use_edns(self, edns, ednsflags, payload):
691 """Configure Edns.
692
693 @param edns: The EDNS level to use. The default is -1, no Edns.
694 @type edns: int
695 @param ednsflags: The EDNS flags
696 @type ednsflags: int
697 @param payload: The EDNS payload size. The default is 0.
698 @type payload: int"""
699
700 if edns is None:
701 edns = -1
702 self.edns = edns
703 self.ednsflags = ednsflags
704 self.payload = payload
705
706 default_resolver = None
707
714
717 """Query nameservers to find the answer to the question.
718
719 This is a convenience function that uses the default resolver
720 object to make the query.
721 @see: L{dns.resolver.Resolver.query} for more information on the
722 parameters."""
723 return get_default_resolver().query(qname, rdtype, rdclass, tcp, source)
724
753