123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- # 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
- class UpdateSection(dns.enum.IntEnum):
- """Update sections"""
- ZONE = 0
- PREREQ = 1
- UPDATE = 2
- ADDITIONAL = 3
- @classmethod
- def _maximum(cls):
- return 3
- class UpdateMessage(dns.message.Message):
- _section_enum = UpdateSection
- def __init__(self, zone=None, rdclass=dns.rdataclass.IN, keyring=None,
- keyname=None, keyalgorithm=dns.tsig.default_algorithm,
- id=None):
- """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``, ``str``, or ``None``, the zone
- which is being updated. ``None`` should only be used by dnspython's
- message constructors, as a zone is required for the convenience
- methods like ``add()``, ``replace()``, etc.
- *rdclass*, an ``int`` or ``str``, the class of the zone.
- The *keyring*, *keyname*, and *keyalgorithm* parameters are passed to
- ``use_tsig()``; see its documentation for details.
- """
- super().__init__(id=id)
- self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE)
- if isinstance(zone, str):
- zone = dns.name.from_text(zone)
- self.origin = zone
- rdclass = dns.rdataclass.RdataClass.make(rdclass)
- self.zone_rdclass = rdclass
- if self.origin:
- self.find_rrset(self.zone, self.origin, rdclass, dns.rdatatype.SOA,
- create=True, force_unique=True)
- if keyring is not None:
- self.use_tsig(keyring, keyname, algorithm=keyalgorithm)
- @property
- def zone(self):
- """The zone section."""
- return self.sections[0]
- @zone.setter
- def zone(self, v):
- self.sections[0] = v
- @property
- def prerequisite(self):
- """The prerequisite section."""
- return self.sections[1]
- @prerequisite.setter
- def prerequisite(self, v):
- self.sections[1] = v
- @property
- def update(self):
- """The update section."""
- return self.sections[2]
- @update.setter
- def update(self, v):
- self.sections[2] = v
- 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.update
- 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, str):
- 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 = dns.rdatatype.RdataType.make(args.pop(0))
- 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.update, 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, str):
- name = dns.name.from_text(name, None)
- if len(args) == 0:
- self.find_rrset(self.update, 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 = dns.rdatatype.RdataType.make(args.pop(0))
- if len(args) == 0:
- self.find_rrset(self.update, 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.update, 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, str):
- name = dns.name.from_text(name, None)
- if len(args) == 0:
- self.find_rrset(self.prerequisite, 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.prerequisite, name, *args)
- else:
- rdtype = dns.rdatatype.RdataType.make(args[0])
- self.find_rrset(self.prerequisite, 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, str):
- name = dns.name.from_text(name, None)
- if rdtype is None:
- self.find_rrset(self.prerequisite, name,
- dns.rdataclass.NONE, dns.rdatatype.ANY,
- dns.rdatatype.NONE, None,
- True, True)
- else:
- rdtype = dns.rdatatype.RdataType.make(rdtype)
- self.find_rrset(self.prerequisite, name,
- dns.rdataclass.NONE, rdtype,
- dns.rdatatype.NONE, None,
- True, True)
- def _get_one_rr_per_rrset(self, value):
- # Updates are always one_rr_per_rrset
- return True
- def _parse_rr_header(self, section, name, rdclass, rdtype):
- deleting = None
- empty = False
- if section == UpdateSection.ZONE:
- if dns.rdataclass.is_metaclass(rdclass) or \
- rdtype != dns.rdatatype.SOA or \
- self.zone:
- raise dns.exception.FormError
- else:
- if not self.zone:
- raise dns.exception.FormError
- if rdclass in (dns.rdataclass.ANY, dns.rdataclass.NONE):
- deleting = rdclass
- rdclass = self.zone[0].rdclass
- empty = (deleting == dns.rdataclass.ANY or
- section == UpdateSection.PREREQ)
- return (rdclass, rdtype, deleting, empty)
- # backwards compatibility
- Update = UpdateMessage
- ### BEGIN generated UpdateSection constants
- ZONE = UpdateSection.ZONE
- PREREQ = UpdateSection.PREREQ
- UPDATE = UpdateSection.UPDATE
- ADDITIONAL = UpdateSection.ADDITIONAL
- ### END generated UpdateSection constants
|