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

Source Code for Module dns.query

  1  # Copyright (C) 2003-2007, 2009-2011 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  """Talk to a DNS server.""" 
 17   
 18  from __future__ import generators 
 19   
 20  import errno 
 21  import select 
 22  import socket 
 23  import struct 
 24  import sys 
 25  import time 
 26   
 27  import dns.exception 
 28  import dns.inet 
 29  import dns.name 
 30  import dns.message 
 31  import dns.rdataclass 
 32  import dns.rdatatype 
 33  from ._compat import long, string_types 
 34   
 35  if sys.version_info > (3,): 
 36      select_error = OSError 
 37  else: 
 38      select_error = select.error 
 39   
 40  # Function used to create a socket.  Can be overridden if needed in special 
 41  # situations. 
 42  socket_factory = socket.socket 
 43   
44 -class UnexpectedSource(dns.exception.DNSException):
45 46 """A DNS query response came from an unexpected address or port."""
47 48
49 -class BadResponse(dns.exception.FormError):
50 51 """A DNS query response does not respond to the question asked."""
52 53
54 -def _compute_expiration(timeout):
55 if timeout is None: 56 return None 57 else: 58 return time.time() + timeout
59 60
61 -def _poll_for(fd, readable, writable, error, timeout):
62 """Poll polling backend. 63 @param fd: File descriptor 64 @type fd: int 65 @param readable: Whether to wait for readability 66 @type readable: bool 67 @param writable: Whether to wait for writability 68 @type writable: bool 69 @param timeout: Deadline timeout (expiration time, in seconds) 70 @type timeout: float 71 @return True on success, False on timeout 72 """ 73 event_mask = 0 74 if readable: 75 event_mask |= select.POLLIN 76 if writable: 77 event_mask |= select.POLLOUT 78 if error: 79 event_mask |= select.POLLERR 80 81 pollable = select.poll() 82 pollable.register(fd, event_mask) 83 84 if timeout: 85 event_list = pollable.poll(long(timeout * 1000)) 86 else: 87 event_list = pollable.poll() 88 89 return bool(event_list)
90 91
92 -def _select_for(fd, readable, writable, error, timeout):
93 """Select polling backend. 94 @param fd: File descriptor 95 @type fd: int 96 @param readable: Whether to wait for readability 97 @type readable: bool 98 @param writable: Whether to wait for writability 99 @type writable: bool 100 @param timeout: Deadline timeout (expiration time, in seconds) 101 @type timeout: float 102 @return True on success, False on timeout 103 """ 104 rset, wset, xset = [], [], [] 105 106 if readable: 107 rset = [fd] 108 if writable: 109 wset = [fd] 110 if error: 111 xset = [fd] 112 113 if timeout is None: 114 (rcount, wcount, xcount) = select.select(rset, wset, xset) 115 else: 116 (rcount, wcount, xcount) = select.select(rset, wset, xset, timeout) 117 118 return bool((rcount or wcount or xcount))
119 120
121 -def _wait_for(fd, readable, writable, error, expiration):
122 done = False 123 while not done: 124 if expiration is None: 125 timeout = None 126 else: 127 timeout = expiration - time.time() 128 if timeout <= 0.0: 129 raise dns.exception.Timeout 130 try: 131 if not _polling_backend(fd, readable, writable, error, timeout): 132 raise dns.exception.Timeout 133 except select_error as e: 134 if e.args[0] != errno.EINTR: 135 raise e 136 done = True
137 138
139 -def _set_polling_backend(fn):
140 """ 141 Internal API. Do not use. 142 """ 143 global _polling_backend 144 145 _polling_backend = fn
146 147 if hasattr(select, 'poll'): 148 # Prefer poll() on platforms that support it because it has no 149 # limits on the maximum value of a file descriptor (plus it will 150 # be more efficient for high values). 151 _polling_backend = _poll_for 152 else: 153 _polling_backend = _select_for 154 155
156 -def _wait_for_readable(s, expiration):
157 _wait_for(s, True, False, True, expiration)
158 159
160 -def _wait_for_writable(s, expiration):
161 _wait_for(s, False, True, True, expiration)
162 163
164 -def _addresses_equal(af, a1, a2):
165 # Convert the first value of the tuple, which is a textual format 166 # address into binary form, so that we are not confused by different 167 # textual representations of the same address 168 n1 = dns.inet.inet_pton(af, a1[0]) 169 n2 = dns.inet.inet_pton(af, a2[0]) 170 return n1 == n2 and a1[1:] == a2[1:]
171 172
173 -def _destination_and_source(af, where, port, source, source_port):
174 # Apply defaults and compute destination and source tuples 175 # suitable for use in connect(), sendto(), or bind(). 176 if af is None: 177 try: 178 af = dns.inet.af_for_address(where) 179 except Exception: 180 af = dns.inet.AF_INET 181 if af == dns.inet.AF_INET: 182 destination = (where, port) 183 if source is not None or source_port != 0: 184 if source is None: 185 source = '0.0.0.0' 186 source = (source, source_port) 187 elif af == dns.inet.AF_INET6: 188 destination = (where, port, 0, 0) 189 if source is not None or source_port != 0: 190 if source is None: 191 source = '::' 192 source = (source, source_port, 0, 0) 193 return (af, destination, source)
194 195
196 -def udp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, 197 ignore_unexpected=False, one_rr_per_rrset=False):
198 """Return the response obtained after sending a query via UDP. 199 200 @param q: the query 201 @type q: dns.message.Message 202 @param where: where to send the message 203 @type where: string containing an IPv4 or IPv6 address 204 @param timeout: The number of seconds to wait before the query times out. 205 If None, the default, wait forever. 206 @type timeout: float 207 @param port: The port to which to send the message. The default is 53. 208 @type port: int 209 @param af: the address family to use. The default is None, which 210 causes the address family to use to be inferred from the form of where. 211 If the inference attempt fails, AF_INET is used. 212 @type af: int 213 @rtype: dns.message.Message object 214 @param source: source address. The default is the wildcard address. 215 @type source: string 216 @param source_port: The port from which to send the message. 217 The default is 0. 218 @type source_port: int 219 @param ignore_unexpected: If True, ignore responses from unexpected 220 sources. The default is False. 221 @type ignore_unexpected: bool 222 @param one_rr_per_rrset: Put each RR into its own RRset 223 @type one_rr_per_rrset: bool 224 """ 225 226 wire = q.to_wire() 227 (af, destination, source) = _destination_and_source(af, where, port, 228 source, source_port) 229 s = socket_factory(af, socket.SOCK_DGRAM, 0) 230 begin_time = None 231 try: 232 expiration = _compute_expiration(timeout) 233 s.setblocking(0) 234 if source is not None: 235 s.bind(source) 236 _wait_for_writable(s, expiration) 237 begin_time = time.time() 238 s.sendto(wire, destination) 239 while 1: 240 _wait_for_readable(s, expiration) 241 (wire, from_address) = s.recvfrom(65535) 242 if _addresses_equal(af, from_address, destination) or \ 243 (dns.inet.is_multicast(where) and 244 from_address[1:] == destination[1:]): 245 break 246 if not ignore_unexpected: 247 raise UnexpectedSource('got a response from ' 248 '%s instead of %s' % (from_address, 249 destination)) 250 finally: 251 if begin_time is None: 252 response_time = 0 253 else: 254 response_time = time.time() - begin_time 255 s.close() 256 r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, 257 one_rr_per_rrset=one_rr_per_rrset) 258 r.time = response_time 259 if not q.is_response(r): 260 raise BadResponse 261 return r
262 263
264 -def _net_read(sock, count, expiration):
265 """Read the specified number of bytes from sock. Keep trying until we 266 either get the desired amount, or we hit EOF. 267 A Timeout exception will be raised if the operation is not completed 268 by the expiration time. 269 """ 270 s = b'' 271 while count > 0: 272 _wait_for_readable(sock, expiration) 273 n = sock.recv(count) 274 if n == b'': 275 raise EOFError 276 count = count - len(n) 277 s = s + n 278 return s
279 280
281 -def _net_write(sock, data, expiration):
282 """Write the specified data to the socket. 283 A Timeout exception will be raised if the operation is not completed 284 by the expiration time. 285 """ 286 current = 0 287 l = len(data) 288 while current < l: 289 _wait_for_writable(sock, expiration) 290 current += sock.send(data[current:])
291 292
293 -def _connect(s, address):
294 try: 295 s.connect(address) 296 except socket.error: 297 (ty, v) = sys.exc_info()[:2] 298 299 if hasattr(v, 'errno'): 300 v_err = v.errno 301 else: 302 v_err = v[0] 303 if v_err not in [errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EALREADY]: 304 raise v
305 306
307 -def tcp(q, where, timeout=None, port=53, af=None, source=None, source_port=0, 308 one_rr_per_rrset=False):
309 """Return the response obtained after sending a query via TCP. 310 311 @param q: the query 312 @type q: dns.message.Message object 313 @param where: where to send the message 314 @type where: string containing an IPv4 or IPv6 address 315 @param timeout: The number of seconds to wait before the query times out. 316 If None, the default, wait forever. 317 @type timeout: float 318 @param port: The port to which to send the message. The default is 53. 319 @type port: int 320 @param af: the address family to use. The default is None, which 321 causes the address family to use to be inferred from the form of where. 322 If the inference attempt fails, AF_INET is used. 323 @type af: int 324 @rtype: dns.message.Message object 325 @param source: source address. The default is the wildcard address. 326 @type source: string 327 @param source_port: The port from which to send the message. 328 The default is 0. 329 @type source_port: int 330 @param one_rr_per_rrset: Put each RR into its own RRset 331 @type one_rr_per_rrset: bool 332 """ 333 334 wire = q.to_wire() 335 (af, destination, source) = _destination_and_source(af, where, port, 336 source, source_port) 337 s = socket_factory(af, socket.SOCK_STREAM, 0) 338 begin_time = None 339 try: 340 expiration = _compute_expiration(timeout) 341 s.setblocking(0) 342 begin_time = time.time() 343 if source is not None: 344 s.bind(source) 345 _connect(s, destination) 346 347 l = len(wire) 348 349 # copying the wire into tcpmsg is inefficient, but lets us 350 # avoid writev() or doing a short write that would get pushed 351 # onto the net 352 tcpmsg = struct.pack("!H", l) + wire 353 _net_write(s, tcpmsg, expiration) 354 ldata = _net_read(s, 2, expiration) 355 (l,) = struct.unpack("!H", ldata) 356 wire = _net_read(s, l, expiration) 357 finally: 358 if begin_time is None: 359 response_time = 0 360 else: 361 response_time = time.time() - begin_time 362 s.close() 363 r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, 364 one_rr_per_rrset=one_rr_per_rrset) 365 r.time = response_time 366 if not q.is_response(r): 367 raise BadResponse 368 return r
369 370
371 -def xfr(where, zone, rdtype=dns.rdatatype.AXFR, rdclass=dns.rdataclass.IN, 372 timeout=None, port=53, keyring=None, keyname=None, relativize=True, 373 af=None, lifetime=None, source=None, source_port=0, serial=0, 374 use_udp=False, keyalgorithm=dns.tsig.default_algorithm):
375 """Return a generator for the responses to a zone transfer. 376 377 @param where: where to send the message 378 @type where: string containing an IPv4 or IPv6 address 379 @param zone: The name of the zone to transfer 380 @type zone: dns.name.Name object or string 381 @param rdtype: The type of zone transfer. The default is 382 dns.rdatatype.AXFR. 383 @type rdtype: int or string 384 @param rdclass: The class of the zone transfer. The default is 385 dns.rdataclass.IN. 386 @type rdclass: int or string 387 @param timeout: The number of seconds to wait for each response message. 388 If None, the default, wait forever. 389 @type timeout: float 390 @param port: The port to which to send the message. The default is 53. 391 @type port: int 392 @param keyring: The TSIG keyring to use 393 @type keyring: dict 394 @param keyname: The name of the TSIG key to use 395 @type keyname: dns.name.Name object or string 396 @param relativize: If True, all names in the zone will be relativized to 397 the zone origin. It is essential that the relativize setting matches 398 the one specified to dns.zone.from_xfr(). 399 @type relativize: bool 400 @param af: the address family to use. The default is None, which 401 causes the address family to use to be inferred from the form of where. 402 If the inference attempt fails, AF_INET is used. 403 @type af: int 404 @param lifetime: The total number of seconds to spend doing the transfer. 405 If None, the default, then there is no limit on the time the transfer may 406 take. 407 @type lifetime: float 408 @rtype: generator of dns.message.Message objects. 409 @param source: source address. The default is the wildcard address. 410 @type source: string 411 @param source_port: The port from which to send the message. 412 The default is 0. 413 @type source_port: int 414 @param serial: The SOA serial number to use as the base for an IXFR diff 415 sequence (only meaningful if rdtype == dns.rdatatype.IXFR). 416 @type serial: int 417 @param use_udp: Use UDP (only meaningful for IXFR) 418 @type use_udp: bool 419 @param keyalgorithm: The TSIG algorithm to use; defaults to 420 dns.tsig.default_algorithm 421 @type keyalgorithm: string 422 """ 423 424 if isinstance(zone, string_types): 425 zone = dns.name.from_text(zone) 426 if isinstance(rdtype, string_types): 427 rdtype = dns.rdatatype.from_text(rdtype) 428 q = dns.message.make_query(zone, rdtype, rdclass) 429 if rdtype == dns.rdatatype.IXFR: 430 rrset = dns.rrset.from_text(zone, 0, 'IN', 'SOA', 431 '. . %u 0 0 0 0' % serial) 432 q.authority.append(rrset) 433 if keyring is not None: 434 q.use_tsig(keyring, keyname, algorithm=keyalgorithm) 435 wire = q.to_wire() 436 (af, destination, source) = _destination_and_source(af, where, port, 437 source, source_port) 438 if use_udp: 439 if rdtype != dns.rdatatype.IXFR: 440 raise ValueError('cannot do a UDP AXFR') 441 s = socket_factory(af, socket.SOCK_DGRAM, 0) 442 else: 443 s = socket_factory(af, socket.SOCK_STREAM, 0) 444 s.setblocking(0) 445 if source is not None: 446 s.bind(source) 447 expiration = _compute_expiration(lifetime) 448 _connect(s, destination) 449 l = len(wire) 450 if use_udp: 451 _wait_for_writable(s, expiration) 452 s.send(wire) 453 else: 454 tcpmsg = struct.pack("!H", l) + wire 455 _net_write(s, tcpmsg, expiration) 456 done = False 457 delete_mode = True 458 expecting_SOA = False 459 soa_rrset = None 460 if relativize: 461 origin = zone 462 oname = dns.name.empty 463 else: 464 origin = None 465 oname = zone 466 tsig_ctx = None 467 first = True 468 while not done: 469 mexpiration = _compute_expiration(timeout) 470 if mexpiration is None or mexpiration > expiration: 471 mexpiration = expiration 472 if use_udp: 473 _wait_for_readable(s, expiration) 474 (wire, from_address) = s.recvfrom(65535) 475 else: 476 ldata = _net_read(s, 2, mexpiration) 477 (l,) = struct.unpack("!H", ldata) 478 wire = _net_read(s, l, mexpiration) 479 is_ixfr = (rdtype == dns.rdatatype.IXFR) 480 r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac, 481 xfr=True, origin=origin, tsig_ctx=tsig_ctx, 482 multi=True, first=first, 483 one_rr_per_rrset=is_ixfr) 484 tsig_ctx = r.tsig_ctx 485 first = False 486 answer_index = 0 487 if soa_rrset is None: 488 if not r.answer or r.answer[0].name != oname: 489 raise dns.exception.FormError( 490 "No answer or RRset not for qname") 491 rrset = r.answer[0] 492 if rrset.rdtype != dns.rdatatype.SOA: 493 raise dns.exception.FormError("first RRset is not an SOA") 494 answer_index = 1 495 soa_rrset = rrset.copy() 496 if rdtype == dns.rdatatype.IXFR: 497 if soa_rrset[0].serial <= serial: 498 # 499 # We're already up-to-date. 500 # 501 done = True 502 else: 503 expecting_SOA = True 504 # 505 # Process SOAs in the answer section (other than the initial 506 # SOA in the first message). 507 # 508 for rrset in r.answer[answer_index:]: 509 if done: 510 raise dns.exception.FormError("answers after final SOA") 511 if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname: 512 if expecting_SOA: 513 if rrset[0].serial != serial: 514 raise dns.exception.FormError( 515 "IXFR base serial mismatch") 516 expecting_SOA = False 517 elif rdtype == dns.rdatatype.IXFR: 518 delete_mode = not delete_mode 519 # 520 # If this SOA RRset is equal to the first we saw then we're 521 # finished. If this is an IXFR we also check that we're seeing 522 # the record in the expected part of the response. 523 # 524 if rrset == soa_rrset and \ 525 (rdtype == dns.rdatatype.AXFR or 526 (rdtype == dns.rdatatype.IXFR and delete_mode)): 527 done = True 528 elif expecting_SOA: 529 # 530 # We made an IXFR request and are expecting another 531 # SOA RR, but saw something else, so this must be an 532 # AXFR response. 533 # 534 rdtype = dns.rdatatype.AXFR 535 expecting_SOA = False 536 if done and q.keyring and not r.had_tsig: 537 raise dns.exception.FormError("missing TSIG") 538 yield r 539 s.close()
540