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

Source Code for Module dns.query

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