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