CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pycares

Python interface for c-ares asynchronous DNS library

Pending
Overview
Eval results
Files

channel-management.mddocs/

DNS Channel Management

Core functionality for creating, configuring, and managing DNS resolution channels. The Channel class serves as the primary interface for all DNS operations in pycares.

Capabilities

Channel Creation

Create a new DNS resolution channel with comprehensive configuration options for controlling query behavior, server selection, and network settings.

class Channel:
    def __init__(
        self,
        flags=None,                          # Optional[int]: Channel behavior flags
        timeout=None,                        # Optional[float]: Query timeout in seconds
        tries=None,                         # Optional[int]: Number of query attempts
        ndots=None,                         # Optional[int]: Number of dots for domain search
        tcp_port=None,                      # Optional[int]: TCP port for DNS queries
        udp_port=None,                      # Optional[int]: UDP port for DNS queries
        servers=None,                       # Optional[Iterable[Union[str, bytes]]]: DNS servers list
        domains=None,                       # Optional[Iterable[Union[str, bytes]]]: Search domains
        lookups=None,                       # Union[str, bytes, None]: Lookup order
        sock_state_cb=None,                 # Optional[Callable[[int, bool, bool], None]]: Socket state callback
        socket_send_buffer_size=None,       # Optional[int]: Send buffer size
        socket_receive_buffer_size=None,    # Optional[int]: Receive buffer size
        rotate=False,                       # bool: Rotate server list
        local_ip=None,                      # Union[str, bytes, None]: Local IP binding
        local_dev=None,                     # Optional[str]: Local device binding
        resolvconf_path=None,               # Union[str, bytes, None]: Custom resolv.conf path
        event_thread=False                  # bool: Use event thread mode
    ):
        """
        Create a new DNS resolution channel.
        
        Args:
            flags: Bitwise OR of ARES_FLAG_* constants for channel behavior
            timeout: Query timeout in seconds (default uses system settings)
            tries: Number of times to try each query (default: 4)
            ndots: Number of dots needed in name for initial absolute query
            tcp_port: TCP port number for DNS queries (default: 53)
            udp_port: UDP port number for DNS queries (default: 53)
            servers: List of DNS server IP addresses as strings
            domains: List of search domains for unqualified names
            lookups: String specifying lookup order (e.g., "bf" for bind then file)
            sock_state_cb: Callback for socket state changes (fd, readable, writable)
            socket_send_buffer_size: SO_SNDBUF socket option value
            socket_receive_buffer_size: SO_RCVBUF socket option value
            rotate: Whether to rotate through servers for load balancing
            local_ip: Local IP address to bind outgoing queries to
            local_dev: Local network device to bind to
            resolvconf_path: Path to custom resolv.conf file
            event_thread: Enable built-in event thread (requires thread-safe c-ares)
        
        Raises:
            AresError: If channel initialization fails
            RuntimeError: If event_thread=True but c-ares not thread-safe
        """

Usage Example:

import pycares

# Basic channel with custom servers
channel = pycares.Channel(
    servers=['8.8.8.8', '8.8.4.4'],
    timeout=5.0,
    tries=2
)

# Advanced channel configuration
channel = pycares.Channel(
    flags=pycares.ARES_FLAG_USEVC | pycares.ARES_FLAG_IGNTC,
    servers=['1.1.1.1', '1.0.0.1'],
    domains=['example.com', 'test.org'],
    timeout=3.0,
    tries=3,
    rotate=True
)

I/O Event Management

Functions for integrating DNS channel I/O with external event loops and monitoring systems. These functions enable non-blocking operation by allowing the application to monitor and process socket events.

def getsock(self):
    """
    Get socket file descriptors for I/O monitoring.
    
    Returns:
        tuple[list[int], list[int]]: (read_fds, write_fds) - Lists of socket 
        file descriptors ready for reading and writing
    """

def process_fd(self, read_fd: int, write_fd: int) -> None:
    """
    Process I/O events on specific file descriptors.
    
    Args:
        read_fd: File descriptor ready for reading (or ARES_SOCKET_BAD if none)
        write_fd: File descriptor ready for writing (or ARES_SOCKET_BAD if none)
    """

def process_read_fd(self, read_fd: int) -> None:
    """
    Process read events on a file descriptor.
    
    Args:
        read_fd: File descriptor ready for reading
    """

def process_write_fd(self, write_fd: int) -> None:
    """
    Process write events on a file descriptor.
    
    Args:
        write_fd: File descriptor ready for writing
    """

def timeout(self, t=None):
    """
    Get or set timeout for next I/O operation.
    
    Args:
        t: Optional[float] - Maximum timeout in seconds, or None for no limit
        
    Returns:
        float: Actual timeout value for next operation in seconds
        
    Raises:
        ValueError: If timeout is negative
    """

Usage Example:

import select
import pycares

