1 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
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.
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.
16 """DNS Dynamic Update Support"""
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.
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.
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
50 @type keyalgorithm: string
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
)
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
)
65 def _add_rr(self
, name
, ttl
, rd
, deleting
=None, section
=None):
66 """Add a single RR to the update section."""
69 section
= self
.authority
71 rrset
= self
.find_rrset(section
, name
, self
.zone_rdclass
, rd
.rdtype
,
72 covers
, deleting
, True, True)
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:
86 - ttl, rdtype, string..."""
88 if isinstance(name
, (str, unicode)):
89 name
= dns
.name
.from_text(name
, None)
90 if isinstance(args
[0], dns
.rdataset
.Rdataset
):
93 self
.delete(name
, rds
.rdtype
)
95 self
._add
_rr
(name
, rds
.ttl
, rd
, section
=section
)
98 ttl
= int(args
.pop(0))
99 if isinstance(args
[0], dns
.rdata
.Rdata
):
101 self
.delete(name
, args
[0].rdtype
)
103 self
._add
_rr
(name
, ttl
, rd
, section
=section
)
106 if isinstance(rdtype
, str):
107 rdtype
= dns
.rdatatype
.from_text(rdtype
)
109 self
.delete(name
, rdtype
)
111 rd
= dns
.rdata
.from_text(self
.zone_rdclass
, rdtype
, s
,
113 self
._add
_rr
(name
, ttl
, rd
, section
=section
)
115 def add(self
, name
, *args
):
116 """Add records. The first argument is always a name. The other
123 - ttl, rdtype, string..."""
124 self
._add
(False, self
.authority
, name
, *args
)
126 def delete(self
, name
, *args
):
127 """Delete records. The first argument is always a name. The other
136 - rdtype, [string...]"""
138 if isinstance(name
, (str, unicode)):
139 name
= dns
.name
.from_text(name
, None)
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
):
147 self
._add
_rr
(name
, 0, rd
, dns
.rdataclass
.NONE
)
150 if isinstance(args
[0], dns
.rdata
.Rdata
):
152 self
._add
_rr
(name
, 0, rd
, dns
.rdataclass
.NONE
)
155 if isinstance(rdtype
, (str, unicode)):
156 rdtype
= dns
.rdatatype
.from_text(rdtype
)
158 rrset
= self
.find_rrset(self
.authority
, name
,
159 self
.zone_rdclass
, rdtype
,
165 rd
= dns
.rdata
.from_text(self
.zone_rdclass
, rdtype
, s
,
167 self
._add
_rr
(name
, 0, rd
, dns
.rdataclass
.NONE
)
169 def replace(self
, name
, *args
):
170 """Replace records. The first argument is always a name. The other
177 - ttl, rdtype, string...
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."""
182 self
._add
(True, self
.authority
, name
, *args
)
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:
194 - rdtype, string..."""
196 if isinstance(name
, (str, unicode)):
197 name
= dns
.name
.from_text(name
, None)
199 rrset
= self
.find_rrset(self
.answer
, name
,
200 dns
.rdataclass
.ANY
, dns
.rdatatype
.ANY
,
201 dns
.rdatatype
.NONE
, None,
203 elif isinstance(args
[0], dns
.rdataset
.Rdataset
) or \
204 isinstance(args
[0], dns
.rdata
.Rdata
) or \
206 if not isinstance(args
[0], dns
.rdataset
.Rdataset
):
210 self
._add
(False, self
.answer
, name
, *args
)
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,
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."""
224 if isinstance(name
, (str, unicode)):
225 name
= dns
.name
.from_text(name
, None)
227 rrset
= self
.find_rrset(self
.answer
, name
,
228 dns
.rdataclass
.NONE
, dns
.rdatatype
.ANY
,
229 dns
.rdatatype
.NONE
, None,
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,
239 def to_wire(self
, origin
=None, max_size
=65535):
240 """Return a string containing the update in DNS compressed wire
245 return super(Update
, self
).to_wire(origin
, max_size
)