DNS toolkit for Python supporting almost all record types with high-level and low-level DNS operations
85
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.
Create DNS query and response messages with full protocol support.
def make_query(qname, rdtype, rdclass='IN', use_edns=None, want_dnssec=False,
ednsflags=None, payload=None, request_payload=None, options=None):
"""
Create a DNS query message.
Args:
qname (str or dns.name.Name): Query name
rdtype (str or int): Record type to query
rdclass (str or int): Record class (default 'IN')
use_edns (int): EDNS version (None to disable, 0 for EDNS0)
want_dnssec (bool): Set DNSSEC OK bit
ednsflags (int): EDNS flags
payload (int): EDNS payload size
request_payload (int): Requested payload size
options (list): EDNS options
Returns:
dns.message.Message: DNS query message
"""
def make_response(query, recursion_available=False, our_payload=8192, fudge=300):
"""
Create a DNS response message from a query.
Args:
query (dns.message.Message): Original query message
recursion_available (bool): Set RA bit
our_payload (int): Our EDNS payload size
fudge (int): TSIG time fudge factor
Returns:
dns.message.Message: DNS response message
"""Parse DNS messages from wire format, text format, or files.
def from_wire(wire, keyring=None, request_mac=b'', xfr=False, origin=None,
tsig_ctx=None, multi=False, first=True, question_only=False,
one_rr_per_rrset=False, ignore_trailing=False):
"""
Parse a DNS message from wire format.
Args:
wire (bytes): Wire format message
keyring (dict): TSIG keyring for validation
request_mac (bytes): TSIG MAC from request
xfr (bool): Message is part of zone transfer
origin (dns.name.Name): Origin for relative names
tsig_ctx (dns.tsig.HMACTSig): TSIG context
multi (bool): Message is part of multi-message response
first (bool): First message in multi-message response
question_only (bool): Parse question section only
one_rr_per_rrset (bool): Put each RR in its own RRset
ignore_trailing (bool): Ignore trailing junk
Returns:
dns.message.Message: Parsed DNS message
"""
def from_text(text):
"""
Parse a DNS message from text format.
Args:
text (str): Text format message
Returns:
dns.message.Message: Parsed DNS message
"""
def from_file(f):
"""
Read and parse a DNS message from a file.
Args:
f (file-like): File to read from
Returns:
dns.message.Message: Parsed DNS message
"""Complete DNS message with header and sections for questions, answers, authority, and additional records.
class Message:
"""
A DNS message with header and sections.
Attributes:
id (int): Message ID
flags (int): Header flags
question (list): Question section
answer (list): Answer section
authority (list): Authority section
additional (list): Additional section
opt (dns.edns.OPT): EDNS OPT record
tsig (dns.rdata.Rdata): TSIG record
request_payload (int): Requested EDNS payload size
keyring (dict): TSIG keyring
mac (bytes): TSIG MAC
xfr (bool): Message is part of zone transfer
origin (dns.name.Name): Origin for relative names
tsig_ctx (dns.tsig.HMACTSig): TSIG context
"""
def __init__(self, id=None):
"""
Initialize DNS message.
Args:
id (int): Message ID (random if None)
"""
def __repr__(self):
"""Return string representation of message."""
def __str__(self):
"""Return string representation of message."""
def to_wire(self, origin=None, max_size=0, **kw):
"""
Convert message to wire format.
Args:
origin (dns.name.Name): Origin for relative names
max_size (int): Maximum message size
**kw: Additional options
Returns:
bytes: Wire format message
"""
def to_text(self, origin=None, relativize=True, **kw):
"""
Convert message to text format.
Args:
origin (dns.name.Name): Origin for relative names
relativize (bool): Relativize names to origin
**kw: Additional options
Returns:
str: Text format message
"""
def is_response(self, other):
"""
Check if this message is a response to another message.
Args:
other (dns.message.Message): Potential query message
Returns:
bool: True if this is a response to other
"""
def is_valid_response(self):
"""
Check if message is a valid response.
Returns:
bool: True if valid response
"""Find, get, and manipulate RRsets within message sections.
def find_rrset(section, name, rdclass, rdtype, covers='NONE', deleting=None,
create=False, force_unique=False):
"""
Find an RRset in the specified message section.
Args:
section (int): Message section (QUESTION, ANSWER, AUTHORITY, ADDITIONAL)
name (dns.name.Name): Record name
rdclass (int): Record class
rdtype (int): Record type
covers (int): Covered type for RRSIG records
deleting (int): Deletion indicator for dynamic updates
create (bool): Create RRset if not found
force_unique (bool): Force unique names
Returns:
dns.rrset.RRset: Found or created RRset
"""
def get_rrset(section, name, rdclass, rdtype, covers='NONE', deleting=None,
create=False, force_unique=False):
"""
Get an RRset from the specified message section.
Args:
section (int): Message section
name (dns.name.Name): Record name
rdclass (int): Record class
rdtype (int): Record type
covers (int): Covered type for RRSIG records
deleting (int): Deletion indicator
create (bool): Create RRset if not found
force_unique (bool): Force unique names
Returns:
dns.rrset.RRset or None: Found RRset or None
"""Configure extended DNS (EDNS) and transaction signatures (TSIG) for messages.
def use_edns(edns=0, ednsflags=0, payload=1280, request_payload=None, options=None):
"""
Configure EDNS for this message.
Args:
edns (int): EDNS version (0 for EDNS0, -1 to disable)
ednsflags (int): EDNS flags
payload (int): Payload size
request_payload (int): Requested payload size
options (list): EDNS options
"""
def use_tsig(keyring, keyname=None, fudge=300, original_id=None, tsig_error=0,
other_data=b'', algorithm='HMAC-MD5.SIG-ALG.REG.INT'):
"""
Configure TSIG for this message.
Args:
keyring (dict): TSIG keyring
keyname (dns.name.Name): Key name
fudge (int): Time fudge factor
original_id (int): Original message ID
tsig_error (int): TSIG error code
other_data (bytes): Additional TSIG data
algorithm (str): TSIG algorithm name
"""
def want_dnssec(wanted=True):
"""
Enable or disable DNSSEC DO bit.
Args:
wanted (bool): Enable DNSSEC
"""Access message header fields and computed properties.
def id():
"""Get message ID."""
def flags():
"""Get header flags."""
def rcode():
"""Get response code."""
def opcode():
"""Get operation code."""
def question_for_answer():
"""Get question corresponding to first answer."""
def had_tsig():
"""Check if message had TSIG."""
def tsig_error():
"""Get TSIG error code."""
def mac():
"""Get TSIG MAC."""import dns.message
import dns.name
import dns.rdatatype
# Basic query
qname = dns.name.from_text('example.com')
query = dns.message.make_query(qname, dns.rdatatype.A)
# Query with EDNS and DNSSEC
query_edns = dns.message.make_query(
qname, dns.rdatatype.DNSKEY,
use_edns=0,
want_dnssec=True,
payload=4096
)
# Multiple questions (not common but supported)
query = dns.message.Message()
query.id = dns.entropy.random_16()
query.flags = dns.flags.RD # Recursion desired
query.find_rrset(dns.message.QUESTION, qname, dns.rdataclass.IN,
dns.rdatatype.A, create=True)import dns.message
# Parse raw DNS packet
wire_data = b'\x12\x34\x01\x00...' # Raw DNS packet bytes
message = dns.message.from_wire(wire_data)
print(f"Message ID: {message.id}")
print(f"Response code: {message.rcode()}")
print(f"Question count: {len(message.question)}")
print(f"Answer count: {len(message.answer)}")
# Process answers
for rrset in message.answer:
print(f"Name: {rrset.name}")
print(f"Type: {rrset.rdtype}")
for rdata in rrset:
print(f" Data: {rdata}")import dns.message
import dns.rrset
import dns.rdata
import dns.rdataclass
import dns.rdatatype
# Create response from query
query = dns.message.make_query('example.com', dns.rdatatype.A)
response = dns.message.make_response(query, recursion_available=True)
# Add answer records
answer_rrset = dns.rrset.from_text('example.com', 300, 'IN', 'A', '192.0.2.1')
response.answer.append(answer_rrset)
# Set response code
response.set_rcode(dns.rcode.NOERROR)
# Convert to wire format for transmission
wire_response = response.to_wire()import dns.message
import dns.name
import dns.rdatatype
import dns.rdataclass
message = dns.message.from_wire(wire_data)
# Find specific RRset
name = dns.name.from_text('example.com')
rrset = message.find_rrset(dns.message.ANSWER, name,
dns.rdataclass.IN, dns.rdatatype.A)
if rrset:
print(f"Found {len(rrset)} A records for {name}")
for rdata in rrset:
print(f" {rdata.address}")
# Check all sections
sections = [
(dns.message.QUESTION, message.question, "Question"),
(dns.message.ANSWER, message.answer, "Answer"),
(dns.message.AUTHORITY, message.authority, "Authority"),
(dns.message.ADDITIONAL, message.additional, "Additional")
]
for section_id, section_list, section_name in sections:
print(f"{section_name} section: {len(section_list)} RRsets")# Message sections
QUESTION = 0
ANSWER = 1
AUTHORITY = 2
ADDITIONAL = 3
# Header flags
QR = 0x8000 # Query/Response
AA = 0x0400 # Authoritative Answer
TC = 0x0200 # Truncated
RD = 0x0100 # Recursion Desired
RA = 0x0080 # Recursion Available
AD = 0x0020 # Authentic Data
CD = 0x0010 # Checking Disabledclass ShortHeader(DNSException):
"""A DNS packet is too short to contain a complete header."""
class TrailingJunk(DNSException):
"""There is trailing junk after the end of the DNS message."""
class UnknownHeaderField(DNSException):
"""An unknown header field name was specified."""
class BadEDNS(DNSException):
"""An OPT record was found somewhere other than the additional section."""
class BadTSIG(DNSException):
"""A TSIG record was found somewhere other than the end of the additional section."""
class UnknownTSIGKey(DNSException):
"""A TSIG record was received with an unknown key."""Install with Tessl CLI
npx tessl i tessl/pypi-dnspythondocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10