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

# Source Code for Module dns.update

      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  """DNS Dynamic Update Support"""
     17
     18
     19  import dns.message
     20  import dns.name
     21  import dns.opcode
     22  import dns.rdata
     23  import dns.rdataclass
     24  import dns.rdataset
     25  import dns.tsig
     26  from ._compat import string_types
     27
     28
     29 -class Update(dns.message.Message):
     30
     31 -    def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None,
     32                   keyname=None, keyalgorithm=dns.tsig.default_algorithm):
     33          """Initialize a new DNS Update object.
     34
     35          @param zone: The zone which is being updated.
     36          @type zone: A dns.name.Name or string
     37          @param rdclass: The class of the zone; defaults to dns.rdataclass.IN.
     38          @type rdclass: An int designating the class, or a string whose value
     39          is the name of a class.
     40          @param keyring: The TSIG keyring to use; defaults to None.
     41          @type keyring: dict
     42          @param keyname: The name of the TSIG key to use; defaults to None.
     43          The key must be defined in the keyring.  If a keyring is specified
     44          but a keyname is not, then the key used will be the first key in the
     45          keyring.  Note that the order of keys in a dictionary is not defined,
     46          so applications should supply a keyname when a keyring is used, unless
     47          they know the keyring contains only one key.
     48          @type keyname: dns.name.Name or string
     49          @param keyalgorithm: The TSIG algorithm to use; defaults to
     50          dns.tsig.default_algorithm.  Constants for TSIG algorithms are defined
     51          in dns.tsig, and the currently implemented algorithms are
     52          HMAC_MD5, HMAC_SHA1, HMAC_SHA224, HMAC_SHA256, HMAC_SHA384, and
     53          HMAC_SHA512.
     54          @type keyalgorithm: string
     55          """
     56          super(Update, self).__init__()
     57          self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
     58          if isinstance(zone, string_types):
     59              zone = dns.name.from_text(zone)
     60          self.origin = zone
     61          if isinstance(rdclass, string_types):
     62              rdclass = dns.rdataclass.from_text(rdclass)
     63          self.zone_rdclass = rdclass
     64          self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA,
     65                          create=True, force_unique=True)
     66          if keyring is not None:
     67              self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
     68
     69 -    def _add_rr(self, name, ttl, rd, deleting=None, section=None):
     70          """Add a single RR to the update section."""
     71
     72          if section is None:
     73              section = self.authority
     74          covers = rd.covers()
     75          rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype,
     76                                  covers, deleting, True, True)
     77          rrset.add(rd, ttl)
     78
     79 -    def _add(self, replace, section, name, *args):
     80          """Add records.  The first argument is the replace mode.  If
     81          false, RRs are added to an existing RRset; if true, the RRset
     82          is replaced with the specified contents.  The second
     83          argument is the section to add to.  The third argument
     84          is always a name.  The other arguments can be:
     85
     86                  - rdataset...
     87
     88                  - ttl, rdata...
     89
     90                  - ttl, rdtype, string..."""
     91
     92          if isinstance(name, string_types):
     93              name = dns.name.from_text(name, None)
     94          if isinstance(args[0], dns.rdataset.Rdataset):
     95              for rds in args:
     96                  if replace:
     97                      self.delete(name, rds.rdtype)
     98                  for rd in rds:
     99                      self._add_rr(name, rds.ttl, rd, section=section)
    100          else:
    101              args = list(args)
    102              ttl = int(args.pop(0))
    103              if isinstance(args[0], dns.rdata.Rdata):
    104                  if replace:
    105                      self.delete(name, args[0].rdtype)
    106                  for rd in args:
    107                      self._add_rr(name, ttl, rd, section=section)
    108              else:
    109                  rdtype = args.pop(0)
    110                  if isinstance(rdtype, string_types):
    111                      rdtype = dns.rdatatype.from_text(rdtype)
    112                  if replace:
    113                      self.delete(name, rdtype)
    114                  for s in args:
    115                      rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
    116                                               self.origin)
    117                      self._add_rr(name, ttl, rd, section=section)
    118
    119 -    def add(self, name, *args):
    120          """Add records.  The first argument is always a name.  The other
    121          arguments can be:
    122
    123                  - rdataset...
    124
    125                  - ttl, rdata...
    126
    127                  - ttl, rdtype, string..."""
    128          self._add(False, self.authority, name, *args)
    129
    130 -    def delete(self, name, *args):
    131          """Delete records.  The first argument is always a name.  The other
    132          arguments can be:
    133
    134                  - I{nothing}
    135
    136                  - rdataset...
    137
    138                  - rdata...
    139
    140                  - rdtype, [string...]"""
    141
    142          if isinstance(name, string_types):
    143              name = dns.name.from_text(name, None)
    144          if len(args) == 0:
    145              self.find_rrset(self.authority, name, dns.rdataclass.ANY,
    146                              dns.rdatatype.ANY, dns.rdatatype.NONE,
    147                              dns.rdatatype.ANY, True, True)
    148          elif isinstance(args[0], dns.rdataset.Rdataset):
    149              for rds in args:
    150                  for rd in rds:
    151                      self._add_rr(name, 0, rd, dns.rdataclass.NONE)
    152          else:
    153              args = list(args)
    154              if isinstance(args[0], dns.rdata.Rdata):
    155                  for rd in args:
    156                      self._add_rr(name, 0, rd, dns.rdataclass.NONE)
    157              else:
    158                  rdtype = args.pop(0)
    159                  if isinstance(rdtype, string_types):
    160                      rdtype = dns.rdatatype.from_text(rdtype)
    161                  if len(args) == 0:
    162                      self.find_rrset(self.authority, name,
    163                                      self.zone_rdclass, rdtype,
    164                                      dns.rdatatype.NONE,
    165                                      dns.rdataclass.ANY,
    166                                      True, True)
    167                  else:
    168                      for s in args:
    169                          rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
    170                                                   self.origin)
    171                          self._add_rr(name, 0, rd, dns.rdataclass.NONE)
    172
    173 -    def replace(self, name, *args):
    174          """Replace records.  The first argument is always a name.  The other
    175          arguments can be:
    176
    177                  - rdataset...
    178
    179                  - ttl, rdata...
    180
    181                  - ttl, rdtype, string...
    182
    183          Note that if you want to replace the entire node, you should do
    184          a delete of the name followed by one or more calls to add."""
    185
    186          self._add(True, self.authority, name, *args)
    187
    188 -    def present(self, name, *args):
    189          """Require that an owner name (and optionally an rdata type,
    190          or specific rdataset) exists as a prerequisite to the
    191          execution of the update.  The first argument is always a name.
    192          The other arguments can be:
    193
    194                  - rdataset...
    195
    196                  - rdata...
    197
    198                  - rdtype, string..."""
    199
    200          if isinstance(name, string_types):
    201              name = dns.name.from_text(name, None)
    202          if len(args) == 0:
    203              self.find_rrset(self.answer, name,
    204                              dns.rdataclass.ANY, dns.rdatatype.ANY,
    205                              dns.rdatatype.NONE, None,
    206                              True, True)
    207          elif isinstance(args[0], dns.rdataset.Rdataset) or \
    208              isinstance(args[0], dns.rdata.Rdata) or \
    209                  len(args) > 1:
    210              if not isinstance(args[0], dns.rdataset.Rdataset):
    211                  # Add a 0 TTL
    212                  args = list(args)
    213                  args.insert(0, 0)
    214              self._add(False, self.answer, name, *args)
    215          else:
    216              rdtype = args[0]
    217              if isinstance(rdtype, string_types):
    218                  rdtype = dns.rdatatype.from_text(rdtype)
    219              self.find_rrset(self.answer, name,
    220                              dns.rdataclass.ANY, rdtype,
    221                              dns.rdatatype.NONE, None,
    222                              True, True)
    223
    224 -    def absent(self, name, rdtype=None):
    225          """Require that an owner name (and optionally an rdata type) does
    226          not exist as a prerequisite to the execution of the update."""
    227
    228          if isinstance(name, string_types):
    229              name = dns.name.from_text(name, None)
    230          if rdtype is None:
    231              self.find_rrset(self.answer, name,
    232                              dns.rdataclass.NONE, dns.rdatatype.ANY,
    233                              dns.rdatatype.NONE, None,
    234                              True, True)
    235          else:
    236              if isinstance(rdtype, string_types):
    237                  rdtype = dns.rdatatype.from_text(rdtype)
    238              self.find_rrset(self.answer, name,
    239                              dns.rdataclass.NONE, rdtype,
    240                              dns.rdatatype.NONE, None,
    241                              True, True)
    242
    243 -    def to_wire(self, origin=None, max_size=65535):
    244          """Return a string containing the update in DNS compressed wire
    245          format.
    246          @rtype: string"""
    247          if origin is None:
    248              origin = self.origin
    249          return super(Update, self).to_wire(origin, max_size)
    250