Package dns :: Module resolver
[hide private]
[frames] | no frames]

Source Code for Module dns.resolver

  1  # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. 
  2  # 
  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. 
  7  # 
  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. 
 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 # 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. 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
50 -class NoNameservers(dns.exception.DNSException):
51 """No non-broken nameservers are available to answer the query.""" 52 pass
53
54 -class NotAbsolute(dns.exception.DNSException):
55 """Raised if an absolute domain name is required but a relative name 56 was provided.""" 57 pass
58
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!""" 62 pass
63
64 -class NoMetaqueries(dns.exception.DNSException):
65 """Metaqueries are not allowed.""" 66 pass
67 68
69 -class Answer(object):
70 """DNS stub resolver answer 71 72 Instances of this class bundle up the result of a successful DNS 73 resolution. 74 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 79 "answer.rrset[i:j]". 80 81 Note that CNAMEs or DNAMEs in the response may mean that answer 82 node's name might not be the query name. 83 84 @ivar qname: The query name 85 @type qname: dns.name.Name object 86 @ivar rdtype: The query type 87 @type rdtype: int 88 @ivar rdclass: The query class 89 @type rdclass: int 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) 96 @ivar canonical_name: The canonical name of the query name 97 @type canonical_name: dns.name.Name object 98 """
99 - def __init__(self, qname, rdtype, rdclass, response, 100 raise_on_no_answer=True):
101 self.qname = qname 102 self.rdtype = rdtype 103 self.rdclass = rdclass 104 self.response = response 105 min_ttl = -1 106 rrset = None 107 for count in xrange(0, 15): 108 try: 109 rrset = response.find_rrset(response.answer, qname, 110 rdclass, rdtype) 111 if min_ttl == -1 or rrset.ttl < min_ttl: 112 min_ttl = rrset.ttl 113 break 114 except KeyError: 115 if rdtype != dns.rdatatype.CNAME: 116 try: 117 crrset = response.find_rrset(response.answer, 118 qname, 119 rdclass, 120 dns.rdatatype.CNAME) 121 if min_ttl == -1 or crrset.ttl < min_ttl: 122 min_ttl = crrset.ttl 123 for rd in crrset: 124 qname = rd.target 125 break 126 continue 127 except KeyError: 128 if raise_on_no_answer: 129 raise NoAnswer 130 if raise_on_no_answer: 131 raise NoAnswer 132 if rrset is None and raise_on_no_answer: 133 raise NoAnswer 134 self.canonical_name = qname 135 self.rrset = rrset 136 if rrset is None: 137 while 1: 138 # Look for a SOA RR whose owner name is a superdomain 139 # of qname. 140 try: 141 srrset = response.find_rrset(response.authority, qname, 142 rdclass, dns.rdatatype.SOA) 143 if min_ttl == -1 or srrset.ttl < min_ttl: 144 min_ttl = srrset.ttl 145 if srrset[0].minimum < min_ttl: 146 min_ttl = srrset[0].minimum 147 break 148 except KeyError: 149 try: 150 qname = qname.parent() 151 except dns.name.NoParent: 152 break 153 self.expiration = time.time() + min_ttl
154
155 - def __getattr__(self, attr):
156 if attr == 'name': 157 return self.rrset.name 158 elif attr == 'ttl': 159 return self.rrset.ttl 160 elif attr == 'covers': 161 return self.rrset.covers 162 elif attr == 'rdclass': 163 return self.rrset.rdclass 164 elif attr == 'rdtype': 165 return self.rrset.rdtype 166 else: 167 raise AttributeError(attr)
168
169 - def __len__(self):
170 return len(self.rrset)
171
172 - def __iter__(self):
173 return iter(self.rrset)
174
175 - def __getitem__(self, i):
176 return self.rrset[i]
177
178 - def __delitem__(self, i):
179 del self.rrset[i]
180
181 - def __getslice__(self, i, j):
182 return self.rrset[i:j]
183
184 - def __delslice__(self, i, j):
185 del self.rrset[i:j]
186
187 -class Cache(object):
188 """Simple DNS answer cache. 189 190 @ivar data: A dictionary of cached data 191 @type data: dict 192 @ivar cleaning_interval: The number of seconds between cleanings. The 193 default is 300 (5 minutes). 194 @type cleaning_interval: float 195 @ivar next_cleaning: The time the cache should next be cleaned (in seconds 196 since the epoch.) 197 @type next_cleaning: float 198 """ 199
200 - def __init__(self, cleaning_interval=300.0):
201 """Initialize a DNS cache. 202 203 @param cleaning_interval: the number of seconds between periodic 204 cleanings. The default is 300.0 205 @type cleaning_interval: float. 206 """ 207 208 self.data = {} 209 self.cleaning_interval = cleaning_interval 210 self.next_cleaning = time.time() + self.cleaning_interval
211
212 - def maybe_clean(self):
213 """Clean the cache if it's time to do so.""" 214 215 now = time.time() 216 if self.next_cleaning <= now: 217 keys_to_delete = [] 218 for (k, v) in self.data.iteritems(): 219 if v.expiration <= now: 220 keys_to_delete.append(k) 221 for k in keys_to_delete: 222 del self.data[k] 223 now = time.time() 224 self.next_cleaning = now + self.cleaning_interval
225
226 - def get(self, key):
227 """Get the answer associated with I{key}. Returns None if 228 no answer is cached for the key. 229 @param key: the key 230 @type key: (dns.name.Name, int, int) tuple whose values are the 231 query name, rdtype, and rdclass. 232 @rtype: dns.resolver.Answer object or None 233 """ 234 235 self.maybe_clean() 236 v = self.data.get(key) 237 if v is None or v.expiration <= time.time(): 238 return None 239 return v
240
241 - def put(self, key, value):
242 """Associate key and value in the cache. 243 @param key: the key 244 @type key: (dns.name.Name, int, int) tuple whose values are the 245 query name, rdtype, and rdclass. 246 @param value: The answer being cached 247 @type value: dns.resolver.Answer object 248 """ 249 250 self.maybe_clean() 251 self.data[key] = value
252
253 - def flush(self, key=None):
254 """Flush the cache. 255 256 If I{key} is specified, only that item is flushed. Otherwise 257 the entire cache is flushed. 258 259 @param key: the key to flush 260 @type key: (dns.name.Name, int, int) tuple or None 261 """ 262 263 if not key is None: 264 if self.data.has_key(key): 265 del self.data[key] 266 else: 267 self.data = {} 268 self.next_cleaning = time.time() + self.cleaning_interval
269
270 -class Resolver(object):
271 """DNS stub resolver 272 273 @ivar domain: The domain of this host 274 @type domain: dns.name.Name object 275 @ivar nameservers: A list of nameservers to query. Each nameserver is 276 a string which contains the IP address of a nameserver. 277 @type nameservers: list of strings 278 @ivar search: The search list. If the query name is a relative name, 279 the resolver will construct an absolute query name by appending the search 280 names one by one to the query name. 281 @type search: list of dns.name.Name objects 282 @ivar port: The port to which to send queries. The default is 53. 283 @type port: int 284 @ivar timeout: The number of seconds to wait for a response from a 285 server, before timing out. 286 @type timeout: float 287 @ivar lifetime: The total number of seconds to spend trying to get an 288 answer to the question. If the lifetime expires, a Timeout exception 289 will occur. 290 @type lifetime: float 291 @ivar keyring: The TSIG keyring to use. The default is None. 292 @type keyring: dict 293 @ivar keyname: The TSIG keyname to use. The default is None. 294 @type keyname: dns.name.Name object 295 @ivar keyalgorithm: The TSIG key algorithm to use. The default is 296 dns.tsig.default_algorithm. 297 @type keyalgorithm: string 298 @ivar edns: The EDNS level to use. The default is -1, no Edns. 299 @type edns: int 300 @ivar ednsflags: The EDNS flags 301 @type ednsflags: int 302 @ivar payload: The EDNS payload size. The default is 0. 303 @type payload: int 304 @ivar cache: The cache to use. The default is None. 305 @type cache: dns.resolver.Cache object 306 """
307 - def __init__(self, filename='/etc/resolv.conf', configure=True):
308 """Initialize a resolver instance. 309 310 @param filename: The filename of a configuration file in 311 standard /etc/resolv.conf format. This parameter is meaningful 312 only when I{configure} is true and the platform is POSIX. 313 @type filename: string or file object 314 @param configure: If True (the default), the resolver instance 315 is configured in the normal fashion for the operating system 316 the resolver is running on. (I.e. a /etc/resolv.conf file on 317 POSIX systems and from the registry on Windows systems.) 318 @type configure: bool""" 319 320 self.reset() 321 if configure: 322 if sys.platform == 'win32': 323 self.read_registry() 324 elif filename: 325 self.read_resolv_conf(filename)
326
327 - def reset(self):
328 """Reset all resolver configuration to the defaults.""" 329 self.domain = \ 330 dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) 331 if len(self.domain) == 0: 332 self.domain = dns.name.root 333 self.nameservers = [] 334 self.search = [] 335 self.port = 53 336 self.timeout = 2.0 337 self.lifetime = 30.0 338 self.keyring = None 339 self.keyname = None 340 self.keyalgorithm = dns.tsig.default_algorithm 341 self.edns = -1 342 self.ednsflags = 0 343 self.payload = 0 344 self.cache = None
345
346 - def read_resolv_conf(self, f):
347 """Process f as a file in the /etc/resolv.conf format. If f is 348 a string, it is used as the name of the file to open; otherwise it 349 is treated as the file itself.""" 350 if isinstance(f, str) or isinstance(f, unicode): 351 try: 352 f = open(f, 'r') 353 except IOError: 354 # /etc/resolv.conf doesn't exist, can't be read, etc. 355 # We'll just use the default resolver configuration. 356 self.nameservers = ['127.0.0.1'] 357 return 358 want_close = True 359 else: 360 want_close = False 361 try: 362 for l in f: 363 if len(l) == 0 or l[0] == '#' or l[0] == ';': 364 continue 365 tokens = l.split() 366 if len(tokens) == 0: 367 continue 368 if tokens[0] == 'nameserver': 369 self.nameservers.append(tokens[1]) 370 elif tokens[0] == 'domain': 371 self.domain = dns.name.from_text(tokens[1]) 372 elif tokens[0] == 'search': 373 for suffix in tokens[1:]: 374 self.search.append(dns.name.from_text(suffix)) 375 finally: 376 if want_close: 377 f.close() 378 if len(self.nameservers) == 0: 379 self.nameservers.append('127.0.0.1')
380
381 - def _determine_split_char(self, entry):
382 # 383 # The windows registry irritatingly changes the list element 384 # delimiter in between ' ' and ',' (and vice-versa) in various 385 # versions of windows. 386 # 387 if entry.find(' ') >= 0: 388 split_char = ' ' 389 elif entry.find(',') >= 0: 390 split_char = ',' 391 else: 392 # probably a singleton; treat as a space-separated list. 393 split_char = ' ' 394 return split_char
395
396 - def _config_win32_nameservers(self, nameservers):
397 """Configure a NameServer registry entry.""" 398 # we call str() on nameservers to convert it from unicode to ascii 399 nameservers = str(nameservers) 400 split_char = self._determine_split_char(nameservers) 401 ns_list = nameservers.split(split_char) 402 for ns in ns_list: 403 if not ns in self.nameservers: 404 self.nameservers.append(ns)
405
406 - def _config_win32_domain(self, domain):
407 """Configure a Domain registry entry.""" 408 # we call str() on domain to convert it from unicode to ascii 409 self.domain = dns.name.from_text(str(domain))
410
411 - def _config_win32_search(self, search):
412 """Configure a Search registry entry.""" 413 # we call str() on search to convert it from unicode to ascii 414 search = str(search) 415 split_char = self._determine_split_char(search) 416 search_list = search.split(split_char) 417 for s in search_list: 418 if not s in self.search: 419 self.search.append(dns.name.from_text(s))
420
421 - def _config_win32_fromkey(self, key):
422 """Extract DNS info from a registry key.""" 423 try: 424 servers, rtype = _winreg.QueryValueEx(key, 'NameServer') 425 except WindowsError: 426 servers = None 427 if servers: 428 self._config_win32_nameservers(servers) 429 try: 430 dom, rtype = _winreg.QueryValueEx(key, 'Domain') 431 if dom: 432 self._config_win32_domain(dom) 433 except WindowsError: 434 pass 435 else: 436 try: 437 servers, rtype = _winreg.QueryValueEx(key, 'DhcpNameServer') 438 except WindowsError: 439 servers = None 440 if servers: 441 self._config_win32_nameservers(servers) 442 try: 443 dom, rtype = _winreg.QueryValueEx(key, 'DhcpDomain') 444 if dom: 445 self._config_win32_domain(dom) 446 except WindowsError: 447 pass 448 try: 449 search, rtype = _winreg.QueryValueEx(key, 'SearchList') 450 except WindowsError: 451 search = None 452 if search: 453 self._config_win32_search(search)
454
455 - def read_registry(self):
456 """Extract resolver configuration from the Windows registry.""" 457 lm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) 458 want_scan = False 459 try: 460 try: 461 # XP, 2000 462 tcp_params = _winreg.OpenKey(lm, 463 r'SYSTEM\CurrentControlSet' 464 r'\Services\Tcpip\Parameters') 465 want_scan = True 466 except EnvironmentError: 467 # ME 468 tcp_params = _winreg.OpenKey(lm, 469 r'SYSTEM\CurrentControlSet' 470 r'\Services\VxD\MSTCP') 471 try: 472 self._config_win32_fromkey(tcp_params) 473 finally: 474 tcp_params.Close() 475 if want_scan: 476 interfaces = _winreg.OpenKey(lm, 477 r'SYSTEM\CurrentControlSet' 478 r'\Services\Tcpip\Parameters' 479 r'\Interfaces') 480 try: 481 i = 0 482 while True: 483 try: 484 guid = _winreg.EnumKey(interfaces, i) 485 i += 1 486 key = _winreg.OpenKey(interfaces, guid) 487 if not self._win32_is_nic_enabled(lm, guid, key): 488 continue 489 try: 490 self._config_win32_fromkey(key) 491 finally: 492 key.Close() 493 except EnvironmentError: 494 break 495 finally: 496 interfaces.Close() 497 finally: 498 lm.Close()
499
500 - def _win32_is_nic_enabled(self, lm, guid, interface_key):
501 # Look in the Windows Registry to determine whether the network 502 # interface corresponding to the given guid is enabled. 503 # 504 # (Code contributed by Paul Marks, thanks!) 505 # 506 try: 507 # This hard-coded location seems to be consistent, at least 508 # from Windows 2000 through Vista. 509 connection_key = _winreg.OpenKey( 510 lm, 511 r'SYSTEM\CurrentControlSet\Control\Network' 512 r'\{4D36E972-E325-11CE-BFC1-08002BE10318}' 513 r'\%s\Connection' % guid) 514 515 try: 516 # The PnpInstanceID points to a key inside Enum 517 (pnp_id, ttype) = _winreg.QueryValueEx( 518 connection_key, 'PnpInstanceID') 519 520 if ttype != _winreg.REG_SZ: 521 raise ValueError 522 523 device_key = _winreg.OpenKey( 524 lm, r'SYSTEM\CurrentControlSet\Enum\%s' % pnp_id) 525 526 try: 527 # Get ConfigFlags for this device 528 (flags, ttype) = _winreg.QueryValueEx( 529 device_key, 'ConfigFlags') 530 531 if ttype != _winreg.REG_DWORD: 532 raise ValueError 533 534 # Based on experimentation, bit 0x1 indicates that the 535 # device is disabled. 536 return not (flags & 0x1) 537 538 finally: 539 device_key.Close() 540 finally: 541 connection_key.Close() 542 except (EnvironmentError, ValueError): 543 # Pre-vista, enabled interfaces seem to have a non-empty 544 # NTEContextList; this was how dnspython detected enabled 545 # nics before the code above was contributed. We've retained 546 # the old method since we don't know if the code above works 547 # on Windows 95/98/ME. 548 try: 549 (nte, ttype) = _winreg.QueryValueEx(interface_key, 550 'NTEContextList') 551 return nte is not None 552 except WindowsError: 553 return False
554
555 - def _compute_timeout(self, start):
556 now = time.time() 557 if now < start: 558 if start - now > 1: 559 # Time going backwards is bad. Just give up. 560 raise Timeout 561 else: 562 # Time went backwards, but only a little. This can 563 # happen, e.g. under vmware with older linux kernels. 564 # Pretend it didn't happen. 565 now = start 566 duration = now - start 567 if duration >= self.lifetime: 568 raise Timeout 569 return min(self.lifetime - duration, self.timeout)
570
571 - def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, 572 tcp=False, source=None, raise_on_no_answer=True):
573 """Query nameservers to find the answer to the question. 574 575 The I{qname}, I{rdtype}, and I{rdclass} parameters may be objects 576 of the appropriate type, or strings that can be converted into objects 577 of the appropriate type. E.g. For I{rdtype} the integer 2 and the 578 the string 'NS' both mean to query for records with DNS rdata type NS. 579 580 @param qname: the query name 581 @type qname: dns.name.Name object or string 582 @param rdtype: the query type 583 @type rdtype: int or string 584 @param rdclass: the query class 585 @type rdclass: int or string 586 @param tcp: use TCP to make the query (default is False). 587 @type tcp: bool 588 @param source: bind to this IP address (defaults to machine default IP). 589 @type source: IP address in dotted quad notation 590 @param raise_on_no_answer: raise NoAnswer if there's no answer 591 (defaults is True). 592 @type raise_on_no_answer: bool 593 @rtype: dns.resolver.Answer instance 594 @raises Timeout: no answers could be found in the specified lifetime 595 @raises NXDOMAIN: the query name does not exist 596 @raises NoAnswer: the response did not contain an answer and 597 raise_on_no_answer is True. 598 @raises NoNameservers: no non-broken nameservers are available to 599 answer the question.""" 600 601 if isinstance(qname, (str, unicode)): 602 qname = dns.name.from_text(qname, None) 603 if isinstance(rdtype, (str, unicode)): 604 rdtype = dns.rdatatype.from_text(rdtype) 605 if dns.rdatatype.is_metatype(rdtype): 606 raise NoMetaqueries 607 if isinstance(rdclass, (str, unicode)): 608 rdclass = dns.rdataclass.from_text(rdclass) 609 if dns.rdataclass.is_metaclass(rdclass): 610 raise NoMetaqueries 611 qnames_to_try = [] 612 if qname.is_absolute(): 613 qnames_to_try.append(qname) 614 else: 615 if len(qname) > 1: 616 qnames_to_try.append(qname.concatenate(dns.name.root)) 617 if self.search: 618 for suffix in self.search: 619 qnames_to_try.append(qname.concatenate(suffix)) 620 else: 621 qnames_to_try.append(qname.concatenate(self.domain)) 622 all_nxdomain = True 623 start = time.time() 624 for qname in qnames_to_try: 625 if self.cache: 626 answer = self.cache.get((qname, rdtype, rdclass)) 627 if answer: 628 return answer 629 request = dns.message.make_query(qname, rdtype, rdclass) 630 if not self.keyname is None: 631 request.use_tsig(self.keyring, self.keyname, 632 algorithm=self.keyalgorithm) 633 request.use_edns(self.edns, self.ednsflags, self.payload) 634 response = None 635 # 636 # make a copy of the servers list so we can alter it later. 637 # 638 nameservers = self.nameservers[:] 639 backoff = 0.10 640 while response is None: 641 if len(nameservers) == 0: 642 raise NoNameservers 643 for nameserver in nameservers[:]: 644 timeout = self._compute_timeout(start) 645 try: 646 if tcp: 647 response = dns.query.tcp(request, nameserver, 648 timeout, self.port, 649 source=source) 650 else: 651 response = dns.query.udp(request, nameserver, 652 timeout, self.port, 653 source=source) 654 except (socket.error, dns.exception.Timeout): 655 # 656 # Communication failure or timeout. Go to the 657 # next server 658 # 659 response = None 660 continue 661 except dns.query.UnexpectedSource: 662 # 663 # Who knows? Keep going. 664 # 665 response = None 666 continue 667 except dns.exception.FormError: 668 # 669 # We don't understand what this server is 670 # saying. Take it out of the mix and 671 # continue. 672 # 673 nameservers.remove(nameserver) 674 response = None 675 continue 676 rcode = response.rcode() 677 if rcode == dns.rcode.NOERROR or \ 678 rcode == dns.rcode.NXDOMAIN: 679 break 680 # 681 # We got a response, but we're not happy with the 682 # rcode in it. Remove the server from the mix if 683 # the rcode isn't SERVFAIL. 684 # 685 if rcode != dns.rcode.SERVFAIL: 686 nameservers.remove(nameserver) 687 response = None 688 if not response is None: 689 break 690 # 691 # All nameservers failed! 692 # 693 if len(nameservers) > 0: 694 # 695 # But we still have servers to try. Sleep a bit 696 # so we don't pound them! 697 # 698 timeout = self._compute_timeout(start) 699 sleep_time = min(timeout, backoff) 700 backoff *= 2 701 time.sleep(sleep_time) 702 if response.rcode() == dns.rcode.NXDOMAIN: 703 continue 704 all_nxdomain = False 705 break 706 if all_nxdomain: 707 raise NXDOMAIN 708 answer = Answer(qname, rdtype, rdclass, response, 709 raise_on_no_answer) 710 if self.cache: 711 self.cache.put((qname, rdtype, rdclass), answer) 712 return answer
713
714 - def use_tsig(self, keyring, keyname=None, 715 algorithm=dns.tsig.default_algorithm):
716 """Add a TSIG signature to the query. 717 718 @param keyring: The TSIG keyring to use; defaults to None. 719 @type keyring: dict 720 @param keyname: The name of the TSIG key to use; defaults to None. 721 The key must be defined in the keyring. If a keyring is specified 722 but a keyname is not, then the key used will be the first key in the 723 keyring. Note that the order of keys in a dictionary is not defined, 724 so applications should supply a keyname when a keyring is used, unless 725 they know the keyring contains only one key. 726 @param algorithm: The TSIG key algorithm to use. The default 727 is dns.tsig.default_algorithm. 728 @type algorithm: string""" 729 self.keyring = keyring 730 if keyname is None: 731 self.keyname = self.keyring.keys()[0] 732 else: 733 self.keyname = keyname 734 self.keyalgorithm = algorithm
735
736 - def use_edns(self, edns, ednsflags, payload):
737 """Configure Edns. 738 739 @param edns: The EDNS level to use. The default is -1, no Edns. 740 @type edns: int 741 @param ednsflags: The EDNS flags 742 @type ednsflags: int 743 @param payload: The EDNS payload size. The default is 0. 744 @type payload: int""" 745 746 if edns is None: 747 edns = -1 748 self.edns = edns 749 self.ednsflags = ednsflags 750 self.payload = payload
751 752 default_resolver = None 753
754 -def get_default_resolver():
755 """Get the default resolver, initializing it if necessary.""" 756 global default_resolver 757 if default_resolver is None: 758 default_resolver = Resolver() 759 return default_resolver
760
761 -def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN, 762 tcp=False, source=None, raise_on_no_answer=True):
763 """Query nameservers to find the answer to the question. 764 765 This is a convenience function that uses the default resolver 766 object to make the query. 767 @see: L{dns.resolver.Resolver.query} for more information on the 768 parameters.""" 769 return get_default_resolver().query(qname, rdtype, rdclass, tcp, source, 770 raise_on_no_answer)
771
772 -def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None):
773 """Find the name of the zone which contains the specified name. 774 775 @param name: the query name 776 @type name: absolute dns.name.Name object or string 777 @param rdclass: The query class 778 @type rdclass: int 779 @param tcp: use TCP to make the query (default is False). 780 @type tcp: bool 781 @param resolver: the resolver to use 782 @type resolver: dns.resolver.Resolver object or None 783 @rtype: dns.name.Name""" 784 785 if isinstance(name, (str, unicode)): 786 name = dns.name.from_text(name, dns.name.root) 787 if resolver is None: 788 resolver = get_default_resolver() 789 if not name.is_absolute(): 790 raise NotAbsolute(name) 791 while 1: 792 try: 793 answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp) 794 if answer.rrset.name == name: 795 return name 796 # otherwise we were CNAMEd or DNAMEd and need to look higher 797 except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): 798 pass 799 try: 800 name = name.parent() 801 except dns.name.NoParent: 802 raise NoRootSOA
803