""" This plugin provides DNSDumpster.com integration for domain reconnaissance and DNS mapping. """ import logging import os import requests import simplematrixbotlib as botlib from dotenv import load_dotenv # Load environment variables from .env file plugin_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(plugin_dir) dotenv_path = os.path.join(parent_dir, '.env') load_dotenv(dotenv_path) DNSDUMPSTER_API_KEY = os.getenv("DNSDUMPSTER_KEY", "") DNSDUMPSTER_API_BASE = "https://api.dnsdumpster.com" async def handle_command(room, message, bot, prefix, config): """ Function to handle DNSDumpster commands. Args: room (Room): The Matrix room where the command was invoked. message (RoomMessage): The message object containing the command. bot (Bot): The bot object. prefix (str): The command prefix. config (dict): Configuration parameters. Returns: None """ match = botlib.MessageMatch(room, message, bot, prefix) if match.is_not_from_this_bot() and match.prefix() and match.command("dnsdumpster"): logging.info("Received !dnsdumpster command") # Check if API key is configured if not DNSDUMPSTER_API_KEY: await bot.api.send_text_message( room.room_id, "DNSDumpster API key not configured. Please set DNSDUMPSTER_KEY environment variable." ) logging.error("DNSDumpster API key not configured") return args = match.args() if len(args) < 1: await show_usage(room, bot) return # Check if it's a test command or domain lookup if args[0].lower() == "test": await test_dnsdumpster_connection(room, bot) else: # Treat the first argument as the domain domain = args[0].lower().strip() await dnsdumpster_domain_lookup(room, bot, domain) async def show_usage(room, bot): """Display DNSDumpster command usage.""" usage = """ 🔍 DNSDumpster Commands: !dnsdumpster <domain_name> - Get comprehensive DNS reconnaissance for a domain !dnsdumpster test - Test API connection Examples:!dnsdumpster google.com!dnsdumpster github.com!dnsdumpster example.com Rate Limit: 1 request per 2 seconds """ await bot.api.send_markdown_message(room.room_id, usage) async def test_dnsdumpster_connection(room, bot): """Test DNSDumpster API connection.""" try: test_domain = "google.com" # Changed from example.com to google.com url = f"{DNSDUMPSTER_API_BASE}/domain/{test_domain}" headers = { "X-API-Key": DNSDUMPSTER_API_KEY } logging.info(f"Testing DNSDumpster API with domain: {test_domain}") response = requests.get(url, headers=headers, timeout=15) debug_info = f"🔧 DNSDumpster API Test
" debug_info += f"Status Code: {response.status_code}
" debug_info += f"Test Domain: {test_domain}
" debug_info += f"Headers Used: X-API-Key
" if response.status_code == 200: data = response.json() debug_info += "✅ SUCCESS - API is working!
" debug_info += f"Response Keys: {list(data.keys())}
" # Show some sample data if data.get('a'): debug_info += f"A Records Found: {len(data['a'])}
" if data.get('ns'): debug_info += f"NS Records Found: {len(data['ns'])}
" if data.get('total_a_recs'): debug_info += f"Total A Records: {data['total_a_recs']}
" elif response.status_code == 400: debug_info += "❌ Bad Request - Check domain format
" debug_info += f"Response: {response.text[:200]}
" elif response.status_code == 401: debug_info += "❌ Unauthorized - Invalid API key
" elif response.status_code == 429: debug_info += "⚠️ Rate Limit Exceeded - Wait 2 seconds
" else: debug_info += f"❌ Error: {response.status_code} - {response.text[:200]}
" await bot.api.send_markdown_message(room.room_id, debug_info) except Exception as e: await bot.api.send_text_message(room.room_id, f"Test failed: {str(e)}") async def dnsdumpster_domain_lookup(room, bot, domain): """Get comprehensive DNS reconnaissance for a domain.""" try: url = f"{DNSDUMPSTER_API_BASE}/domain/{domain}" headers = { "X-API-Key": DNSDUMPSTER_API_KEY } logging.info(f"Fetching DNSDumpster data for domain: {domain}") # Send initial processing message await bot.api.send_text_message(room.room_id, f"🔍 Processing DNS reconnaissance for {domain}...") response = requests.get(url, headers=headers, timeout=30) if response.status_code == 400: await bot.api.send_text_message(room.room_id, f"Bad request - check domain format: {domain}") return elif response.status_code == 401: await bot.api.send_text_message(room.room_id, "Invalid DNSDumpster API key") return elif response.status_code == 403: await bot.api.send_text_message(room.room_id, "Access denied - check API key permissions") return elif response.status_code == 429: await bot.api.send_text_message(room.room_id, "Rate limit exceeded - wait 2 seconds between requests") return elif response.status_code != 200: await bot.api.send_text_message(room.room_id, f"DNSDumpster API error: {response.status_code} - {response.text[:100]}") return data = response.json() logging.info(f"DNSDumpster response keys: {list(data.keys())}") # Format the comprehensive DNS report output = await format_dnsdumpster_report(domain, data) await bot.api.send_markdown_message(room.room_id, output) logging.info(f"Sent DNSDumpster data for {domain}") except requests.exceptions.Timeout: await bot.api.send_text_message(room.room_id, "DNSDumpster API request timed out") logging.error("DNSDumpster API timeout") except Exception as e: await bot.api.send_text_message(room.room_id, f"Error fetching DNSDumpster data: {str(e)}") logging.error(f"Error in dnsdumpster_domain_lookup: {e}") async def format_dnsdumpster_report(domain, data): """Format DNSDumpster JSON response into a readable report.""" output = f"🔍 DNSDumpster Report: {domain}

" # Summary statistics if data.get('total_a_recs'): output += f"📊 Summary
" output += f" • Total A Records: {data['total_a_recs']}
" # A Records - Show ALL records if data.get('a') and data['a']: output += f"
📍 A Records (IPv4) - {len(data['a'])} found
" for record in data['a']: # Show ALL A records host = record.get('host', 'N/A') ips = record.get('ips', []) output += f" • {host}
" for ip_info in ips: # Show ALL IPs per host ip = ip_info.get('ip', 'N/A') country = ip_info.get('country', 'Unknown') asn_name = ip_info.get('asn_name', 'Unknown') output += f" └─ {ip} ({country})
" output += f" └─ {asn_name}
" # Show banner information if available banners = ip_info.get('banners', {}) if banners.get('http') or banners.get('https'): output += f" └─ Web Services: " services = [] if banners.get('http'): services.append("HTTP") if banners.get('https'): services.append("HTTPS") output += f"{', '.join(services)}
" # NS Records - Show ALL records if data.get('ns') and data['ns']: output += f"
🔗 NS Records (Name Servers) - {len(data['ns'])} found
" for record in data['ns']: # Show ALL NS records host = record.get('host', 'N/A') ips = record.get('ips', []) output += f" • {host}
" for ip_info in ips: # Show ALL IPs ip = ip_info.get('ip', 'N/A') country = ip_info.get('country', 'Unknown') output += f" └─ {ip} ({country})
" # MX Records - Show ALL records if data.get('mx') and data['mx']: output += f"
📧 MX Records (Mail Servers) - {len(data['mx'])} found
" for record in data['mx']: # Show ALL MX records host = record.get('host', 'N/A') ips = record.get('ips', []) output += f" • {host}
" for ip_info in ips: # Show ALL IPs ip = ip_info.get('ip', 'N/A') country = ip_info.get('country', 'Unknown') output += f" └─ {ip} ({country})
" # CNAME Records - Show ALL records if data.get('cname') and data['cname']: output += f"
🔀 CNAME Records - {len(data['cname'])} found
" for record in data['cname']: # Show ALL CNAME records host = record.get('host', 'N/A') target = record.get('target', 'N/A') output += f" • {host} → {target}
" # TXT Records - Show ALL records if data.get('txt') and data['txt']: output += f"
📄 TXT Records - {len(data['txt'])} found
" for txt in data['txt']: # Show ALL TXT records # Truncate very long TXT records but show more content if len(txt) > 200: txt = txt[:200] + "..." output += f" • {txt}
" # Additional record types that might be present - Show ALL records other_records = ['aaaa', 'srv', 'soa', 'ptr'] for record_type in other_records: if data.get(record_type) and data[record_type]: output += f"
🔧 {record_type.upper()} Records - {len(data[record_type])} found
" for record in data[record_type]: # Show ALL records if isinstance(record, dict): # Format dictionary records nicely record_str = ", ".join([f"{k}: {v}" for k, v in record.items()]) if len(record_str) > 150: record_str = record_str[:150] + "..." output += f" • {record_str}
" else: output += f" • {record}
" # Add rate limit reminder output += "
💡 Rate Limit: 1 request per 2 seconds" # Always wrap in collapsible details since we're showing all results output = f"
🔍 DNSDumpster Report: {domain} (Click to expand){output}
" return output