0
# DNS Messages
1
2
DNS message manipulation functionality for creating, parsing, and modifying DNS protocol messages. Provides complete control over DNS message structure including headers, questions, answers, authority, and additional sections.
3
4
## Capabilities
5
6
### Message Creation
7
8
Create DNS query and response messages with full protocol support.
9
10
```python { .api }
11
def make_query(qname, rdtype, rdclass='IN', use_edns=None, want_dnssec=False,
12
ednsflags=None, payload=None, request_payload=None, options=None):
13
"""
14
Create a DNS query message.
15
16
Args:
17
qname (str or dns.name.Name): Query name
18
rdtype (str or int): Record type to query
19
rdclass (str or int): Record class (default 'IN')
20
use_edns (int): EDNS version (None to disable, 0 for EDNS0)
21
want_dnssec (bool): Set DNSSEC OK bit
22
ednsflags (int): EDNS flags
23
payload (int): EDNS payload size
24
request_payload (int): Requested payload size
25
options (list): EDNS options
26
27
Returns:
28
dns.message.Message: DNS query message
29
"""
30
31
def make_response(query, recursion_available=False, our_payload=8192, fudge=300):
32
"""
33
Create a DNS response message from a query.
34
35
Args:
36
query (dns.message.Message): Original query message
37
recursion_available (bool): Set RA bit
38
our_payload (int): Our EDNS payload size
39
fudge (int): TSIG time fudge factor
40
41
Returns:
42
dns.message.Message: DNS response message
43
"""
44
```
45
46
### Message Parsing
47
48
Parse DNS messages from wire format, text format, or files.
49
50
```python { .api }
51
def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None,
52
tsig_ctx=None, multi=False, first=True, question_only=False,
53
one_rr_per_rrset=False, ignore_trailing=False):
54
"""
55
Parse a DNS message from wire format.
56
57
Args:
58
wire (bytes): Wire format message
59
keyring (dict): TSIG keyring for validation
60
request_mac (bytes): TSIG MAC from request
61
xfr (bool): Message is part of zone transfer
62
origin (dns.name.Name): Origin for relative names
63
tsig_ctx (dns.tsig.HMACTSig): TSIG context
64
multi (bool): Message is part of multi-message response
65
first (bool): First message in multi-message response
66
question_only (bool): Parse question section only
67
one_rr_per_rrset (bool): Put each RR in its own RRset
68
ignore_trailing (bool): Ignore trailing junk
69
70
Returns:
71
dns.message.Message: Parsed DNS message
72
"""
73
74
def from_text(text):
75
"""
76
Parse a DNS message from text format.
77
78
Args:
79
text (str): Text format message
80
81
Returns:
82
dns.message.Message: Parsed DNS message
83
"""
84
85
def from_file(f):
86
"""
87
Read and parse a DNS message from a file.
88
89
Args:
90
f (file-like): File to read from
91
92
Returns:
93
dns.message.Message: Parsed DNS message
94
"""
95
```
96
97
### Message Class
98
99
Complete DNS message with header and sections for questions, answers, authority, and additional records.
100
101
```python { .api }
102
class Message:
103
"""
104
A DNS message with header and sections.
105
106
Attributes:
107
id (int): Message ID
108
flags (int): Header flags
109
question (list): Question section
110
answer (list): Answer section
111
authority (list): Authority section
112
additional (list): Additional section
113
opt (dns.edns.OPT): EDNS OPT record
114
tsig (dns.rdata.Rdata): TSIG record
115
request_payload (int): Requested EDNS payload size
116
keyring (dict): TSIG keyring
117
mac (bytes): TSIG MAC
118
xfr (bool): Message is part of zone transfer
119
origin (dns.name.Name): Origin for relative names
120
tsig_ctx (dns.tsig.HMACTSig): TSIG context
121
"""
122
123
def __init__(self, id=None):
124
"""
125
Initialize DNS message.
126
127
Args:
128
id (int): Message ID (random if None)
129
"""
130
131
def __repr__(self):
132
"""Return string representation of message."""
133
134
def __str__(self):
135
"""Return string representation of message."""
136
137
def to_wire(self, origin=None, max_size=0, **kw):
138
"""
139
Convert message to wire format.
140
141
Args:
142
origin (dns.name.Name): Origin for relative names
143
max_size (int): Maximum message size
144
**kw: Additional options
145
146
Returns:
147
bytes: Wire format message
148
"""
149
150
def to_text(self, origin=None, relativize=True, **kw):
151
"""
152
Convert message to text format.
153
154
Args:
155
origin (dns.name.Name): Origin for relative names
156
relativize (bool): Relativize names to origin
157
**kw: Additional options
158
159
Returns:
160
str: Text format message
161
"""
162
163
def is_response(self, other):
164
"""
165
Check if this message is a response to another message.
166
167
Args:
168
other (dns.message.Message): Potential query message
169
170
Returns:
171
bool: True if this is a response to other
172
"""
173
174
def is_valid_response(self):
175
"""
176
Check if message is a valid response.
177
178
Returns:
179
bool: True if valid response
180
"""
181
```
182
183
### Message Section Operations
184
185
Find, get, and manipulate RRsets within message sections.
186
187
```python { .api }
188
def find_rrset(section, name, rdclass, rdtype, covers='NONE', deleting=None,
189
create=False, force_unique=False):
190
"""
191
Find an RRset in the specified message section.
192
193
Args:
194
section (int): Message section (QUESTION, ANSWER, AUTHORITY, ADDITIONAL)
195
name (dns.name.Name): Record name
196
rdclass (int): Record class
197
rdtype (int): Record type
198
covers (int): Covered type for RRSIG records
199
deleting (int): Deletion indicator for dynamic updates
200
create (bool): Create RRset if not found
201
force_unique (bool): Force unique names
202
203
Returns:
204
dns.rrset.RRset: Found or created RRset
205
"""
206
207
def get_rrset(section, name, rdclass, rdtype, covers='NONE', deleting=None,
208
create=False, force_unique=False):
209
"""
210
Get an RRset from the specified message section.
211
212
Args:
213
section (int): Message section
214
name (dns.name.Name): Record name
215
rdclass (int): Record class
216
rdtype (int): Record type
217
covers (int): Covered type for RRSIG records
218
deleting (int): Deletion indicator
219
create (bool): Create RRset if not found
220
force_unique (bool): Force unique names
221
222
Returns:
223
dns.rrset.RRset or None: Found RRset or None
224
"""
225
```
226
227
### EDNS and TSIG Configuration
228
229
Configure extended DNS (EDNS) and transaction signatures (TSIG) for messages.
230
231
```python { .api }
232
def use_edns(edns=0, ednsflags=0, payload=1280, request_payload=None, options=None):
233
"""
234
Configure EDNS for this message.
235
236
Args:
237
edns (int): EDNS version (0 for EDNS0, -1 to disable)
238
ednsflags (int): EDNS flags
239
payload (int): Payload size
240
request_payload (int): Requested payload size
241
options (list): EDNS options
242
"""
243
244
def use_tsig(keyring, keyname=None, fudge=300, original_id=None, tsig_error=0,
245
other_data=b'', algorithm='HMAC-MD5.SIG-ALG.REG.INT'):
246
"""
247
Configure TSIG for this message.
248
249
Args:
250
keyring (dict): TSIG keyring
251
keyname (dns.name.Name): Key name
252
fudge (int): Time fudge factor
253
original_id (int): Original message ID
254
tsig_error (int): TSIG error code
255
other_data (bytes): Additional TSIG data
256
algorithm (str): TSIG algorithm name
257
"""
258
259
def want_dnssec(wanted=True):
260
"""
261
Enable or disable DNSSEC DO bit.
262
263
Args:
264
wanted (bool): Enable DNSSEC
265
"""
266
```
267
268
### Message Properties
269
270
Access message header fields and computed properties.
271
272
```python { .api }
273
def id():
274
"""Get message ID."""
275
276
def flags():
277
"""Get header flags."""
278
279
def rcode():
280
"""Get response code."""
281
282
def opcode():
283
"""Get operation code."""
284
285
def question_for_answer():
286
"""Get question corresponding to first answer."""
287
288
def had_tsig():
289
"""Check if message had TSIG."""
290
291
def tsig_error():
292
"""Get TSIG error code."""
293
294
def mac():
295
"""Get TSIG MAC."""
296
```
297
298
## Usage Examples
299
300
### Creating Query Messages
301
302
```python
303
import dns.message
304
import dns.name
305
import dns.rdatatype
306
307
# Basic query
308
qname = dns.name.from_text('example.com')
309
query = dns.message.make_query(qname, dns.rdatatype.A)
310
311
# Query with EDNS and DNSSEC
312
query_edns = dns.message.make_query(
313
qname, dns.rdatatype.DNSKEY,
314
use_edns=0,
315
want_dnssec=True,
316
payload=4096
317
)
318
319
# Multiple questions (not common but supported)
320
query = dns.message.Message()
321
query.id = dns.entropy.random_16()
322
query.flags = dns.flags.RD # Recursion desired
323
query.find_rrset(dns.message.QUESTION, qname, dns.rdataclass.IN,
324
dns.rdatatype.A, create=True)
325
```
326
327
### Parsing Wire Format
328
329
```python
330
import dns.message
331
332
# Parse raw DNS packet
333
wire_data = b'\x12\x34\x01\x00...' # Raw DNS packet bytes
334
message = dns.message.from_wire(wire_data)
335
336
print(f"Message ID: {message.id}")
337
print(f"Response code: {message.rcode()}")
338
print(f"Question count: {len(message.question)}")
339
print(f"Answer count: {len(message.answer)}")
340
341
# Process answers
342
for rrset in message.answer:
343
print(f"Name: {rrset.name}")
344
print(f"Type: {rrset.rdtype}")
345
for rdata in rrset:
346
print(f" Data: {rdata}")
347
```
348
349
### Creating Response Messages
350
351
```python
352
import dns.message
353
import dns.rrset
354
import dns.rdata
355
import dns.rdataclass
356
import dns.rdatatype
357
358
# Create response from query
359
query = dns.message.make_query('example.com', dns.rdatatype.A)
360
response = dns.message.make_response(query, recursion_available=True)
361
362
# Add answer records
363
answer_rrset = dns.rrset.from_text('example.com', 300, 'IN', 'A', '192.0.2.1')
364
response.answer.append(answer_rrset)
365
366
# Set response code
367
response.set_rcode(dns.rcode.NOERROR)
368
369
# Convert to wire format for transmission
370
wire_response = response.to_wire()
371
```
372
373
### Working with Message Sections
374
375
```python
376
import dns.message
377
import dns.name
378
import dns.rdatatype
379
import dns.rdataclass
380
381
message = dns.message.from_wire(wire_data)
382
383
# Find specific RRset
384
name = dns.name.from_text('example.com')
385
rrset = message.find_rrset(dns.message.ANSWER, name,
386
dns.rdataclass.IN, dns.rdatatype.A)
387
388
if rrset:
389
print(f"Found {len(rrset)} A records for {name}")
390
for rdata in rrset:
391
print(f" {rdata.address}")
392
393
# Check all sections
394
sections = [
395
(dns.message.QUESTION, message.question, "Question"),
396
(dns.message.ANSWER, message.answer, "Answer"),
397
(dns.message.AUTHORITY, message.authority, "Authority"),
398
(dns.message.ADDITIONAL, message.additional, "Additional")
399
]
400
401
for section_id, section_list, section_name in sections:
402
print(f"{section_name} section: {len(section_list)} RRsets")
403
```
404
405
## Constants
406
407
```python { .api }
408
# Message sections
409
QUESTION = 0
410
ANSWER = 1
411
AUTHORITY = 2
412
ADDITIONAL = 3
413
414
# Header flags
415
QR = 0x8000 # Query/Response
416
AA = 0x0400 # Authoritative Answer
417
TC = 0x0200 # Truncated
418
RD = 0x0100 # Recursion Desired
419
RA = 0x0080 # Recursion Available
420
AD = 0x0020 # Authentic Data
421
CD = 0x0010 # Checking Disabled
422
```
423
424
## Exceptions
425
426
```python { .api }
427
class ShortHeader(DNSException):
428
"""A DNS packet is too short to contain a complete header."""
429
430
class TrailingJunk(DNSException):
431
"""There is trailing junk after the end of the DNS message."""
432
433
class UnknownHeaderField(DNSException):
434
"""An unknown header field name was specified."""
435
436
class BadEDNS(DNSException):
437
"""An OPT record was found somewhere other than the additional section."""
438
439
class BadTSIG(DNSException):
440
"""A TSIG record was found somewhere other than the end of the additional section."""
441
442
class UnknownTSIGKey(DNSException):
443
"""A TSIG record was received with an unknown key."""
444
```