""" This plugin provides a command to perform DNS reconnaissance on a domain. """ import logging import dns.resolver import dns.reversename import simplematrixbotlib as botlib import re from plugins.utils import is_public_destination RECORD_TYPES = ['A', 'AAAA', 'MX', 'NS', 'TXT', 'CNAME', 'SOA', 'PTR', 'SRV'] def is_valid_domain(domain): pattern = r'^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$' return re.match(pattern, domain) is not None def format_dns_record(record_type, records): if not records: return "" output = f"{record_type} Records:
" for record in records: output += f" • {record}
" return output async def query_dns_records(domain): results = {} resolver = dns.resolver.Resolver() resolver.timeout = 5 resolver.lifetime = 5 for record_type in RECORD_TYPES: try: logging.info(f"Querying {record_type} records for {domain}") answers = resolver.resolve(domain, record_type) records = [] for rdata in answers: if record_type == 'MX': records.append(f"{rdata.preference} {rdata.exchange}") elif record_type == 'SOA': records.append(f"{rdata.mname} {rdata.rname}") elif record_type == 'SRV': records.append(f"{rdata.priority} {rdata.weight} {rdata.port} {rdata.target}") elif record_type == 'TXT': txt_data = ' '.join([s.decode() if isinstance(s, bytes) else str(s) for s in rdata.strings]) records.append(txt_data) else: records.append(str(rdata)) if records: results[record_type] = records logging.info(f"Found {len(records)} {record_type} record(s)") except dns.resolver.NoAnswer: continue except dns.resolver.NXDOMAIN: logging.warning(f"Domain {domain} does not exist") return None except dns.resolver.Timeout: continue except Exception as e: logging.error(f"Error querying {record_type} for {domain}: {e}") continue return results async def handle_command(room, message, bot, prefix, config): match = botlib.MessageMatch(room, message, bot, prefix) if match.is_not_from_this_bot() and match.prefix() and match.command("dns"): logging.info("Received !dns command") args = match.args() if len(args) != 1: await bot.api.send_text_message(room.room_id, "Usage: !dns \nExample: !dns example.com") return domain = args[0].lower().strip() domain = domain.replace('http://', '').replace('https://', '').rstrip('/') if not is_valid_domain(domain): await bot.api.send_text_message(room.room_id, f"Invalid domain name: {domain}") return try: await bot.api.send_text_message(room.room_id, f"🔍 Performing DNS reconnaissance on {domain}...") results = await query_dns_records(domain) if results is None: await bot.api.send_text_message(room.room_id, f"Domain {domain} does not exist (NXDOMAIN)") return if not results: await bot.api.send_text_message(room.room_id, f"No DNS records found for {domain}") return # SSRF / privacy check: if all A/AAAA records are private, refuse. a_records = results.get('A', []) aaaa_records = results.get('AAAA', []) all_ips = a_records + aaaa_records if all_ips and not any(is_public_destination(ip) for ip in all_ips): await bot.api.send_text_message(room.room_id, "❌ This domain resolves exclusively to private/internal IPs.") return output = f"🔍 DNS Records for {domain}

" preferred_order = ['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT', 'SOA', 'SRV', 'PTR'] for record_type in preferred_order: if record_type in results: output += format_dns_record(record_type, results[record_type]) output += "
" for record_type in results: if record_type not in preferred_order: output += format_dns_record(record_type, results[record_type]) output += "
" if output.count('
') > 15: output = f"
🔍 DNS Records for {domain}{output}
" await bot.api.send_markdown_message(room.room_id, output) logging.info(f"Sent DNS records for {domain}") except Exception as e: await bot.api.send_text_message(room.room_id, f"An error occurred while performing DNS lookup: {str(e)}") logging.error(f"Error in DNS plugin for {domain}: {e}", exc_info=True) # --------------------------------------------------------------------------- # Plugin Metadata # --------------------------------------------------------------------------- __version__ = "1.0.1" __author__ = "Funguy Bot" __description__ = "DNS reconnaissance (SSRF‑safe)" __help__ = """
!dns – DNS reconnaissance

!dns <domain> – Queries A, AAAA, MX, NS, TXT, CNAME, SOA, SRV, PTR records.

"""