CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pycares

Python interface for c-ares asynchronous DNS library

Pending
Overview
Eval results
Files

host-resolution.mddocs/

Host Resolution

Traditional host resolution functions providing asynchronous versions of standard socket library operations. These functions offer familiar interfaces for hostname-to-address and address-to-hostname resolution with callback-based completion.

Capabilities

Forward Resolution

Resolve hostnames to IP addresses using traditional gethostbyname-style operations.

def gethostbyname(self, name: str, family, callback) -> None:
    """
    Resolve a hostname to IP addresses for the specified address family.
    
    Args:
        name: str - Hostname to resolve
        family: socket.AddressFamily - Address family (socket.AF_INET or socket.AF_INET6)
        callback: Callable - Function called with (result, error) when complete
        
    Raises:
        TypeError: If callback is not callable
        RuntimeError: If channel is destroyed
    """

Usage Example:

import socket
import pycares

def host_callback(result, error):
    if error:
        print(f'Error: {pycares.errno.strerror(error)}')
        return
        
    print(f'Hostname: {result.name}')
    print(f'Addresses: {result.addresses}')
    if result.aliases:
        print(f'Aliases: {result.aliases}')

channel = pycares.Channel()

# Resolve IPv4 addresses
channel.gethostbyname('google.com', socket.AF_INET, host_callback)

# Resolve IPv6 addresses
channel.gethostbyname('google.com', socket.AF_INET6, host_callback)

Reverse Resolution

Resolve IP addresses back to hostnames.

def gethostbyaddr(self, addr: str, callback) -> None:
    """
    Resolve an IP address to hostname(s).
    
    Args:
        addr: str - IP address to resolve (IPv4 or IPv6)
        callback: Callable - Function called with (result, error) when complete
        
    Raises:
        ValueError: If IP address format is invalid
        TypeError: If callback is not callable
        RuntimeError: If channel is destroyed
    """

Usage Example:

def addr_callback(result, error):
    if error:
        print(f'Error: {pycares.errno.strerror(error)}')
        return
        
    print(f'Primary hostname: {result.name}')
    if result.aliases:
        print(f'Aliases: {result.aliases}')
    print(f'Addresses: {result.addresses}')

channel = pycares.Channel()

# Reverse resolve IPv4 address
channel.gethostbyaddr('8.8.8.8', addr_callback)

# Reverse resolve IPv6 address
channel.gethostbyaddr('2001:4860:4860::8888', addr_callback)

Address Info Resolution

Modern getaddrinfo-style resolution supporting service names, protocol specifications, and comprehensive address information.

def getaddrinfo(
    self,
    host: str,
    port,  # Union[int, str, None]
    callback,
    family=0,    # socket.AddressFamily
    type=0,      # int (socket type)
    proto=0,     # int (protocol)
    flags=0      # int (AI_* flags)
) -> None:
    """
    Resolve hostname and service to socket addresses with detailed information.
    
    Args:
        host: str - Hostname to resolve
        port: Union[int, str, None] - Port number or service name (e.g., 'http', 80, None)
        callback: Callable - Function called with (result, error) when complete
        family: socket.AddressFamily - Address family filter (0 for any)
        type: int - Socket type (socket.SOCK_STREAM, socket.SOCK_DGRAM, etc.)
        proto: int - Protocol (socket.IPPROTO_TCP, socket.IPPROTO_UDP, etc.)
        flags: int - Additional flags for resolution behavior
        
    Raises:
        TypeError: If callback is not callable
        RuntimeError: If channel is destroyed
    """

Usage Example:

import socket

def addrinfo_callback(result, error):
    if error:
        print(f'Error: {pycares.errno.strerror(error)}')
        return
        
    print('CNAME records:')
    for cname in result.cnames:
        print(f'  {cname.alias} -> {cname.name} (TTL: {cname.ttl})')
        
    print('Address records:')
    for node in result.nodes:
        addr = node.addr
        if node.family == socket.AF_INET:
            print(f'  IPv4: {addr[0]}:{addr[1]} (TTL: {node.ttl})')
        elif node.family == socket.AF_INET6:
            print(f'  IPv6: [{addr[0]}]:{addr[1]} (TTL: {node.ttl})')

channel = pycares.Channel()

# Resolve with port number
channel.getaddrinfo('google.com', 80, addrinfo_callback)

# Resolve with service name
channel.getaddrinfo('google.com', 'http', addrinfo_callback)

# Resolve with family filter
channel.getaddrinfo('google.com', 443, addrinfo_callback, family=socket.AF_INET6)

# Resolve without port
channel.getaddrinfo('google.com', None, addrinfo_callback)

Name Info Resolution

Resolve socket addresses back to hostname and service names.

def getnameinfo(self, address, flags: int, callback) -> None:
    """
    Resolve socket address to hostname and service name.
    
    Args:
        address: Union[tuple[str, int], tuple[str, int, int, int]] - Socket address
                 IPv4: (host, port) tuple
                 IPv6: (host, port, flowinfo, scope_id) tuple
        flags: int - Bitwise OR of ARES_NI_* flags controlling resolution behavior
        callback: Callable - Function called with (result, error) when complete
        
    Raises:
        ValueError: If address format is invalid
        TypeError: If callback is not callable
        RuntimeError: If channel is destroyed
    """

Usage Example:

def nameinfo_callback(result, error):
    if error:
        print(f'Error: {pycares.errno.strerror(error)}')
        return
        
    print(f'Hostname: {result.node}')
    if result.service:
        print(f'Service: {result.service}')

