Skip to main content

High-performance async DNS resolver validation and speed testing library

Project description

PyResolvers Logo

PyResolvers

High-Performance Async DNS Resolver Validation & Speed Testing

Tests Python 3.8+ PyPI version License: GPL v3 Code style: black

Validate DNS resolvers, measure response times, identify the fastest servers


Overview

PyResolvers is a high-performance async Python library and CLI tool for validating DNS resolvers. It performs comprehensive validation (baseline comparison, poisoning detection, NXDOMAIN verification) and orders results by speed.

Features

  • High-Performance Async - 2-3x faster than thread-based validators
  • 🚀 Speed Testing - Measures and orders resolvers by latency
  • 🔍 Comprehensive Validation - Multiple validation layers
  • 🛡️ Poisoning Detection - Identifies DNS hijacking
  • 📊 Multiple Formats - JSON, plain text, text+speed
  • 🎯 Speed Filtering - Filter by min/max latency thresholds

Performance

Method Time (5 servers) Improvement
Original (unoptimized) 5.36s baseline
PyResolvers 2.32s 56.7% faster
Speedup 2.31x

Installation

git clone https://github.com/PigeonSec/pyresolvers.git
cd pyresolvers
python3 -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -e .

Or install from PyPI:

pip install pyresolvers

Requirements: Python 3.8+, aiodns, pycares, colorclass


Quick Start

Command Line

# Test single resolver
pyresolvers -t 1.1.1.1

# Test from file
pyresolvers -tL dns_servers.txt

# Get fastest resolvers (< 50ms)
pyresolvers -tL resolvers.txt --max-speed 50 --format text-with-speed

# Export as JSON
pyresolvers -tL resolvers.txt --format json -o valid_dns.json

Python Library

from pyresolvers import Validator

# Basic usage
validator = Validator()
servers = ['1.1.1.1', '8.8.8.8', '9.9.9.9']
results = validator.validate_by_speed(servers)

for server, latency in results:
    print(f"{server}: {latency:.2f}ms")
# High concurrency for large lists
validator = Validator(concurrency=100)
results = validator.validate_by_speed(large_server_list, max_ms=100)
# Async usage
import asyncio

async def main():
    validator = Validator(concurrency=200)
    results = await validator.validate_by_speed_async(servers)
    return results

results = asyncio.run(main())

Examples

CLI Usage

# Speed filtering
pyresolvers -tL resolvers.txt --min-speed 10 --max-speed 100

# Silent mode (IPs only)
pyresolvers -tL resolvers.txt --silent

# Exclude specific servers
pyresolvers -tL all_resolvers.txt -e 8.8.8.8

# High performance (100 concurrent)
pyresolvers -tL large_list.txt -threads 100

Library Usage

Filter by Speed:

validator = Validator(concurrency=50)
fast = validator.validate_by_speed(servers, max_ms=50)

Detailed Results:

results = validator.validate(servers)
for r in results:
    if r.valid:
        print(f"✓ {r.server}: {r.latency_ms:.2f}ms")
    else:
        print(f"✗ {r.server}: {r.error}")

JSON Export:

json_output = validator.to_json(servers, max_ms=100, pretty=True)
with open('valid_dns.json', 'w') as f:
    f.write(json_output)

Streaming (Memory Efficient):

async def process_huge_list():
    validator = Validator(concurrency=100)
    async for server, latency in validator.validate_streaming_async(servers):
        print(f"{server}: {latency:.2f}ms")

asyncio.run(process_huge_list())

Cronjob Example

Bash Script:

#!/bin/bash
# /usr/local/bin/dns_monitor.sh

API_URL="https://api.example.com/dns/update"
OUTPUT_DIR="/var/lib/dns-monitor"

mkdir -p "$OUTPUT_DIR"

# Validate and save
pyresolvers -tL https://public-dns.info/nameservers.txt \
    --max-speed 100 \
    --format json \
    -o "$OUTPUT_DIR/resolvers.json"

# Send to API
curl -X POST "$API_URL" \
    -H "Content-Type: application/json" \
    -d @"$OUTPUT_DIR/resolvers.json"

Crontab:

# Run every 6 hours
0 */6 * * * /usr/local/bin/dns_monitor.sh >> /var/log/dns-monitor.log 2>&1

API Reference

Validator

High-performance async DNS validator.

Validator(
    trusted_resolvers: Optional[List[str]] = None,  # ["1.1.1.1", "8.8.8.8"]
    test_domains: Optional[List[str]] = None,       # ["bet365.com", "telegram.com"]
    poison_check_domains: Optional[List[str]] = None,
    baseline_domain: str = "bet365.com",
    query_prefix: str = "dnsvalidator",
    concurrency: int = 50,                          # Async concurrency
    timeout: int = 5,                               # DNS timeout (seconds)
    use_fast_timeout: bool = False,                 # Fast dead server detection (optional speedup)
    batch_size: int = 100,                          # Memory management
    verbose: bool = False
)

