CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dnspython

DNS toolkit for Python supporting almost all record types with high-level and low-level DNS operations

85

1.37x
Overview
Eval results
Files

dns-updates.mddocs/

DNS Updates

Dynamic DNS update functionality for creating and sending DNS UPDATE messages. Provides complete support for adding, deleting, and replacing resource records with optional TSIG authentication.

Capabilities

Update Message Creation

Create DNS UPDATE messages for dynamic zone modifications.

class Update:
    """
    A DNS dynamic update message.
    
    Represents a DNS UPDATE message that can add, delete, or replace
    resource records in a DNS zone. Supports prerequisites and 
    TSIG authentication.
    
    Attributes:
        zone (dns.name.Name): Zone being updated
        rdclass (int): Zone class
        keyring (dict): TSIG keyring for authentication
        keyname (dns.name.Name): TSIG key name
        keyalgorithm (dns.name.Name): TSIG algorithm
    """
    
    def __init__(self, zone, rdclass='IN', keyring=None, keyname=None,
                 keyalgorithm='HMAC-MD5.SIG-ALG.REG.INT'):
        """
        Initialize DNS update message.
        
        Args:
            zone (str or dns.name.Name): Zone name to update
            rdclass (str or int): Zone class (default 'IN')
            keyring (dict): TSIG keyring for authentication
            keyname (str or dns.name.Name): TSIG key name
            keyalgorithm (str): TSIG algorithm name
        """
    
    def __str__(self):
        """Return string representation of update."""
    
    def __repr__(self):
        """Return detailed string representation."""

Record Addition

Add resource records to the zone.

def add(name, *args):
    """
    Add resource records to the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        *args: Variable arguments specifying record data
               Can be (ttl, rdtype, rdata...) or (rdtype, rdata...)
               
    Examples:
        update.add('www.example.com', 300, 'A', '192.0.2.1')
        update.add('mail', 'MX', 10, 'mail.example.com.')
        update.add('_sip._tcp', 'SRV', 10, 20, 5060, 'sip.example.com.')
    """

def add_rrset(name, rrset):
    """
    Add an entire RRset to the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        rrset (dns.rrset.RRset): RRset to add
    """

def add_rdataset(name, rdataset):
    """
    Add an rdataset to the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        rdataset (dns.rdataset.Rdataset): Rdataset to add
    """

Record Deletion

Delete resource records from the zone.

def delete(name, *args):
    """
    Delete resource records from the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        *args: Optional arguments specifying what to delete
               () - delete all records at name
               (rdtype) - delete all records of specified type
               (rdtype, rdata...) - delete specific record
               
    Examples:
        update.delete('old.example.com')  # Delete all records
        update.delete('www.example.com', 'A')  # Delete all A records
        update.delete('www.example.com', 'A', '192.0.2.1')  # Delete specific A record
    """

def delete_rrset(name, rdtype, covers='NONE'):
    """
    Delete an entire RRset from the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        rdtype (str or int): Record type to delete
        covers (str or int): Covered type for RRSIG records
    """

def delete_rdataset(name, rdataset):
    """
    Delete specific rdataset from the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        rdataset (dns.rdataset.Rdataset): Rdataset to delete
    """

Record Replacement

Replace existing resource records in the zone.

def replace(name, *args):
    """
    Replace resource records in the zone.
    
    Deletes all existing records of the specified type and adds new ones.
    
    Args:
        name (str or dns.name.Name): Record name
        *args: Arguments specifying replacement records
               Format: (ttl, rdtype, rdata...) or (rdtype, rdata...)
               
    Examples:
        update.replace('www.example.com', 300, 'A', '192.0.2.10')
        update.replace('mail', 'MX', 10, 'mail1.example.com.', 20, 'mail2.example.com.')
    """

def replace_rrset(name, rrset):
    """
    Replace an RRset in the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        rrset (dns.rrset.RRset): Replacement RRset
    """

def replace_rdataset(name, rdataset):
    """
    Replace an rdataset in the zone.
    
    Args:
        name (str or dns.name.Name): Record name
        rdataset (dns.rdataset.Rdataset): Replacement rdataset
    """

Prerequisites

Set prerequisites that must be satisfied for the update to succeed.

def present(name, *args):
    """
    Specify that records must be present for update to succeed.
    
    Args:
        name (str or dns.name.Name): Record name
        *args: Optional arguments specifying what must be present
               () - name must exist (any record type)
               (rdtype) - name must have records of specified type  
               (rdtype, rdata...) - specific record must exist
               
    Examples:
        update.present('www.example.com')  # Name must exist
        update.present('www.example.com', 'A')  # Must have A records
        update.present('www.example.com', 'A', '192.0.2.1')  # Specific A record must exist
    """