channel = pycares.Channel()

# Resolve IPv4 address and port
channel.getnameinfo(
    ('8.8.8.8', 53),
    pycares.ARES_NI_LOOKUPHOST | pycares.ARES_NI_LOOKUPSERVICE,
    nameinfo_callback
)

# Resolve IPv6 address
channel.getnameinfo(
    ('2001:4860:4860::8888', 53, 0, 0),
    pycares.ARES_NI_LOOKUPHOST,
    nameinfo_callback
)

# Get numeric host only
channel.getnameinfo(
    ('192.168.1.1', 80),
    pycares.ARES_NI_NUMERICHOST | pycares.ARES_NI_NUMERICSERV,
    nameinfo_callback
)

Result Types

Host Result

Result object for gethostbyname and gethostbyaddr operations.

class ares_host_result:
    """Host resolution result."""
    name: str           # Primary hostname
    aliases: list[str]  # List of hostname aliases
    addresses: list[str] # List of IP addresses

Address Info Result

Comprehensive result object for getaddrinfo operations.

class ares_addrinfo_result:
    """Address info resolution result."""
    cnames: list[ares_addrinfo_cname_result]  # CNAME chain information
    nodes: list[ares_addrinfo_node_result]    # Address information nodes

class ares_addrinfo_cname_result:
    """CNAME information in address resolution."""
    ttl: int      # Time to live
    alias: str    # Alias name
    name: str     # Canonical name

class ares_addrinfo_node_result:
    """Individual address node in getaddrinfo result."""
    ttl: int                    # Time to live
    flags: int                  # Address flags
    family: int                 # Address family (AF_INET or AF_INET6)
    socktype: int              # Socket type
    protocol: int              # Protocol number
    addr: tuple                 # Address tuple (format depends on family)
                               # IPv4: (address_str, port)
                               # IPv6: (address_str, port, flowinfo, scope_id)

Name Info Result

Result object for getnameinfo operations.

class ares_nameinfo_result:
    """Name info resolution result."""
    node: str           # Hostname (may be None if not requested)
    service: str        # Service name (may be None if not requested or unavailable)

Name Info Flags

Flags controlling getnameinfo behavior:

# Don't return fully qualified domain name
ARES_NI_NOFQDN = 1

# Return numeric host address instead of name
ARES_NI_NUMERICHOST = 2

# Require hostname (fail if not available)
ARES_NI_NAMEREQD = 4

# Return numeric service instead of name
ARES_NI_NUMERICSERV = 8

# Datagram service (affects service name lookup)
ARES_NI_DGRAM = 16

# TCP service
ARES_NI_TCP = 32

# UDP service
ARES_NI_UDP = 64

# SCTP service
ARES_NI_SCTP = 128

# DCCP service
ARES_NI_DCCP = 256

# Return numeric scope identifier
ARES_NI_NUMERICSCOPE = 512

# Lookup hostname
ARES_NI_LOOKUPHOST = 1024

# Lookup service name
ARES_NI_LOOKUPSERVICE = 2048

# Use IDNA encoding
ARES_NI_IDN = 4096

# Allow unassigned code points in IDNA
ARES_NI_IDN_ALLOW_UNASSIGNED = 8192

# Use STD3 ASCII rules for IDNA
ARES_NI_IDN_USE_STD3_ASCII_RULES = 16384

Type Aliases

Type definitions for address tuples:

# IPv4 address tuple: (address, port)
IP4 = tuple[str, int]

# IPv6 address tuple: (address, port, flowinfo, scope_id)
IP6 = tuple[str, int, int, int]

Advanced Usage Examples

Parallel Resolution

import socket
import pycares

results = {}
pending = 0

def collect_result(name, result, error):
    global pending
    results[name] = (result, error)
    pending -= 1

def resolve_multiple_hosts(channel, hostnames):
    global pending
    pending = len(hostnames)
    
    for hostname in hostnames:
        channel.gethostbyname(hostname, socket.AF_INET, 
                            lambda r, e, h=hostname: collect_result(h, r, e))
    
    # Wait for all to complete
    while pending > 0:
        wait_channel(channel)
    
    return results

channel = pycares.Channel()
hosts = ['google.com', 'github.com', 'stackoverflow.com']
results = resolve_multiple_hosts(channel, hosts)

for host, (result, error) in results.items():
    if error:
        print(f'{host}: Error - {pycares.errno.strerror(error)}')
    else:
        print(f'{host}: {result.addresses[0]}')

Service Discovery

def discover_service(hostname, service_port):
    """Discover service endpoints for a hostname."""
    def addrinfo_cb(result, error):
        if error:
            print(f'Service discovery failed: {pycares.errno.strerror(error)}')
            return
            
        print(f'Service endpoints for {hostname}:')
        for node in result.nodes:
            family = 'IPv4' if node.family == socket.AF_INET else 'IPv6'
            addr_str = f'[{node.addr[0]}]' if node.family == socket.AF_INET6 else node.addr[0]
            print(f'  {family}: {addr_str}:{node.addr[1]} (TTL: {node.ttl})')
    
    channel = pycares.Channel()
    channel.getaddrinfo(hostname, service_port, addrinfo_cb,
                       type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
    wait_channel(channel)

discover_service('www.google.com', 'https')

Install with Tessl CLI

npx tessl i tessl/pypi-pycares

docs

channel-management.md

dns-queries.md

error-handling.md

host-resolution.md

index.md

utilities.md

tile.json