Methods:

  • validate(servers)List[ValidationResult] - Validate servers
  • validate_by_speed(servers, min_ms, max_ms)List[Tuple[str, float]] - Get valid servers ordered by speed
  • to_json(servers, min_ms, max_ms)str - Export as JSON
  • to_text(servers, min_ms, max_ms, show_speed)str - Export as text

Async Methods:

  • await validate_async(servers) - Async validation
  • await validate_by_speed_async(servers, min_ms, max_ms) - Async speed validation
  • async for server, latency in validate_streaming_async(servers) - Async streaming

ValidationResult

@dataclass
class ValidationResult:
    server: str
    valid: bool
    latency_ms: float
    error: Optional[str] = None

CLI Options

Option Description
-t SERVER Test single server
-tL FILE/URL Test from file or URL
-e SERVER Exclude server
-eL FILE/URL Exclude from file/URL
-r DOMAIN Baseline domain (default: bet365.com)
-threads N Concurrency (default: 5)
-timeout N Timeout seconds (default: 600)
-o FILE Output file
--format FORMAT text, json, text-with-speed
--max-speed MS Max latency filter (ms)
--min-speed MS Min latency filter (ms)
--silent Only output IPs
-v, --verbose Verbose output
--no-color Disable colors

Performance Tips

  • Concurrency: 50-100 for best performance on most systems
  • Timeout: Lower (3s) for speed, higher (10s+) for thoroughness
  • Fast Timeout: Enable (use_fast_timeout=True) for 30-50% speedup (may miss slow servers)
  • Batch Size: Increase for more memory, decrease for less
  • Network: Run from VPS to avoid ISP throttling

Optimization Features

  1. Async I/O - Non-blocking DNS queries with aiodns
  2. Parallel Baseline - Queries trusted resolvers simultaneously
  3. Fast Timeout - 1s initial timeout for dead server detection
  4. Combined Queries - Reduces DNS round trips
  5. Batch Processing - Memory-efficient for huge lists
  6. Streaming - Progressive results without holding all in memory

How It Works

  1. Baseline - Query trusted DNS (Cloudflare, Google) for ground truth
  2. Poisoning Check - Test random subdomains to detect hijacking
  3. NXDOMAIN - Verify correct NXDOMAIN behavior
  4. Baseline Compare - Ensure responses match baseline
  5. Speed Test - Measure latency and order results

Important Notes

Thread Count

Keep concurrency reasonable (50-100) to avoid triggering rate limits. Very high concurrency may be blocked by ISPs or DNS providers.

Domain Selection

Use non-geolocated domains for baseline (bet365.com works well). Avoid google.com, facebook.com as they return different IPs by location.


Project Structure

pyresolvers/
├── pyresolvers/
│   ├── __init__.py          # Package exports
│   ├── __main__.py          # CLI entry
│   ├── validator.py         # Async validation
│   └── lib/core/
│       ├── input.py         # CLI args
│       ├── output.py        # Formatting
│       └── __version__.py
├── requirements.txt
├── setup.py
└── README.md

License

GNU General Public License v3.0 - see LICENSE


Acknowledgments

Based on dnsvalidator by:

Enhanced with async architecture, speed testing, and performance optimizations by Karl.


⬆ back to top

Made with ❤️ by Karl | Based on dnsvalidator by @vortexau & @codingo_

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyresolvers-1.0.0.tar.gz (197.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyresolvers-1.0.0-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file pyresolvers-1.0.0.tar.gz.

File metadata

  • Download URL: pyresolvers-1.0.0.tar.gz
  • Upload date:
  • Size: 197.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for pyresolvers-1.0.0.tar.gz
Algorithm Hash digest
SHA256 99cd28b521c4cae0a916c8ee1447f5ffac83dfcd1faedd885f7ccc2538a33dea
MD5 54bb7e85fc0d2e37306a38f7f3bbe94d
BLAKE2b-256 6e263473ce9c1dc8014c173988a5cd571439030a1db8e65e6bc784f7496f4d92

See more details on using hashes here.

File details

Details for the file pyresolvers-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: pyresolvers-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for pyresolvers-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e5c54c52d41acf32285da34a0935aae5d076a15e1e165760456f7931b6b1a715
MD5 6b1f0ce1431418a4c9f487851b3925ce
BLAKE2b-256 7aa4d4ef2b506b0f4dc1ded22eb59e6cdf003eee6e220bfe9b7b1b34662ebf78

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page