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):
94 self.qname = qname
95 self.rdtype = rdtype
96 self.rdclass = rdclass
97 self.response = response
98 min_ttl = -1
99 rrset = None
100 for count in range(0, 15):
101 try:
102 rrset = response.find_rrset(response.answer, qname,
103 rdclass, rdtype)
104 if min_ttl == -1 or rrset.ttl < min_ttl:
105 min_ttl = rrset.ttl
106 break
107 except KeyError:
108 if rdtype != dns.rdatatype.CNAME:
109 try:
110 crrset = response.find_rrset(response.answer,
111 qname,
112 rdclass,
113 dns.rdatatype.CNAME)
114 if min_ttl == -1 or crrset.ttl < min_ttl:
115 min_ttl = crrset.ttl
116 for rd in crrset:
117 qname = rd.target
118 break
119 continue
120 except KeyError:
121 raise NoAnswer
122 raise NoAnswer
123 if rrset is None:
124 raise NoAnswer
125 self.rrset = rrset
126 self.expiration = time.time() + min_ttl
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.items():
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 key in self.data:
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 keyalgorithm: The TSIG key algorithm to use. The default is
269 dns.tsig.default_algorithm.
270 @type keyalgorithm: string
271 @ivar edns: The EDNS level to use. The default is -1, no Edns.
272 @type edns: int
273 @ivar ednsflags: The EDNS flags
274 @type ednsflags: int
275 @ivar payload: The EDNS payload size. The default is 0.
276 @type payload: int
277 @ivar cache: The cache to use. The default is None.
278 @type cache: dns.resolver.Cache object
279 """
280 - def __init__(self, filename='/etc/resolv.conf', configure=True):
281 """Initialize a resolver instance.
282
283 @param filename: The filename of a configuration file in
284 standard /etc/resolv.conf format. This parameter is meaningful
285 only when I{configure} is true and the platform is POSIX.
286 @type filename: string or file object
287 @param configure: If True (the default), the resolver instance
288 is configured in the normal fashion for the operating system
289 the resolver is running on. (I.e. a /etc/resolv.conf file on
290 POSIX systems and from the registry on Windows systems.)
291 @type configure: bool"""
292
293 self.reset()
294 if configure:
295 if sys.platform == 'win32':
296 self.read_registry()
297 elif filename:
298 self.read_resolv_conf(filename)
299
301 """Reset all resolver configuration to the defaults."""
302 self.domain = \
303 dns.name.Name(dns.name.from_text(socket.gethostname())[1:])
304 if len(self.domain) == 0:
305 self.domain = dns.name.root
306 self.nameservers = []
307 self.search = []
308 self.port = 53
309 self.timeout = 2.0
310 self.lifetime = 30.0
311 self.keyring = None
312 self.keyname = None
313 self.keyalgorithm = dns.tsig.default_algorithm
314 self.edns = -1
315 self.ednsflags = 0
316 self.payload = 0
317 self.cache = None
318
320 """Process f as a file in the /etc/resolv.conf format. If f is
321 a string, it is used as the name of the file to open; otherwise it
322 is treated as the file itself."""
323 if isinstance(f, str):
324 try:
325 f = open(f, 'r')
326 except IOError:
327
328
329 self.nameservers = ['127.0.0.1']
330 return
331 want_close = True
332 else:
333 want_close = False
334 try:
335 for l in f:
336 if len(l) == 0 or l[0] == '#' or l[0] == ';':
337 continue
338 tokens = l.split()
339 if len(tokens) == 0:
340 continue
341 if tokens[0] == 'nameserver':
342 self.nameservers.append(tokens[1])
343 elif tokens[0] == 'domain':
344 self.domain = dns.name.from_text(tokens[1])
345 elif tokens[0] == 'search':
346 for suffix in tokens[1:]:
347 self.search.append(dns.name.from_text(suffix))
348 finally:
349 if want_close:
350 f.close()
351 if len(self.nameservers) == 0:
352 self.nameservers.append('127.0.0.1')
353
355
356
357
358
359
360 if entry.find(' ') >= 0:
361 split_char = ' '
362 elif entry.find(',') >= 0:
363 split_char = ','
364 else:
365
366 split_char = ' '
367 return split_char
368
370 """Configure a NameServer registry entry."""
371 split_char = self._determine_split_char(nameservers)
372 ns_list = nameservers.split(split_char)
373 for ns in ns_list:
374 if not ns in self.nameservers:
375 self.nameservers.append(ns)
376
377 - def _config_win32_domain(self, domain):
378 """Configure a Domain registry entry."""
379 self.domain = dns.name.from_text(domain)
380
382 """Configure a Search registry entry."""
383 split_char = self._determine_split_char(search)
384 search_list = search.split(split_char)
385 for s in search_list:
386 if not s in self.search:
387 self.search.append(dns.name.from_text(s))
388
390 """Extract DNS info from a registry key."""
391 try:
392 servers, rtype = _winreg.QueryValueEx(key, 'NameServer')
393 except WindowsError:
394 servers = None
395 if servers:
396 self._config_win32_nameservers(servers)
397 try:
398 dom, rtype = _winreg.QueryValueEx(key, 'Domain')
399 if dom:
400 self._config_win32_domain(dom)
401 except WindowsError:
402 pass
403 else:
404 try:
405 servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer')
406 except WindowsError:
407 servers = None
408 if servers:
409 self._config_win32_nameservers(servers)
410 try:
411 dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain')
412 if dom:
413 self._config_win32_domain(dom)
414 except WindowsError:
415 pass
416 try:
417 search, rtype = _winreg.QueryValueEx(key, 'SearchList')
418 except WindowsError:
419 search = None
420 if search:
421 self._config_win32_search(search)
422
424 """Extract resolver configuration from the Windows registry."""
425 lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
426 want_scan = False
427 try:
428 try:
429
430 tcp_params = _winreg.OpenKey(lm,
431 r'SYSTEM\CurrentControlSet'
432 r'\Services\Tcpip\Parameters')
433 want_scan = True
434 except EnvironmentError:
435
436 tcp_params = _winreg.OpenKey(lm,
437 r'SYSTEM\CurrentControlSet'
438 r'\Services\VxD\MSTCP')
439 try:
440 self._config_win32_fromkey(tcp_params)
441 finally:
442 tcp_params.Close()
443 if want_scan:
444 interfaces = _winreg.OpenKey(lm,
445 r'SYSTEM\CurrentControlSet'
446 r'\Services\Tcpip\Parameters'
447 r'\Interfaces')
448 try:
449 i = 0
450 while True:
451 try:
452 guid = _winreg.EnumKey(interfaces, i)
453 i += 1
454 key = _winreg.OpenKey(interfaces, guid)
455 if not self._win32_is_nic_enabled(lm, guid, key):
456 continue
457 try:
458 self._config_win32_fromkey(key)
459 finally:
460 key.Close()
461 except EnvironmentError:
462 break
463 finally:
464 interfaces.Close()
465 finally:
466 lm.Close()
467
469
470
471
472
473
474 try:
475
476
477 connection_key = _winreg.OpenKey(
478 lm,
479 r'SYSTEM\CurrentControlSet\Control\Network'
480 r'\{4D36E972-E325-11CE-BFC1-08002BE10318}'
481 r'\%s\Connection' % guid)
482
483 try:
484
485 (pnp_id, ttype) = _winreg.QueryValueEx(
486 connection_key, 'PnpInstanceID')
487
488 if ttype != _winreg.REG_SZ:
489 raise ValueError
490
491 device_key = _winreg.OpenKey(
492 lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id)
493
494 try:
495
496 (flags, ttype) = _winreg.QueryValueEx(
497 device_key, 'ConfigFlags')
498
499 if ttype != _winreg.REG_DWORD:
500 raise ValueError
501
502
503
504 return not (flags & 0x1)
505
506 finally:
507 device_key.Close()
508 finally:
509 connection_key.Close()
510 except (EnvironmentError, ValueError):
511
512
513
514
515
516 try:
517 (nte, ttype) = _winreg.QueryValueEx(interface_key,
518 'NTEContextList')
519 return nte is not None
520 except WindowsError:
521 return False
522
524 now = time.time()
525 if now < start:
526 if start - now > 1:
527
528 raise Timeout
529 else:
530
531
532
533 now = start
534 duration = now - start
535 if duration >= self.lifetime:
536 raise Timeout
537 return min(self.lifetime - duration, self.timeout)
538
541 """Query nameservers to find the answer to the question.
542
543 The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects
544 of the appropriate type, or strings that can be converted into objects
545 of the appropriate type. E.g. For I{rdtype} the integer 2 and the
546 the string 'NS' both mean to query for records with DNS rdata type NS.
547
548 @param qname: the query name
549 @type qname: dns.name.Name object or string
550 @param rdtype: the query type
551 @type rdtype: int or string
552 @param rdclass: the query class
553 @type rdclass: int or string
554 @param tcp: use TCP to make the query (default is False).
555 @type tcp: bool
556 @param source: bind to this IP address (defaults to machine default IP).
557 @type source: IP address in dotted quad notation
558 @rtype: dns.resolver.Answer instance
559 @raises Timeout: no answers could be found in the specified lifetime
560 @raises NXDOMAIN: the query name does not exist
561 @raises NoAnswer: the response did not contain an answer
562 @raises NoNameservers: no non-broken nameservers are available to
563 answer the question."""
564
565 if isinstance(qname, str):
566 qname = dns.name.from_text(qname, None)
567 if isinstance(rdtype, str):
568 rdtype = dns.rdatatype.from_text(rdtype)
569 if isinstance(rdclass, str):
570 rdclass = dns.rdataclass.from_text(rdclass)
571 qnames_to_try = []
572 if qname.is_absolute():
573 qnames_to_try.append(qname)
574 else:
575 if len(qname) > 1:
576 qnames_to_try.append(qname.concatenate(dns.name.root))
577 if self.search:
578 for suffix in self.search:
579 qnames_to_try.append(qname.concatenate(suffix))
580 else:
581 qnames_to_try.append(qname.concatenate(self.domain))
582 all_nxdomain = True
583 start = time.time()
584 for qname in qnames_to_try:
585 if self.cache:
586 answer = self.cache.get((qname, rdtype, rdclass))
587 if answer:
588 return answer
589 request = dns.message.make_query(qname, rdtype, rdclass)
590 if not self.keyname is None:
591 request.use_tsig(self.keyring, self.keyname, self.keyalgorithm)
592 request.use_edns(self.edns, self.ednsflags, self.payload)
593 response = None
594
595
596
597 nameservers = self.nameservers[:]
598 backoff = 0.10
599 while response is None:
600 if len(nameservers) == 0:
601 raise NoNameservers
602 for nameserver in nameservers[:]:
603 timeout = self._compute_timeout(start)
604 try:
605 if tcp:
606 response = dns.query.tcp(request, nameserver,
607 timeout, self.port,
608 source=source)
609 else:
610 response = dns.query.udp(request, nameserver,
611 timeout, self.port,
612 source=source)
613 except (socket.error, dns.exception.Timeout):
614
615
616
617
618 response = None
619 continue
620 except dns.query.UnexpectedSource:
621
622
623
624 response = None
625 continue
626 except dns.exception.FormError:
627
628
629
630
631
632 nameservers.remove(nameserver)
633 response = None
634 continue
635 rcode = response.rcode()
636 if rcode == dns.rcode.NOERROR or \
637 rcode == dns.rcode.NXDOMAIN:
638 break
639
640
641
642
643
644 if rcode != dns.rcode.SERVFAIL:
645 nameservers.remove(nameserver)
646 response = None
647 if not response is None:
648 break
649
650
651
652 if len(nameservers) > 0:
653
654
655
656
657 timeout = self._compute_timeout(start)
658 sleep_time = min(timeout, backoff)
659 backoff *= 2
660 time.sleep(sleep_time)
661 if response.rcode() == dns.rcode.NXDOMAIN:
662 continue
663 all_nxdomain = False
664 break
665 if all_nxdomain:
666 raise NXDOMAIN
667 answer = Answer(qname, rdtype, rdclass, response)
668 if self.cache:
669 self.cache.put((qname, rdtype, rdclass), answer)
670 return answer
671
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 @param algorithm: The TSIG key algorithm to use. The default
685 is dns.tsig.default_algorithm.
686 @type algorithm: string"""
687 self.keyring = keyring
688 if keyname is None:
689 self.keyname = self.keyring.keys()[0]
690 else:
691 self.keyname = keyname
692 self.keyalgorithm = algorithm
693
694 - def use_edns(self, edns, ednsflags, payload):
695 """Configure Edns.
696
697 @param edns: The EDNS level to use. The default is -1, no Edns.
698 @type edns: int
699 @param ednsflags: The EDNS flags
700 @type ednsflags: int
701 @param payload: The EDNS payload size. The default is 0.
702 @type payload: int"""
703
704 if edns is None:
705 edns = -1
706 self.edns = edns
707 self.ednsflags = ednsflags
708 self.payload = payload
709
710 default_resolver = None
711
718
721 """Query nameservers to find the answer to the question.
722
723 This is a convenience function that uses the default resolver
724 object to make the query.
725 @see: L{dns.resolver.Resolver.query} for more information on the
726 parameters."""
727 return get_default_resolver().query(qname, rdtype, rdclass, tcp, source)
728
757