High-Performance Async DNS Resolver Validation & Speed Testing
Validate DNS resolvers, measure response times, identify the fastest servers
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.
- ⚡ High-Performance Async - Up to 3x faster than thread-based validators
- 🚀 Speed Testing - Measures and orders resolvers by latency
- 🌐 URL Support - Download resolver lists from URLs (HTTP/HTTPS)
- 🔍 Comprehensive Validation - Baseline, poisoning, NXDOMAIN checks
- 🛡️ Poisoning Detection - 3-domain check (Amazon, PayPal, Netflix)
- 📊 Multiple Formats - JSON, plain text, text+speed
- 🎯 Speed Filtering - Filter by min/max latency thresholds
- 📝 Smart Parsing - Auto-extracts IPs from CSV, text, and mixed formats
- 🔊 Verbose Mode - See rejected/filtered servers with reasons
- 💨 Streaming Results - Real-time output as servers are validated
Benchmark (500 servers from public-dns.info):
| Concurrency | Timeout | Time | Test Rate | Speedup |
|---|---|---|---|---|
| 50 (default) | 1.0s | 36.7s | 13.6/sec | baseline |
| 100 | 1.0s | 18.3s | 27.3/sec | 2.0x ⚡ |
| 200 | 0.5s | 14.5s | 34.6/sec | 2.5x 🚀 |
Estimated time for 62,607 servers:
- Default (50 threads): ~77 minutes
- Optimized (200 threads, 0.5s timeout): ~30 minutes
If this is too slow for you, you can use my pre-computed resolver lists, updated automatically every 48 hours:
| File | Description | Raw URL |
|---|---|---|
resolvers.json |
Full dataset with latency metrics | raw link |
fast_resolvers.txt |
Fast resolvers (< 50 ms) | raw link |
medium_resolvers.txt |
Medium resolvers (< 150 ms) | raw link |
all_resolvers.txt |
All working resolvers | raw link |
📦 Repository: PigeonSec/dns-resolvers
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 pyresolversRequirements: Python 3.12+, aiodns, pycares, colorclass
Basic validation:
$ pyresolvers -t 1.1.1.1
=======================================================
pyresolvers v2.0.1 - DNS Resolver Validator
=======================================================
[10:25:04] [INFO] Testing 1 servers
[10:25:04] [INFO] Establishing baseline...
[10:25:04] [INFO] Validating with concurrency=50...
[10:25:04] [ACCEPTED] [1.1.1.1] 20.49ms
[10:25:04] [INFO] Found 1 valid serversTest multiple servers from file:
$ pyresolvers -tL dns_servers.txt
[10:25:11] [INFO] Testing 3 servers
[10:25:11] [INFO] Establishing baseline...
[10:25:11] [INFO] Validating with concurrency=50...
[10:25:11] [ACCEPTED] [9.9.9.9] 9.00ms
[10:25:11] [ACCEPTED] [8.8.8.8] 15.79ms
[10:25:11] [ACCEPTED] [1.1.1.1] 19.69ms
[10:25:11] [INFO] Found 3 valid serversSilent mode (IPs only, perfect for piping):
$ pyresolvers -tL dns_servers.txt --silent
9.9.9.9
8.8.8.8
1.1.1.1Verbose mode with speed filtering:
$ pyresolvers -tL dns_servers.txt --max-speed 15 -v
[10:25:27] [INFO] Testing 3 servers
[10:25:27] [INFO] Max speed filter: 15.0ms
[10:25:28] [ACCEPTED] [9.9.9.9] 9.05ms
[10:25:28] [REJECTED] [1.1.1.1] Too slow: 20.30ms
[10:25:28] [REJECTED] [8.8.8.8] Too slow: 20.22ms
[10:25:28] [INFO] Found 1 valid serversJSON output format:
$ pyresolvers -tL dns_servers.txt --format json
{
"servers": [
{"ip": "9.9.9.9", "latency_ms": 10.3},
{"ip": "8.8.8.8", "latency_ms": 15.96},
{"ip": "1.1.1.1", "latency_ms": 20.35}
],
"count": 3,
"filters": {"min_ms": null, "max_ms": null}
}More examples:
# Test from URL
pyresolvers -tL https://public-dns.info/nameservers.txt
# Speed filtering and save to file
pyresolvers -tL resolvers.txt --max-speed 50 -o fast_dns.txt
pyresolvers -tL resolvers.txt --min-speed 10 --max-speed 100
# Text with speed output
pyresolvers -tL resolvers.txt --format text-with-speed -o dns_with_speed.txt
# Exclusions
pyresolvers -tL all_resolvers.txt -e 8.8.8.8
pyresolvers -tL resolvers.txt -eL blacklist.txt
# Performance tuning
pyresolvers -tL large_list.txt -threads 200 -timeout 0.5
pyresolvers -tL https://public-dns.info/nameservers.txt -threads 200 --max-speed 30 | head -10from pyresolvers import Validator
# Basic usage - get valid servers ordered by speed
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 with speed filtering
validator = Validator(concurrency=100)
fast = validator.validate_by_speed(servers, max_ms=50)
# Detailed results with error information
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)
# 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())
# Streaming for huge lists (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())Daily DNS validation with API upload:
#!/bin/bash
# /usr/local/bin/dns_monitor.sh
OUTPUT_DIR="/var/lib/dns-monitor"
mkdir -p "$OUTPUT_DIR"
# Validate and save to JSON
pyresolvers -tL https://public-dns.info/nameservers.txt \
--max-speed 100 \
--format json \
-o "$OUTPUT_DIR/resolvers.json"
# Upload to API
curl -X POST "https://api.example.com/dns/update" \
-H "Content-Type: application/json" \
-d @"$OUTPUT_DIR/resolvers.json"Fast resolver discovery and update:
#!/bin/bash
# /usr/local/bin/update_fast_dns.sh
# Get fastest resolvers (< 30ms) and update system config
pyresolvers -tL https://public-dns.info/nameservers.txt \
--max-speed 30 \
--silent \
-threads 200 \
-timeout 0.5 \
-o /tmp/fast_dns.txt
# Use top 3 fastest
head -3 /tmp/fast_dns.txt > /etc/my_app/dns_servers.confCrontab entries:
# Daily API sync at 2 AM
0 2 * * * /usr/local/bin/dns_monitor.sh >> /var/log/dns-monitor.log 2>&1
# Update fast DNS every 6 hours
0 */6 * * * /usr/local/bin/update_fast_dns.sh >> /var/log/dns-update.log 2>&1PyResolvers supports multiple input methods for maximum flexibility.
Download resolver lists directly from URLs:
# Public DNS list (62,000+ resolvers)
pyresolvers -tL https://public-dns.info/nameservers.txt --max-speed 50
# Your own hosted list
pyresolvers -tL https://example.com/dns-servers.txt
# GitHub raw files
pyresolvers -tL https://raw.githubusercontent.com/user/repo/main/resolvers.txtLoad from local files:
# Plain text file (one IP per line)
pyresolvers -tL resolvers.txt
# CSV format (automatically extracts IPs)
pyresolvers -tL servers.csv
# Mixed format with comments
pyresolvers -tL list.txtPyResolvers automatically extracts valid IPv4 addresses from:
Plain Text:
8.8.8.8
1.1.1.1
208.67.222.222
CSV/TSV:
8.8.8.8,Google,US,Fast
1.1.1.1,Cloudflare,US,Fast
208.67.222.222,OpenDNS,US,Moderate
With Comments:
# Google Public DNS
8.8.8.8
# Cloudflare
1.1.1.1
# OpenDNS
208.67.222.222
Mixed Format:
Server: 8.8.8.8 (Google)
dns1=1.1.1.1
208.67.222.222 # OpenDNS Primary
All input is validated automatically:
- ✅ Extracts IPv4 addresses from any position in a line
- ✅ Validates IP format (0-255 per octet)
- ✅ Skips empty lines and comments (#)
- ✅ Handles CSV, TSV, and space-separated formats
- ✅ Removes duplicates automatically
Exclude servers using the same formats:
# Exclude from URL
pyresolvers -tL all.txt -eL https://example.com/blacklist.txt
# Exclude from file
pyresolvers -tL https://public-dns.info/nameservers.txt -eL blocked.txt
# Exclude single IP
pyresolvers -tL resolvers.txt -e 8.8.8.8High-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 serversvalidate_by_speed(servers, min_ms, max_ms)→List[Tuple[str, float]]- Get valid servers ordered by speedto_json(servers, min_ms, max_ms)→str- Export as JSONto_text(servers, min_ms, max_ms, show_speed)→str- Export as text
Async Methods:
await validate_async(servers)- Async validationawait validate_by_speed_async(servers, min_ms, max_ms)- Async speed validationasync for server, latency in validate_streaming_async(servers)- Async streaming
@dataclass
class ValidationResult:
server: str
valid: bool
latency_ms: float
error: Optional[str] = None| 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: 50) |
-timeout N |
Timeout seconds (default: 1) |
-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 |
For 60K+ servers (fastest):
pyresolvers -tL https://public-dns.info/nameservers.txt -threads 200 -timeout 0.5 --max-speed 200 -o results.txtFor balanced speed/accuracy:
pyresolvers -tL large_list.txt -threads 100 -timeout 1 --max-speed 100For verbose debugging:
pyresolvers -tL resolvers.txt -threads 50 -v-
Concurrency:
- 50-100 for stable performance
- 200-300 for maximum speed (requires good network)
- Higher may trigger rate limits
-
Timeout:
- 0.5s for fast dead server detection
- 1s for balanced performance (default)
- 2s+ for slow/distant servers
-
Verbose Mode: Use
-vto see rejected servers with reasons:- "Too slow: XXms" - Exceeded max-speed filter
- "Timeout" - Server didn't respond
- "Invalid" - Failed validation checks
- "DNS poisoning" - Detected hijacking
- Async I/O - Non-blocking DNS queries with aiodns
- Parallel Validation - All checks run simultaneously
- Streaming Output - Results appear in real-time
- Smart Poisoning - 3 diverse domains (Amazon, PayPal, Netflix)
- Optimized Defaults - 50 threads, 1s timeout, fast mode enabled
- Progress Indicators - Shows validation progress every 100 servers
- Baseline Setup - Query trusted resolvers (1.1.1.1, 8.8.8.8) for ground truth
- Parallel Validation - For each server, run simultaneously:
- Poisoning Check - Test 3 random subdomains (amazon.com, paypal.com, netflix.com)
- NXDOMAIN Check - Verify correct NXDOMAIN behavior
- Baseline Compare - Ensure responses match trusted resolvers
- Latency Measurement - Measure DNS query speed for valid servers
- Real-time Output - Stream results as validation completes
- Speed Filtering - Apply min/max latency filters and output sorted results
Keep concurrency reasonable (50-100) to avoid triggering rate limits. Very high concurrency may be blocked by ISPs or DNS providers.
Use non-geolocated domains for baseline (bet365.com works well). Avoid google.com, facebook.com as they return different IPs by location.
GNU General Public License v3.0 - see LICENSE
Based on dnsvalidator by:
Enhanced with async architecture, speed testing, and performance optimizations by Karl.
Made with ❤️ by Karl | Based on dnsvalidator by @vortexau & @codingo_