diff --git a/src/certificate_checker.py b/src/certificate_checker.py
index e546792..73cea67 100644
--- a/src/certificate_checker.py
+++ b/src/certificate_checker.py
@@ -208,9 +208,6 @@ class CertificateChecker:
# Track connection failures
if isinstance(e, requests.exceptions.ConnectionError):
self.connection_failures += 1
- if self.connection_failures >= self.max_connection_failures:
- logger.error(f"❌ Too many connection failures to crt.sh. Disabling certificate lookups.")
- return certificates
if attempt < max_retries - 1:
time.sleep(backoff_delays[attempt])
diff --git a/src/dns_resolver.py b/src/dns_resolver.py
index 43dedc6..3d2bd27 100644
--- a/src/dns_resolver.py
+++ b/src/dns_resolver.py
@@ -1,5 +1,5 @@
# File: src/dns_resolver.py
-"""DNS resolution functionality."""
+"""DNS resolution functionality with enhanced TLD testing."""
import dns.resolver
import dns.reversename
@@ -16,7 +16,7 @@ from .config import Config
logger = logging.getLogger(__name__)
class DNSResolver:
- """DNS resolution and record lookup."""
+ """DNS resolution and record lookup with optimized TLD testing."""
# All DNS record types to query
RECORD_TYPES = [
@@ -42,14 +42,46 @@ class DNSResolver:
sleep_time = min_interval - time_since_last
# Only log if sleep is significant to reduce spam
if sleep_time > 0.1:
- logger.debug(f"⏸️ DNS rate limiting: sleeping for {sleep_time:.2f}s")
+ logger.debug(f"⏸️ DNS rate limiting: sleeping for {sleep_time:.2f}s")
time.sleep(sleep_time)
self.last_request = time.time()
self.query_count += 1
+ def resolve_hostname_fast(self, hostname: str) -> List[str]:
+ """Fast hostname resolution optimized for TLD testing."""
+ ips = []
+
+ logger.debug(f"🚀 Fast resolving hostname: {hostname}")
+
+ # Use only the first DNS server and shorter timeout for TLD testing
+ resolver = dns.resolver.Resolver()
+ resolver.nameservers = [self.config.DNS_SERVERS[0]] # Use primary DNS only
+ resolver.timeout = 2 # Shorter timeout for TLD testing
+ resolver.lifetime = 2 # Total query time limit
+
+ try:
+ # Try A records only for speed (most common)
+ answers = resolver.resolve(hostname, 'A')
+ for answer in answers:
+ ips.append(str(answer))
+ logger.debug(f"⚡ Fast A record for {hostname}: {answer}")
+ except dns.resolver.NXDOMAIN:
+ logger.debug(f"❌ NXDOMAIN for {hostname}")
+ except dns.resolver.NoAnswer:
+ logger.debug(f"⚠️ No A record for {hostname}")
+ except dns.resolver.Timeout:
+ logger.debug(f"⏱️ Timeout for {hostname}")
+ except Exception as e:
+ logger.debug(f"⚠️ Error fast resolving {hostname}: {e}")
+
+ if ips:
+ logger.debug(f"⚡ Fast resolved {hostname} to {len(ips)} IPs: {ips}")
+
+ return ips
+
def resolve_hostname(self, hostname: str) -> List[str]:
- """Resolve hostname to IP addresses."""
+ """Resolve hostname to IP addresses (full resolution with retries)."""
ips = []
logger.debug(f"🔍 Resolving hostname: {hostname}")
@@ -69,9 +101,9 @@ class DNSResolver:
except dns.resolver.NXDOMAIN:
logger.debug(f"❌ NXDOMAIN for {hostname} A record on {dns_server}")
except dns.resolver.NoAnswer:
- logger.debug(f"⚠️ No A record for {hostname} on {dns_server}")
+ logger.debug(f"⚠️ No A record for {hostname} on {dns_server}")
except Exception as e:
- logger.debug(f"⚠️ Error resolving A record for {hostname} on {dns_server}: {e}")
+ logger.debug(f"⚠️ Error resolving A record for {hostname} on {dns_server}: {e}")
try:
# Try AAAA records (IPv6)
@@ -82,9 +114,9 @@ class DNSResolver:
except dns.resolver.NXDOMAIN:
logger.debug(f"❌ NXDOMAIN for {hostname} AAAA record on {dns_server}")
except dns.resolver.NoAnswer:
- logger.debug(f"⚠️ No AAAA record for {hostname} on {dns_server}")
+ logger.debug(f"⚠️ No AAAA record for {hostname} on {dns_server}")
except Exception as e:
- logger.debug(f"⚠️ Error resolving AAAA record for {hostname} on {dns_server}: {e}")
+ logger.debug(f"⚠️ Error resolving AAAA record for {hostname} on {dns_server}: {e}")
unique_ips = list(set(ips))
if unique_ips:
@@ -130,13 +162,13 @@ class DNSResolver:
logger.debug(f"❌ NXDOMAIN for {hostname} {record_type} on {dns_server}")
break # Domain doesn't exist, no point checking other servers
except dns.resolver.NoAnswer:
- logger.debug(f"⚠️ No {record_type} record for {hostname} on {dns_server}")
+ logger.debug(f"⚠️ No {record_type} record for {hostname} on {dns_server}")
continue # Try next DNS server
except dns.resolver.Timeout:
- logger.debug(f"⏱️ Timeout for {hostname} {record_type} on {dns_server}")
+ logger.debug(f"⏱️ Timeout for {hostname} {record_type} on {dns_server}")
continue # Try next DNS server
except Exception as e:
- logger.debug(f"⚠️ Error querying {record_type} for {hostname} on {dns_server}: {e}")
+ logger.debug(f"⚠️ Error querying {record_type} for {hostname} on {dns_server}: {e}")
continue # Try next DNS server
logger.info(f"📋 Found {len(records)} DNS records for {hostname} across {len(set(r.record_type for r in records))} record types")
@@ -160,7 +192,7 @@ class DNSResolver:
logger.debug(f"❌ No reverse DNS for {ip}")
return None
except Exception as e:
- logger.debug(f"⚠️ Error in reverse DNS for {ip}: {e}")
+ logger.debug(f"⚠️ Error in reverse DNS for {ip}: {e}")
return None
def extract_subdomains_from_dns(self, records: List[DNSRecord]) -> Set[str]:
@@ -219,7 +251,7 @@ class DNSResolver:
logger.debug(f"🌿 Found subdomain from SRV: {hostname}")
except Exception as e:
- logger.debug(f"⚠️ Error extracting subdomain from {record.record_type} record '{value}': {e}")
+ logger.debug(f"⚠️ Error extracting subdomain from {record.record_type} record '{value}': {e}")
continue
if subdomains:
diff --git a/src/reconnaissance.py b/src/reconnaissance.py
index 6ac93ee..512b485 100644
--- a/src/reconnaissance.py
+++ b/src/reconnaissance.py
@@ -1,11 +1,11 @@
# File: src/reconnaissance.py
-"""Main reconnaissance logic."""
+"""Main reconnaissance logic with enhanced TLD expansion."""
import threading
import concurrent.futures
import logging
from datetime import datetime
-from typing import Set, List, Optional
+from typing import Set, List, Optional, Tuple
from .data_structures import ReconData
from .config import Config
from .dns_resolver import DNSResolver
@@ -18,7 +18,7 @@ from .tld_fetcher import TLDFetcher
logger = logging.getLogger(__name__)
class ReconnaissanceEngine:
- """Main reconnaissance engine."""
+ """Main reconnaissance engine with smart TLD expansion."""
def __init__(self, config: Config):
self.config = config
@@ -91,7 +91,7 @@ class ReconnaissanceEngine:
else:
logger.info(f"🔍 Target '{target}' appears to be a hostname, expanding to all TLDs")
self._update_progress(f"Expanding {target} to all TLDs", 5)
- initial_targets = self._expand_hostname_to_tlds(target)
+ initial_targets = self._expand_hostname_to_tlds_smart(target)
logger.info(f"📋 Found {len(initial_targets)} valid domains after TLD expansion")
self._update_progress("Resolving initial targets", 10)
@@ -119,36 +119,115 @@ class ReconnaissanceEngine:
return self.data
- def _expand_hostname_to_tlds(self, hostname: str) -> Set[str]:
- """Expand hostname to all possible TLDs."""
- logger.info(f"🌐 Fetching TLD list for hostname expansion")
- tlds = self.tld_fetcher.get_tlds()
- logger.info(f"🔍 Testing against {len(tlds)} TLDs")
+ def _expand_hostname_to_tlds_smart(self, hostname: str) -> Set[str]:
+ """Smart TLD expansion with prioritization and parallel processing."""
+ logger.info(f"🌐 Starting smart TLD expansion for hostname: {hostname}")
- targets = set()
+ # Get prioritized TLD lists
+ priority_tlds, normal_tlds, deprioritized_tlds = self.tld_fetcher.get_prioritized_tlds()
+
+ logger.info(f"📊 TLD categories: {len(priority_tlds)} priority, "
+ f"{len(normal_tlds)} normal, {len(deprioritized_tlds)} deprioritized")
+
+ valid_domains = set()
+
+ # Phase 1: Check priority TLDs first (parallel processing)
+ logger.info("🚀 Phase 1: Checking priority TLDs...")
+ priority_results = self._check_tlds_parallel(hostname, priority_tlds, "priority")
+ valid_domains.update(priority_results)
+
+ self._update_progress(f"Phase 1 complete: {len(priority_results)} priority TLD matches", 6)
+
+ # Phase 2: Check normal TLDs (if we found fewer than 5 results)
+ if len(valid_domains) < 5:
+ logger.info("🔍 Phase 2: Checking normal TLDs...")
+ normal_results = self._check_tlds_parallel(hostname, normal_tlds, "normal")
+ valid_domains.update(normal_results)
+
+ self._update_progress(f"Phase 2 complete: {len(normal_results)} normal TLD matches", 8)
+ else:
+ logger.info(f"⏭️ Skipping normal TLDs (found {len(valid_domains)} matches in priority)")
+
+ # Phase 3: Check deprioritized TLDs only if we found very few results
+ if len(valid_domains) < 2:
+ logger.info("🔍 Phase 3: Checking deprioritized TLDs (limited results so far)...")
+ depri_results = self._check_tlds_parallel(hostname, deprioritized_tlds, "deprioritized")
+ valid_domains.update(depri_results)
+
+ self._update_progress(f"Phase 3 complete: {len(depri_results)} deprioritized TLD matches", 9)
+ else:
+ logger.info(f"⏭️ Skipping deprioritized TLDs (found {len(valid_domains)} matches already)")
+
+ logger.info(f"🎯 Smart TLD expansion complete: found {len(valid_domains)} valid domains")
+ return valid_domains
+
+ def _check_tlds_parallel(self, hostname: str, tlds: List[str], phase_name: str) -> Set[str]:
+ """Check TLDs in parallel with optimized settings."""
+ valid_domains = set()
tested_count = 0
+ wildcard_detected = set()
- for i, tld in enumerate(tlds):
- full_hostname = f"{hostname}.{tld}"
-
- # Quick check if domain resolves
- ips = self.dns_resolver.resolve_hostname(full_hostname)
- tested_count += 1
-
- if ips:
- logger.info(f"✅ Found valid domain: {full_hostname} -> {ips}")
- self.data.add_hostname(full_hostname, 0)
- targets.add(full_hostname)
- for ip in ips:
- self.data.add_ip_address(ip)
-
- # Progress update every 100 TLDs
- if i % 100 == 0:
- progress = 5 + int((i / len(tlds)) * 5) # 5-10% range
- self._update_progress(f"Checked {i}/{len(tlds)} TLDs, found {len(targets)} valid domains", progress)
+ # Use thread pool for parallel processing
+ max_workers = min(20, len(tlds)) # Limit concurrent requests
- logger.info(f"🎯 TLD expansion complete: tested {tested_count} TLDs, found {len(targets)} valid domains")
- return targets
+ logger.info(f"⚡ Starting parallel check of {len(tlds)} {phase_name} TLDs "
+ f"with {max_workers} workers")
+
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
+ # Submit all tasks
+ future_to_tld = {
+ executor.submit(self._check_single_tld, hostname, tld): tld
+ for tld in tlds
+ }
+
+ # Process results as they complete
+ for future in concurrent.futures.as_completed(future_to_tld):
+ tld = future_to_tld[future]
+ tested_count += 1
+
+ try:
+ result = future.result(timeout=10) # 10 second timeout per future
+
+ if result:
+ full_hostname, ips = result
+
+
+ logger.info(f"✅ Valid domain found: {full_hostname} -> {ips}")
+ self.data.add_hostname(full_hostname, 0)
+ valid_domains.add(full_hostname)
+
+ for ip in ips:
+ self.data.add_ip_address(ip)
+
+ # Progress update every 50 TLDs in this phase
+ if tested_count % 50 == 0:
+ logger.info(f"📊 {phase_name.title()} phase progress: "
+ f"{tested_count}/{len(tlds)} tested, "
+ f"{len(valid_domains)} found")
+
+ except concurrent.futures.TimeoutError:
+ logger.debug(f"⏱️ Timeout checking {hostname}.{tld}")
+ except Exception as e:
+ logger.debug(f"⚠️ Error checking {hostname}.{tld}: {e}")
+
+ logger.info(f"📊 {phase_name.title()} phase complete: "
+ f"tested {tested_count} TLDs, found {len(valid_domains)} valid domains, "
+ f"detected {len(wildcard_detected)} wildcards")
+
+ return valid_domains
+
+ def _check_single_tld(self, hostname: str, tld: str) -> Optional[Tuple[str, List[str]]]:
+ """Check a single TLD combination with optimized DNS resolution."""
+ full_hostname = f"{hostname}.{tld}"
+
+ # Use faster DNS resolution with shorter timeout for TLD testing
+ ips = self.dns_resolver.resolve_hostname_fast(full_hostname)
+
+ if ips:
+ logger.debug(f"✅ {full_hostname} -> {ips}")
+ return (full_hostname, ips)
+
+ return None
def _process_targets_recursively(self, targets: Set[str]):
"""Process targets with recursive subdomain discovery."""
@@ -161,7 +240,7 @@ class ReconnaissanceEngine:
new_targets = set()
for target in targets:
- logger.debug(f"🔍 Processing target: {target}")
+ logger.debug(f"🎯 Processing target: {target}")
# DNS resolution and record gathering
self._process_single_target(target, current_depth)
@@ -223,7 +302,7 @@ class ReconnaissanceEngine:
self.data.certificates[hostname]
)
new_subdomains.update(cert_subdomains)
- logger.debug(f"🔐 Extracted {len(cert_subdomains)} subdomains from certificates of {hostname}")
+ logger.debug(f"🔍 Extracted {len(cert_subdomains)} subdomains from certificates of {hostname}")
# Filter out already known hostnames
filtered_subdomains = new_subdomains - self.data.hostnames
@@ -313,4 +392,9 @@ class ReconnaissanceEngine:
'shodan_results': len(self.data.shodan_results),
'virustotal_results': len(self.data.virustotal_results)
}
- logger.info(f"📊 External lookups summary: {ext_stats}")
\ No newline at end of file
+ logger.info(f"📊 External lookups summary: {ext_stats}")
+
+ # Keep the original method name for backward compatibility
+ def _expand_hostname_to_tlds(self, hostname: str) -> Set[str]:
+ """Legacy method - redirects to smart expansion."""
+ return self._expand_hostname_to_tlds_smart(hostname)
\ No newline at end of file
diff --git a/src/tld_fetcher.py b/src/tld_fetcher.py
index e6f936b..d21591c 100644
--- a/src/tld_fetcher.py
+++ b/src/tld_fetcher.py
@@ -1,9 +1,9 @@
# File: src/tld_fetcher.py
-"""Fetch and cache IANA TLD list."""
+"""Fetch and cache IANA TLD list with smart prioritization."""
import requests
import logging
-from typing import List, Set, Optional
+from typing import List, Set, Optional, Tuple
import os
import time
@@ -11,15 +11,43 @@ import time
logger = logging.getLogger(__name__)
class TLDFetcher:
- """Fetches and caches IANA TLD list."""
+ """Fetches and caches IANA TLD list with smart prioritization."""
IANA_TLD_URL = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
CACHE_FILE = "tlds_cache.txt"
CACHE_DURATION = 86400 # 24 hours in seconds
+ # Common TLDs that should be checked first (high success rate)
+ PRIORITY_TLDS = {
+ # Generic top-level domains (most common)
+ 'com', 'org', 'net', 'edu', 'gov', 'mil', 'int', 'info', 'biz', 'name',
+ 'io', 'co', 'me', 'tv', 'cc', 'ly', 'to', 'us', 'uk', 'ca',
+
+ # Major country codes (high usage)
+ 'de', 'fr', 'it', 'es', 'nl', 'be', 'ch', 'at', 'se', 'no', 'dk', 'fi',
+ 'au', 'nz', 'jp', 'kr', 'cn', 'hk', 'sg', 'my', 'th', 'in', 'br', 'mx',
+ 'ru', 'pl', 'cz', 'hu', 'ro', 'bg', 'hr', 'si', 'sk', 'lt', 'lv', 'ee',
+ 'ie', 'pt', 'gr', 'cy', 'mt', 'lu', 'is', 'tr', 'il', 'za', 'ng', 'eg',
+
+ # Popular new gTLDs (established, not spam-prone)
+ 'app', 'dev', 'tech', 'blog', 'news', 'shop', 'store', 'cloud', 'digital',
+ 'website', 'site', 'online', 'world', 'global', 'international'
+ }
+
+ # TLDs to deprioritize (often have wildcard DNS or low-quality domains)
+ DEPRIORITIZED_PATTERNS = [
+ 'xn--', # Internationalized domain names (often less common)
+ # These TLDs are known for high wildcard/parking rates
+ 'tk', 'ml', 'ga', 'cf', # Free TLDs often misused
+ 'top', 'win', 'download', 'stream', 'science', 'click', 'link',
+ 'loan', 'men', 'racing', 'review', 'party', 'trade', 'date',
+ 'cricket', 'accountant', 'faith', 'gdn', 'realtor'
+ ]
+
def __init__(self):
self._tlds: Optional[Set[str]] = None
- logger.info("🌐 TLD fetcher initialized")
+ self._prioritized_tlds: Optional[Tuple[List[str], List[str], List[str]]] = None
+ logger.info("🌐 TLD fetcher initialized with smart prioritization")
def get_tlds(self) -> Set[str]:
"""Get list of TLDs, using cache if available."""
@@ -29,6 +57,40 @@ class TLDFetcher:
logger.info(f"✅ Loaded {len(self._tlds)} TLDs")
return self._tlds
+ def get_prioritized_tlds(self) -> Tuple[List[str], List[str], List[str]]:
+ """Get TLDs sorted by priority: (priority, normal, deprioritized)."""
+ if self._prioritized_tlds is None:
+ all_tlds = self.get_tlds()
+ logger.debug("📊 Categorizing TLDs by priority...")
+
+ priority_list = []
+ normal_list = []
+ deprioritized_list = []
+
+ for tld in all_tlds:
+ tld_lower = tld.lower()
+
+ if tld_lower in self.PRIORITY_TLDS:
+ priority_list.append(tld_lower)
+ elif any(pattern in tld_lower for pattern in self.DEPRIORITIZED_PATTERNS):
+ deprioritized_list.append(tld_lower)
+ else:
+ normal_list.append(tld_lower)
+
+ # Sort each category alphabetically for consistency
+ priority_list.sort()
+ normal_list.sort()
+ deprioritized_list.sort()
+
+ self._prioritized_tlds = (priority_list, normal_list, deprioritized_list)
+
+ logger.info(f"📊 TLD prioritization complete: "
+ f"{len(priority_list)} priority, "
+ f"{len(normal_list)} normal, "
+ f"{len(deprioritized_list)} deprioritized")
+
+ return self._prioritized_tlds
+
def _load_tlds(self) -> Set[str]:
"""Load TLDs from cache or fetch from IANA."""
if self._is_cache_valid():
@@ -100,12 +162,12 @@ class TLDFetcher:
f.write(response.text)
logger.info(f"💾 TLD list cached to {self.CACHE_FILE}")
except Exception as cache_error:
- logger.warning(f"⚠️ Could not cache TLD list: {cache_error}")
+ logger.warning(f"⚠️ Could not cache TLD list: {cache_error}")
return tlds
except requests.exceptions.Timeout:
- logger.error("⏱️ Timeout fetching TLD list from IANA")
+ logger.error("⏱️ Timeout fetching TLD list from IANA")
return self._get_fallback_tlds()
except requests.exceptions.RequestException as e:
logger.error(f"🌐 Network error fetching TLD list: {e}")
@@ -115,28 +177,37 @@ class TLDFetcher:
return self._get_fallback_tlds()
def _get_fallback_tlds(self) -> Set[str]:
- """Return a minimal set of common TLDs if fetch fails."""
- logger.warning("⚠️ Using fallback TLD list")
+ """Return a minimal set of short TLDs if fetch fails."""
+ logger.warning("⚠️ Using fallback TLD list")
+ # Use only short, well-established TLDs as fallback
fallback_tlds = {
- # Generic top-level domains
- 'com', 'org', 'net', 'edu', 'gov', 'mil', 'int', 'info', 'biz', 'name',
+ # 2-character TLDs (country codes - most established)
+ 'ad', 'ae', 'af', 'ag', 'ai', 'al', 'am', 'ao', 'aq', 'ar', 'as', 'at',
+ 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi',
+ 'bj', 'bl', 'bm', 'bn', 'bo', 'bq', 'br', 'bs', 'bt', 'bv', 'bw', 'by',
+ 'bz', 'ca', 'cc', 'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn',
+ 'co', 'cr', 'cu', 'cv', 'cw', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm',
+ 'do', 'dz', 'ec', 'ee', 'eg', 'eh', 'er', 'es', 'et', 'eu', 'fi', 'fj',
+ 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi',
+ 'gl', 'gm', 'gn', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk',
+ 'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'io', 'iq',
+ 'ir', 'is', 'it', 'je', 'jm', 'jo', 'jp', 'ke', 'kg', 'kh', 'ki', 'km',
+ 'kn', 'kp', 'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr',
+ 'ls', 'lt', 'lu', 'lv', 'ly', 'ma', 'mc', 'md', 'me', 'mf', 'mg', 'mh',
+ 'mk', 'ml', 'mm', 'mn', 'mo', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'mv',
+ 'mw', 'mx', 'my', 'mz', 'na', 'nc', 'ne', 'nf', 'ng', 'ni', 'nl', 'no',
+ 'np', 'nr', 'nu', 'nz', 'om', 'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl',
+ 'pm', 'pn', 'pr', 'ps', 'pt', 'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru',
+ 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl',
+ 'sm', 'sn', 'so', 'sr', 'ss', 'st', 'sv', 'sx', 'sy', 'sz', 'tc', 'td',
+ 'tf', 'tg', 'th', 'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tr', 'tt', 'tv',
+ 'tw', 'tz', 'ua', 'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've',
+ 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'ye', 'yt', 'za', 'zm', 'zw',
- # Country code top-level domains (major ones)
- 'us', 'uk', 'de', 'fr', 'it', 'es', 'nl', 'be', 'ch', 'at', 'se', 'no',
- 'dk', 'fi', 'pl', 'cz', 'hu', 'ro', 'bg', 'hr', 'si', 'sk', 'lt', 'lv',
- 'ee', 'ie', 'pt', 'gr', 'cy', 'mt', 'lu', 'is', 'li', 'ad', 'mc', 'sm',
- 'va', 'by', 'ua', 'md', 'ru', 'kz', 'kg', 'tj', 'tm', 'uz', 'am', 'az',
- 'ge', 'tr', 'il', 'jo', 'lb', 'sy', 'iq', 'ir', 'af', 'pk', 'in', 'lk',
- 'mv', 'bt', 'bd', 'np', 'mm', 'th', 'la', 'kh', 'vn', 'my', 'sg', 'bn',
- 'id', 'tl', 'ph', 'tw', 'hk', 'mo', 'cn', 'kp', 'kr', 'jp', 'mn',
-
- # Common compound TLDs
- 'co.uk', 'org.uk', 'ac.uk', 'gov.uk', 'com.au', 'org.au', 'net.au',
- 'gov.au', 'edu.au', 'co.za', 'org.za', 'net.za', 'gov.za', 'ac.za',
- 'co.nz', 'org.nz', 'net.nz', 'govt.nz', 'ac.nz', 'co.jp', 'or.jp',
- 'ne.jp', 'go.jp', 'ac.jp', 'ad.jp', 'ed.jp', 'gr.jp', 'lg.jp'
+ # 3-character TLDs (generic - most common)
+ 'com', 'org', 'net', 'edu', 'gov', 'mil', 'int'
}
- logger.info(f"📋 Using {len(fallback_tlds)} fallback TLDs")
+ logger.info(f"📋 Using {len(fallback_tlds)} fallback TLDs (≤3 characters)")
return fallback_tlds
\ No newline at end of file
diff --git a/static/script.js b/static/script.js
index 343a5d1..195ecc6 100644
--- a/static/script.js
+++ b/static/script.js
@@ -319,7 +319,7 @@ class ReconTool {
const hostnameList = document.querySelector('#recentHostnames .hostname-list');
if (hostnameList && data.hostnames && data.hostnames.length > 0) {
// Show last 10 hostnames
- const recentHostnames = data.hostnames.slice(-10);
+ const recentHostnames = data.hostnames;
hostnameList.innerHTML = recentHostnames.map(hostname =>
`${hostname}`
).join('');
@@ -332,7 +332,7 @@ class ReconTool {
const ipList = document.querySelector('#recentIPs .ip-list');
if (ipList && data.ip_addresses && data.ip_addresses.length > 0) {
// Show last 10 IPs
- const recentIPs = data.ip_addresses.slice(-10);
+ const recentIPs = data.ip_addresses;
ipList.innerHTML = recentIPs.map(ip =>
`${ip}`
).join('');