def absent(name, *args):
    """
    Specify that records must be absent for update to succeed.
    
    Args:
        name (str or dns.name.Name): Record name
        *args: Optional arguments specifying what must be absent
               () - name must not exist (no records of any type)
               (rdtype) - name must not have records of specified type
               (rdtype, rdata...) - specific record must not exist
               
    Examples:
        update.absent('new.example.com')  # Name must not exist
        update.absent('www.example.com', 'AAAA')  # Must not have AAAA records
        update.absent('www.example.com', 'A', '192.0.2.99')  # Specific A record must not exist
    """

Update Execution

Send update messages to nameservers.

def to_wire(self):
    """
    Convert update message to wire format.
    
    Returns:
        bytes: Wire format update message
    """

def __call__(self, nameserver, timeout=None, port=53, af=None, source=None, source_port=0):
    """
    Send the update to a nameserver (callable interface).
    
    Args:
        nameserver (str): Nameserver IP address
        timeout (float): Query timeout
        port (int): Destination port (default 53)
        af (int): Address family
        source (str): Source IP address
        source_port (int): Source port
        
    Returns:
        dns.message.Message: Response message
    """

Usage Examples

Basic Record Updates

import dns.update
import dns.query
import dns.rdatatype

# Create update message
zone = 'example.com.'
update = dns.update.Update(zone)

# Add records
update.add('www.example.com.', 300, 'A', '192.0.2.10')
update.add('www.example.com.', 300, 'AAAA', '2001:db8::10')
update.add('mail.example.com.', 300, 'A', '192.0.2.20')

# Add MX record
update.add('example.com.', 300, 'MX', 10, 'mail.example.com.')

# Send update
nameserver = '192.0.2.1'  # Authoritative nameserver
response = dns.query.tcp(update, nameserver)

if response.rcode() == dns.rcode.NOERROR:
    print("Update successful")
else:
    print(f"Update failed: {dns.rcode.to_text(response.rcode())}")

Record Deletion and Replacement

import dns.update

update = dns.update.Update('example.com.')

# Delete specific record
update.delete('old.example.com.', 'A', '192.0.2.99')

# Delete all A records at name
update.delete('www.example.com.', 'A')

# Delete all records at name
update.delete('obsolete.example.com.')

# Replace all A records
update.replace('www.example.com.', 300, 'A', '192.0.2.100')

# Replace MX records
update.replace('example.com.', 300, 'MX', 10, 'mail1.example.com.', 20, 'mail2.example.com.')

# Send update
response = dns.query.tcp(update, '192.0.2.1')

Updates with Prerequisites

import dns.update

update = dns.update.Update('example.com.')

# Only proceed if www.example.com has specific A record
update.present('www.example.com.', 'A', '192.0.2.1')

# Replace it with new address
update.replace('www.example.com.', 300, 'A', '192.0.2.10')

# Only proceed if new.example.com doesn't exist
update.absent('new.example.com.')

# Add new record
update.add('new.example.com.', 300, 'A', '192.0.2.50')

# Send conditional update
response = dns.query.tcp(update, '192.0.2.1')

Authenticated Updates with TSIG

import dns.update
import dns.tsigkeyring
import dns.query

# Create TSIG keyring
keyring = dns.tsigkeyring.from_text({
    'update-key.example.com.': 'base64-encoded-key-data'
})

# Create authenticated update
update = dns.update.Update(
    'example.com.',
    keyring=keyring,
    keyname='update-key.example.com.'
)

# Add records
update.add('secure.example.com.', 300, 'A', '192.0.2.100')
update.add('secure.example.com.', 300, 'TXT', 'Updated via authenticated TSIG')

# Send authenticated update
response = dns.query.tcp(update, '192.0.2.1')

if response.rcode() == dns.rcode.NOERROR:
    print("Authenticated update successful")
    
    # Verify TSIG authentication
    if response.had_tsig():
        if response.tsig_error() == 0:
            print("TSIG verification successful")
        else:
            print(f"TSIG error: {response.tsig_error()}")
else:
    print(f"Update failed: {dns.rcode.to_text(response.rcode())}")

Complex Service Record Updates

import dns.update

update = dns.update.Update('example.com.')

