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