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

# Source Code for Module dns.update

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