Files
ppf/dns.py
Username 0d7d2dce70
All checks were successful
CI / syntax-check (push) Successful in 3s
CI / memory-leak-check (push) Successful in 11s
refactor: extract modules from proxywatchd.py
Extract focused modules to reduce proxywatchd.py complexity:
- stats.py: JudgeStats, Stats, regexes, ssl_targets (557 lines)
- mitm.py: MITMCertStats, cert extraction functions (239 lines)
- dns.py: socks4_resolve with TTL caching (86 lines)
- job.py: PriorityJobQueue, calculate_priority (103 lines)

proxywatchd.py reduced from 2488 to 1591 lines (-36%).
2025-12-28 15:45:24 +01:00

87 lines
2.1 KiB
Python

#!/usr/bin/env python2
"""DNS resolution with caching for PPF proxy testing."""
from __future__ import division
import time
from misc import _log
import rocksock
# Module-level config reference
config = None
# DNS cache: {hostname: (ip, timestamp)}
cached_dns = {}
DNS_CACHE_TTL = 3600 # 1 hour
def set_config(cfg):
"""Set the config object (called from proxywatchd/ppf)."""
global config
config = cfg
def socks4_resolve(srvname, server_port):
"""Resolve hostname to IP for SOCKS4 (which requires numeric IP).
Caches results for DNS_CACHE_TTL seconds to avoid repeated lookups.
Args:
srvname: Hostname to resolve
server_port: Port number (for resolution)
Returns:
IP address string, or False on failure
"""
now = time.time()
# Check cache with TTL
if srvname in cached_dns:
ip, ts = cached_dns[srvname]
if now - ts < DNS_CACHE_TTL:
if config and config.watchd.debug:
_log("using cached ip (%s) for %s" % (ip, srvname), "debug")
return ip
# Expired - fall through to re-resolve
# Resolve hostname
dns_fail = False
try:
af, sa = rocksock.resolve(rocksock.RocksockHostinfo(srvname, server_port), want_v4=True)
if sa is not None:
cached_dns[srvname] = (sa[0], now)
return sa[0]
else:
dns_fail = True
except rocksock.RocksockException as e:
assert(e.get_errortype() == rocksock.RS_ET_GAI)
dns_fail = True
if dns_fail:
_log("could not resolve connection target %s" % srvname, "ERROR")
return False
return srvname
def clear_cache():
"""Clear the DNS cache."""
global cached_dns
cached_dns = {}
def cache_stats():
"""Return DNS cache statistics.
Returns:
dict with count, oldest_age, newest_age
"""
if not cached_dns:
return {'count': 0, 'oldest_age': 0, 'newest_age': 0}
now = time.time()
ages = [now - ts for _, ts in cached_dns.values()]
return {
'count': len(cached_dns),
'oldest_age': max(ages),
'newest_age': min(ages),
}