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:
@ -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)
|
||||||
|
|||||||
@ -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}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user