Simple library to encode/decode DNS wire-format packets
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.
Main container class for complete DNS packets with header, questions, answers, authority, and additional sections.
class DNSRecord:
"""
DNS packet container with header and resource record sections.
Args:
header (DNSHeader, optional): DNS packet header
q (DNSQuestion or list, optional): Question section entries
a (RR or list, optional): Answer section entries
auth (RR or list, optional): Authority section entries
ar (RR or list, optional): Additional section entries
"""
def __init__(self, header=None, q=None, a=None, auth=None, ar=None): ...
@classmethod
def parse(cls, packet):
"""
Parse DNS packet from wire format bytes.
Args:
packet (bytes): DNS packet in wire format
Returns:
DNSRecord: Parsed DNS record object
Raises:
DNSError: If packet is malformed
"""
@classmethod
def question(cls, qname, qtype="A", qclass="IN"):
"""
Create DNS query packet.
Args:
qname (str): Domain name to query
qtype (str or int, optional): Query type (default: "A")
qclass (str or int, optional): Query class (default: "IN")
Returns:
DNSRecord: DNS query packet
"""
def pack(self):
"""
Pack DNS record to wire format bytes.
Returns:
bytes: DNS packet in wire format
"""
def reply(self, ra=1, aa=1):
"""
Create reply packet based on this query.
Args:
ra (int, optional): Recursion available flag (default: 1)
aa (int, optional): Authoritative answer flag (default: 1)
Returns:
DNSRecord: DNS response packet
"""
def replyZone(self, zone):
"""
Create reply packet from zone file format string.
Args:
zone (str): Zone file format resource records
Returns:
DNSRecord: DNS response packet with zone data
"""
def add_question(self, q):
"""Add question to question section."""
def add_answer(self, *rr):
"""Add resource records to answer section."""
def add_auth(self, *rr):
"""Add resource records to authority section."""
def add_ar(self, *rr):
"""Add resource records to additional section."""
def send(self, dest, port=53, tcp=False, timeout=None, ipv6=False):
"""
Send DNS query and return response.
Args:
dest (str): Destination server address
port (int, optional): Destination port (default: 53)
tcp (bool, optional): Use TCP instead of UDP (default: False)
timeout (int, optional): Query timeout in seconds (default: None)
ipv6 (bool, optional): Use IPv6 (default: False)
Returns:
bytes: DNS response packet in wire format
"""
def format(self, prefix="", sort=False):
"""
Format DNS record as string with optional prefix and sorting.
Args:
prefix (str, optional): Prefix for each line
sort (bool, optional): Sort resource records
Returns:
str: Formatted DNS record
"""
def toZone(self, prefix=""):
"""
Convert DNS record to zone file format.
Args:
prefix (str, optional): Prefix for each line
Returns:
str: Zone file format string
"""
def short(self):
"""
Return short string representation of DNS record.
Returns:
str: Short format string
"""
def diff(self, other):
"""
Compare DNS records and return differences.
Args:
other (DNSRecord): Other DNS record to compare
Returns:
str: Difference description
"""
def truncate(self):
"""
Truncate DNS record by removing additional and authority sections.
Sets TC flag in header.
"""
def set_header_qa(self):
"""Update header question/answer counts from sections."""DNS packet header containing ID, flags, and section counts.
class DNSHeader:
"""
DNS packet header with ID, flags, and section record counts.
Args:
id (int, optional): Transaction ID (random if not specified)
qr (int, optional): Query/Response flag (0=query, 1=response)
opcode (int, optional): Operation code (0=QUERY, 1=IQUERY, etc.)
aa (int, optional): Authoritative answer flag
tc (int, optional): Truncation flag
rd (int, optional): Recursion desired flag
ra (int, optional): Recursion available flag
z (int, optional): Reserved bits (must be 0)
ad (int, optional): Authentic data flag (DNSSEC)
cd (int, optional): Checking disabled flag (DNSSEC)
rcode (int, optional): Response code (0=NOERROR, 3=NXDOMAIN, etc.)
q (int, optional): Question count
a (int, optional): Answer count
auth (int, optional): Authority count
ar (int, optional): Additional count
"""
def __init__(self, id=None, **kwargs): ...
def pack(self):
"""
Pack header to wire format bytes.
Returns:
bytes: Header in wire format
"""
@classmethod
def parse(cls, buffer):
"""
Parse header from wire format buffer.
Args:
buffer (Buffer): Wire format buffer
Returns:
DNSHeader: Parsed header object
"""
def toZone(self):
"""
Convert header to zone file format comment.
Returns:
str: Zone file format header comment
"""
# Properties for flag access
@property
def qr(self):
"""Query/Response flag (0=query, 1=response)."""
@qr.setter
def qr(self, value):
"""Set Query/Response flag."""
@property
def opcode(self):
"""Operation code (QUERY, IQUERY, STATUS, etc.)."""
@opcode.setter
def opcode(self, value):
"""Set operation code."""
@property
def aa(self):
"""Authoritative Answer flag."""
@aa.setter
def aa(self, value):
"""Set Authoritative Answer flag."""
@property
def tc(self):
"""Truncation flag."""
@tc.setter
def tc(self, value):
"""Set Truncation flag."""
@property
def rd(self):
"""Recursion Desired flag."""
@rd.setter
def rd(self, value):
"""Set Recursion Desired flag."""
@property
def ra(self):
"""Recursion Available flag."""
@ra.setter
def ra(self, value):
"""Set Recursion Available flag."""
@property
def z(self):
"""Reserved bits (must be 0)."""
@z.setter
def z(self, value):
"""Set reserved bits."""
@property
def ad(self):
"""Authentic Data flag (DNSSEC)."""
@ad.setter
def ad(self, value):
"""Set Authentic Data flag."""
@property
def cd(self):
"""Checking Disabled flag (DNSSEC)."""
@cd.setter
def cd(self, value):
"""Set Checking Disabled flag."""
@property
def rcode(self):
"""Response code (NOERROR, NXDOMAIN, etc.)."""
@rcode.setter
def rcode(self, value):
"""Set response code."""DNS question section specifying query name, type, and class.
class DNSQuestion:
"""
DNS question entry specifying query parameters.
Args:
qname (str or DNSLabel): Domain name to query
qtype (str or int, optional): Query type (default: "A")
qclass (str or int, optional): Query class (default: "IN")
"""
def __init__(self, qname, qtype="A", qclass="IN"): ...
def pack(self):
"""
Pack question to wire format bytes.
Returns:
bytes: Question in wire format
"""
@classmethod
def parse(cls, buffer):
"""
Parse question from wire format buffer.
Args:
buffer (Buffer): Wire format buffer
Returns:
DNSQuestion: Parsed question object
"""DNS resource record containing name, type, class, TTL, and resource data.
class RR:
"""
DNS resource record with header and resource data.
Args:
rname (str or DNSLabel): Resource record name
rtype (str or int, optional): Record type (default: "A")
rclass (str or int, optional): Record class (default: "IN")
ttl (int, optional): Time to live in seconds (default: 0)
rdata (RD object, optional): Resource data
"""
def __init__(self, rname, rtype="A", rclass="IN", ttl=0, rdata=None): ...
def pack(self):
"""
Pack resource record to wire format bytes.
Returns:
bytes: Resource record in wire format
"""
@classmethod
def parse(cls, buffer):
"""
Parse resource record from wire format buffer.
Args:
buffer (Buffer): Wire format buffer
Returns:
RR: Parsed resource record object
"""
@classmethod
def fromZone(cls, zone, origin=None, ttl=None):
"""
Parse resource records from zone file format string.
Args:
zone (str): Zone file format string
origin (str, optional): Default origin domain
ttl (int, optional): Default TTL value
Returns:
list[RR]: List of parsed resource records
"""DNS name representation with IDNA encoding support and label manipulation.
class DNSLabel:
"""
DNS label with IDNA encoding support.
Args:
label (str or list): Domain name string or label components
"""
def __init__(self, label): ...
def encode(self):
"""
Encode label to wire format bytes.
Returns:
bytes: Encoded label
"""
@classmethod
def decode(cls, buffer):
"""
Decode label from wire format buffer.
Args:
buffer (Buffer): Wire format buffer
Returns:
DNSLabel: Decoded label object
"""
def idna(self):
"""
Convert label to IDNA format.
Returns:
DNSLabel: IDNA encoded label
"""
def matchSuffix(self, suffix):
"""
Check if label ends with suffix (case-insensitive).
Args:
suffix (str or DNSLabel): Suffix to match
Returns:
bool: True if label ends with suffix
"""
def stripSuffix(self, suffix):
"""
Remove suffix from label (case-insensitive).
Args:
suffix (str or DNSLabel): Suffix to remove
Returns:
DNSLabel: Label with suffix removed
"""
def matchWildcard(self, pattern):
"""
Match label against wildcard pattern.
Args:
pattern (str or DNSLabel): Wildcard pattern (* supported)
Returns:
bool: True if label matches pattern
"""Extended DNS (EDNS0) support for larger packets and options.
class EDNS0:
"""
EDNS0 (Extended DNS) pseudo-record for enhanced DNS functionality.
Args:
version (int, optional): EDNS version (default: 0)
flags (str or int, optional): EDNS flags ("do" for DNSSEC OK)
udp_len (int, optional): UDP payload size (default: 4096)
rcode_ext (int, optional): Extended response code
options (list, optional): EDNS options
"""
def __init__(self, version=0, flags=0, udp_len=4096, rcode_ext=0, options=None): ...
def pack(self):
"""Pack EDNS0 record to wire format."""
@classmethod
def parse(cls, buffer):
"""Parse EDNS0 record from wire format buffer."""EDNS option handling for extended functionality.
class EDNSOption:
"""
EDNS option for extended DNS functionality.
Args:
code (int): Option code
data (bytes): Option data
"""
def __init__(self, code, data): ...
def pack(self):
"""Pack option to wire format."""
@classmethod
def parse(cls, buffer):
"""Parse option from wire format buffer."""Core buffer functionality for wire format packet handling.
class Buffer:
"""
Simple data buffer with pack/unpack support for binary data manipulation.
Args:
data (bytes, optional): Initial buffer data
"""
def __init__(self, data=b""): ...
def remaining(self):
"""
Get remaining bytes in buffer.
Returns:
int: Number of remaining bytes
"""
def get(self, length):
"""
Get specified number of bytes from buffer.
Args:
length (int): Number of bytes to read
Returns:
bytes: Buffer data
"""
def pack(self, fmt, *args):
"""
Pack data using struct format and append to buffer.
Args:
fmt (str): Struct format string
*args: Values to pack
"""
def append(self, data):
"""
Append data to buffer.
Args:
data (bytes): Data to append
"""
def unpack(self, fmt):
"""
Unpack data from buffer using struct format.
Args:
fmt (str): Struct format string
Returns:
tuple: Unpacked values
"""
def update(self, ptr, fmt, *args):
"""
Update buffer at specific position.
Args:
ptr (int): Buffer position
fmt (str): Struct format string
*args: Values to pack
"""
def hex(self):
"""
Return hex representation of buffer data.
Returns:
str: Hexadecimal string
"""Extended buffer with DNS name encoding/decoding and compression support.
class DNSBuffer(Buffer):
"""
DNS-specific buffer with name compression and caching support.
Args:
data (bytes, optional): Initial buffer data
"""
def __init__(self, data=b""): ...
def decode_name(self, last=-1):
"""
Decode DNS name from buffer with compression support.
Args:
last (int, optional): Last position for compression
Returns:
DNSLabel: Decoded DNS name
"""
def encode_name(self, name):
"""
Encode DNS name to buffer with compression.
Args:
name (str or DNSLabel): DNS name to encode
"""
def encode_name_nocompress(self, name):
"""
Encode DNS name without compression.
Args:
name (str or DNSLabel): DNS name to encode
"""Bidirectional mapping between numeric codes and text labels.
class Bimap:
"""
Bidirectional mapping for DNS constants (e.g., QTYPE, CLASS, RCODE).
Args:
name (str): Mapping name for error messages
forward (dict): Forward mapping (code -> text)
error (Exception, optional): Exception class for errors
"""
def __init__(self, name, forward, error=AttributeError): ...
def get(self, key, default=None):
"""
Get value with default fallback.
Args:
key: Lookup key
default: Default value if key not found
Returns:
Value or default
"""
def __getitem__(self, key):
"""
Forward lookup: code -> text.
Args:
key: Numeric code
Returns:
str: Text label
"""
def __getattr__(self, key):
"""
Reverse lookup: text -> code.
Args:
key (str): Text label
Returns:
int: Numeric code
"""Parser for DNS zone file format with support for $TTL and $ORIGIN directives.
class ZoneParser:
"""
Parser for DNS zone file format strings.
Args:
zone (str): Zone file format string
origin (str, optional): Default origin domain
ttl (int, optional): Default TTL value
"""
def __init__(self, zone, origin="", ttl=0): ...
def parse(self):
"""
Parse zone file and return resource records.
Returns:
list[RR]: List of parsed resource records
"""
def expect(self, token):
"""
Expect specific token in zone file.
Args:
token (str): Expected token
Raises:
Exception: If token not found
"""
def parse_label(self, label):
"""
Parse DNS label from zone file.
Args:
label (str): Label string
Returns:
DNSLabel: Parsed label
"""
def parse_rr(self, rr_line):
"""
Parse resource record line from zone file.
Args:
rr_line (str): Resource record line
Returns:
RR: Parsed resource record
"""Core utility functions for DNS label and time handling.
def label(label, origin=None):
"""
Create DNS label with optional origin.
Args:
label (str): Label string
origin (str or DNSLabel, optional): Origin domain
Returns:
DNSLabel: DNS label object
"""
def parse_time(s):
"""
Parse time value for DNS records.
Args:
s (str): Time string (e.g., "1h", "30m", "3600")
Returns:
int: Time value in seconds
"""
def decode_type_bitmap(type_bitmap):
"""
Decode NSEC type bitmap.
Args:
type_bitmap (bytes): Type bitmap data
Returns:
list[int]: List of record type numbers
"""
def encode_type_bitmap(rrlist):
"""
Encode NSEC type bitmap.
Args:
rrlist (list[int]): List of record type numbers
Returns:
bytes: Type bitmap data
"""
def unknown_qtype(name, key, forward):
"""
Handle unknown query types for dynamic mapping.
Args:
name (str): Mapping name
key: Lookup key
forward (bool): True for forward lookup
Returns:
Mapped value or TYPE### format
"""import binascii
from dnslib import *
# Parse DNS response packet
packet_hex = 'd5ad818000010005000000000377777706676f6f676c6503636f6d0000010001...'
packet = binascii.unhexlify(packet_hex.encode())
d = DNSRecord.parse(packet)
print(f"Header: {d.header}")
print(f"Question: {d.q}")
for rr in d.rr:
print(f"Answer: {rr}")from dnslib import *
# Simple A query
q = DNSRecord.question("example.com")
print(q)
# MX query with specific header flags
q = DNSRecord.question("example.com", "MX")
q.header.rd = 1 # Recursion desired
print(q)
# DNSSEC query with EDNS0
q = DNSRecord.question("example.com", "A")
q.add_ar(EDNS0(flags="do", udp_len=4096))
q.header.ad = 1 # Authentic data
print(q)from dnslib import *
# Create response manually
response = DNSRecord(
DNSHeader(qr=1, aa=1, ra=1),
q=DNSQuestion("example.com"),
a=RR("example.com", rdata=A("1.2.3.4"), ttl=300)
)
# Create response from query
q = DNSRecord.question("example.com")
a = q.reply()
a.add_answer(*RR.fromZone("example.com 300 IN A 1.2.3.4"))
a.add_answer(*RR.fromZone("example.com 300 IN AAAA 2001:db8::1"))
# Verify round-trip
packed = a.pack()
parsed = DNSRecord.parse(packed)
assert str(parsed) == str(a)from dnslib import *
# Parse zone file format
zone_data = """
$TTL 300
$ORIGIN example.com.
@ IN MX 10 mail.example.com.
www IN A 1.2.3.4
IN TXT "Some Text"
mail IN CNAME www.example.com.
"""
# Create records from zone data
records = RR.fromZone(zone_data.strip())
for rr in records:
print(rr)
# Create response with zone data
q = DNSRecord.question("example.com", "ANY")
a = q.replyZone(zone_data.strip())
print(a)Install with Tessl CLI
npx tessl i tessl/pypi-dnslib@0.9.2