# Add SRV records for SIP service
update.add('_sip._tcp.example.com.', 300, 'SRV', 10, 20, 5060, 'sip1.example.com.')
update.add('_sip._tcp.example.com.', 300, 'SRV', 10, 30, 5060, 'sip2.example.com.')
update.add('_sip._tcp.example.com.', 300, 'SRV', 20, 10, 5060, 'sip3.example.com.')

# Add corresponding A records
update.add('sip1.example.com.', 300, 'A', '192.0.2.11')
update.add('sip2.example.com.', 300, 'A', '192.0.2.12')
update.add('sip3.example.com.', 300, 'A', '192.0.2.13')

# Add NAPTR record for service discovery
update.add('example.com.', 300, 'NAPTR', 100, 10, 'u', 'E2U+sip', 
           '!^.*$!sip:info@example.com!', '.')

# Send update
response = dns.query.tcp(update, '192.0.2.1')

Batch Updates

import dns.update
import dns.rrset
import dns.rdata
import dns.rdatatype
import dns.rdataclass

update = dns.update.Update('example.com.')

# Create RRsets for batch operations
www_rrset = dns.rrset.RRset(dns.name.from_text('www.example.com.'), 
                           dns.rdataclass.IN, dns.rdatatype.A)
www_rrset.add(dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'), ttl=300)
www_rrset.add(dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.2'), ttl=300)

# Add entire RRset
update.add_rrset('www.example.com.', www_rrset)

# Create and add MX RRset
mx_rrset = dns.rrset.from_text('example.com.', 300, 'IN', 'MX', 
                              '10 mail1.example.com.', '20 mail2.example.com.')
update.add_rrset('example.com.', mx_rrset)

# Send batch update
response = dns.query.tcp(update, '192.0.2.1')

Error Handling

import dns.update
import dns.query
import dns.rcode
import dns.exception

try:
    update = dns.update.Update('example.com.')
    update.add('test.example.com.', 300, 'A', '192.0.2.50')
    
    response = dns.query.tcp(update, '192.0.2.1', timeout=10)
    
    rcode = response.rcode()
    if rcode == dns.rcode.NOERROR:
        print("Update completed successfully")
    elif rcode == dns.rcode.REFUSED:
        print("Update refused - check authorization")
    elif rcode == dns.rcode.NOTAUTH:
        print("Server not authoritative for zone")
    elif rcode == dns.rcode.YXDOMAIN:
        print("Prerequisites failed - domain exists when it shouldn't")
    elif rcode == dns.rcode.NXDOMAIN:
        print("Prerequisites failed - domain doesn't exist when it should")
    elif rcode == dns.rcode.YXRRSET:
        print("Prerequisites failed - RRset exists when it shouldn't")
    elif rcode == dns.rcode.NXRRSET:
        print("Prerequisites failed - RRset doesn't exist when it should")
    else:
        print(f"Update failed with rcode: {dns.rcode.to_text(rcode)}")
        
except dns.exception.Timeout:
    print("Update timed out")
except dns.tsig.BadSignature:
    print("TSIG signature verification failed")
except dns.exception.DNSException as e:
    print(f"DNS error: {e}")

Update Response Codes

# Standard response codes relevant to updates
NOERROR = 0     # No error - update successful
FORMERR = 1     # Format error in update message
SERVFAIL = 2    # Server failure
NXDOMAIN = 3    # Name does not exist (prerequisite)
NOTIMP = 4      # Not implemented
REFUSED = 5     # Update refused
YXDOMAIN = 6    # Name exists when it should not (prerequisite)
YXRRSET = 7     # RRset exists when it should not (prerequisite)
NXRRSET = 8     # RRset does not exist when it should (prerequisite)
NOTAUTH = 9     # Server not authoritative for zone
NOTZONE = 10    # Name not contained in zone

Integration with Other Modules

DNS Updates work seamlessly with other dnspython modules:

  • TSIG Authentication: Use dns.tsig and dns.tsigkeyring for secure updates
  • Record Creation: Use dns.rdata and dns.rrset for complex record structures
  • Name Handling: Use dns.name for proper name formatting and validation
  • Message Transport: Use dns.query for sending updates via UDP or TCP
  • Response Handling: Use dns.message for processing update responses

Install with Tessl CLI

npx tessl i tessl/pypi-dnspython

docs

dns-constants.md

dns-exceptions.md

dns-messages.md

dns-names.md

dns-queries.md

dns-records.md

dns-resolution.md

dns-updates.md

dns-utilities.md

dns-zones.md

dnssec.md

index.md

tsig.md

tile.json