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"""
25 class Update(dns
.message
.Message
):
26 def __init__(self
, zone
, rdclass
=dns
.rdataclass
.IN
, keyring
=None,
27 keyname
=None, keyalgorithm
=dns
.tsig
.default_algorithm
):
28 """Initialize a new DNS Update object.
30 @param zone: The zone which is being updated.
31 @type zone: A dns.name.Name or string
32 @param rdclass: The class of the zone; defaults to dns.rdataclass.IN.
33 @type rdclass: An int designating the class, or a string whose value
34 is the name of a class.
35 @param keyring: The TSIG keyring to use; defaults to None.
37 @param keyname: The name of the TSIG key to use; defaults to None.
38 The key must be defined in the keyring. If a keyring is specified
39 but a keyname is not, then the key used will be the first key in the
40 keyring. Note that the order of keys in a dictionary is not defined,
41 so applications should supply a keyname when a keyring is used, unless
42 they know the keyring contains only one key.
43 @type keyname: dns.name.Name or string
44 @param keyalgorithm: The TSIG algorithm to use; defaults to
45 dns.tsig.default_algorithm
46 @type keyalgorithm: string
48 super(Update
, self
).__init
__()
49 self
.flags |
= dns
.opcode
.to_flags(dns
.opcode
.UPDATE
)
50 if isinstance(zone
, (str, unicode)):
51 zone
= dns
.name
.from_text(zone
)
53 if isinstance(rdclass
, str):
54 rdclass
= dns
.rdataclass
.from_text(rdclass
)
55 self
.zone_rdclass
= rdclass
56 self
.find_rrset(self
.question
, self
.origin
, rdclass
, dns
.rdatatype
.SOA
,
57 create
=True, force_unique
=True)
58 if not keyring
is None:
59 self
.use_tsig(keyring
, keyname
, keyalgorithm
)
61 def _add_rr(self
, name
, ttl
, rd
, deleting
=None, section
=None):
62 """Add a single RR to the update section."""
65 section
= self
.authority
67 rrset
= self
.find_rrset(section
, name
, self
.zone_rdclass
, rd
.rdtype
,
68 covers
, deleting
, True, True)
71 def _add(self
, replace
, section
, name
, *args
):
72 """Add records. The first argument is the replace mode. If
73 false, RRs are added to an existing RRset; if true, the RRset
74 is replaced with the specified contents. The second
75 argument is the section to add to. The third argument
76 is always a name. The other arguments can be:
82 - ttl, rdtype, string..."""
84 if isinstance(name
, (str, unicode)):
85 name
= dns
.name
.from_text(name
, None)
86 if isinstance(args
[0], dns
.rdataset
.Rdataset
):
89 self
.delete(name
, rds
.rdtype
)
91 self
._add
_rr
(name
, rds
.ttl
, rd
, section
=section
)
94 ttl
= int(args
.pop(0))
95 if isinstance(args
[0], dns
.rdata
.Rdata
):
97 self
.delete(name
, args
[0].rdtype
)
99 self
._add
_rr
(name
, ttl
, rd
, section
=section
)
102 if isinstance(rdtype
, str):
103 rdtype
= dns
.rdatatype
.from_text(rdtype
)
105 self
.delete(name
, rdtype
)
107 rd
= dns
.rdata
.from_text(self
.zone_rdclass
, rdtype
, s
,
109 self
._add
_rr
(name
, ttl
, rd
, section
=section
)
111 def add(self
, name
, *args
):
112 """Add records. The first argument is always a name. The other
119 - ttl, rdtype, string..."""
120 self
._add
(False, self
.authority
, name
, *args
)
122 def delete(self
, name
, *args
):
123 """Delete records. The first argument is always a name. The other
132 - rdtype, [string...]"""
134 if isinstance(name
, (str, unicode)):
135 name
= dns
.name
.from_text(name
, None)
137 rrset
= self
.find_rrset(self
.authority
, name
, dns
.rdataclass
.ANY
,
138 dns
.rdatatype
.ANY
, dns
.rdatatype
.NONE
,
139 dns
.rdatatype
.ANY
, True, True)
140 elif isinstance(args
[0], dns
.rdataset
.Rdataset
):
143 self
._add
_rr
(name
, 0, rd
, dns
.rdataclass
.NONE
)
146 if isinstance(args
[0], dns
.rdata
.Rdata
):
148 self
._add
_rr
(name
, 0, rd
, dns
.rdataclass
.NONE
)
151 if isinstance(rdtype
, str):
152 rdtype
= dns
.rdatatype
.from_text(rdtype
)
154 rrset
= self
.find_rrset(self
.authority
, name
,
155 self
.zone_rdclass
, rdtype
,
161 rd
= dns
.rdata
.from_text(self
.zone_rdclass
, rdtype
, s
,
163 self
._add
_rr
(name
, 0, rd
, dns
.rdataclass
.NONE
)
165 def replace(self
, name
, *args
):
166 """Replace records. The first argument is always a name. The other
173 - ttl, rdtype, string...
175 Note that if you want to replace the entire node, you should do
176 a delete of the name followed by one or more calls to add."""
178 self
._add
(True, self
.authority
, name
, *args
)
180 def present(self
, name
, *args
):
181 """Require that an owner name (and optionally an rdata type,
182 or specific rdataset) exists as a prerequisite to the
183 execution of the update. The first argument is always a name.
184 The other arguments can be:
190 - rdtype, string..."""
192 if isinstance(name
, (str, unicode)):
193 name
= dns
.name
.from_text(name
, None)
195 rrset
= self
.find_rrset(self
.answer
, name
,
196 dns
.rdataclass
.ANY
, dns
.rdatatype
.ANY
,
197 dns
.rdatatype
.NONE
, None,
199 elif isinstance(args
[0], dns
.rdataset
.Rdataset
) or \
200 isinstance(args
[0], dns
.rdata
.Rdata
) or \
202 if not isinstance(args
[0], dns
.rdataset
.Rdataset
):
206 self
._add
(False, self
.answer
, name
, *args
)
209 if isinstance(rdtype
, str):
210 rdtype
= dns
.rdatatype
.from_text(rdtype
)
211 rrset
= self
.find_rrset(self
.answer
, name
,
212 dns
.rdataclass
.ANY
, rdtype
,
213 dns
.rdatatype
.NONE
, None,
216 def absent(self
, name
, rdtype
=None):
217 """Require that an owner name (and optionally an rdata type) does
218 not exist as a prerequisite to the execution of the update."""
220 if isinstance(name
, (str, unicode)):
221 name
= dns
.name
.from_text(name
, None)
223 rrset
= self
.find_rrset(self
.answer
, name
,
224 dns
.rdataclass
.NONE
, dns
.rdatatype
.ANY
,
225 dns
.rdatatype
.NONE
, None,
228 if isinstance(rdtype
, str):
229 rdtype
= dns
.rdatatype
.from_text(rdtype
)
230 rrset
= self
.find_rrset(self
.answer
, name
,
231 dns
.rdataclass
.NONE
, rdtype
,
232 dns
.rdatatype
.NONE
, None,
235 def to_wire(self
, origin
=None, max_size
=65535):
236 """Return a string containing the update in DNS compressed wire
241 return super(Update
, self
).to_wire(origin
, max_size
)