|
|
- # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
-
- # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
- #
- # Permission to use, copy, modify, and distribute this software and its
- # documentation for any purpose with or without fee is hereby granted,
- # provided that the above copyright notice and this permission notice
- # appear in all copies.
- #
- # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
- # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
- # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
- """DNS Dynamic Update Support"""
-
-
- import dns.message
- import dns.name
- import dns.opcode
- import dns.rdata
- import dns.rdataclass
- import dns.rdataset
- import dns.tsig
- from ._compat import string_types
-
-
- class Update(dns.message.Message):
-
- def __init__(self, zone, rdclass=dns.rdataclass.IN, keyring=None,
- keyname=None, keyalgorithm=dns.tsig.default_algorithm):
- """Initialize a new DNS Update object.
-
- See the documentation of the Message class for a complete
- description of the keyring dictionary.
-
- *zone*, a ``dns.name.Name`` or ``text``, the zone which is being
- updated.
-
- *rdclass*, an ``int`` or ``text``, the class of the zone.
-
- *keyring*, a ``dict``, the TSIG keyring to use. If a
- *keyring* is specified but a *keyname* is not, then the key
- used will be the first key in the *keyring*. Note that the
- order of keys in a dictionary is not defined, so applications
- should supply a keyname when a keyring is used, unless they
- know the keyring contains only one key.
-
- *keyname*, a ``dns.name.Name`` or ``None``, the name of the TSIG key
- to use; defaults to ``None``. The key must be defined in the keyring.
-
- *keyalgorithm*, a ``dns.name.Name``, the TSIG algorithm to use.
- """
- super(Update, self).__init__()
- self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
- if isinstance(zone, string_types):
- zone = dns.name.from_text(zone)
- self.origin = zone
- if isinstance(rdclass, string_types):
- rdclass = dns.rdataclass.from_text(rdclass)
- self.zone_rdclass = rdclass
- self.find_rrset(self.question, self.origin, rdclass, dns.rdatatype.SOA,
- create=True, force_unique=True)
- if keyring is not None:
- self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
-
- def _add_rr(self, name, ttl, rd, deleting=None, section=None):
- """Add a single RR to the update section."""
-
- if section is None:
- section = self.authority
- covers = rd.covers()
- rrset = self.find_rrset(section, name, self.zone_rdclass, rd.rdtype,
- covers, deleting, True, True)
- rrset.add(rd, ttl)
-
- def _add(self, replace, section, name, *args):
- """Add records.
-
- *replace* is the replacement mode. If ``False``,
- RRs are added to an existing RRset; if ``True``, the RRset
- is replaced with the specified contents. The second
- argument is the section to add to. The third argument
- is always a name. The other arguments can be:
-
- - rdataset...
-
- - ttl, rdata...
-
- - ttl, rdtype, string...
- """
-
- if isinstance(name, string_types):
- name = dns.name.from_text(name, None)
- if isinstance(args[0], dns.rdataset.Rdataset):
- for rds in args:
- if replace:
- self.delete(name, rds.rdtype)
- for rd in rds:
- self._add_rr(name, rds.ttl, rd, section=section)
- else:
- args = list(args)
- ttl = int(args.pop(0))
- if isinstance(args[0], dns.rdata.Rdata):
- if replace:
- self.delete(name, args[0].rdtype)
- for rd in args:
- self._add_rr(name, ttl, rd, section=section)
- else:
- rdtype = args.pop(0)
- if isinstance(rdtype, string_types):
- rdtype = dns.rdatatype.from_text(rdtype)
- if replace:
- self.delete(name, rdtype)
- for s in args:
- rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
- self.origin)
- self._add_rr(name, ttl, rd, section=section)
-
- def add(self, name, *args):
- """Add records.
-
- The first argument is always a name. The other
- arguments can be:
-
- - rdataset...
-
- - ttl, rdata...
-
- - ttl, rdtype, string...
- """
-
- self._add(False, self.authority, name, *args)
-
- def delete(self, name, *args):
- """Delete records.
-
- The first argument is always a name. The other
- arguments can be:
-
- - *empty*
-
- - rdataset...
-
- - rdata...
-
- - rdtype, [string...]
- """
-
- if isinstance(name, string_types):
- name = dns.name.from_text(name, None)
- if len(args) == 0:
- self.find_rrset(self.authority, name, dns.rdataclass.ANY,
- dns.rdatatype.ANY, dns.rdatatype.NONE,
- dns.rdatatype.ANY, True, True)
- elif isinstance(args[0], dns.rdataset.Rdataset):
- for rds in args:
- for rd in rds:
- self._add_rr(name, 0, rd, dns.rdataclass.NONE)
- else:
- args = list(args)
- if isinstance(args[0], dns.rdata.Rdata):
- for rd in args:
- self._add_rr(name, 0, rd, dns.rdataclass.NONE)
- else:
- rdtype = args.pop(0)
- if isinstance(rdtype, string_types):
- rdtype = dns.rdatatype.from_text(rdtype)
- if len(args) == 0:
- self.find_rrset(self.authority, name,
- self.zone_rdclass, rdtype,
- dns.rdatatype.NONE,
- dns.rdataclass.ANY,
- True, True)
- else:
- for s in args:
- rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s,
- self.origin)
- self._add_rr(name, 0, rd, dns.rdataclass.NONE)
-
- def replace(self, name, *args):
- """Replace records.
-
- The first argument is always a name. The other
- arguments can be:
-
- - rdataset...
-
- - ttl, rdata...
-
- - ttl, rdtype, string...
-
- Note that if you want to replace the entire node, you should do
- a delete of the name followed by one or more calls to add.
- """
-
- self._add(True, self.authority, name, *args)
-
- def present(self, name, *args):
- """Require that an owner name (and optionally an rdata type,
- or specific rdataset) exists as a prerequisite to the
- execution of the update.
-
- The first argument is always a name.
- The other arguments can be:
-
- - rdataset...
-
- - rdata...
-
- - rdtype, string...
- """
-
- if isinstance(name, string_types):
- name = dns.name.from_text(name, None)
- if len(args) == 0:
- self.find_rrset(self.answer, name,
- dns.rdataclass.ANY, dns.rdatatype.ANY,
- dns.rdatatype.NONE, None,
- True, True)
- elif isinstance(args[0], dns.rdataset.Rdataset) or \
- isinstance(args[0], dns.rdata.Rdata) or \
- len(args) > 1:
- if not isinstance(args[0], dns.rdataset.Rdataset):
- # Add a 0 TTL
- args = list(args)
- args.insert(0, 0)
- self._add(False, self.answer, name, *args)
- else:
- rdtype = args[0]
- if isinstance(rdtype, string_types):
- rdtype = dns.rdatatype.from_text(rdtype)
- self.find_rrset(self.answer, name,
- dns.rdataclass.ANY, rdtype,
- dns.rdatatype.NONE, None,
- True, True)
-
- def absent(self, name, rdtype=None):
- """Require that an owner name (and optionally an rdata type) does
- not exist as a prerequisite to the execution of the update."""
-
- if isinstance(name, string_types):
- name = dns.name.from_text(name, None)
- if rdtype is None:
- self.find_rrset(self.answer, name,
- dns.rdataclass.NONE, dns.rdatatype.ANY,
- dns.rdatatype.NONE, None,
- True, True)
- else:
- if isinstance(rdtype, string_types):
- rdtype = dns.rdatatype.from_text(rdtype)
- self.find_rrset(self.answer, name,
- dns.rdataclass.NONE, rdtype,
- dns.rdatatype.NONE, None,
- True, True)
-
- def to_wire(self, origin=None, max_size=65535):
- """Return a string containing the update in DNS compressed wire
- format.
-
- *origin*, a ``dns.name.Name`` or ``None``, the origin to be
- appended to any relative names. If *origin* is ``None``, then
- the origin of the ``dns.update.Update`` message object is used
- (i.e. the *zone* parameter passed when the Update object was
- created).
-
- *max_size*, an ``int``, the maximum size of the wire format
- output; default is 0, which means "the message's request
- payload, if nonzero, or 65535".
-
- Returns a ``binary``.
- """
-
- if origin is None:
- origin = self.origin
- return super(Update, self).to_wire(origin, max_size)
|