#!/usr/bin/python
# -*- coding: utf-8 -*-

# --------------------------------------------------------------------------- #
#                                                                             #
#    Plugin for iSida Jabber Bot                                              #
#                                                                             #
#    This program is free software: you can redistribute it and/or modify     #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation, either version 3 of the License, or        #
#    (at your option) any later version.                                      #
#                                                                             #
# --------------------------------------------------------------------------- #

import subprocess
import re
import os

# --------------------------------------------------------------------------- #
# CONFIGURATION                                                               #
# --------------------------------------------------------------------------- #

DNSRECON_OPTS = ['-n', '8.8.8.8,1.1.1.1', '--threads', '1', '--lifetime', '10', '--disable_check_recursion']
DNSRECON_TIMEOUT = 30

PROTOCOL_MAP = {
    'xmpp': ['xmpp-client', 'xmpp-server', 'xmpps-client', 'xmpps-server'],
    'call': ['turn', 'turns', 'stun', 'stuns'],
    'sip': ['sip', 'sips'],
    'mail': ['pop', 'pops', 'imap', 'imaps', 'smtp', 'smtps', 'submission'],
}

ALL_PROTOCOLS = list(set([p for sublist in PROTOCOL_MAP.values() for p in sublist]))

# --------------------------------------------------------------------------- #
# HELPERS                                                                     #
# --------------------------------------------------------------------------- #

def _is_valid_domain(domain):
    domain = domain.strip().lower()
    if not domain or len(domain) < 3:
        return False
    if domain.count('.') < 1:
        return False
    pattern = r'^[a-z0-9]([a-z0-9\-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9\-]*[a-z0-9])?)*$'
    return re.match(pattern, domain) is not None

def _check_dnsrecon():
    for path in os.environ.get('PATH', '').split(':'):
        if os.path.exists(os.path.join(path, 'dnsrecon')):
            return True, None

    if os.path.exists('/usr/bin/apt') or os.path.exists('/usr/bin/apt-get'):
        return False, 'apt install dnsrecon'
    elif os.path.exists('/usr/bin/dnf'):
        return False, 'dnf install dnsrecon'
    elif os.path.exists('/usr/bin/yum'):
        return False, 'yum install dnsrecon'
    elif os.path.exists('/usr/bin/zypper'):
        return False, 'zypper install dnsrecon'
    elif os.path.exists('/usr/bin/pacman'):
        return False, 'pacman -S dnsrecon'
    else:
        return False, 'dnsrecon (unknown package manager)'

def _parse_filters(filter_str):
    if filter_str.lower() == 'all':
        return ALL_PROTOCOLS

    protocols = []
    for part in filter_str.lower().split(','):
        part = part.strip()
        if part in PROTOCOL_MAP:
            protocols.extend(PROTOCOL_MAP[part])
        else:
            protocols.append(part)
    return list(set(protocols))

def _run_dnsrecon(domain):
    """Запускает dnsrecon один раз для всех SRV-записей."""
    cmd = ['dnsrecon', '-t', 'srv', '-d', domain] + DNSRECON_OPTS

    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=DNSRECON_TIMEOUT
        )

        lines = result.stdout.split('\n')
        records = []
        for line in lines:
            if 'SRV' in line.upper() and '_' in line:
                records.append(line.strip())

        return records, None

    except subprocess.TimeoutExpired:
        return None, L('Timeout: dnsrecon took too long (limit %s sec.)', '') % DNSRECON_TIMEOUT
    except FileNotFoundError:
        return None, L('dnsrecon not found', '')
    except Exception as e:
        return None, L('Error: %s', '') % str(e)

def _filter_records(records, protocols):
    if not records:
        return []

    filtered = []
    for record in records:
        for proto in protocols:
            if '_%s._' % proto in record.lower():
                filtered.append(record)
                break
    return filtered

def _format_records(records):
    if not records:
        return L('No matching SRV records found', '')

    formatted = []
    for record in records:
        parts = record.split()
        if len(parts) >= 8 and parts[3].upper() == 'SRV':
            priority = parts[4]
            weight = parts[5]
            port = parts[6]
            target = parts[7].rstrip('.')
            formatted.append('SRV %s %s %s %s' % (priority, weight, port, target))
        else:
            cleaned = re.sub(r'\d+\.\d+\.\d+\.\d+', '', record)
            cleaned = re.sub(r'\s+', ' ', cleaned).strip()
            formatted.append(cleaned)

    return '\n'.join(formatted)

# --------------------------------------------------------------------------- #
# COMMAND HANDLER                                                             #
# --------------------------------------------------------------------------- #

def dnsrecon_cmd(type, jid, nick, text):
    text = text.strip()
    if not text:
        send_msg(type, jid, nick, L('Usage: dnsrecon <filter> <domain>\nExample: dnsrecon xmpp,call ubunix.pro\nFilters: xmpp, call, sip, mail, all', '%s/%s' % (jid, nick)))
        return

    parts = text.split()
    if len(parts) < 2:
        send_msg(type, jid, nick, L('Usage: dnsrecon <filter> <domain>\nExample: dnsrecon xmpp,call ubunix.pro', '%s/%s' % (jid, nick)))
        return

    filter_str = parts[0]
    domain = ' '.join(parts[1:])

    if not _is_valid_domain(domain):
        send_msg(type, jid, nick, L('Invalid domain: %s', '%s/%s' % (jid, nick)) % domain)
        return

    has_dnsrecon, install_cmd = _check_dnsrecon()
    if not has_dnsrecon:
        if install_cmd:
            msg = L('dnsrecon is not installed on this server.\nTo use this plugin, install it with: sudo %s', '%s/%s' % (jid, nick)) % install_cmd
        else:
            msg = L('dnsrecon is not installed on this server.\nPlease install it using your package manager.', '%s/%s' % (jid, nick))
        send_msg(type, jid, nick, msg)
        return

    protocols = _parse_filters(filter_str)
    if not protocols:
        send_msg(type, jid, nick, L('No valid protocols found in filter: %s\nAvailable: xmpp, call, sip, mail, all', '%s/%s' % (jid, nick)) % filter_str)
        return

    records, error = _run_dnsrecon(domain)
    if error:
        send_msg(type, jid, nick, error)
        return

    filtered = _filter_records(records, protocols)

    if filtered:
        output = _format_records(filtered)
        msg = L('Found SRV records for %s (filter: %s):\n%s', '%s/%s' % (jid, nick)) % (domain, filter_str, output)
    else:
        msg = L('No SRV records found for %s with filter: %s', '%s/%s' % (jid, nick)) % (domain, filter_str)

    send_msg(type, jid, nick, msg)

# --------------------------------------------------------------------------- #
# REGISTER COMMANDS                                                           #
# --------------------------------------------------------------------------- #

execute = [
    (6, 'dnsrecon', dnsrecon_cmd, 2, 'DNS SRV reconnaissance.\ndnsrecon <filter> <domain>\nFilters: xmpp, call, sip, mail, all\nExample: dnsrecon xmpp,call ubunix.pro'),
]
