fix: RDAP ban prevention and DNS fallback

Problem: We are banned from Afilias (.info/.biz) and Google (.dev/.app)
RDAP servers due to too many requests, causing timeouts.

Solution:
1. Added RDAP_BLOCKED_TLDS list in domain_checker.py
2. Skip RDAP for blocked TLDs, use DNS+WHOIS instead
3. Updated drop_status_checker.py to skip blocked TLDs
4. Removed banned endpoints from RDAP_ENDPOINTS

TLDs now using DNS-only: .info, .biz, .org, .dev, .app, .xyz, .online, .com, .net
TLDs still using RDAP: .ch, .li, .de (working fine)

This prevents bans and timeouts while still providing availability checks.
This commit is contained in:
2025-12-21 14:39:40 +01:00
parent 84964ccb84
commit 5f3856fce6
2 changed files with 72 additions and 19 deletions

View File

@ -87,6 +87,20 @@ class DomainChecker:
'ru', 'su', 'ua', 'by', 'kz', 'ru', 'su', 'ua', 'by', 'kz',
} }
# TLDs where we are rate-limited/banned from RDAP
# Use DNS+WHOIS only for these (no RDAP calls!)
RDAP_BLOCKED_TLDS = {
'info', # Afilias - banned, timeouts
'biz', # Afilias - banned, timeouts
'org', # PIR - might be blocked
'dev', # Google - blocked
'app', # Google - blocked
'xyz', # CentralNic - slow/limited
'online', # CentralNic - slow/limited
'com', # Verisign - heavy rate limits
'net', # Verisign - heavy rate limits
}
def __init__(self): def __init__(self):
"""Initialize the domain checker.""" """Initialize the domain checker."""
self._dns_resolver = dns.resolver.Resolver() self._dns_resolver = dns.resolver.Resolver()
@ -602,8 +616,10 @@ class DomainChecker:
# If custom RDAP fails, fall through to DNS check # If custom RDAP fails, fall through to DNS check
logger.info(f"Custom RDAP failed for {domain}, using DNS fallback") logger.info(f"Custom RDAP failed for {domain}, using DNS fallback")
# Priority 2: Try standard RDAP via whodap # Priority 2: Try standard RDAP via whodap (skip blocked TLDs!)
if tld not in self.WHOIS_ONLY_TLDS and tld not in self.CUSTOM_RDAP_ENDPOINTS: if (tld not in self.WHOIS_ONLY_TLDS and
tld not in self.CUSTOM_RDAP_ENDPOINTS and
tld not in self.RDAP_BLOCKED_TLDS):
rdap_result = await self._check_rdap(domain) rdap_result = await self._check_rdap(domain)
if rdap_result: if rdap_result:
# Validate with DNS if RDAP says available # Validate with DNS if RDAP says available
@ -614,6 +630,30 @@ class DomainChecker:
rdap_result.is_available = False rdap_result.is_available = False
return rdap_result return rdap_result
# For RDAP-blocked TLDs: Use DNS first, then WHOIS for details
if tld in self.RDAP_BLOCKED_TLDS:
logger.debug(f"Using DNS+WHOIS fallback for blocked TLD .{tld}: {domain}")
dns_available = await self._check_dns(domain)
if dns_available:
# No DNS records = likely available, verify with WHOIS
whois_result = await self._check_whois(domain)
return whois_result
else:
# Has DNS records = taken, try to get details from WHOIS
try:
whois_result = await self._check_whois(domain)
whois_result.check_method = "dns+whois"
return whois_result
except Exception:
# WHOIS failed, return DNS-only result
return DomainCheckResult(
domain=domain,
status=DomainStatus.TAKEN,
is_available=False,
check_method="dns",
)
# Priority 3: Fall back to WHOIS (skip for TLDs that block it like .ch) # Priority 3: Fall back to WHOIS (skip for TLDs that block it like .ch)
if tld not in self.CUSTOM_RDAP_ENDPOINTS: if tld not in self.CUSTOM_RDAP_ENDPOINTS:
whois_result = await self._check_whois(domain) whois_result = await self._check_whois(domain)

View File

@ -16,24 +16,26 @@ from typing import Optional
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# RDAP endpoints for different TLDs # RDAP endpoints for different TLDs
# ONLY include TLDs where RDAP is NOT blocked!
RDAP_ENDPOINTS = { RDAP_ENDPOINTS = {
# ccTLDs # ccTLDs (these work reliably)
'ch': 'https://rdap.nic.ch/domain/', 'ch': 'https://rdap.nic.ch/domain/',
'li': 'https://rdap.nic.ch/domain/', 'li': 'https://rdap.nic.ch/domain/',
'de': 'https://rdap.denic.de/domain/', 'de': 'https://rdap.denic.de/domain/',
# gTLDs via CentralNic # gTLDs - DISABLED due to rate limits / bans:
'online': 'https://rdap.centralnic.com/online/domain/', # 'online': 'https://rdap.centralnic.com/online/domain/', # rate-limited
'xyz': 'https://rdap.centralnic.com/xyz/domain/', # 'xyz': 'https://rdap.centralnic.com/xyz/domain/', # rate-limited
'club': 'https://rdap.nic.club/domain/', # 'club': 'https://rdap.nic.club/domain/', # unreliable
# gTLDs via Afilias/Donuts # 'info': 'https://rdap.afilias.net/rdap/info/domain/', # BANNED
'info': 'https://rdap.afilias.net/rdap/info/domain/', # 'biz': 'https://rdap.afilias.net/rdap/biz/domain/', # BANNED
'biz': 'https://rdap.afilias.net/rdap/biz/domain/', # 'org': 'https://rdap.publicinterestregistry.org/rdap/org/domain/', # blocked
'org': 'https://rdap.publicinterestregistry.org/rdap/org/domain/', # 'dev': 'https://rdap.nic.google/domain/', # BANNED
# Google TLDs # 'app': 'https://rdap.nic.google/domain/', # BANNED
'dev': 'https://rdap.nic.google/domain/',
'app': 'https://rdap.nic.google/domain/',
} }
# TLDs where RDAP is blocked/banned - skip RDAP entirely
RDAP_BLOCKED_TLDS = {'info', 'biz', 'org', 'dev', 'app', 'xyz', 'online', 'club', 'com', 'net'}
@dataclass @dataclass
class DropStatus: class DropStatus:
@ -60,16 +62,27 @@ async def check_drop_status(domain: str) -> DropStatus:
""" """
tld = domain.split('.')[-1].lower() tld = domain.split('.')[-1].lower()
endpoint = RDAP_ENDPOINTS.get(tld) # Skip RDAP for blocked TLDs to avoid bans/timeouts
if not endpoint: if tld in RDAP_BLOCKED_TLDS:
# Try generic lookup logger.debug(f"Skipping RDAP for blocked TLD .{tld}: {domain}")
logger.warning(f"No RDAP endpoint for .{tld}, returning unknown")
return DropStatus( return DropStatus(
domain=domain, domain=domain,
status='unknown', status='unknown',
rdap_status=[], rdap_status=[],
can_register_now=False, can_register_now=False,
should_monitor=False, should_monitor=True, # Still worth monitoring via zone files
message=f"RDAP disabled for .{tld} (rate-limited)"
)
endpoint = RDAP_ENDPOINTS.get(tld)
if not endpoint:
logger.debug(f"No RDAP endpoint for .{tld}, returning unknown")
return DropStatus(
domain=domain,
status='unknown',
rdap_status=[],
can_register_now=False,
should_monitor=True,
message=f"No RDAP endpoint for .{tld}" message=f"No RDAP endpoint for .{tld}"
) )