Python interface for c-ares asynchronous DNS library
—
Comprehensive DNS query operations supporting all major record types including A, AAAA, MX, TXT, SOA, SRV, and others. All queries are asynchronous and use callback-based completion notification.
Perform DNS queries for specific record types. Supports both direct queries and search-based queries that use configured search domains.
def query(self, name: str, query_type: int, callback, query_class: Optional[int] = None) -> None:
"""
Perform a DNS query for a specific record type.
Args:
name: str - Domain name to query
query_type: int - DNS record type (QUERY_TYPE_* constants)
callback: Callable - Function called with (result, error) when complete
query_class: Optional[int] - DNS class (default: QUERY_CLASS_IN)
Raises:
TypeError: If callback is not callable
ValueError: If query_type or query_class is invalid
RuntimeError: If channel is destroyed
"""
def search(self, name: str, query_type: int, callback, query_class: Optional[int] = None) -> None:
"""
Perform a DNS search query using configured search domains.
Similar to query() but will try appending search domains if the initial
query fails and the name doesn't contain enough dots (controlled by ndots).
Args:
name: str - Domain name to search for
query_type: int - DNS record type (QUERY_TYPE_* constants)
callback: Callable - Function called with (result, error) when complete
query_class: Optional[int] - DNS class (default: QUERY_CLASS_IN)
Raises:
TypeError: If callback is not callable
ValueError: If query_type or query_class is invalid
RuntimeError: If channel is destroyed
"""Usage Example:
import pycares
def query_callback(result, error):
if error is not None:
print(f'DNS Error: {pycares.errno.strerror(error)}')
return
if isinstance(result, list):
for record in result:
print(f'{record.type} record: {record}')
else:
print(f'{result.type} record: {result}')
channel = pycares.Channel()
# Direct query for A records
channel.query('google.com', pycares.QUERY_TYPE_A, query_callback)
# Search query with domain search
channel.search('mail', pycares.QUERY_TYPE_MX, query_callback)
# Query for TXT records
channel.query('google.com', pycares.QUERY_TYPE_TXT, query_callback)DNS record types supported for queries:
# IPv4 address records
QUERY_TYPE_A = 1
# IPv6 address records
QUERY_TYPE_AAAA = 28
# Canonical name records
QUERY_TYPE_CNAME = 5
# Mail exchange records
QUERY_TYPE_MX = 15
# Text records
QUERY_TYPE_TXT = 16
# Name server records
QUERY_TYPE_NS = 2
# Pointer records (reverse DNS)
QUERY_TYPE_PTR = 12
# Start of authority records
QUERY_TYPE_SOA = 6
# Service location records
QUERY_TYPE_SRV = 33
# Certification Authority Authorization records
QUERY_TYPE_CAA = 257
# Naming Authority Pointer records
QUERY_TYPE_NAPTR = 35
# Query for any available record type
QUERY_TYPE_ANY = 255DNS query classes (rarely needed, defaults to IN):
# Internet class (default)
QUERY_CLASS_IN = 1
# CHAOS class
QUERY_CLASS_CHAOS = 3
# Hesiod class
QUERY_CLASS_HS = 4
# None class
QUERY_CLASS_NONE = 254
# Any class
QUERY_CLASS_ANY = 255Each DNS record type returns specific result objects with relevant attributes:
class ares_query_a_result:
"""IPv4 address record result."""
type = 'A'
host: str # IPv4 address as string
ttl: int # Time to live in secondsclass ares_query_aaaa_result:
"""IPv6 address record result."""
type = 'AAAA'
host: str # IPv6 address as string
ttl: int # Time to live in secondsclass ares_query_cname_result:
"""Canonical name record result."""
type = 'CNAME'
cname: str # Canonical name
ttl: int # Time to live (-1 if not available)class ares_query_mx_result:
"""Mail exchange record result."""
type = 'MX'
host: str # Mail server hostname
priority: int # Mail server priority (lower = higher priority)
ttl: int # Time to live (-1 if not available)class ares_query_txt_result:
"""Text record result."""
type = 'TXT'
text: str # Text content
ttl: int # Time to live (-1 if not available)class ares_query_ns_result:
"""Name server record result."""
type = 'NS'
host: str # Name server hostname
ttl: int # Time to live (-1 if not available)class ares_query_ptr_result:
"""Pointer record result."""
type = 'PTR'
name: str # Domain name
ttl: int # Time to live (-1 if not available)
aliases: list[str] # List of aliasesclass ares_query_soa_result:
"""Start of authority record result."""
type = 'SOA'
nsname: str # Primary name server
hostmaster: str # Responsible person's email
serial: int # Serial number
refresh: int # Refresh interval in seconds
retry: int # Retry interval in seconds
expires: int # Expiration time in seconds
minttl: int # Minimum TTL in seconds
ttl: int # Time to live (-1 if not available)class ares_query_srv_result:
"""Service record result."""
type = 'SRV'
host: str # Target hostname
port: int # Service port number
priority: int # Priority (lower = higher priority)
weight: int # Weight for load balancing
ttl: int # Time to live (-1 if not available)class ares_query_caa_result:
"""Certification Authority Authorization record result."""
type = 'CAA'
critical: int # Critical flag (0 or 1)
property: str # Property name (e.g., 'issue', 'issuewild', 'iodef')
value: str # Property value
ttl: int # Time to live (-1 if not available)class ares_query_naptr_result:
"""Naming Authority Pointer record result."""
type = 'NAPTR'
order: int # Order preference
preference: int # Preference within same order
flags: str # Flags string
service: str # Service parameters
regex: str # Regular expression
replacement: str # Replacement string
ttl: int # Time to live (-1 if not available)def a_record_callback(result, error):
if error:
print(f'Error: {pycares.errno.strerror(error)}')
return
for record in result:
print(f'A record: {record.host} (TTL: {record.ttl})')
channel.query('google.com', pycares.QUERY_TYPE_A, a_record_callback)def mx_record_callback(result, error):
if error:
print(f'Error: {pycares.errno.strerror(error)}')
return
# Sort by priority (lower number = higher priority)
mx_records = sorted(result, key=lambda x: x.priority)
for record in mx_records:
print(f'MX record: {record.host} (priority: {record.priority})')
channel.query('google.com', pycares.QUERY_TYPE_MX, mx_record_callback)def txt_record_callback(result, error):
if error:
print(f'Error: {pycares.errno.strerror(error)}')
return
for record in result:
print(f'TXT record: {record.text}')
channel.query('google.com', pycares.QUERY_TYPE_TXT, txt_record_callback)def any_query_callback(result, error):
if error:
print(f'Error: {pycares.errno.strerror(error)}')
return
for record in result:
if record.type == 'A':
print(f'A: {record.host}')
elif record.type == 'AAAA':
print(f'AAAA: {record.host}')
elif record.type == 'MX':
print(f'MX: {record.host} (priority: {record.priority})')
elif record.type == 'TXT':
print(f'TXT: {record.text}')
else:
print(f'{record.type}: {record}')
channel.query('google.com', pycares.QUERY_TYPE_ANY, any_query_callback)def ptr_callback(result, error):
if error:
print(f'Error: {pycares.errno.strerror(error)}')
return
for record in result:
print(f'PTR: {record.name}')
if record.aliases:
print(f'Aliases: {", ".join(record.aliases)}')
# Reverse lookup for IP address
channel.query('8.8.8.8.in-addr.arpa', pycares.QUERY_TYPE_PTR, ptr_callback)All query callbacks receive an error parameter. Common DNS errors include:
See Error Handling for complete error code documentation.
Install with Tessl CLI
npx tessl i tessl/pypi-pycares