Files
FunguyBot/plugins/dns.py
T
2026-05-07 17:37:20 -05:00

129 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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"<strong>{record_type} Records:</strong><br>"
for record in records:
output += f"{record}<br>"
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 <domain>\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"<strong>🔍 DNS Records for {domain}</strong><br><br>"
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 += "<br>"
for record_type in results:
if record_type not in preferred_order:
output += format_dns_record(record_type, results[record_type])
output += "<br>"
if output.count('<br>') > 15:
output = f"<details><summary><strong>🔍 DNS Records for {domain}</strong></summary>{output}</details>"
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 (SSRFsafe)"
__help__ = """
<details>
<summary><strong>!dns</strong> DNS reconnaissance</summary>
<p><code>!dns &lt;domain&gt;</code> Queries A, AAAA, MX, NS, TXT, CNAME, SOA, SRV, PTR records.</p>
</details>
"""