Of course! The import dns statement in Python refers to the dnspython library, a powerful and widely-used toolkit for DNS (Domain Name System) lookups.
Here’s a complete guide covering what it is, how to install it, and how to use it for common tasks.
What is dnspython?
dnspython is a pure Python implementation of the DNS protocol. It allows you to perform DNS queries programmatically from within your Python scripts. It's not part of Python's standard library, so you need to install it separately.
Key features include:
- Full DNS Record Support: A, AAAA, MX, CNAME, NS, SOA, TXT, SRV, and more.
- DNSSEC Support: For validating DNS responses.
- EDNS0 Support: For extended DNS features.
- Asynchronous Lookups: You can perform queries without blocking your program's execution.
- DNS Name Server (Authoritative Server): You can even use it to create your own simple DNS server.
Installation
First, you need to install the library using pip.
pip install dnspython
Basic Usage: Performing Queries
The main entry point for performing queries is the dns.resolver module.
Let's start with a simple A record lookup (which returns an IPv4 address).
Example 1: Looking up an A record
import dns.resolver
def get_a_record(domain):
"""
Performs an A record lookup for a given domain.
"""
try:
# The resolve() function performs the query
answers = dns.resolver.resolve(domain, 'A')
print(f"A records for {domain}:")
# The answer is a list of RRset (Resource Record set) objects
for rdata in answers:
# rdata.address contains the IP address for A records
print(f" - {rdata.address}")
except dns.resolver.NXDOMAIN:
print(f"Error: The domain {domain} does not exist.")
except dns.resolver.NoAnswer:
print(f"Error: The domain {domain} does not have an A record.")
except dns.resolver.Timeout:
print(f"Error: The query timed out for {domain}.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
# --- Usage ---
get_a_record('google.com')
get_a_record('non-existent-domain-12345.com') # Will trigger NXDOMAIN
Example 2: Looking up different record types
The resolve() function is versatile. You just change the second argument to the record type you want.
import dns.resolver
def get_mx_records(domain):
"""Looks up MX (Mail Exchange) records."""
try:
answers = dns.resolver.resolve(domain, 'MX')
print(f"\nMX records for {domain}:")
for rdata in answers:
# MX records have preference and exchange data
print(f" - Priority: {rdata.preference}, Mail Server: {rdata.exchange}")
except Exception as e:
print(f"Could not get MX records for {domain}: {e}")
def get_txt_records(domain):
"""Looks up TXT records."""
try:
answers = dns.resolver.resolve(domain, 'TXT')
print(f"\nTXT records for {domain}:")
for rdata in answers:
# TXT records have one or more strings
print(f" - {rdata.strings}")
except Exception as e:
print(f"Could not get TXT records for {domain}: {e}")
# --- Usage ---
get_mx_records('gmail.com')
get_txt_records('google.com') # Often contains SPF or DKIM records
Handling DNSSEC
dnspython has excellent support for DNSSEC. You can validate the authenticity of DNS responses.
First, you need to trust a DNSSEC-validating resolver. Google's Public DNS (8.8.8) is a popular choice.
Example 3: Validating a DNSSEC-signed record
import dns.resolver
import dns.exception
def get_nsec_record_with_validation(domain):
"""
Tries to get an NSEC record and validates it using DNSSEC.
"""
# Configure the resolver to use a DNSSEC-validating server
resolver = dns.resolver.Resolver()
resolver.nameservers = ['8.8.8.8'] # Use Google's DNS
resolver.set_flags(dns.flags.AD) # Ask for Authenticated Data
try:
# NSEC records are used for DNSSEC to prove a non-existence
answers = resolver.resolve(domain, 'NSEC')
print(f"\nValidated NSEC record for {domain}:")
for rdata in answers:
print(f" - Next Domain: {rdata.next}, Type Bit Map: {rdata.type_bit_map}")
except dns.resolver.NoAnswer:
# This can happen if the domain exists and is signed
print(f"\n{domain} exists and is DNSSEC-signed, so no NSEC record was returned.")
except dns.exception.DNSException as e:
# This will catch validation failures (e.g., RRSIG mismatch)
print(f"\nDNSSEC validation failed for {domain}: {e}")
# --- Usage ---
# Try a domain known to be DNSSEC signed
get_nsec_record_with_validation('icann.org')
Reverse DNS Lookups
You can also perform reverse DNS lookups (finding the domain name for an IP address) using the dns.reversename module.
Example 4: Reverse DNS lookup
import dns.resolver
import dns.reversename
def reverse_dns_lookup(ip_address):
"""
Performs a reverse DNS lookup on an IP address.
"""
try:
# Convert the IP address to the in-addr.arpa or ip6.arpa format
rev_name = dns.reversename.from_address(ip_address)
# Query the PTR record
answers = dns.resolver.resolve(rev_name, 'PTR')
print(f"\nReverse DNS for {ip_address}:")
for rdata in answers:
# PTR records point to a domain name
print(f" - {rdata.target}")
except Exception as e:
print(f"Could not perform reverse lookup for {ip_address}: {e}")
# --- Usage ---
reverse_dns_lookup('8.8.8.8')
reverse_dns_lookup('1.1.1.1')
Practical Use Case: Checking for SPF Records
A common task for email-related applications is to check a domain's Sender Policy Framework (SPF) record to see which servers are authorized to send email on its behalf.
Example 5: Parsing an SPF record
import dns.resolver
def get_spf_record(domain):
"""
Retrieves and prints the SPF record for a domain.
"""
try:
answers = dns.resolver.resolve(domain, 'TXT')
spf_record = None
for rdata in answers:
# SPF records are TXT records that start with 'v=spf1'
if rdata.strings[0].startswith(b'v=spf1'):
spf_record = rdata.strings[0].decode('utf-8')
break
if spf_record:
print(f"\nSPF Record for {domain}:")
print(f" {spf_record}")
# You can parse this string further to find mechanisms like -all, +all, include: etc.
else:
print(f"\nNo SPF record found for {domain}.")
except Exception as e:
print(f"Could not get SPF record for {domain}: {e}")
# --- Usage ---
get_spf_record('google.com')
get_spf_record('microsoft.com')
Summary
| Task | Code Snippet |
|---|---|
| Import | import dns.resolver |
| Install | pip install dnspython |
| Lookup A Record | dns.resolver.resolve('google.com', 'A') |
| Lookup MX Record | dns.resolver.resolve('gmail.com', 'MX') |
| Reverse DNS | dns.resolver.resolve(dns.reversename.from_address('8.8.8.8'), 'PTR') |
| Handle Errors | try...except dns.resolver.NXDOMAIN, dns.resolver.NoAnswer... |
| Use DNSSEC | Set resolver nameservers to a validating one (e.g., 8.8.8) and set dns.flags.AD flag. |
dnspython is an essential library for any Python developer working with networking, DevOps, security, or any application that needs to interact with the DNS system. Its flexibility and comprehensive feature set make it the go-to choice for DNS manipulation in Python.
