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(long(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, e:
    119              if e.args[0] != 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 = ''
    242      while count > 0:
    243          _wait_for_readable(sock, expiration)
    244          n = sock.recv(count)
    245          if n == '':
    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[0] != errno.EINPROGRESS and \
    268                 v[0] != errno.EWOULDBLOCK and \
    269                 v[0] != 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.rdataclass.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, unicode)):
    382          zone = dns.name.from_text(zone)
    383      if isinstance(rdtype, (str, unicode)):
    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      delete_mode = True
    415      expecting_SOA = False
    416      soa_rrset = None
    417      soa_count = 0
    418      if relativize:
    419          origin = zone
    420          oname = dns.name.empty
    421      else:
    422          origin = None
    423          oname = zone
    424      tsig_ctx = None
    425      first = True
    426      while not done:
    427          mexpiration = _compute_expiration(timeout)
    428          if mexpiration is None or mexpiration > expiration:
    429              mexpiration = expiration
    430          if use_udp:
    431              _wait_for_readable(s, expiration)
    432              (wire, from_address) = s.recvfrom(65535)
    433          else:
    434              ldata = _net_read(s, 2, mexpiration)
    435              (l,) = struct.unpack("!H", ldata)
    436              wire = _net_read(s, l, mexpiration)
    437          r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac,
    438                                    xfr=True, origin=origin, tsig_ctx=tsig_ctx,
    439                                    multi=True, first=first,
    440                                    one_rr_per_rrset=(rdtype==dns.rdatatype.IXFR))
    441          tsig_ctx = r.tsig_ctx
    442          first = False
    443          answer_index = 0
    444          if soa_rrset is None:
    445              if not r.answer or r.answer[0].name != oname:
    446                  raise dns.exception.FormError("No answer or RRset not for qname")
    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                  #
    475                  # If this SOA RRset is equal to the first we saw then we're
    476                  # finished. If this is an IXFR we also check that we're seeing
    477                  # the record in the expected part of the response.
    478                  #
    479                  if rrset == soa_rrset and \
    480                          (rdtype == dns.rdatatype.AXFR or \
    481                          (rdtype == dns.rdatatype.IXFR and delete_mode)):
    482                      done = True
    483              elif expecting_SOA:
    484                  #
    485                  # We made an IXFR request and are expecting another
    486                  # SOA RR, but saw something else, so this must be an
    487                  # AXFR response.
    488                  #
    489                  rdtype = dns.rdatatype.AXFR
    490                  expecting_SOA = False
    491          if done and q.keyring and not r.had_tsig:
    492              raise dns.exception.FormError("missing TSIG")
    493          yield r
    494      s.close()
    495