0
# DNS Updates
1
2
Dynamic DNS update functionality for creating and sending DNS UPDATE messages. Provides complete support for adding, deleting, and replacing resource records with optional TSIG authentication.
3
4
## Capabilities
5
6
### Update Message Creation
7
8
Create DNS UPDATE messages for dynamic zone modifications.
9
10
```python { .api }
11
class Update:
12
"""
13
A DNS dynamic update message.
14
15
Represents a DNS UPDATE message that can add, delete, or replace
16
resource records in a DNS zone. Supports prerequisites and
17
TSIG authentication.
18
19
Attributes:
20
zone (dns.name.Name): Zone being updated
21
rdclass (int): Zone class
22
keyring (dict): TSIG keyring for authentication
23
keyname (dns.name.Name): TSIG key name
24
keyalgorithm (dns.name.Name): TSIG algorithm
25
"""
26
27
def __init__(self, zone, rdclass='IN', keyring=None, keyname=None,
28
keyalgorithm='HMAC-MD5.SIG-ALG.REG.INT'):
29
"""
30
Initialize DNS update message.
31
32
Args:
33
zone (str or dns.name.Name): Zone name to update
34
rdclass (str or int): Zone class (default 'IN')
35
keyring (dict): TSIG keyring for authentication
36
keyname (str or dns.name.Name): TSIG key name
37
keyalgorithm (str): TSIG algorithm name
38
"""
39
40
def __str__(self):
41
"""Return string representation of update."""
42
43
def __repr__(self):
44
"""Return detailed string representation."""
45
```
46
47
### Record Addition
48
49
Add resource records to the zone.
50
51
```python { .api }
52
def add(name, *args):
53
"""
54
Add resource records to the zone.
55
56
Args:
57
name (str or dns.name.Name): Record name
58
*args: Variable arguments specifying record data
59
Can be (ttl, rdtype, rdata...) or (rdtype, rdata...)
60
61
Examples:
62
update.add('www.example.com', 300, 'A', '192.0.2.1')
63
update.add('mail', 'MX', 10, 'mail.example.com.')
64
update.add('_sip._tcp', 'SRV', 10, 20, 5060, 'sip.example.com.')
65
"""
66
67
def add_rrset(name, rrset):
68
"""
69
Add an entire RRset to the zone.
70
71
Args:
72
name (str or dns.name.Name): Record name
73
rrset (dns.rrset.RRset): RRset to add
74
"""
75
76
def add_rdataset(name, rdataset):
77
"""
78
Add an rdataset to the zone.
79
80
Args:
81
name (str or dns.name.Name): Record name
82
rdataset (dns.rdataset.Rdataset): Rdataset to add
83
"""
84
```
85
86
### Record Deletion
87
88
Delete resource records from the zone.
89
90
```python { .api }
91
def delete(name, *args):
92
"""
93
Delete resource records from the zone.
94
95
Args:
96
name (str or dns.name.Name): Record name
97
*args: Optional arguments specifying what to delete
98
() - delete all records at name
99
(rdtype) - delete all records of specified type
100
(rdtype, rdata...) - delete specific record
101
102
Examples:
103
update.delete('old.example.com') # Delete all records
104
update.delete('www.example.com', 'A') # Delete all A records
105
update.delete('www.example.com', 'A', '192.0.2.1') # Delete specific A record
106
"""
107
108
def delete_rrset(name, rdtype, covers='NONE'):
109
"""
110
Delete an entire RRset from the zone.
111
112
Args:
113
name (str or dns.name.Name): Record name
114
rdtype (str or int): Record type to delete
115
covers (str or int): Covered type for RRSIG records
116
"""
117
118
def delete_rdataset(name, rdataset):
119
"""
120
Delete specific rdataset from the zone.
121
122
Args:
123
name (str or dns.name.Name): Record name
124
rdataset (dns.rdataset.Rdataset): Rdataset to delete
125
"""
126
```
127
128
### Record Replacement
129
130
Replace existing resource records in the zone.
131
132
```python { .api }
133
def replace(name, *args):
134
"""
135
Replace resource records in the zone.
136
137
Deletes all existing records of the specified type and adds new ones.
138
139
Args:
140
name (str or dns.name.Name): Record name
141
*args: Arguments specifying replacement records
142
Format: (ttl, rdtype, rdata...) or (rdtype, rdata...)
143
144
Examples:
145
update.replace('www.example.com', 300, 'A', '192.0.2.10')
146
update.replace('mail', 'MX', 10, 'mail1.example.com.', 20, 'mail2.example.com.')
147
"""
148
149
def replace_rrset(name, rrset):
150
"""
151
Replace an RRset in the zone.
152
153
Args:
154
name (str or dns.name.Name): Record name
155
rrset (dns.rrset.RRset): Replacement RRset
156
"""
157
158
def replace_rdataset(name, rdataset):
159
"""
160
Replace an rdataset in the zone.
161
162
Args:
163
name (str or dns.name.Name): Record name
164
rdataset (dns.rdataset.Rdataset): Replacement rdataset
165
"""
166
```
167
168
### Prerequisites
169
170
Set prerequisites that must be satisfied for the update to succeed.
171
172
```python { .api }
173
def present(name, *args):
174
"""
175
Specify that records must be present for update to succeed.
176
177
Args:
178
name (str or dns.name.Name): Record name
179
*args: Optional arguments specifying what must be present
180
() - name must exist (any record type)
181
(rdtype) - name must have records of specified type
182
(rdtype, rdata...) - specific record must exist
183
184
Examples:
185
update.present('www.example.com') # Name must exist
186
update.present('www.example.com', 'A') # Must have A records
187
update.present('www.example.com', 'A', '192.0.2.1') # Specific A record must exist
188
"""
189
190
def absent(name, *args):
191
"""
192
Specify that records must be absent for update to succeed.
193
194
Args:
195
name (str or dns.name.Name): Record name
196
*args: Optional arguments specifying what must be absent
197
() - name must not exist (no records of any type)
198
(rdtype) - name must not have records of specified type
199
(rdtype, rdata...) - specific record must not exist
200
201
Examples:
202
update.absent('new.example.com') # Name must not exist
203
update.absent('www.example.com', 'AAAA') # Must not have AAAA records
204
update.absent('www.example.com', 'A', '192.0.2.99') # Specific A record must not exist
205
"""
206
```
207
208
### Update Execution
209
210
Send update messages to nameservers.
211
212
```python { .api }
213
def to_wire(self):
214
"""
215
Convert update message to wire format.
216
217
Returns:
218
bytes: Wire format update message
219
"""
220
221
def __call__(self, nameserver, timeout=None, port=53, af=None, source=None, source_port=0):
222
"""
223
Send the update to a nameserver (callable interface).
224
225
Args:
226
nameserver (str): Nameserver IP address
227
timeout (float): Query timeout
228
port (int): Destination port (default 53)
229
af (int): Address family
230
source (str): Source IP address
231
source_port (int): Source port
232
233
Returns:
234
dns.message.Message: Response message
235
"""
236
```
237
238
## Usage Examples
239
240
### Basic Record Updates
241
242
```python
243
import dns.update
244
import dns.query
245
import dns.rdatatype
246
247
# Create update message
248
zone = 'example.com.'
249
update = dns.update.Update(zone)
250
251
# Add records
252
update.add('www.example.com.', 300, 'A', '192.0.2.10')
253
update.add('www.example.com.', 300, 'AAAA', '2001:db8::10')
254
update.add('mail.example.com.', 300, 'A', '192.0.2.20')
255
256
# Add MX record
257
update.add('example.com.', 300, 'MX', 10, 'mail.example.com.')
258
259
# Send update
260
nameserver = '192.0.2.1' # Authoritative nameserver
261
response = dns.query.tcp(update, nameserver)
262
263
if response.rcode() == dns.rcode.NOERROR:
264
print("Update successful")
265
else:
266
print(f"Update failed: {dns.rcode.to_text(response.rcode())}")
267
```
268
269
### Record Deletion and Replacement
270
271
```python
272
import dns.update
273
274
update = dns.update.Update('example.com.')
275
276
# Delete specific record
277
update.delete('old.example.com.', 'A', '192.0.2.99')
278
279
# Delete all A records at name
280
update.delete('www.example.com.', 'A')
281
282
# Delete all records at name
283
update.delete('obsolete.example.com.')
284
285
# Replace all A records
286
update.replace('www.example.com.', 300, 'A', '192.0.2.100')
287
288
# Replace MX records
289
update.replace('example.com.', 300, 'MX', 10, 'mail1.example.com.', 20, 'mail2.example.com.')
290
291
# Send update
292
response = dns.query.tcp(update, '192.0.2.1')
293
```
294
295
### Updates with Prerequisites
296
297
```python
298
import dns.update
299
300
update = dns.update.Update('example.com.')
301
302
# Only proceed if www.example.com has specific A record
303
update.present('www.example.com.', 'A', '192.0.2.1')
304
305
# Replace it with new address
306
update.replace('www.example.com.', 300, 'A', '192.0.2.10')
307
308
# Only proceed if new.example.com doesn't exist
309
update.absent('new.example.com.')
310
311
# Add new record
312
update.add('new.example.com.', 300, 'A', '192.0.2.50')
313
314
# Send conditional update
315
response = dns.query.tcp(update, '192.0.2.1')
316
```
317
318
### Authenticated Updates with TSIG
319
320
```python
321
import dns.update
322
import dns.tsigkeyring
323
import dns.query
324
325
# Create TSIG keyring
326
keyring = dns.tsigkeyring.from_text({
327
'update-key.example.com.': 'base64-encoded-key-data'
328
})
329
330
# Create authenticated update
331
update = dns.update.Update(
332
'example.com.',
333
keyring=keyring,
334
keyname='update-key.example.com.'
335
)
336
337
# Add records
338
update.add('secure.example.com.', 300, 'A', '192.0.2.100')
339
update.add('secure.example.com.', 300, 'TXT', 'Updated via authenticated TSIG')
340
341
# Send authenticated update
342
response = dns.query.tcp(update, '192.0.2.1')
343
344
if response.rcode() == dns.rcode.NOERROR:
345
print("Authenticated update successful")
346
347
# Verify TSIG authentication
348
if response.had_tsig():
349
if response.tsig_error() == 0:
350
print("TSIG verification successful")
351
else:
352
print(f"TSIG error: {response.tsig_error()}")
353
else:
354
print(f"Update failed: {dns.rcode.to_text(response.rcode())}")
355
```
356
357
### Complex Service Record Updates
358
359
```python
360
import dns.update
361
362
update = dns.update.Update('example.com.')
363
364
# Add SRV records for SIP service
365
update.add('_sip._tcp.example.com.', 300, 'SRV', 10, 20, 5060, 'sip1.example.com.')
366
update.add('_sip._tcp.example.com.', 300, 'SRV', 10, 30, 5060, 'sip2.example.com.')
367
update.add('_sip._tcp.example.com.', 300, 'SRV', 20, 10, 5060, 'sip3.example.com.')
368
369
# Add corresponding A records
370
update.add('sip1.example.com.', 300, 'A', '192.0.2.11')
371
update.add('sip2.example.com.', 300, 'A', '192.0.2.12')
372
update.add('sip3.example.com.', 300, 'A', '192.0.2.13')
373
374
# Add NAPTR record for service discovery
375
update.add('example.com.', 300, 'NAPTR', 100, 10, 'u', 'E2U+sip',
376
'!^.*$!sip:info@example.com!', '.')
377
378
# Send update
379
response = dns.query.tcp(update, '192.0.2.1')
380
```
381
382
### Batch Updates
383
384
```python
385
import dns.update
386
import dns.rrset
387
import dns.rdata
388
import dns.rdatatype
389
import dns.rdataclass
390
391
update = dns.update.Update('example.com.')
392
393
# Create RRsets for batch operations
394
www_rrset = dns.rrset.RRset(dns.name.from_text('www.example.com.'),
395
dns.rdataclass.IN, dns.rdatatype.A)
396
www_rrset.add(dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'), ttl=300)
397
www_rrset.add(dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'), ttl=300)
398
399
# Add entire RRset
400
update.add_rrset('www.example.com.', www_rrset)
401
402
# Create and add MX RRset
403
mx_rrset = dns.rrset.from_text('example.com.', 300, 'IN', 'MX',
404
'10 mail1.example.com.', '20 mail2.example.com.')
405
update.add_rrset('example.com.', mx_rrset)
406
407
# Send batch update
408
response = dns.query.tcp(update, '192.0.2.1')
409
```
410
411
### Error Handling
412
413
```python
414
import dns.update
415
import dns.query
416
import dns.rcode
417
import dns.exception
418
419
try:
420
update = dns.update.Update('example.com.')
421
update.add('test.example.com.', 300, 'A', '192.0.2.50')
422
423
response = dns.query.tcp(update, '192.0.2.1', timeout=10)
424
425
rcode = response.rcode()
426
if rcode == dns.rcode.NOERROR:
427
print("Update completed successfully")
428
elif rcode == dns.rcode.REFUSED:
429
print("Update refused - check authorization")
430
elif rcode == dns.rcode.NOTAUTH:
431
print("Server not authoritative for zone")
432
elif rcode == dns.rcode.YXDOMAIN:
433
print("Prerequisites failed - domain exists when it shouldn't")
434
elif rcode == dns.rcode.NXDOMAIN:
435
print("Prerequisites failed - domain doesn't exist when it should")
436
elif rcode == dns.rcode.YXRRSET:
437
print("Prerequisites failed - RRset exists when it shouldn't")
438
elif rcode == dns.rcode.NXRRSET:
439
print("Prerequisites failed - RRset doesn't exist when it should")
440
else:
441
print(f"Update failed with rcode: {dns.rcode.to_text(rcode)}")
442
443
except dns.exception.Timeout:
444
print("Update timed out")
445
except dns.tsig.BadSignature:
446
print("TSIG signature verification failed")
447
except dns.exception.DNSException as e:
448
print(f"DNS error: {e}")
449
```
450
451
## Update Response Codes
452
453
```python { .api }
454
# Standard response codes relevant to updates
455
NOERROR = 0 # No error - update successful
456
FORMERR = 1 # Format error in update message
457
SERVFAIL = 2 # Server failure
458
NXDOMAIN = 3 # Name does not exist (prerequisite)
459
NOTIMP = 4 # Not implemented
460
REFUSED = 5 # Update refused
461
YXDOMAIN = 6 # Name exists when it should not (prerequisite)
462
YXRRSET = 7 # RRset exists when it should not (prerequisite)
463
NXRRSET = 8 # RRset does not exist when it should (prerequisite)
464
NOTAUTH = 9 # Server not authoritative for zone
465
NOTZONE = 10 # Name not contained in zone
466
```
467
468
## Integration with Other Modules
469
470
DNS Updates work seamlessly with other dnspython modules:
471
472
- **TSIG Authentication**: Use `dns.tsig` and `dns.tsigkeyring` for secure updates
473
- **Record Creation**: Use `dns.rdata` and `dns.rrset` for complex record structures
474
- **Name Handling**: Use `dns.name` for proper name formatting and validation
475
- **Message Transport**: Use `dns.query` for sending updates via UDP or TCP
476
- **Response Handling**: Use `dns.message` for processing update responses