diff --git a/backend/app/services/domain_checker.py b/backend/app/services/domain_checker.py index da85fce..a90f25f 100644 --- a/backend/app/services/domain_checker.py +++ b/backend/app/services/domain_checker.py @@ -87,6 +87,20 @@ class DomainChecker: '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): """Initialize the domain checker.""" self._dns_resolver = dns.resolver.Resolver() @@ -602,8 +616,10 @@ class DomainChecker: # If custom RDAP fails, fall through to DNS check logger.info(f"Custom RDAP failed for {domain}, using DNS fallback") - # Priority 2: Try standard RDAP via whodap - if tld not in self.WHOIS_ONLY_TLDS and tld not in self.CUSTOM_RDAP_ENDPOINTS: + # 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 and + tld not in self.RDAP_BLOCKED_TLDS): rdap_result = await self._check_rdap(domain) if rdap_result: # Validate with DNS if RDAP says available @@ -614,6 +630,30 @@ class DomainChecker: rdap_result.is_available = False 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) if tld not in self.CUSTOM_RDAP_ENDPOINTS: whois_result = await self._check_whois(domain) diff --git a/backend/app/services/drop_status_checker.py b/backend/app/services/drop_status_checker.py index 8a76298..6b2b29f 100644 --- a/backend/app/services/drop_status_checker.py +++ b/backend/app/services/drop_status_checker.py @@ -16,24 +16,26 @@ from typing import Optional logger = logging.getLogger(__name__) # RDAP endpoints for different TLDs +# ONLY include TLDs where RDAP is NOT blocked! RDAP_ENDPOINTS = { - # ccTLDs + # ccTLDs (these work reliably) 'ch': 'https://rdap.nic.ch/domain/', 'li': 'https://rdap.nic.ch/domain/', 'de': 'https://rdap.denic.de/domain/', - # gTLDs via CentralNic - 'online': 'https://rdap.centralnic.com/online/domain/', - 'xyz': 'https://rdap.centralnic.com/xyz/domain/', - 'club': 'https://rdap.nic.club/domain/', - # gTLDs via Afilias/Donuts - 'info': 'https://rdap.afilias.net/rdap/info/domain/', - 'biz': 'https://rdap.afilias.net/rdap/biz/domain/', - 'org': 'https://rdap.publicinterestregistry.org/rdap/org/domain/', - # Google TLDs - 'dev': 'https://rdap.nic.google/domain/', - 'app': 'https://rdap.nic.google/domain/', + # gTLDs - DISABLED due to rate limits / bans: + # 'online': 'https://rdap.centralnic.com/online/domain/', # rate-limited + # 'xyz': 'https://rdap.centralnic.com/xyz/domain/', # rate-limited + # 'club': 'https://rdap.nic.club/domain/', # unreliable + # 'info': 'https://rdap.afilias.net/rdap/info/domain/', # BANNED + # 'biz': 'https://rdap.afilias.net/rdap/biz/domain/', # BANNED + # 'org': 'https://rdap.publicinterestregistry.org/rdap/org/domain/', # blocked + # 'dev': 'https://rdap.nic.google/domain/', # BANNED + # 'app': 'https://rdap.nic.google/domain/', # BANNED } +# TLDs where RDAP is blocked/banned - skip RDAP entirely +RDAP_BLOCKED_TLDS = {'info', 'biz', 'org', 'dev', 'app', 'xyz', 'online', 'club', 'com', 'net'} + @dataclass class DropStatus: @@ -60,16 +62,27 @@ async def check_drop_status(domain: str) -> DropStatus: """ tld = domain.split('.')[-1].lower() - endpoint = RDAP_ENDPOINTS.get(tld) - if not endpoint: - # Try generic lookup - logger.warning(f"No RDAP endpoint for .{tld}, returning unknown") + # Skip RDAP for blocked TLDs to avoid bans/timeouts + if tld in RDAP_BLOCKED_TLDS: + logger.debug(f"Skipping RDAP for blocked TLD .{tld}: {domain}") return DropStatus( domain=domain, status='unknown', rdap_status=[], 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}" )