0
# DNS Core Functionality
1
2
Core DNS packet encoding, decoding, and manipulation functionality that forms the foundation of dnslib. Handles conversion between wire format, Python objects, and Zone/DiG textual representations.
3
4
## Capabilities
5
6
### DNS Record Container
7
8
Main container class for complete DNS packets with header, questions, answers, authority, and additional sections.
9
10
```python { .api }
11
class DNSRecord:
12
"""
13
DNS packet container with header and resource record sections.
14
15
Args:
16
header (DNSHeader, optional): DNS packet header
17
q (DNSQuestion or list, optional): Question section entries
18
a (RR or list, optional): Answer section entries
19
auth (RR or list, optional): Authority section entries
20
ar (RR or list, optional): Additional section entries
21
"""
22
def __init__(self, header=None, q=None, a=None, auth=None, ar=None): ...
23
24
@classmethod
25
def parse(cls, packet):
26
"""
27
Parse DNS packet from wire format bytes.
28
29
Args:
30
packet (bytes): DNS packet in wire format
31
32
Returns:
33
DNSRecord: Parsed DNS record object
34
35
Raises:
36
DNSError: If packet is malformed
37
"""
38
39
@classmethod
40
def question(cls, qname, qtype="A", qclass="IN"):
41
"""
42
Create DNS query packet.
43
44
Args:
45
qname (str): Domain name to query
46
qtype (str or int, optional): Query type (default: "A")
47
qclass (str or int, optional): Query class (default: "IN")
48
49
Returns:
50
DNSRecord: DNS query packet
51
"""
52
53
def pack(self):
54
"""
55
Pack DNS record to wire format bytes.
56
57
Returns:
58
bytes: DNS packet in wire format
59
"""
60
61
def reply(self, ra=1, aa=1):
62
"""
63
Create reply packet based on this query.
64
65
Args:
66
ra (int, optional): Recursion available flag (default: 1)
67
aa (int, optional): Authoritative answer flag (default: 1)
68
69
Returns:
70
DNSRecord: DNS response packet
71
"""
72
73
def replyZone(self, zone):
74
"""
75
Create reply packet from zone file format string.
76
77
Args:
78
zone (str): Zone file format resource records
79
80
Returns:
81
DNSRecord: DNS response packet with zone data
82
"""
83
84
def add_question(self, q):
85
"""Add question to question section."""
86
87
def add_answer(self, *rr):
88
"""Add resource records to answer section."""
89
90
def add_auth(self, *rr):
91
"""Add resource records to authority section."""
92
93
def add_ar(self, *rr):
94
"""Add resource records to additional section."""
95
96
def send(self, dest, port=53, tcp=False, timeout=None, ipv6=False):
97
"""
98
Send DNS query and return response.
99
100
Args:
101
dest (str): Destination server address
102
port (int, optional): Destination port (default: 53)
103
tcp (bool, optional): Use TCP instead of UDP (default: False)
104
timeout (int, optional): Query timeout in seconds (default: None)
105
ipv6 (bool, optional): Use IPv6 (default: False)
106
107
Returns:
108
bytes: DNS response packet in wire format
109
"""
110
111
def format(self, prefix="", sort=False):
112
"""
113
Format DNS record as string with optional prefix and sorting.
114
115
Args:
116
prefix (str, optional): Prefix for each line
117
sort (bool, optional): Sort resource records
118
119
Returns:
120
str: Formatted DNS record
121
"""
122
123
def toZone(self, prefix=""):
124
"""
125
Convert DNS record to zone file format.
126
127
Args:
128
prefix (str, optional): Prefix for each line
129
130
Returns:
131
str: Zone file format string
132
"""
133
134
def short(self):
135
"""
136
Return short string representation of DNS record.
137
138
Returns:
139
str: Short format string
140
"""
141
142
def diff(self, other):
143
"""
144
Compare DNS records and return differences.
145
146
Args:
147
other (DNSRecord): Other DNS record to compare
148
149
Returns:
150
str: Difference description
151
"""
152
153
def truncate(self):
154
"""
155
Truncate DNS record by removing additional and authority sections.
156
Sets TC flag in header.
157
"""
158
159
def set_header_qa(self):
160
"""Update header question/answer counts from sections."""
161
```
162
163
### DNS Header
164
165
DNS packet header containing ID, flags, and section counts.
166
167
```python { .api }
168
class DNSHeader:
169
"""
170
DNS packet header with ID, flags, and section record counts.
171
172
Args:
173
id (int, optional): Transaction ID (random if not specified)
174
qr (int, optional): Query/Response flag (0=query, 1=response)
175
opcode (int, optional): Operation code (0=QUERY, 1=IQUERY, etc.)
176
aa (int, optional): Authoritative answer flag
177
tc (int, optional): Truncation flag
178
rd (int, optional): Recursion desired flag
179
ra (int, optional): Recursion available flag
180
z (int, optional): Reserved bits (must be 0)
181
ad (int, optional): Authentic data flag (DNSSEC)
182
cd (int, optional): Checking disabled flag (DNSSEC)
183
rcode (int, optional): Response code (0=NOERROR, 3=NXDOMAIN, etc.)
184
q (int, optional): Question count
185
a (int, optional): Answer count
186
auth (int, optional): Authority count
187
ar (int, optional): Additional count
188
"""
189
def __init__(self, id=None, **kwargs): ...
190
191
def pack(self):
192
"""
193
Pack header to wire format bytes.
194
195
Returns:
196
bytes: Header in wire format
197
"""
198
199
@classmethod
200
def parse(cls, buffer):
201
"""
202
Parse header from wire format buffer.
203
204
Args:
205
buffer (Buffer): Wire format buffer
206
207
Returns:
208
DNSHeader: Parsed header object
209
"""
210
211
def toZone(self):
212
"""
213
Convert header to zone file format comment.
214
215
Returns:
216
str: Zone file format header comment
217
"""
218
219
# Properties for flag access
220
@property
221
def qr(self):
222
"""Query/Response flag (0=query, 1=response)."""
223
224
@qr.setter
225
def qr(self, value):
226
"""Set Query/Response flag."""
227
228
@property
229
def opcode(self):
230
"""Operation code (QUERY, IQUERY, STATUS, etc.)."""
231
232
@opcode.setter
233
def opcode(self, value):
234
"""Set operation code."""
235
236
@property
237
def aa(self):
238
"""Authoritative Answer flag."""
239
240
@aa.setter
241
def aa(self, value):
242
"""Set Authoritative Answer flag."""
243
244
@property
245
def tc(self):
246
"""Truncation flag."""
247
248
@tc.setter
249
def tc(self, value):
250
"""Set Truncation flag."""
251
252
@property
253
def rd(self):
254
"""Recursion Desired flag."""
255
256
@rd.setter
257
def rd(self, value):
258
"""Set Recursion Desired flag."""
259
260
@property
261
def ra(self):
262
"""Recursion Available flag."""
263
264
@ra.setter
265
def ra(self, value):
266
"""Set Recursion Available flag."""
267
268
@property
269
def z(self):
270
"""Reserved bits (must be 0)."""
271
272
@z.setter
273
def z(self, value):
274
"""Set reserved bits."""
275
276
@property
277
def ad(self):
278
"""Authentic Data flag (DNSSEC)."""
279
280
@ad.setter
281
def ad(self, value):
282
"""Set Authentic Data flag."""
283
284
@property
285
def cd(self):
286
"""Checking Disabled flag (DNSSEC)."""
287
288
@cd.setter
289
def cd(self, value):
290
"""Set Checking Disabled flag."""
291
292
@property
293
def rcode(self):
294
"""Response code (NOERROR, NXDOMAIN, etc.)."""
295
296
@rcode.setter
297
def rcode(self, value):
298
"""Set response code."""
299
```
300
301
### DNS Question
302
303
DNS question section specifying query name, type, and class.
304
305
```python { .api }
306
class DNSQuestion:
307
"""
308
DNS question entry specifying query parameters.
309
310
Args:
311
qname (str or DNSLabel): Domain name to query
312
qtype (str or int, optional): Query type (default: "A")
313
qclass (str or int, optional): Query class (default: "IN")
314
"""
315
def __init__(self, qname, qtype="A", qclass="IN"): ...
316
317
def pack(self):
318
"""
319
Pack question to wire format bytes.
320
321
Returns:
322
bytes: Question in wire format
323
"""
324
325
@classmethod
326
def parse(cls, buffer):
327
"""
328
Parse question from wire format buffer.
329
330
Args:
331
buffer (Buffer): Wire format buffer
332
333
Returns:
334
DNSQuestion: Parsed question object
335
"""
336
```
337
338
### Resource Record
339
340
DNS resource record containing name, type, class, TTL, and resource data.
341
342
```python { .api }
343
class RR:
344
"""
345
DNS resource record with header and resource data.
346
347
Args:
348
rname (str or DNSLabel): Resource record name
349
rtype (str or int, optional): Record type (default: "A")
350
rclass (str or int, optional): Record class (default: "IN")
351
ttl (int, optional): Time to live in seconds (default: 0)
352
rdata (RD object, optional): Resource data
353
"""
354
def __init__(self, rname, rtype="A", rclass="IN", ttl=0, rdata=None): ...
355
356
def pack(self):
357
"""
358
Pack resource record to wire format bytes.
359
360
Returns:
361
bytes: Resource record in wire format
362
"""
363
364
@classmethod
365
def parse(cls, buffer):
366
"""
367
Parse resource record from wire format buffer.
368
369
Args:
370
buffer (Buffer): Wire format buffer
371
372
Returns:
373
RR: Parsed resource record object
374
"""
375
376
@classmethod
377
def fromZone(cls, zone, origin=None, ttl=None):
378
"""
379
Parse resource records from zone file format string.
380
381
Args:
382
zone (str): Zone file format string
383
origin (str, optional): Default origin domain
384
ttl (int, optional): Default TTL value
385
386
Returns:
387
list[RR]: List of parsed resource records
388
"""
389
```
390
391
### DNS Labels
392
393
DNS name representation with IDNA encoding support and label manipulation.
394
395
```python { .api }
396
class DNSLabel:
397
"""
398
DNS label with IDNA encoding support.
399
400
Args:
401
label (str or list): Domain name string or label components
402
"""
403
def __init__(self, label): ...
404
405
def encode(self):
406
"""
407
Encode label to wire format bytes.
408
409
Returns:
410
bytes: Encoded label
411
"""
412
413
@classmethod
414
def decode(cls, buffer):
415
"""
416
Decode label from wire format buffer.
417
418
Args:
419
buffer (Buffer): Wire format buffer
420
421
Returns:
422
DNSLabel: Decoded label object
423
"""
424
425
def idna(self):
426
"""
427
Convert label to IDNA format.
428
429
Returns:
430
DNSLabel: IDNA encoded label
431
"""
432
433
def matchSuffix(self, suffix):
434
"""
435
Check if label ends with suffix (case-insensitive).
436
437
Args:
438
suffix (str or DNSLabel): Suffix to match
439
440
Returns:
441
bool: True if label ends with suffix
442
"""
443
444
def stripSuffix(self, suffix):
445
"""
446
Remove suffix from label (case-insensitive).
447
448
Args:
449
suffix (str or DNSLabel): Suffix to remove
450
451
Returns:
452
DNSLabel: Label with suffix removed
453
"""
454
455
def matchWildcard(self, pattern):
456
"""
457
Match label against wildcard pattern.
458
459
Args:
460
pattern (str or DNSLabel): Wildcard pattern (* supported)
461
462
Returns:
463
bool: True if label matches pattern
464
"""
465
```
466
467
### EDNS0 Support
468
469
Extended DNS (EDNS0) support for larger packets and options.
470
471
```python { .api }
472
class EDNS0:
473
"""
474
EDNS0 (Extended DNS) pseudo-record for enhanced DNS functionality.
475
476
Args:
477
version (int, optional): EDNS version (default: 0)
478
flags (str or int, optional): EDNS flags ("do" for DNSSEC OK)
479
udp_len (int, optional): UDP payload size (default: 4096)
480
rcode_ext (int, optional): Extended response code
481
options (list, optional): EDNS options
482
"""
483
def __init__(self, version=0, flags=0, udp_len=4096, rcode_ext=0, options=None): ...
484
485
def pack(self):
486
"""Pack EDNS0 record to wire format."""
487
488
@classmethod
489
def parse(cls, buffer):
490
"""Parse EDNS0 record from wire format buffer."""
491
```
492
493
### EDNS Options
494
495
EDNS option handling for extended functionality.
496
497
```python { .api }
498
class EDNSOption:
499
"""
500
EDNS option for extended DNS functionality.
501
502
Args:
503
code (int): Option code
504
data (bytes): Option data
505
"""
506
def __init__(self, code, data): ...
507
508
def pack(self):
509
"""Pack option to wire format."""
510
511
@classmethod
512
def parse(cls, buffer):
513
"""Parse option from wire format buffer."""
514
```
515
516
### Buffer Management
517
518
Core buffer functionality for wire format packet handling.
519
520
```python { .api }
521
class Buffer:
522
"""
523
Simple data buffer with pack/unpack support for binary data manipulation.
524
525
Args:
526
data (bytes, optional): Initial buffer data
527
"""
528
def __init__(self, data=b""): ...
529
530
def remaining(self):
531
"""
532
Get remaining bytes in buffer.
533
534
Returns:
535
int: Number of remaining bytes
536
"""
537
538
def get(self, length):
539
"""
540
Get specified number of bytes from buffer.
541
542
Args:
543
length (int): Number of bytes to read
544
545
Returns:
546
bytes: Buffer data
547
"""
548
549
def pack(self, fmt, *args):
550
"""
551
Pack data using struct format and append to buffer.
552
553
Args:
554
fmt (str): Struct format string
555
*args: Values to pack
556
"""
557
558
def append(self, data):
559
"""
560
Append data to buffer.
561
562
Args:
563
data (bytes): Data to append
564
"""
565
566
def unpack(self, fmt):
567
"""
568
Unpack data from buffer using struct format.
569
570
Args:
571
fmt (str): Struct format string
572
573
Returns:
574
tuple: Unpacked values
575
"""
576
577
def update(self, ptr, fmt, *args):
578
"""
579
Update buffer at specific position.
580
581
Args:
582
ptr (int): Buffer position
583
fmt (str): Struct format string
584
*args: Values to pack
585
"""
586
587
def hex(self):
588
"""
589
Return hex representation of buffer data.
590
591
Returns:
592
str: Hexadecimal string
593
"""
594
```
595
596
### DNS Buffer
597
598
Extended buffer with DNS name encoding/decoding and compression support.
599
600
```python { .api }
601
class DNSBuffer(Buffer):
602
"""
603
DNS-specific buffer with name compression and caching support.
604
605
Args:
606
data (bytes, optional): Initial buffer data
607
"""
608
def __init__(self, data=b""): ...
609
610
def decode_name(self, last=-1):
611
"""
612
Decode DNS name from buffer with compression support.
613
614
Args:
615
last (int, optional): Last position for compression
616
617
Returns:
618
DNSLabel: Decoded DNS name
619
"""
620
621
def encode_name(self, name):
622
"""
623
Encode DNS name to buffer with compression.
624
625
Args:
626
name (str or DNSLabel): DNS name to encode
627
"""
628
629
def encode_name_nocompress(self, name):
630
"""
631
Encode DNS name without compression.
632
633
Args:
634
name (str or DNSLabel): DNS name to encode
635
"""
636
```
637
638
### Bidirectional Mapping
639
640
Bidirectional mapping between numeric codes and text labels.
641
642
```python { .api }
643
class Bimap:
644
"""
645
Bidirectional mapping for DNS constants (e.g., QTYPE, CLASS, RCODE).
646
647
Args:
648
name (str): Mapping name for error messages
649
forward (dict): Forward mapping (code -> text)
650
error (Exception, optional): Exception class for errors
651
"""
652
def __init__(self, name, forward, error=AttributeError): ...
653
654
def get(self, key, default=None):
655
"""
656
Get value with default fallback.
657
658
Args:
659
key: Lookup key
660
default: Default value if key not found
661
662
Returns:
663
Value or default
664
"""
665
666
def __getitem__(self, key):
667
"""
668
Forward lookup: code -> text.
669
670
Args:
671
key: Numeric code
672
673
Returns:
674
str: Text label
675
"""
676
677
def __getattr__(self, key):
678
"""
679
Reverse lookup: text -> code.
680
681
Args:
682
key (str): Text label
683
684
Returns:
685
int: Numeric code
686
"""
687
```
688
689
### Zone File Parser
690
691
Parser for DNS zone file format with support for $TTL and $ORIGIN directives.
692
693
```python { .api }
694
class ZoneParser:
695
"""
696
Parser for DNS zone file format strings.
697
698
Args:
699
zone (str): Zone file format string
700
origin (str, optional): Default origin domain
701
ttl (int, optional): Default TTL value
702
"""
703
def __init__(self, zone, origin="", ttl=0): ...
704
705
def parse(self):
706
"""
707
Parse zone file and return resource records.
708
709
Returns:
710
list[RR]: List of parsed resource records
711
"""
712
713
def expect(self, token):
714
"""
715
Expect specific token in zone file.
716
717
Args:
718
token (str): Expected token
719
720
Raises:
721
Exception: If token not found
722
"""
723
724
def parse_label(self, label):
725
"""
726
Parse DNS label from zone file.
727
728
Args:
729
label (str): Label string
730
731
Returns:
732
DNSLabel: Parsed label
733
"""
734
735
def parse_rr(self, rr_line):
736
"""
737
Parse resource record line from zone file.
738
739
Args:
740
rr_line (str): Resource record line
741
742
Returns:
743
RR: Parsed resource record
744
"""
745
```
746
747
### Utility Functions
748
749
Core utility functions for DNS label and time handling.
750
751
```python { .api }
752
def label(label, origin=None):
753
"""
754
Create DNS label with optional origin.
755
756
Args:
757
label (str): Label string
758
origin (str or DNSLabel, optional): Origin domain
759
760
Returns:
761
DNSLabel: DNS label object
762
"""
763
764
def parse_time(s):
765
"""
766
Parse time value for DNS records.
767
768
Args:
769
s (str): Time string (e.g., "1h", "30m", "3600")
770
771
Returns:
772
int: Time value in seconds
773
"""
774
775
def decode_type_bitmap(type_bitmap):
776
"""
777
Decode NSEC type bitmap.
778
779
Args:
780
type_bitmap (bytes): Type bitmap data
781
782
Returns:
783
list[int]: List of record type numbers
784
"""
785
786
def encode_type_bitmap(rrlist):
787
"""
788
Encode NSEC type bitmap.
789
790
Args:
791
rrlist (list[int]): List of record type numbers
792
793
Returns:
794
bytes: Type bitmap data
795
"""
796
797
def unknown_qtype(name, key, forward):
798
"""
799
Handle unknown query types for dynamic mapping.
800
801
Args:
802
name (str): Mapping name
803
key: Lookup key
804
forward (bool): True for forward lookup
805
806
Returns:
807
Mapped value or TYPE### format
808
"""
809
```
810
811
## Usage Examples
812
813
### Basic Packet Parsing
814
815
```python
816
import binascii
817
from dnslib import *
818
819
# Parse DNS response packet
820
packet_hex = 'd5ad818000010005000000000377777706676f6f676c6503636f6d0000010001...'
821
packet = binascii.unhexlify(packet_hex.encode())
822
d = DNSRecord.parse(packet)
823
824
print(f"Header: {d.header}")
825
print(f"Question: {d.q}")
826
for rr in d.rr:
827
print(f"Answer: {rr}")
828
```
829
830
### Creating DNS Queries
831
832
```python
833
from dnslib import *
834
835
# Simple A query
836
q = DNSRecord.question("example.com")
837
print(q)
838
839
# MX query with specific header flags
840
q = DNSRecord.question("example.com", "MX")
841
q.header.rd = 1 # Recursion desired
842
print(q)
843
844
# DNSSEC query with EDNS0
845
q = DNSRecord.question("example.com", "A")
846
q.add_ar(EDNS0(flags="do", udp_len=4096))
847
q.header.ad = 1 # Authentic data
848
print(q)
849
```
850
851
### Creating DNS Responses
852
853
```python
854
from dnslib import *
855
856
# Create response manually
857
response = DNSRecord(
858
DNSHeader(qr=1, aa=1, ra=1),
859
q=DNSQuestion("example.com"),
860
a=RR("example.com", rdata=A("1.2.3.4"), ttl=300)
861
)
862
863
# Create response from query
864
q = DNSRecord.question("example.com")
865
a = q.reply()
866
a.add_answer(*RR.fromZone("example.com 300 IN A 1.2.3.4"))
867
a.add_answer(*RR.fromZone("example.com 300 IN AAAA 2001:db8::1"))
868
869
# Verify round-trip
870
packed = a.pack()
871
parsed = DNSRecord.parse(packed)
872
assert str(parsed) == str(a)
873
```
874
875
### Zone File Processing
876
877
```python
878
from dnslib import *
879
880
# Parse zone file format
881
zone_data = """
882
$TTL 300
883
$ORIGIN example.com.
884
885
@ IN MX 10 mail.example.com.
886
www IN A 1.2.3.4
887
IN TXT "Some Text"
888
mail IN CNAME www.example.com.
889
"""
890
891
# Create records from zone data
892
records = RR.fromZone(zone_data.strip())
893
for rr in records:
894
print(rr)
895
896
# Create response with zone data
897
q = DNSRecord.question("example.com", "ANY")
898
a = q.replyZone(zone_data.strip())
899
print(a)
900
```