def wait_channel(channel):
    """Process all pending DNS operations until completion."""
    while True:
        read_fds, write_fds = channel.getsock()
        if not read_fds and not write_fds:
            break
            
        timeout = channel.timeout()
        if timeout == 0.0:
            # No timeout needed, process immediately
            channel.process_fd(pycares.ARES_SOCKET_BAD, pycares.ARES_SOCKET_BAD)
            continue
            
        # Wait for I/O events
        rlist, wlist, xlist = select.select(read_fds, write_fds, [], timeout)
        
        # Process ready file descriptors
        for fd in rlist:
            channel.process_read_fd(fd)
        for fd in wlist:
            channel.process_write_fd(fd)

Server Configuration

Manage the list of DNS servers used by the channel for query resolution.

@property
def servers(self) -> list[str]:
    """
    Get the current list of DNS servers.
    
    Returns:
        list[str]: List of DNS server IP addresses
        
    Raises:
        AresError: If unable to retrieve server list
    """

@servers.setter
def servers(self, servers) -> None:
    """
    Set the list of DNS servers for the channel.
    
    Args:
        servers: Iterable[Union[str, bytes]] - IP addresses of DNS servers
        
    Raises:
        ValueError: If any IP address is invalid
        AresError: If unable to set servers
    """

Usage Example:

channel = pycares.Channel()

# Get current servers
current_servers = channel.servers
print(f"Current servers: {current_servers}")

# Set new servers
channel.servers = ['8.8.8.8', '8.8.4.4', '1.1.1.1']

# Add IPv6 servers
channel.servers = ['2001:4860:4860::8888', '2001:4860:4860::8844']

Channel Control

Functions for managing channel lifecycle and query operations.

def cancel(self) -> None:
    """
    Cancel all pending queries on this channel.
    
    All pending query callbacks will be called with ARES_ECANCELLED error.
    """

def reinit(self) -> None:
    """
    Reinitialize the channel with current system DNS configuration.
    
    Rereads system DNS settings (resolv.conf, registry, etc.) and applies
    them to the channel.
    
    Raises:
        AresError: If reinitialization fails
    """

def close(self) -> None:
    """
    Close the channel and clean up resources.
    
    This method schedules safe channel destruction in a background thread.
    Once called, no new queries can be started. Pending queries will be
    cancelled and their callbacks will receive ARES_ECANCELLED.
    
    This method is thread-safe and can be called multiple times.
    """

Network Binding

Configure local network interface binding for outgoing DNS queries.

def set_local_ip(self, ip) -> None:
    """
    Set the local IP address to bind outgoing queries to.
    
    Args:
        ip: Union[str, bytes] - IPv4 or IPv6 address to bind to
        
    Raises:
        ValueError: If IP address is invalid
    """

def set_local_dev(self, dev) -> None:
    """
    Set the local network device to bind outgoing queries to.
    
    Args:
        dev: str - Network device name (e.g., 'eth0', 'wlan0')
    """

Usage Example:

channel = pycares.Channel()

# Bind to specific IP address
channel.set_local_ip('192.168.1.100')

# Bind to specific network interface
channel.set_local_dev('eth0')

# Perform queries - they will use the bound interface
channel.query('google.com', pycares.QUERY_TYPE_A, callback)

Channel Flags

Configuration flags that control channel behavior:

# Use TCP instead of UDP for queries
ARES_FLAG_USEVC = 1

# Only query primary DNS servers
ARES_FLAG_PRIMARY = 2

# Ignore truncated responses
ARES_FLAG_IGNTC = 4

# Don't perform recursive queries
ARES_FLAG_NORECURSE = 8

# Keep TCP connections open for multiple queries
ARES_FLAG_STAYOPEN = 16

# Don't use search domains for single-label names
ARES_FLAG_NOSEARCH = 32

# Don't look up aliases
ARES_FLAG_NOALIASES = 64

# Don't check response against query
ARES_FLAG_NOCHECKRESP = 128

# Enable EDNS (Extension Mechanisms for DNS)
ARES_FLAG_EDNS = 256

# Don't add default servers if none specified
ARES_FLAG_NO_DFLT_SVR = 512

Socket State Callback

For advanced event loop integration, channels can be configured with a socket state callback:

def socket_callback(fd, readable, writable):
    """
    Socket state change callback.
    
    Args:
        fd: int - Socket file descriptor
        readable: bool - Whether socket is ready for reading
        writable: bool - Whether socket is ready for writing
    """
    if readable:
        # Add fd to read monitoring
        loop.add_reader(fd, process_read, fd)
    if writable:
        # Add fd to write monitoring  
        loop.add_writer(fd, process_write, fd)
    if not readable and not writable:
        # Socket closed, remove from monitoring
        loop.remove_reader(fd)
        loop.remove_writer(fd)

channel = pycares.Channel(sock_state_cb=socket_callback)

Thread Safety

Check if the underlying c-ares library supports thread-safe operation:

def ares_threadsafety() -> bool:
    """
    Check if c-ares was compiled with thread safety support.
    
    Returns:
        bool: True if thread-safe, False otherwise
    """

When thread safety is available, you can use event_thread=True in Channel constructor to enable built-in event processing without manual I/O monitoring.

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