DNS toolkit for Python supporting almost all record types with high-level and low-level DNS operations
85
DNS zone parsing, creation, and manipulation functionality. Provides complete zone file support including zone transfers, zone validation, and comprehensive zone data operations for authoritative DNS server implementations.
Create DNS zones from text files, zone transfers, or programmatically.
def from_text(text, origin=None, rdclass='IN', relativize=True, zone_factory=None,
filename=None, allow_include=False, check_origin=True):
"""
Parse a DNS zone from text format.
Args:
text (str): Zone file text content
origin (str or dns.name.Name): Zone origin (default from SOA)
rdclass (str or int): Record class (default 'IN')
relativize (bool): Make names relative to origin
zone_factory (class): Zone class to instantiate
filename (str): Filename for error reporting
allow_include (bool): Allow $INCLUDE directives
check_origin (bool): Verify zone has proper SOA and NS records
Returns:
dns.zone.Zone: Parsed DNS zone
"""
def from_file(f, origin=None, rdclass='IN', relativize=True, zone_factory=None,
filename=None, allow_include=True, check_origin=True):
"""
Parse a DNS zone from a file.
Args:
f (file-like): File object to read from
origin (str or dns.name.Name): Zone origin
rdclass (str or int): Record class
relativize (bool): Make names relative to origin
zone_factory (class): Zone class to instantiate
filename (str): Filename for error reporting
allow_include (bool): Allow $INCLUDE directives
check_origin (bool): Verify zone structure
Returns:
dns.zone.Zone: Parsed DNS zone
"""
def from_xfr(xfr, zone_factory=None, relativize=True, check_origin=True):
"""
Create a DNS zone from a zone transfer.
Args:
xfr (generator): Zone transfer message generator
zone_factory (class): Zone class to instantiate
relativize (bool): Make names relative to origin
check_origin (bool): Verify zone structure
Returns:
dns.zone.Zone: Zone created from transfer
"""DNS zone represented as a mapping from names to nodes containing resource record sets.
class Zone:
"""
A DNS zone as a mapping from names to nodes.
A zone is a collection of DNS resource records with a common origin.
The zone is represented as a mapping from names to nodes, where each
node contains the resource record sets for that name.
Attributes:
nodes (dict): Mapping from names to nodes
origin (dns.name.Name): Zone origin name
rdclass (int): Zone record class
"""
def __init__(self, origin, rdclass='IN', relativize=True):
"""
Initialize a DNS zone.
Args:
origin (str or dns.name.Name): Zone origin
rdclass (str or int): Record class
relativize (bool): Store names relative to origin
"""
def __repr__(self):
"""Return string representation of zone."""
def __eq__(self, other):
"""Test zone equality."""
def __ne__(self, other):
"""Test zone inequality."""
def __iter__(self):
"""Iterate over names in zone."""
def keys(self):
"""Return iterator over zone names."""
def values(self):
"""Return iterator over zone nodes."""
def items(self):
"""Return iterator over (name, node) pairs."""
def __len__(self):
"""Return number of names in zone."""
def __getitem__(self, key):
"""Get node by name."""
def __setitem__(self, key, value):
"""Set node for name."""
def __delitem__(self, key):
"""Delete node by name."""
def __contains__(self, key):
"""Test if name exists in zone."""Find, create, and manipulate nodes within the zone.
def find_node(name, create=False):
"""
Find a node in the zone.
Args:
name (str or dns.name.Name): Node name
create (bool): Create node if it doesn't exist
Returns:
dns.node.Node: Zone node
Raises:
KeyError: If name not found and create=False
"""
def get_node(name, create=False):
"""
Get a node from the zone.
Args:
name (str or dns.name.Name): Node name
create (bool): Create node if it doesn't exist
Returns:
dns.node.Node or None: Zone node or None if not found
"""
def delete_node(name):
"""
Delete a node from the zone.
Args:
name (str or dns.name.Name): Node name to delete
Raises:
KeyError: If name not found
"""Find, get, and manipulate resource record sets within the zone.
def find_rdataset(name, rdtype, covers='NONE', create=False):
"""
Find an rdataset in the zone.
Args:
name (str or dns.name.Name): Record name
rdtype (str or int): Record type
covers (str or int): Covered type for RRSIG records
create (bool): Create rdataset if not found
Returns:
dns.rdataset.Rdataset: Resource record set
"""
def get_rdataset(name, rdtype, covers='NONE', create=False):
"""
Get an rdataset from the zone.
Args:
name (str or dns.name.Name): Record name
rdtype (str or int): Record type
covers (str or int): Covered type for RRSIG records
create (bool): Create rdataset if not found
Returns:
dns.rdataset.Rdataset or None: Rdataset or None if not found
"""
def delete_rdataset(name, rdtype, covers='NONE'):
"""
Delete an rdataset from the zone.
Args:
name (str or dns.name.Name): Record name
rdtype (str or int): Record type
covers (str or int): Covered type for RRSIG records
"""
def replace_rdataset(name, replacement):
"""
Replace an rdataset in the zone.
Args:
name (str or dns.name.Name): Record name
replacement (dns.rdataset.Rdataset): Replacement rdataset
"""Work with complete resource record sets including name and TTL information.
def find_rrset(name, rdtype, covers='NONE'):
"""
Find an RRset in the zone.
Args:
name (str or dns.name.Name): Record name
rdtype (str or int): Record type
covers (str or int): Covered type for RRSIG records
Returns:
dns.rrset.RRset: Resource record set
"""
def get_rrset(name, rdtype, covers='NONE'):
"""
Get an RRset from the zone.
Args:
name (str or dns.name.Name): Record name
rdtype (str or int): Record type
covers (str or int): Covered type for RRSIG records
Returns:
dns.rrset.RRset or None: RRset or None if not found
"""Iterate over zone contents with filtering and processing options.
def iterate_rdatasets(rdtype='ANY', covers='NONE'):
"""
Generate all rdatasets in the zone.
Args:
rdtype (str or int): Filter by record type ('ANY' for all)
covers (str or int): Filter by covered type for RRSIG
Yields:
tuple: (name, rdataset) pairs
"""
def iterate_rdatas(rdtype='ANY', covers='NONE'):
"""
Generate all resource records in the zone.
Args:
rdtype (str or int): Filter by record type ('ANY' for all)
covers (str or int): Filter by covered type for RRSIG
Yields:
tuple: (name, ttl, rdata) tuples
"""Convert zones to text format or write to files.
def to_file(f, sorted=True, relativize=True, nl=None):
"""
Write zone to a file.
Args:
f (file-like): File to write to
sorted (bool): Sort records by name
relativize (bool): Make names relative to origin
nl (bytes): Line ending (default system)
"""
def to_text(sorted=True, relativize=True, nl=None):
"""
Convert zone to text format.
Args:
sorted (bool): Sort records by name
relativize (bool): Make names relative to origin
nl (str): Line ending (default system)
Returns:
str: Zone in text format
"""Validate zone structure and check for required records.
def check_origin():
"""
Check that the zone is properly structured.
Validates that:
- Zone has SOA record at origin
- Zone has NS records at origin
- SOA serial number is reasonable
Raises:
dns.zone.NoSOA: If no SOA at origin
dns.zone.NoNS: If no NS at origin
dns.zone.BadZone: If other structural problems
"""import dns.zone
import dns.name
# Load zone from file
zone = dns.zone.from_file('example.com.zone', origin='example.com.')
print(f"Zone origin: {zone.origin}")
print(f"Zone class: {zone.rdclass}")
print(f"Number of names: {len(zone)}")
# Load zone from text
zone_text = '''
$ORIGIN example.com.
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2023010101 ; serial
10800 ; refresh
3600 ; retry
604800 ; expire
86400 ) ; minimum
IN NS ns1.example.com.
IN NS ns2.example.com.
www IN A 192.0.2.1
IN AAAA 2001:db8::1
mail IN A 192.0.2.2
'''
zone = dns.zone.from_text(zone_text)import dns.zone
import dns.query
import dns.name
# Perform zone transfer and create zone
zone_name = dns.name.from_text('example.com.')
xfr_messages = dns.query.xfr('ns1.example.com', zone_name)
zone = dns.zone.from_xfr(xfr_messages)
print(f"Transferred zone: {zone.origin}")
print(f"Zone records: {len(zone)}")import dns.zone
import dns.name
import dns.rdataset
import dns.rdatatype
import dns.rdataclass
import dns.rdata
# Create new zone
origin = dns.name.from_text('test.example.')
zone = dns.zone.Zone(origin)
# Add SOA record
soa_rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.SOA,
'ns1.test.example. admin.test.example. 1 3600 1800 1209600 300')
soa_rdataset = dns.rdataset.from_rdata(300, soa_rdata)
zone.replace_rdataset('@', soa_rdataset)
# Add NS records
ns_rdata1 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns1.test.example.')
ns_rdata2 = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.NS, 'ns2.test.example.')
ns_rdataset = dns.rdataset.from_rdata_list(300, [ns_rdata1, ns_rdata2])
zone.replace_rdataset('@', ns_rdataset)
# Add A record
www_name = dns.name.from_text('www', origin)
a_rdata = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.10')
a_rdataset = dns.rdataset.from_rdata(300, a_rdata)
zone.replace_rdataset(www_name, a_rdataset)import dns.zone
import dns.rdatatype
zone = dns.zone.from_file('example.com.zone')
# Find specific records
try:
www_a = zone.find_rdataset('www', dns.rdatatype.A)
print(f"www A records: {list(www_a)}")
except KeyError:
print("www A record not found")
# Get records safely
mx_records = zone.get_rdataset('@', dns.rdatatype.MX)
if mx_records:
print(f"MX records: {list(mx_records)}")
# Iterate over all records
print("All zone records:")
for name, rdataset in zone.iterate_rdatasets():
print(f"{name} {rdataset.ttl} {rdataset.rdclass} {rdataset.rdtype}")
for rdata in rdataset:
print(f" {rdata}")
# Filter by record type
print("Only A records:")
for name, rdataset in zone.iterate_rdatasets(rdtype=dns.rdatatype.A):
for rdata in rdataset:
print(f"{name} A {rdata.address}")import dns.zone
zone = dns.zone.from_file('example.com.zone')
# Validate zone structure
try:
zone.check_origin()
print("Zone validation passed")
except dns.zone.NoSOA:
print("Zone missing SOA record")
except dns.zone.NoNS:
print("Zone missing NS records")
except dns.zone.BadZone as e:
print(f"Zone validation failed: {e}")
# Output zone
print("Zone as text:")
print(zone.to_text())
# Write to file
with open('output.zone', 'w') as f:
zone.to_file(f, sorted=True)class Node:
"""
A DNS node containing resource record sets.
A node represents all the resource records at a particular name
in a DNS zone. It contains a mapping from (rdtype, covers) tuples
to rdatasets.
"""
def __init__(self):
"""Initialize empty node."""
def find_rdataset(self, rdclass, rdtype, covers='NONE', create=False):
"""Find rdataset in node."""
def get_rdataset(self, rdclass, rdtype, covers='NONE', create=False):
"""Get rdataset from node."""
def delete_rdataset(self, rdclass, rdtype, covers='NONE'):
"""Delete rdataset from node."""
def replace_rdataset(self, replacement):
"""Replace rdataset in node."""class BadZone(DNSException):
"""The zone is malformed."""
class NoSOA(BadZone):
"""The zone has no SOA RR at its origin."""
class NoNS(BadZone):
"""The zone has no NS RRset at its origin."""
class UnknownOrigin(DNSException):
"""The zone's origin is unknown."""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