#!/usr/bin/env python3

import os
import subprocess
import argparse
import sys
import re
import socket
import ipaddress
import threading
import time
import signal
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
import json
import csv
import logging

# Global configuration
MAX_THREADS = 10
REACHABILITY_THREADS = 100
COMMON_PORTS = "21,22,23,25,53,80,110,111,135,139,143,443,993,995,1723,3306,3389,5432,5900,8080"
TCPDUMP_DURATION = 300  # 5 minutes default
REACHABILITY_TIMEOUT = 3  # seconds for each connectivity test
REACHABILITY_PORTS = [22, 23, 25, 53, 80, 135, 139, 443, 445, 993, 995, 3389, 5985, 5986, 8080, 8443]

# Global logging variables
csv_logger = None
verbose_logger = None

class Colors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'

def setup_logging(base_dir):
    """Setup comprehensive logging for TrashPanda operations."""
    global csv_logger, verbose_logger
    
    logs_dir = os.path.join(base_dir, "logs")
    timestamp = time.strftime("%Y%m%d_%H%M%S")
    
    # Setup CSV command logging
    csv_file = os.path.join(logs_dir, f"trashpanda_commands_{timestamp}.csv")
    csv_fieldnames = ['start_time', 'end_time', 'hostname', 'command', 'exit_code', 'duration_seconds']
    
    with open(csv_file, 'w', newline='') as f:
        writer = csv.DictWriter(f, fieldnames=csv_fieldnames)
        writer.writeheader()
    
    # Setup verbose console logging
    verbose_file = os.path.join(logs_dir, f"trashpanda_verbose_{timestamp}.log")
    verbose_logger = logging.getLogger('trashpanda_verbose')
    verbose_logger.setLevel(logging.DEBUG)
    
    # Create file handler for verbose log
    file_handler = logging.FileHandler(verbose_file)
    file_handler.setLevel(logging.DEBUG)
    
    # Create console handler that captures all output
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(logging.DEBUG)
    
    # Create formatter
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(formatter)
    
    verbose_logger.addHandler(file_handler)
    verbose_logger.propagate = False  # Prevent duplicate console output
    
    print(f"{Colors.OKGREEN}[+] Logging initialized:{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] CSV commands log: {csv_file}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Verbose log: {verbose_file}{Colors.ENDC}")
    
    return csv_file, verbose_file

def log_command(command, start_time=None, end_time=None, exit_code=None, hostname=None):
    """Log command execution to CSV file."""
    global csv_logger
    
    if not hasattr(log_command, 'csv_file'):
        return  # Logging not initialized
    
    try:
        duration = (end_time - start_time) if start_time and end_time else None
        
        with open(log_command.csv_file, 'a', newline='') as f:
            writer = csv.DictWriter(f, fieldnames=['start_time', 'end_time', 'hostname', 'command', 'exit_code', 'duration_seconds'])
            writer.writerow({
                'start_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time)) if start_time else '',
                'end_time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time)) if end_time else '',
                'hostname': hostname or socket.gethostname(),
                'command': command,
                'exit_code': exit_code,
                'duration_seconds': f"{duration:.2f}" if duration else ''
            })
    except Exception as e:
        print(f"{Colors.WARNING}[!] Logging error: {e}{Colors.ENDC}")

def log_verbose(message, level='INFO'):
    """Log message to verbose log file."""
    global verbose_logger
    
    if verbose_logger:
        if level == 'DEBUG':
            verbose_logger.debug(message)
        elif level == 'WARNING':
            verbose_logger.warning(message)
        elif level == 'ERROR':
            verbose_logger.error(message)
        else:
            verbose_logger.info(message)

class LoggingPrint:
    """Wrapper to capture and log all print statements."""
    def __init__(self, original_stdout):
        self.original_stdout = original_stdout
    
    def write(self, message):
        # Write to original stdout
        self.original_stdout.write(message)
        # Log to verbose log (strip ANSI colors for log file)
        if message.strip():
            clean_message = re.sub(r'\033\[[0-9;]*m', '', message.strip())
            log_verbose(clean_message)
    
    def flush(self):
        self.original_stdout.flush()

def is_public_ip(ip_str):
    """Check if an IP address is public (not private/reserved)."""
    try:
        ip = ipaddress.ip_address(ip_str)
        return not (ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_multicast or ip.is_reserved)
    except ValueError:
        return False

def filter_public_ips_from_targets(targets):
    """Filter out public IPs from target list and warn user."""
    filtered_targets = []
    public_ips = []
    
    for target in targets:
        # Handle CIDR ranges
        if '/' in target:
            try:
                network = ipaddress.ip_network(target, strict=False)
                if any(is_public_ip(str(ip)) for ip in list(network)[:5]):  # Check first 5 IPs
                    public_ips.append(target)
                    print(f"{Colors.WARNING}[!] Skipping public network range: {target}{Colors.ENDC}")
                else:
                    filtered_targets.append(target)
            except ValueError:
                filtered_targets.append(target)  # Keep if not valid CIDR
        # Handle IP ranges
        elif '-' in target and not target.count('.') > 3:
            try:
                base_ip = target.split('-')[0]
                if is_public_ip(base_ip):
                    public_ips.append(target)
                    print(f"{Colors.WARNING}[!] Skipping public IP range: {target}{Colors.ENDC}")
                else:
                    filtered_targets.append(target)
            except:
                filtered_targets.append(target)  # Keep if parsing fails
        # Handle single IPs
        else:
            try:
                # Try to parse as IP first
                ip = ipaddress.ip_address(target)
                if is_public_ip(str(ip)):
                    public_ips.append(target)
                    print(f"{Colors.WARNING}[!] Skipping public IP: {target}{Colors.ENDC}")
                else:
                    filtered_targets.append(target)
            except ValueError:
                # Not an IP, probably hostname - keep it
                filtered_targets.append(target)
    
    if public_ips:
        print(f"{Colors.WARNING}[!] Filtered out {len(public_ips)} public IP targets for safety{Colors.ENDC}")
        response = input(f"{Colors.WARNING}Continue with remaining {len(filtered_targets)} targets? [y/N]: {Colors.ENDC}")
        if response.lower() != 'y':
            print(f"{Colors.FAIL}[!] Scan aborted by user{Colors.ENDC}")
            sys.exit(0)
    
    return filtered_targets

def print_banner():
    banner = f"""
{Colors.HEADER}{Colors.BOLD}
████████╗██████╗  █████╗ ███████╗██╗  ██╗██████╗  █████╗ ███╗   ██╗██████╗  █████╗ 
╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██║  ██║██╔══██╗██╔══██╗████╗  ██║██╔══██╗██╔══██╗
   ██║   ██████╔╝███████║███████╗███████║██████╔╝███████║██╔██╗ ██║██║  ██║███████║
   ██║   ██╔══██╗██╔══██║╚════██║██╔══██║██╔═══╝ ██╔══██║██║╚██╗██║██║  ██║██╔══██║
   ██║   ██║  ██║██║  ██║███████║██║  ██║██║     ██║  ██║██║ ╚████║██████╔╝██║  ██║
   ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝  ╚═╝╚═╝  ╚═══╝╚═════╝ ╚═╝  ╚═╝
                                                                                      
                🦝 TrashPanda - Network Enumeration Tool v2.4 🦝
                   Professional Penetration Testing Framework
{Colors.ENDC}
    """
    print(banner)

def create_pentest_structure(base_name="/root/dmealey"):
    """Create a comprehensive penetration testing directory structure."""
    
    # Main engagement directory
    base_dir = os.path.abspath(base_name)
    
    # Primary directories
    main_dirs = {
        "tools": "Downloaded/compiled tools and scripts",
        "scans": "All scan results organized by type",
        "logs": "Execution logs and debug output", 
        "loot": "Extracted credentials, hashes, and sensitive data",
        "payloads": "Custom payloads and exploit code",
        "targets": "Target lists and reconnaissance data",
        "screenshots": "Visual evidence and GUI captures",
        "reports": "Draft reports and documentation",
        "notes": "Manual notes and observations",
        "exploits": "Working exploits and proof-of-concepts",
        "wordlists": "Custom and downloaded wordlists",
        "pcaps": "Network captures and traffic analysis"
    }
    
    # Scan subdirectories
    scan_subdirs = {
        "nmap": "Network discovery and port scanning",
        "dns": "DNS enumeration and zone transfers", 
        "snmp": "SNMP enumeration and community strings",
        "smb": "SMB/NetBIOS enumeration and shares",
        "web": "Web application scanning and enumeration",
        "ssl": "SSL/TLS certificate and cipher analysis",
        "vulns": "Vulnerability scanning and NSE scripts",
        "ldap": "LDAP enumeration and directory services",
        "ftp": "FTP enumeration and anonymous access",
        "ssh": "SSH enumeration and key analysis",
        "databases": "Database enumeration (MySQL, MSSQL, etc)",
        "custom": "Custom and manual scans",
        "reachability": "Network reachability test results"
    }
    
    # Loot subdirectories  
    loot_subdirs = {
        "credentials": "Usernames, passwords, and authentication data",
        "hashes": "Password hashes and cracking results",
        "keys": "SSH keys, certificates, and crypto material",
        "configs": "Configuration files and sensitive data",
        "databases": "Extracted database contents",
        "files": "Interesting files and documents"
    }
    
    print(f"{Colors.OKGREEN}[+] Creating penetration testing structure: {base_dir}{Colors.ENDC}")
    
    # Create main directories
    for dir_name, description in main_dirs.items():
        dir_path = os.path.join(base_dir, dir_name)
        Path(dir_path).mkdir(parents=True, exist_ok=True)
        
        # Create README files for documentation
        readme_path = os.path.join(dir_path, "README.md")
        if not os.path.exists(readme_path):
            with open(readme_path, 'w') as f:
                f.write(f"# {dir_name.upper()}\n\n")
                f.write(f"{description}\n\n")
                f.write(f"Created by TrashPanda on {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
    
    # Create scan subdirectories
    scans_dir = os.path.join(base_dir, "scans")
    for subdir, description in scan_subdirs.items():
        subdir_path = os.path.join(scans_dir, subdir)
        Path(subdir_path).mkdir(parents=True, exist_ok=True)
        
        readme_path = os.path.join(subdir_path, "README.md")
        if not os.path.exists(readme_path):
            with open(readme_path, 'w') as f:
                f.write(f"# {subdir.upper()} SCANS\n\n")
                f.write(f"{description}\n\n")
    
    # Create loot subdirectories
    loot_dir = os.path.join(base_dir, "loot")
    for subdir, description in loot_subdirs.items():
        subdir_path = os.path.join(loot_dir, subdir)
        Path(subdir_path).mkdir(parents=True, exist_ok=True)
        
        readme_path = os.path.join(subdir_path, "README.md")
        if not os.path.exists(readme_path):
            with open(readme_path, 'w') as f:
                f.write(f"# {subdir.upper()}\n\n")
                f.write(f"{description}\n\n")
    
    # Create engagement log
    engagement_log = os.path.join(base_dir, "logs", "engagement.log")
    with open(engagement_log, 'w') as f:
        f.write(f"TrashPanda Engagement Log\n")
        f.write(f"========================\n")
        f.write(f"Started: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"Operator: dmealey\n")
        f.write(f"Tool: TrashPanda v2.4\n\n")
    
    # Create initial target file
    target_template = os.path.join(base_dir, "targets", "targets.txt")
    if not os.path.exists(target_template):
        with open(target_template, 'w') as f:
            f.write("# Target List\n")
            f.write("# Add IPs, ranges, or hostnames (one per line)\n")
            f.write("# Examples:\n")
            f.write("# 192.168.1.1\n")
            f.write("# 192.168.1.0/24\n")
            f.write("# 192.168.1.1-50\n")
            f.write("# target.domain.com\n\n")
    
    # Create manual commands file
    manual_commands = os.path.join(base_dir, "scans", "_manual_commands.txt")
    with open(manual_commands, 'w') as f:
        f.write("# Manual Commands for Further Enumeration\n")
        f.write("# ======================================\n")
        f.write(f"# Generated by TrashPanda on {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
    
    print(f"{Colors.OKGREEN}[+] Penetration testing structure created successfully{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Add targets to: {target_template}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Engagement log: {engagement_log}{Colors.ENDC}")
    
    return base_dir

def start_tcpdump(base_dir, duration=TCPDUMP_DURATION, interface="any"):
    """Start tcpdump for network capture, but only if not already running."""
    pcap_dir = os.path.join(base_dir, "pcaps")
    
    # Check if capture files already exist
    try:
        import glob
        existing_captures = glob.glob(os.path.join(pcap_dir, "capture_*.pcap"))
        if existing_captures:
            print(f"{Colors.WARNING}[!] Found {len(existing_captures)} existing capture file(s):{Colors.ENDC}")
            for capture in existing_captures[-3:]:  # Show last 3 files
                file_size = os.path.getsize(capture) / (1024*1024)  # MB
                mod_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(os.path.getmtime(capture)))
                print(f"{Colors.WARNING}    - {os.path.basename(capture)} ({file_size:.1f}MB, {mod_time}){Colors.ENDC}")
            if len(existing_captures) > 3:
                print(f"{Colors.WARNING}    ... and {len(existing_captures)-3} more{Colors.ENDC}")
            print(f"{Colors.WARNING}[!] Skipping new capture to avoid overwriting existing data{Colors.ENDC}")
            return None
    except Exception as e:
        print(f"{Colors.WARNING}[!] Error checking for existing captures: {e}{Colors.ENDC}")
        pass  # Continue with capture if check fails
    
    timestamp = time.strftime("%Y%m%d_%H%M%S")
    pcap_file = os.path.join(pcap_dir, f"capture_{timestamp}.pcap")
    
    print(f"{Colors.OKBLUE}[*] Starting tcpdump capture for {duration} seconds...{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Capture file: {pcap_file}{Colors.ENDC}")
    
    # Build tcpdump command
    tcpdump_cmd = [
        "sudo", "tcpdump", 
        "-i", interface,
        "-U",  # Unbuffered output
        "-w", pcap_file,
        "-s", "65535",  # Capture full packets
        "not", "port", "22"  # Exclude SSH traffic to reduce noise
    ]
    
    try:
        # Start tcpdump process
        tcpdump_process = subprocess.Popen(
            tcpdump_cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        
        # Log the process
        log_file = os.path.join(base_dir, "logs", "tcpdump.log")
        with open(log_file, 'a') as f:
            f.write(f"TCPDump started at {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"Command: {' '.join(tcpdump_cmd)}\n")
            f.write(f"PID: {tcpdump_process.pid}\n")
            f.write(f"Duration: {duration} seconds\n")
            f.write(f"Output: {pcap_file}\n\n")
        
        # Return process info for later termination
        return {
            'process': tcpdump_process,
            'start_time': time.time(),
            'duration': duration,
            'pcap_file': pcap_file,
            'log_file': log_file
        }
        
    except Exception as e:
        print(f"{Colors.FAIL}[!] Failed to start tcpdump: {e}{Colors.ENDC}")
        print(f"{Colors.WARNING}[!] Make sure you have sudo privileges{Colors.ENDC}")
        return None

def stop_tcpdump(tcpdump_info):
    """Stop tcpdump and log results."""
    if not tcpdump_info:
        return
    
    try:
        process = tcpdump_info['process']
        
        # Terminate gracefully
        process.terminate()
        
        # Wait for termination with timeout
        try:
            process.wait(timeout=5)
        except subprocess.TimeoutExpired:
            print(f"{Colors.WARNING}[!] TCPDump didn't terminate gracefully, killing...{Colors.ENDC}")
            process.kill()
            process.wait()
        
        # Log completion
        end_time = time.time()
        actual_duration = end_time - tcpdump_info['start_time']
        
        with open(tcpdump_info['log_file'], 'a') as f:
            f.write(f"TCPDump stopped at {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"Actual duration: {actual_duration:.2f} seconds\n")
            f.write(f"Exit code: {process.returncode}\n")
        
        # Check file size
        pcap_file = tcpdump_info['pcap_file']
        if os.path.exists(pcap_file):
            file_size = os.path.getsize(pcap_file)
            print(f"{Colors.OKGREEN}[+] TCPDump capture completed{Colors.ENDC}")
            print(f"{Colors.OKGREEN}[+] Capture file: {pcap_file} ({file_size:,} bytes){Colors.ENDC}")
        else:
            print(f"{Colors.WARNING}[!] TCPDump capture file not found{Colors.ENDC}")
            
    except Exception as e:
        print(f"{Colors.FAIL}[!] Error stopping tcpdump: {e}{Colors.ENDC}")

def run_command(command, output_file=None, debug=False, stealth=False):
    """Run a command with comprehensive logging."""
    start_time = time.time()
    hostname = socket.gethostname()
    
    if debug:
        print(f"{Colors.OKCYAN}[DEBUG] Running: {command}{Colors.ENDC}")
    
    log_verbose(f"COMMAND START: {command}", 'INFO')
    
    try:
        # Increase timeout for stealth mode (slower scans)
        timeout = 7200 if stealth else 3600
        result = subprocess.run(command, shell=True, text=True, capture_output=True, timeout=timeout)
        
        end_time = time.time()
        duration = end_time - start_time
        
        # Log command to CSV
        log_command(command, start_time, end_time, result.returncode, hostname)
        
        # Log to verbose log
        log_verbose(f"COMMAND END: {command} (exit_code: {result.returncode}, duration: {duration:.2f}s)", 'INFO')
        
        if result.stdout:
            log_verbose(f"STDOUT: {result.stdout[:1000]}{'...' if len(result.stdout) > 1000 else ''}", 'DEBUG')
        if result.stderr:
            log_verbose(f"STDERR: {result.stderr[:1000]}{'...' if len(result.stderr) > 1000 else ''}", 'WARNING')
        
        if output_file:
            with open(output_file, 'w') as f:
                f.write(f"Command: {command}\n")
                f.write(f"Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))}\n")
                f.write(f"Duration: {duration:.2f} seconds\n")
                f.write(f"Return Code: {result.returncode}\n")
                f.write(f"STDOUT:\n{result.stdout}\n")
                f.write(f"STDERR:\n{result.stderr}\n")
        
        if debug:
            print(f"{Colors.OKCYAN}[DEBUG] Return code: {result.returncode}, Duration: {duration:.2f}s{Colors.ENDC}")
            if result.stdout:
                print(f"{Colors.OKCYAN}[DEBUG] STDOUT: {result.stdout[:500]}...{Colors.ENDC}")
        
        return result
    except subprocess.TimeoutExpired:
        end_time = time.time()
        error_msg = f"Command timed out: {command}"
        print(f"{Colors.WARNING}[!] {error_msg}{Colors.ENDC}")
        log_command(command, start_time, end_time, -1, hostname)  # -1 for timeout
        log_verbose(f"TIMEOUT: {error_msg}", 'ERROR')
        return None
    except Exception as e:
        end_time = time.time()
        error_msg = f"Error running command: {e}"
        print(f"{Colors.FAIL}[!] {error_msg}{Colors.ENDC}")
        log_command(command, start_time, end_time, -2, hostname)  # -2 for error
        log_verbose(f"ERROR: {error_msg}", 'ERROR')
        return None

def add_manual_command(base_dir, service_name, commands):
    """Add manual commands to the manual commands file."""
    manual_file = os.path.join(base_dir, "scans", "_manual_commands.txt")
    
    with open(manual_file, 'a') as f:
        f.write(f"\n[*] {service_name}\n")
        f.write("=" * (len(service_name) + 4) + "\n\n")
        
        if isinstance(commands, str):
            commands = [commands]
        
        for cmd in commands:
            f.write(f"    {cmd}\n")
        f.write("\n")

def parse_targets(target_input):
    """Parse various target formats (IPs, ranges, CIDRs, hostnames)."""
    targets = []
    
    if os.path.isfile(target_input):
        with open(target_input, 'r') as f:
            lines = f.read().splitlines()
    else:
        lines = [target_input]
    
    for line in lines:
        line = line.strip()
        if not line or line.startswith('#'):
            continue
            
        try:
            # Check if it's a CIDR range
            if '/' in line:
                network = ipaddress.ip_network(line, strict=False)
                targets.extend([str(ip) for ip in network.hosts()])
            # Check if it's an IP range (e.g., 192.168.1.1-50)
            elif '-' in line and not line.count('-') > 1:
                ip_parts = line.split('-')
                if len(ip_parts) == 2:
                    base_ip = ip_parts[0]
                    end_range = ip_parts[1]
                    
                    # Handle cases like 192.168.1.1-50
                    if '.' in base_ip and '.' not in end_range:
                        base_parts = base_ip.split('.')
                        start_num = int(base_parts[3])
                        end_num = int(end_range)
                        for i in range(start_num, end_num + 1):
                            targets.append(f"{'.'.join(base_parts[:3])}.{i}")
                    else:
                        targets.append(line)  # Add as-is if format not recognized
            else:
                # Single IP or hostname
                targets.append(line)
        except Exception as e:
            print(f"{Colors.WARNING}[!] Error parsing target {line}: {e}{Colors.ENDC}")
            targets.append(line)  # Add as-is and let tools handle it
    
    return list(set(targets))  # Remove duplicates

def classify_network_ranges(targets):
    """Intelligently classify and group IP targets into appropriate network ranges for scanning."""
    rfc1918_networks = {
        'class_a': set(),      # 10.0.0.0/8
        'class_b': set(),      # 172.16.0.0/12
        'class_c': set(),      # 192.168.0.0/16
    }
    
    non_rfc1918_ips = []
    hostnames = []
    
    for target in targets:
        try:
            ip_obj = ipaddress.ip_address(target)
            
            if ip_obj.is_private:
                ip_str = str(ip_obj)
                
                # Class A: 10.0.0.0/8
                if ip_str.startswith('10.'):
                    octets = ip_str.split('.')
                    # Group by /16 networks within Class A
                    network_prefix = f"{octets[0]}.{octets[1]}"
                    rfc1918_networks['class_a'].add(f"{network_prefix}.0.0/16")
                
                # Class B: 172.16.0.0/12 (172.16.0.0 to 172.31.255.255)
                elif ip_str.startswith('172.'):
                    octets = ip_str.split('.')
                    second_octet = int(octets[1])
                    if 16 <= second_octet <= 31:
                        network_prefix = f"{octets[0]}.{octets[1]}"
                        rfc1918_networks['class_b'].add(f"{network_prefix}.0.0/16")
                
                # Class C: 192.168.0.0/16
                elif ip_str.startswith('192.168.'):
                    octets = ip_str.split('.')
                    network_prefix = f"{octets[0]}.{octets[1]}.{octets[2]}"
                    rfc1918_networks['class_c'].add(f"{network_prefix}.0/24")
            else:
                non_rfc1918_ips.append(target)
                
        except ValueError:
            # Not an IP address, likely a hostname
            hostnames.append(target)
    
    return rfc1918_networks, non_rfc1918_ips, hostnames

def discover_services_from_nmap(base_dir):
    """Parse nmap results to discover services for enhanced enumeration."""
    services = {}
    nmap_dir = os.path.join(base_dir, "scans", "nmap")
    
    # Parse nmap gnmap files for services
    for nmap_file in Path(nmap_dir).glob("*.gnmap"):
        try:
            with open(nmap_file, 'r') as f:
                for line in f:
                    if "open" in line:
                        parts = line.split()
                        if len(parts) > 1:
                            ip = parts[1]
                            if ip not in services:
                                services[ip] = []
                            
                            # Extract port info
                            port_info = [p for p in parts if "open" in p]
                            for port_data in port_info:
                                port_match = re.search(r'(\d+)/(tcp|udp)', port_data)
                                service_match = re.search(r'//(.+?)/', port_data)
                                
                                if port_match:
                                    port = port_match.group(1)
                                    protocol = port_match.group(2)
                                    service = service_match.group(1) if service_match else "unknown"
                                    
                                    service_info = {
                                        'port': port,
                                        'protocol': protocol,
                                        'service': service,
                                        'ssl': 'ssl' in port_data or 'https' in port_data
                                    }
                                    
                                    if service_info not in services[ip]:
                                        services[ip].append(service_info)
        except Exception as e:
            print(f"{Colors.WARNING}[!] Error parsing {nmap_file}: {e}{Colors.ENDC}")
    
    return services

# Network Reachability Testing Functions
def test_icmp_connectivity(target, timeout=REACHABILITY_TIMEOUT):
    """Test ICMP connectivity using ping."""
    try:
        if sys.platform.startswith('win'):
            result = subprocess.run(['ping', '-n', '1', '-w', str(timeout*1000), target], 
                                  capture_output=True, text=True, timeout=timeout+2)
        else:
            result = subprocess.run(['ping', '-c', '1', '-W', str(timeout), target], 
                                  capture_output=True, text=True, timeout=timeout+2)
        return result.returncode == 0
    except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
        return False

def test_dns_connectivity(dns_server, timeout=3):
    """Test DNS server connectivity with actual DNS query."""
    try:
        # Test UDP DNS first with a real DNS query
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(timeout)
        
        # DNS query for google.com (more realistic than generic UDP test)
        dns_query = b'\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01'
        sock.sendto(dns_query, (dns_server, 53))
        
        # Wait for response
        response, addr = sock.recvfrom(1024)
        sock.close()
        
        # Check if we got a valid DNS response
        if len(response) > 12:  # Minimum DNS response size
            return True
            
    except (socket.error, socket.timeout):
        pass
    
    # Fallback: test TCP connectivity to port 53
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        result = sock.connect_ex((dns_server, 53))
        sock.close()
        return result == 0
    except (socket.error, socket.timeout):
        return False

def test_tcp_connectivity(target, port, timeout=REACHABILITY_TIMEOUT):
    """Test TCP connectivity to specific port."""
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        result = sock.connect_ex((target, port))
        sock.close()
        return result == 0
    except (socket.error, socket.timeout):
        return False

def test_udp_connectivity(target, port=53, timeout=REACHABILITY_TIMEOUT):
    """Test UDP connectivity (primarily DNS)."""
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(timeout)
        # Send a simple DNS query for connectivity test
        if port == 53:
            # Simple DNS query packet for google.com
            dns_query = b'\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01'
            sock.sendto(dns_query, (target, port))
            data, addr = sock.recvfrom(1024)
            sock.close()
            return True
        else:
            # For other UDP ports, just try to send a packet
            sock.sendto(b'test', (target, port))
            sock.close()
            return True
    except (socket.error, socket.timeout):
        return False

def test_comprehensive_connectivity(target, debug=False):
    """Run comprehensive connectivity tests for a single target."""
    results = {
        'target': target,
        'icmp': False,
        'tcp_ports': {},
        'udp_dns': False,
        'reachable': False,
        'response_time': 0,
        'best_ports': []
    }
    
    start_time = time.time()
    
    # Test ICMP first
    if debug:
        print(f"{Colors.OKCYAN}[DEBUG] Testing ICMP to {target}{Colors.ENDC}")
    
    results['icmp'] = test_icmp_connectivity(target)
    
    # Test common TCP ports
    tcp_results = {}
    for port in REACHABILITY_PORTS:
        if debug:
            print(f"{Colors.OKCYAN}[DEBUG] Testing TCP {target}:{port}{Colors.ENDC}")
        tcp_results[port] = test_tcp_connectivity(target, port)
        if tcp_results[port]:
            results['best_ports'].append(port)
    
    results['tcp_ports'] = tcp_results
    
    # Test UDP DNS
    if debug:
        print(f"{Colors.OKCYAN}[DEBUG] Testing UDP DNS to {target}{Colors.ENDC}")
    results['udp_dns'] = test_udp_connectivity(target, 53)
    
    # Determine overall reachability
    results['reachable'] = (results['icmp'] or 
                           any(tcp_results.values()) or 
                           results['udp_dns'])
    
    results['response_time'] = round(time.time() - start_time, 2)
    
    return results

def analyze_network_infrastructure(targets, debug=False):
    """Analyze targets and identify key network infrastructure to test."""
    infrastructure = {
        'subnets': {},  # Changed from 'enclaves' to 'subnets'
        'individual_hosts': [],
        'dns_servers': set(),
        'analysis_summary': {},
        'total_original_targets': 0  # Track original scope
    }
    
    print(f"{Colors.OKGREEN}[+] Analyzing Target Infrastructure{Colors.ENDC}")
    
    # Count original targets for reduction metrics
    total_original = 0
    for target in targets:
        try:
            if '/' in target:
                network = ipaddress.ip_network(target, strict=False)
                total_original += network.num_addresses - 2  # Exclude network and broadcast
            else:
                total_original += 1
        except ValueError:
            total_original += 1
    
    infrastructure['total_original_targets'] = total_original
    print(f"{Colors.OKBLUE}[*] Original scope: ~{total_original:,} potential targets{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Performing intelligent analysis to avoid brute force scanning...{Colors.ENDC}")
    
    subnet_count = 0
    
    for target in targets:
        try:
            # Try to parse as IP address
            ip_obj = ipaddress.ip_address(target)
            
            # Determine which subnet/network this belongs to
            if ip_obj.is_private:
                octets = str(ip_obj).split('.')
                
                # Group by /24 networks for now (can be adjusted)
                if octets[0] == '10':
                    # For 10.x networks, group by /16
                    subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
                elif octets[0] == '172' and 16 <= int(octets[1]) <= 31:
                    # For 172.16-31 networks, group by /16  
                    subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
                elif octets[0] == '192' and octets[1] == '168':
                    # For 192.168 networks, group by /24
                    subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
                else:
                    # Other private ranges, group by /24
                    subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
            else:
                # Public IP - each gets its own "subnet"
                subnet_key = f"public_{str(ip_obj)}"
            
            # Initialize subnet if not seen before
            if subnet_key not in infrastructure['subnets']:
                subnet_count += 1
                infrastructure['subnets'][subnet_key] = {
                    'network': subnet_key,
                    'targets': [],
                    'sample_targets': [],
                    'key_infrastructure': [],
                    'subnet_id': subnet_count
                }
                
                # Add key infrastructure for this subnet
                try:
                    network = ipaddress.ip_network(subnet_key, strict=False)
                    if network.is_private and network.num_addresses > 2:
                        # Add potential gateways and key servers
                        base_ip = str(network.network_address).split('.')
                        key_ips = [
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.1",      # Common gateway
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.254",    # Alt gateway
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.10",     # Common server IP
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.53",     # DNS server
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.100",    # Common server range
                        ]
                        
                        # Only add IPs that are actually in the network
                        for key_ip in key_ips:
                            try:
                                if ipaddress.ip_address(key_ip) in network:
                                    infrastructure['subnets'][subnet_key]['key_infrastructure'].append(key_ip)
                            except ValueError:
                                pass
                except ValueError:
                    pass
            
            # Add target to subnet
            infrastructure['subnets'][subnet_key]['targets'].append(str(ip_obj))
            
        except ValueError:
            # Handle CIDR ranges
            if '/' in target:
                try:
                    network = ipaddress.ip_network(target, strict=False)
                    subnet_key = str(network)
                    
                    if subnet_key not in infrastructure['subnets']:
                        subnet_count += 1
                        infrastructure['subnets'][subnet_key] = {
                            'network': subnet_key,
                            'targets': [],
                            'sample_targets': [],
                            'key_infrastructure': [],
                            'subnet_id': subnet_count,
                            'is_full_network': True
                        }
                    
                    # For full networks, we'll sample them intelligently (not all hosts)
                    all_hosts = list(network.hosts())
                    if len(all_hosts) > 50:
                        # Large network - take strategic samples
                        sample_hosts = all_hosts[:10] + all_hosts[-10:] + all_hosts[len(all_hosts)//2:len(all_hosts)//2+10]
                        infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in sample_hosts[:30]]
                    else:
                        # Small network - include all
                        infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in all_hosts]
                    
                except ValueError:
                    infrastructure['individual_hosts'].append(target)
            else:
                # Hostname or IP range
                infrastructure['individual_hosts'].append(target)
    
    # Generate sample targets for each subnet (for testing) - SMALL samples only
    for subnet_key, subnet_data in infrastructure['subnets'].items():
        targets_in_subnet = subnet_data['targets']
        
        if len(targets_in_subnet) <= 5:
            # Very small subnet - test all targets
            subnet_data['sample_targets'] = targets_in_subnet.copy()
        else:
            # Larger subnet - SMALL intelligent sampling (max 8 targets)
            sample_size = min(8, max(3, len(targets_in_subnet) // 20))  # Much smaller sample
            
            # Always include first, last, and some middle targets
            samples = []
            samples.append(targets_in_subnet[0])  # First
            if len(targets_in_subnet) > 1:
                samples.append(targets_in_subnet[-1])  # Last
            
            # Add evenly distributed samples
            remaining = sample_size - len(samples)
            if remaining > 0 and len(targets_in_subnet) > 2:
                step = len(targets_in_subnet) // (remaining + 1)
                for i in range(remaining):
                    idx = (i + 1) * step
                    if idx < len(targets_in_subnet):
                        samples.append(targets_in_subnet[idx])
            
            subnet_data['sample_targets'] = list(set(samples))
        
        # Add key infrastructure to samples
        subnet_data['sample_targets'].extend(subnet_data['key_infrastructure'])
        subnet_data['sample_targets'] = list(set(subnet_data['sample_targets']))
    
    # Discover system DNS servers
    try:
        with open('/etc/resolv.conf', 'r') as f:
            for line in f:
                if line.startswith('nameserver'):
                    dns_ip = line.split()[1]
                    try:
                        ipaddress.ip_address(dns_ip)
                        infrastructure['dns_servers'].add(dns_ip)
                    except ValueError:
                        pass
    except FileNotFoundError:
        pass
    
    # Add common external DNS if none found
    if not infrastructure['dns_servers']:
        infrastructure['dns_servers'].update(['8.8.8.8', '1.1.1.1'])
    
    # Generate summary
    total_samples = sum(len(e['sample_targets']) for e in infrastructure['subnets'].values())
    infrastructure['analysis_summary'] = {
        'total_subnets': len(infrastructure['subnets']),
        'total_targets': sum(len(e['targets']) for e in infrastructure['subnets'].values()),
        'total_samples': total_samples,
        'individual_hosts': len(infrastructure['individual_hosts']),
        'dns_servers': len(infrastructure['dns_servers']),
        'reduction_ratio': total_original / max(total_samples, 1)
    }
    
    # Print detailed analysis
    print(f"{Colors.OKGREEN}[+] Infrastructure Analysis Complete{Colors.ENDC}")
    reduction_pct = (1 - total_samples / total_original) * 100
    print(f"{Colors.OKGREEN}[+] Smart sampling: {total_samples:,} tests vs {total_original:,} original ({reduction_pct:.1f}% reduction){Colors.ENDC}")
    
    print(f"{Colors.OKBLUE}[*] Identified {len(infrastructure['subnets'])} network subnets:{Colors.ENDC}")
    
    for subnet_key, subnet_data in infrastructure['subnets'].items():
        subnet_id = subnet_data['subnet_id']
        target_count = len(subnet_data['targets'])
        sample_count = len(subnet_data['sample_targets'])
        
        print(f"{Colors.OKCYAN}    [{subnet_id}] {subnet_key}: {target_count} targets → {sample_count} samples{Colors.ENDC}")
    
    return infrastructure

def test_subnet_reachability(infrastructure, debug=False, timeout=3):
    """Test each subnet independently to determine reachability."""
    results = {
        'reachable_subnets': {},
        'unreachable_subnets': {},
        'dns_servers': {'reachable': [], 'unreachable': []},
        'individual_hosts': {'reachable': [], 'unreachable': []},
        'testing_summary': {}
    }
    
    print(f"\n{Colors.OKGREEN}[+] Testing Subnet Reachability{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Strategy: ANY response from subnet = entire subnet reachable{Colors.ENDC}")
    
    # Test DNS servers first
    dns_servers_list = list(infrastructure['dns_servers'])
    total_dns = len(dns_servers_list)
    
    if total_dns > 0:
        print(f"{Colors.OKBLUE}[*] Step 1: Testing DNS connectivity ({total_dns} servers){Colors.ENDC}")
        
        for dns_idx, dns_server in enumerate(dns_servers_list, 1):
            print(f"{Colors.OKCYAN}    [{dns_idx}/{total_dns}] Testing DNS server {dns_server}...{Colors.ENDC}", end=' ')
            
            is_reachable = test_dns_connectivity(dns_server, timeout)
            
            if is_reachable:
                results['dns_servers']['reachable'].append(dns_server)
                print(f"{Colors.OKGREEN}✓ REACHABLE{Colors.ENDC}")
            else:
                results['dns_servers']['unreachable'].append(dns_server)
                print(f"{Colors.FAIL}✗ UNREACHABLE{Colors.ENDC}")
        
        print(f"{Colors.OKBLUE}[*] DNS Summary: {len(results['dns_servers']['reachable'])}/{total_dns} reachable{Colors.ENDC}")
    
    # Test each subnet - handle both 'subnets' and 'enclaves' keys for compatibility
    subnet_dict = infrastructure.get('subnets', infrastructure.get('enclaves', {}))
    total_subnets = len(subnet_dict)
    
    print(f"\n{Colors.OKBLUE}[*] Step 2: Testing Network Subnet Reachability ({total_subnets} subnets){Colors.ENDC}")
    
    subnet_counter = 0
    for subnet_key, subnet_data in subnet_dict.items():
        subnet_counter += 1
        subnet_id = subnet_data['subnet_id']
        sample_targets = subnet_data['sample_targets']
        
        print(f"\n{Colors.OKCYAN}[{subnet_counter}/{total_subnets}] Testing subnet: {subnet_key} (ID: {subnet_id}){Colors.ENDC}")
        print(f"{Colors.OKCYAN}    Sample size: {len(sample_targets)} targets{Colors.ENDC}")
        
        # Test samples in parallel
        subnet_results = []
        reachable_count = 0
        
        with ThreadPoolExecutor(max_workers=min(20, len(sample_targets))) as executor:
            future_to_target = {
                executor.submit(test_basic_connectivity, target, timeout): target 
                for target in sample_targets
            }
            
            target_counter = 0
            for future in as_completed(future_to_target):
                target = future_to_target[future]
                target_counter += 1
                try:
                    is_reachable = future.result()
                    subnet_results.append((target, is_reachable))
                    
                    if is_reachable:
                        reachable_count += 1
                        if debug:
                            print(f"{Colors.OKGREEN}      [{target_counter}/{len(sample_targets)}] ✓ {target}{Colors.ENDC}")
                    else:
                        if debug:
                            print(f"{Colors.FAIL}      [{target_counter}/{len(sample_targets)}] ✗ {target}{Colors.ENDC}")
                            
                except Exception as e:
                    subnet_results.append((target, False))
                    if debug:
                        print(f"{Colors.WARNING}      [{target_counter}/{len(sample_targets)}] ! {target} (error: {e}){Colors.ENDC}")
        
        # Calculate reachability
        reachability_percentage = (reachable_count / len(sample_targets)) * 100 if sample_targets else 0
        
        print(f"{Colors.OKCYAN}    Results: {reachable_count}/{len(sample_targets)} samples reachable ({reachability_percentage:.1f}%){Colors.ENDC}")
        
        # New logic: ANY response means subnet is reachable
        if reachable_count > 0:
            results['reachable_subnets'][subnet_key] = {
                'subnet_data': subnet_data,
                'sample_results': subnet_results,
                'reachable_count': reachable_count,
                'total_tested': len(sample_targets),
                'reachability_percentage': reachability_percentage,
                'confidence': 'high' if reachability_percentage >= 50 else 'medium' if reachable_count >= 3 else 'low'
            }
            confidence = results['reachable_subnets'][subnet_key]['confidence']
            print(f"{Colors.OKGREEN}    → Subnet REACHABLE (confidence: {confidence.upper()}){Colors.ENDC}")
        else:
            results['unreachable_subnets'][subnet_key] = {
                'subnet_data': subnet_data,
                'sample_results': subnet_results,
                'reachable_count': reachable_count,
                'total_tested': len(sample_targets),
                'reachability_percentage': reachability_percentage
            }
            print(f"{Colors.FAIL}    → Subnet UNREACHABLE (zero response - no routing){Colors.ENDC}")
        
        # Show overall progress
        remaining_subnets = total_subnets - subnet_counter
        if remaining_subnets > 0:
            print(f"{Colors.OKBLUE}    Progress: {subnet_counter}/{total_subnets} subnets completed ({remaining_subnets} remaining){Colors.ENDC}")
    
    # Test individual hosts
    individual_hosts = infrastructure['individual_hosts']
    total_individual = len(individual_hosts)
    
    if total_individual > 0:
        print(f"\n{Colors.OKBLUE}[*] Step 3: Testing Individual Hosts ({total_individual} hosts){Colors.ENDC}")
        
        with ThreadPoolExecutor(max_workers=20) as executor:
            future_to_host = {
                executor.submit(test_basic_connectivity, host, timeout): host 
                for host in individual_hosts
            }
            
            host_counter = 0
            for future in as_completed(future_to_host):
                host = future_to_host[future]
                host_counter += 1
                try:
                    is_reachable = future.result()
                    if is_reachable:
                        results['individual_hosts']['reachable'].append(host)
                        print(f"{Colors.OKGREEN}    [{host_counter}/{total_individual}] ✓ {host}{Colors.ENDC}")
                    else:
                        results['individual_hosts']['unreachable'].append(host)
                        print(f"{Colors.FAIL}    [{host_counter}/{total_individual}] ✗ {host}{Colors.ENDC}")
                except Exception as e:
                    results['individual_hosts']['unreachable'].append(host)
                    print(f"{Colors.WARNING}    [{host_counter}/{total_individual}] ! {host} (error){Colors.ENDC}")
    
    # Generate testing summary
    results['testing_summary'] = {
        'total_subnets_tested': total_subnets,
        'reachable_subnets_count': len(results['reachable_subnets']),
        'unreachable_subnets_count': len(results['unreachable_subnets']),
        'subnet_reachability_percentage': len(results['reachable_subnets']) / max(total_subnets, 1) * 100,
        'dns_reachability_percentage': len(results['dns_servers']['reachable']) / max(total_dns, 1) * 100 if total_dns > 0 else 0
    }
    
    return results

def test_basic_connectivity(target, timeout=3):
    """Test basic connectivity using multiple methods quickly."""
    # Method 1: ICMP ping (fastest)
    try:
        if sys.platform.startswith('win'):
            result = subprocess.run(['ping', '-n', '1', '-w', str(timeout*1000), target], 
                                  capture_output=True, text=True, timeout=timeout+1)
        else:
            result = subprocess.run(['ping', '-c', '1', '-W', str(timeout), target], 
                                  capture_output=True, text=True, timeout=timeout+1)
        
        if result.returncode == 0:
            return True
    except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
        pass
    
    # Method 2: ARP ping for local networks (often more reliable than ICMP)
    try:
        # Check if target appears to be in local network (basic check)
        target_ip = ipaddress.ip_address(target)
        if target_ip.is_private:
            # Use nmap ARP ping for local networks
            result = subprocess.run(['nmap', '-PR', '-sn', '--max-retries', '1', 
                                   '--max-rtt-timeout', f'{timeout}s', target], 
                                  capture_output=True, text=True, timeout=timeout+2)
            if result.returncode == 0 and "Host is up" in result.stdout:
                return True
    except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError, ValueError):
        pass
    
    # Method 3: Quick TCP tests on common ports
    common_ports = [22, 80, 443, 135, 139, 445, 3389]
    
    for port in common_ports:
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout / len(common_ports))
            result = sock.connect_ex((target, port))
            sock.close()
            
            if result == 0:
                return True
        except (socket.error, socket.timeout):
            continue
    
    return False

def test_optimized_connectivity(targets, timeout=3, debug=False):
    """Optimized connectivity testing with fastest methods first and verified host tracking."""
    verified_hosts = set()
    unverified_hosts = set(targets)
    
    print(f"{Colors.OKBLUE}[*] Optimized connectivity testing: fastest methods first{Colors.ENDC}")
    
    # Phase 1: ICMP ping sweep (fastest method) 
    print(f"{Colors.OKCYAN}[*] Phase 1: ICMP ping sweep (fastest)...{Colors.ENDC}")
    icmp_verified = 0
    for target in list(unverified_hosts):
        try:
            if sys.platform.startswith('win'):
                result = subprocess.run(['ping', '-n', '1', '-w', str(timeout*1000), target], 
                                      capture_output=True, text=True, timeout=timeout+1)
            else:
                result = subprocess.run(['ping', '-c', '1', '-W', str(timeout), target], 
                                      capture_output=True, text=True, timeout=timeout+1)
            
            if result.returncode == 0:
                verified_hosts.add(target)
                unverified_hosts.remove(target)
                icmp_verified += 1
                if debug:
                    print(f"{Colors.OKGREEN}[+] ICMP: {target} verified{Colors.ENDC}")
        except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
            continue
    
    print(f"{Colors.OKGREEN}[+] ICMP verified: {icmp_verified} hosts, {len(unverified_hosts)} remaining{Colors.ENDC}")
    
    if not unverified_hosts:
        return list(verified_hosts), []
    
    # Phase 2: ARP ping for local networks (often catches hosts that don't respond to ICMP)
    print(f"{Colors.OKCYAN}[*] Phase 2: ARP ping for remaining local network hosts...{Colors.ENDC}")
    arp_verified = 0
    for target in list(unverified_hosts):
        try:
            target_ip = ipaddress.ip_address(target)
            if target_ip.is_private:
                result = subprocess.run(['nmap', '-PR', '-sn', '--max-retries', '1', 
                                       '--max-rtt-timeout', f'{timeout}s', target], 
                                      capture_output=True, text=True, timeout=timeout+2)
                if result.returncode == 0 and "Host is up" in result.stdout:
                    verified_hosts.add(target)
                    unverified_hosts.remove(target)
                    arp_verified += 1
                    if debug:
                        print(f"{Colors.OKGREEN}[+] ARP: {target} verified{Colors.ENDC}")
        except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError, ValueError):
            continue
    
    print(f"{Colors.OKGREEN}[+] ARP verified: {arp_verified} hosts, {len(unverified_hosts)} remaining{Colors.ENDC}")
    
    if not unverified_hosts:
        return list(verified_hosts), []
    
    # Phase 3: TCP port checks for remaining hosts (thorough but slower)
    print(f"{Colors.OKCYAN}[*] Phase 3: TCP port checks for remaining {len(unverified_hosts)} hosts...{Colors.ENDC}")
    tcp_verified = 0
    common_ports = [22, 80, 443, 135, 139, 445, 3389]
    
    for target in list(unverified_hosts):
        for port in common_ports:
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(timeout / len(common_ports))
                result = sock.connect_ex((target, port))
                sock.close()
                
                if result == 0:
                    verified_hosts.add(target)
                    unverified_hosts.remove(target)
                    tcp_verified += 1
                    if debug:
                        print(f"{Colors.OKGREEN}[+] TCP: {target}:{port} verified{Colors.ENDC}")
                    break  # Host verified, no need to test other ports
            except (socket.error, socket.timeout):
                continue
    
    print(f"{Colors.OKGREEN}[+] TCP verified: {tcp_verified} hosts, {len(unverified_hosts)} remaining{Colors.ENDC}")
    
    # Note: DNS testing is handled separately as it's already optimized but slow
    # DNS testing should be done last if all other methods fail
    
    print(f"{Colors.OKGREEN}[+] Fast connectivity testing complete: {len(verified_hosts)} verified, {len(unverified_hosts)} for DNS testing{Colors.ENDC}")
    
    return list(verified_hosts), list(unverified_hosts)

def generate_conservative_target_list(infrastructure, subnet_results, debug=False):
    """Generate a realistic target list based on actual reachability testing."""
    
    print(f"\n{Colors.OKGREEN}[+] Generating Realistic Target List{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Logic: ANY reachable host in subnet = entire subnet is reachable{Colors.ENDC}")
    
    final_targets = []
    decision_log = []
    
    # Add reachable DNS servers
    dns_targets = subnet_results['dns_servers']['reachable']
    if dns_targets:
        final_targets.extend(dns_targets)
        decision_log.append(f"Added {len(dns_targets)} reachable DNS servers")
        print(f"{Colors.OKCYAN}[+] DNS Servers: Added {len(dns_targets)} confirmed reachable{Colors.ENDC}")
    
    # Process ALL subnets - if ANY sample responds, include the whole subnet
    all_subnets = {**subnet_results['reachable_subnets'], **subnet_results['unreachable_subnets']}
    
    for subnet_key, subnet_info in all_subnets.items():
        subnet_data = subnet_info['subnet_data']
        reachable_count = subnet_info['reachable_count']
        total_tested = subnet_info['total_tested']
        reachability_pct = subnet_info['reachability_percentage']
        
        print(f"\n{Colors.OKCYAN}[{subnet_data['subnet_id']}] Processing subnet: {subnet_key}{Colors.ENDC}")
        print(f"{Colors.OKCYAN}    Test Results: {reachable_count}/{total_tested} samples responded ({reachability_pct:.1f}%){Colors.ENDC}")
        
        if reachable_count > 0:
            # ANY response means the network is reachable
            targets_to_add = subnet_data['targets']
            final_targets.extend(targets_to_add)
            
            # Determine confidence level for user awareness
            if reachability_pct >= 50:
                confidence = "HIGH"
                reason = "majority of samples responded"
            elif reachable_count >= 3:
                confidence = "MEDIUM" 
                reason = "multiple samples responded"
            else:
                confidence = "LOW"
                reason = "minimal samples responded, but network is routable"
            
            decision_log.append(f"subnet {subnet_key}: added all {len(targets_to_add)} targets (confidence: {confidence})")
            print(f"{Colors.OKGREEN}    → INCLUDED all {len(targets_to_add)} targets{Colors.ENDC}")
            print(f"{Colors.OKGREEN}    → Confidence: {confidence} ({reason}){Colors.ENDC}")
            
        else:
            # Absolutely no response - likely not routable
            decision_log.append(f"subnet {subnet_key}: excluded - zero response from all {total_tested} samples")
            print(f"{Colors.FAIL}    → EXCLUDED (zero response from all samples - likely not routable){Colors.ENDC}")
    
    # Add individual reachable hosts
    individual_reachable = subnet_results['individual_hosts']['reachable']
    if individual_reachable:
        final_targets.extend(individual_reachable)
        decision_log.append(f"Added {len(individual_reachable)} individual reachable hosts")
        print(f"{Colors.OKCYAN}[+] Individual Hosts: Added {len(individual_reachable)} confirmed reachable{Colors.ENDC}")
    
    # Remove duplicates while preserving order
    seen = set()
    unique_targets = []
    for target in final_targets:
        if target not in seen:
            seen.add(target)
            unique_targets.append(target)
    
    print(f"\n{Colors.OKGREEN}[+] Realistic Target List Summary:{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Total unique targets: {len(unique_targets)}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Original target count: {infrastructure['analysis_summary']['total_targets']}{Colors.ENDC}")
    
    if infrastructure['analysis_summary']['total_targets'] > 0:
        reduction_pct = (1 - len(unique_targets) / infrastructure['analysis_summary']['total_targets']) * 100
        if reduction_pct > 0:
            print(f"{Colors.OKBLUE}[*] Filtered out: {reduction_pct:.1f}% (unreachable networks){Colors.ENDC}")
        else:
            print(f"{Colors.OKBLUE}[*] No networks filtered - all appear reachable{Colors.ENDC}")
    
    return unique_targets, decision_log

def run_dns_intelligence_gathering(targets, base_dir=None, debug=False, timeout=5):
    """Run comprehensive DNS intelligence gathering as first phase of reachability testing."""
    
    if base_dir:
        dns_dir = os.path.join(base_dir, "scans", "dns")
        os.makedirs(dns_dir, exist_ok=True)
    else:
        dns_dir = "./dns_intelligence"
        os.makedirs(dns_dir, exist_ok=True)
    
    print(f"{Colors.OKGREEN}[+] DNS Intelligence Gathering Phase{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Strategy: Extract reachable hosts from DNS before connectivity testing{Colors.ENDC}")
    
    start_time = time.time()
    
    # Phase 1: Discover DNS servers
    dns_servers = discover_dns_servers(targets, debug)
    print(f"{Colors.OKBLUE}[*] Found {len(dns_servers)} DNS servers to query{Colors.ENDC}")
    
    # Phase 2: Attempt zone transfers
    zone_transfer_results = attempt_zone_transfers(dns_servers, debug, timeout)
    
    # Phase 3: Reverse DNS sweeps 
    reverse_dns_results = perform_reverse_dns_sweeps(targets, dns_servers, debug, timeout)
    
    # Phase 4: Forward DNS brute forcing
    forward_dns_results = perform_forward_dns_enumeration(targets, dns_servers, debug, timeout)
    
    # Phase 5: Compile intelligence
    dns_intelligence = compile_dns_intelligence(
        zone_transfer_results, reverse_dns_results, forward_dns_results, debug
    )
    
    total_time = time.time() - start_time
    
    # Generate reports
    timestamp = time.strftime("%Y%m%d_%H%M%S")
    generate_dns_intelligence_reports(dns_intelligence, dns_dir, timestamp, total_time)
    
    return dns_intelligence

def discover_dns_servers(targets, debug=False):
    """Discover all potential DNS servers from targets and system config."""
    dns_servers = set()
    
    print(f"{Colors.OKBLUE}[*] Phase 1: DNS Server Discovery{Colors.ENDC}")
    
    # Get system DNS servers
    try:
        with open('/etc/resolv.conf', 'r') as f:
            for line in f:
                if line.startswith('nameserver'):
                    dns_ip = line.split()[1]
                    try:
                        ipaddress.ip_address(dns_ip)
                        dns_servers.add(dns_ip)
                        if debug:
                            print(f"{Colors.OKCYAN}    Found system DNS: {dns_ip}{Colors.ENDC}")
                    except ValueError:
                        pass
    except FileNotFoundError:
        pass
    
    # Extract potential DNS servers from target networks
    for target in targets:
        try:
            if '/' in target:
                # CIDR network
                network = ipaddress.ip_network(target, strict=False)
                if network.is_private and network.num_addresses > 2:
                    # Common DNS server positions in networks
                    common_dns_positions = [1, 2, 10, 53, 100]
                    base_ip = str(network.network_address)
                    
                    for pos in common_dns_positions:
                        try:
                            dns_candidate = str(list(network.hosts())[pos-1])
                            dns_servers.add(dns_candidate)
                        except (IndexError, ValueError):
                            pass
            else:
                # Individual IP - check if it could be a DNS server
                try:
                    ip_obj = ipaddress.ip_address(target)
                    if ip_obj.is_private:
                        octets = str(ip_obj).split('.')
                        # Add likely DNS servers in same subnet
                        subnet_dns = [
                            f"{octets[0]}.{octets[1]}.{octets[2]}.1",
                            f"{octets[0]}.{octets[1]}.{octets[2]}.2", 
                            f"{octets[0]}.{octets[1]}.{octets[2]}.10",
                            f"{octets[0]}.{octets[1]}.{octets[2]}.53"
                        ]
                        dns_servers.update(subnet_dns)
                except ValueError:
                    pass
        except ValueError:
            continue
    
    # Add common external DNS servers
    dns_servers.update(['8.8.8.8', '8.8.4.4', '1.1.1.1', '1.0.0.1'])
    
    # Test which DNS servers actually respond
    working_dns = []
    print(f"{Colors.OKCYAN}    Testing {len(dns_servers)} potential DNS servers...{Colors.ENDC}")
    
    with ThreadPoolExecutor(max_workers=20) as executor:
        future_to_dns = {
            executor.submit(test_dns_connectivity, dns_server, 3): dns_server 
            for dns_server in dns_servers
        }
        
        for future in as_completed(future_to_dns):
            dns_server = future_to_dns[future]
            try:
                if future.result():
                    working_dns.append(dns_server)
                    if debug:
                        print(f"{Colors.OKGREEN}      ✓ {dns_server}{Colors.ENDC}")
            except Exception:
                pass
    
    print(f"{Colors.OKGREEN}    → {len(working_dns)} working DNS servers found{Colors.ENDC}")
    return working_dns

def attempt_zone_transfers(dns_servers, debug=False, timeout=10):
    """Attempt DNS zone transfers (AXFR) from discovered domains."""
    print(f"\n{Colors.OKBLUE}[*] Phase 2: DNS Zone Transfer Attempts{Colors.ENDC}")
    
    zone_results = {
        'successful_transfers': {},
        'failed_transfers': [],
        'discovered_domains': set(),
        'discovered_hosts': set()
    }
    
    # Common internal domain patterns to try
    common_domains = [
        'local', 'internal', 'corp', 'company', 'domain', 'ad', 'lan',
        'intranet', 'office', 'net', 'priv', 'private'
    ]
    
    # Also try reverse zones for common private networks
    reverse_zones = [
        '10.in-addr.arpa', '168.192.in-addr.arpa', 
        '16.172.in-addr.arpa', '17.172.in-addr.arpa', '18.172.in-addr.arpa'
    ]
    
    all_zones_to_try = common_domains + reverse_zones
    
    print(f"{Colors.OKCYAN}    Attempting zone transfers for {len(all_zones_to_try)} common zones...{Colors.ENDC}")
    
    for dns_server in dns_servers[:5]:  # Limit to first 5 DNS servers
        for zone in all_zones_to_try:
            try:
                if debug:
                    print(f"{Colors.OKCYAN}      Trying {zone} from {dns_server}...{Colors.ENDC}")
                
                # Use dig for zone transfer
                cmd = f"dig @{dns_server} {zone} AXFR +time={timeout}"
                result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
                
                if result.returncode == 0 and len(result.stdout) > 100:
                    # Successful transfer
                    zone_results['successful_transfers'][f"{dns_server}_{zone}"] = result.stdout
                    zone_results['discovered_domains'].add(zone)
                    
                    # Parse hosts from zone transfer
                    for line in result.stdout.split('\n'):
                        if '\tA\t' in line or '\tAAAA\t' in line:
                            parts = line.split()
                            if len(parts) >= 5:
                                hostname = parts[0].rstrip('.')
                                ip = parts[4]
                                zone_results['discovered_hosts'].add(ip)
                                if debug:
                                    print(f"{Colors.OKGREEN}        Found: {hostname} -> {ip}{Colors.ENDC}")
                    
                    print(f"{Colors.OKGREEN}    ✓ Zone transfer successful: {zone} from {dns_server}{Colors.ENDC}")
                else:
                    zone_results['failed_transfers'].append(f"{dns_server}_{zone}")
                    
            except subprocess.TimeoutExpired:
                if debug:
                    print(f"{Colors.WARNING}      Timeout: {zone} from {dns_server}{Colors.ENDC}")
            except Exception as e:
                if debug:
                    print(f"{Colors.WARNING}      Error: {zone} from {dns_server} - {e}{Colors.ENDC}")
    
    success_count = len(zone_results['successful_transfers'])
    host_count = len(zone_results['discovered_hosts'])
    
    if success_count > 0:
        print(f"{Colors.OKGREEN}    → {success_count} successful zone transfers, {host_count} hosts discovered{Colors.ENDC}")
    else:
        print(f"{Colors.WARNING}    → No successful zone transfers (transfers likely disabled){Colors.ENDC}")
    
    return zone_results

def perform_reverse_dns_sweeps(targets, dns_servers, debug=False, timeout=3):
    """Perform SMART reverse DNS sampling - not brute force sweeps."""
    print(f"\n{Colors.OKBLUE}[*] Phase 3: Smart Reverse DNS Sampling{Colors.ENDC}")
    
    reverse_results = {
        'networks_with_dns': {},
        'discovered_hosts': set(),
        'hostname_patterns': set(),
        'networks_with_no_dns': set()
    }
    
    # Extract IP networks from targets
    networks_to_sample = []
    for target in targets:
        try:
            if '/' in target:
                network = ipaddress.ip_network(target, strict=False)
                networks_to_sample.append(network)
            else:
                ip = ipaddress.ip_address(target)
                if ip.version == 4:
                    network = ipaddress.ip_network(f"{ip}/24", strict=False)
                    networks_to_sample.append(network)
        except ValueError:
            continue
    
    total_networks = len(networks_to_sample)
    print(f"{Colors.OKCYAN}    Smart sampling {total_networks} networks (not brute forcing){Colors.ENDC}")
    
    network_counter = 0
    for network in networks_to_sample:
        network_counter += 1
        print(f"{Colors.OKCYAN}    [{network_counter}/{total_networks}] Sampling network: {network}...{Colors.ENDC}", end=' ')
        
        # SMART SAMPLING: Only test a few strategic IPs per network
        all_hosts = list(network.hosts())
        if len(all_hosts) == 0:
            print(f"{Colors.WARNING}No hosts{Colors.ENDC}")
            continue
        
        # Sample strategy: Test 5-8 strategic IPs to determine if reverse DNS exists
        sample_ips = []
        
        # Always test first few IPs (common for infrastructure)
        sample_ips.extend(all_hosts[:3])
        
        # Test some IPs from the middle
        if len(all_hosts) > 10:
            mid_point = len(all_hosts) // 2
            sample_ips.extend(all_hosts[mid_point:mid_point+2])
        
        # Test last few IPs
        if len(all_hosts) > 5:
            sample_ips.extend(all_hosts[-2:])
        
        # Remove duplicates and limit to max 8 samples
        sample_ips = list(dict.fromkeys(sample_ips))[:8]
        
        # Test samples quickly
        network_has_reverse_dns = False
        found_hosts = []
        
        with ThreadPoolExecutor(max_workers=8) as executor:
            future_to_ip = {
                executor.submit(reverse_dns_lookup, str(ip), dns_servers[0] if dns_servers else '8.8.8.8', timeout): str(ip) 
                for ip in sample_ips
            }
            
            for future in as_completed(future_to_ip):
                ip = future_to_ip[future]
                try:
                    hostname = future.result()
                    if hostname:
                        network_has_reverse_dns = True
                        found_hosts.append((ip, hostname))
                        reverse_results['discovered_hosts'].add(ip)
                        reverse_results['hostname_patterns'].add(hostname)
                        
                        if debug:
                            print(f"\n{Colors.OKGREEN}        {ip} -> {hostname}{Colors.ENDC}")
                            
                except Exception:
                    pass
        
        if network_has_reverse_dns:
            reverse_results['networks_with_dns'][str(network)] = found_hosts
            print(f"{Colors.OKGREEN}✓ HAS REVERSE DNS ({len(found_hosts)} found){Colors.ENDC}")
            
            # If we found reverse DNS in samples, it's worth checking a few more strategic IPs
            if len(found_hosts) >= 2:
                print(f"{Colors.OKCYAN}      Network appears to use reverse DNS - checking key infrastructure IPs...{Colors.ENDC}")
                
                # Check common infrastructure IPs that might have DNS
                key_infrastructure_ips = []
                base_octets = str(network.network_address).split('.')
                
                # Common server/infrastructure IPs
                common_endings = [1, 2, 10, 25, 53, 100, 200, 250, 254]
                for ending in common_endings:
                    try:
                        potential_ip = f"{base_octets[0]}.{base_octets[1]}.{base_octets[2]}.{ending}"
                        if ipaddress.ip_address(potential_ip) in network:
                            key_infrastructure_ips.append(potential_ip)
                    except ValueError:
                        pass
                
                # Test infrastructure IPs (max 10)
                with ThreadPoolExecutor(max_workers=10) as executor:
                    infra_futures = {
                        executor.submit(reverse_dns_lookup, ip, dns_servers[0] if dns_servers else '8.8.8.8', timeout): ip 
                        for ip in key_infrastructure_ips[:10]
                    }
                    
                    for future in as_completed(infra_futures):
                        ip = infra_futures[future]
                        try:
                            hostname = future.result()
                            if hostname and ip not in [h[0] for h in found_hosts]:
                                found_hosts.append((ip, hostname))
                                reverse_results['discovered_hosts'].add(ip)
                                reverse_results['hostname_patterns'].add(hostname)
                                if debug:
                                    print(f"{Colors.OKGREEN}        Infrastructure: {ip} -> {hostname}{Colors.ENDC}")
                        except Exception:
                            pass
                
                reverse_results['networks_with_dns'][str(network)] = found_hosts
        else:
            reverse_results['networks_with_no_dns'].add(str(network))
            print(f"{Colors.FAIL}✗ No reverse DNS{Colors.ENDC}")
        
        # Show progress
        remaining = total_networks - network_counter
        if remaining > 0 and network_counter % 5 == 0:  # Show progress every 5 networks
            print(f"{Colors.OKBLUE}    Progress: {network_counter}/{total_networks} networks completed ({remaining} remaining){Colors.ENDC}")
    
    total_hosts = len(reverse_results['discovered_hosts'])
    networks_with_dns = len(reverse_results['networks_with_dns'])
    networks_without_dns = len(reverse_results['networks_with_no_dns'])
    
    print(f"{Colors.OKGREEN}    → {total_hosts} hosts with reverse DNS across {networks_with_dns} networks{Colors.ENDC}")
    print(f"{Colors.OKCYAN}    → {networks_without_dns} networks have no reverse DNS configured{Colors.ENDC}")
    
    return reverse_results

def analyze_network_infrastructure(targets, debug=False):
    """Analyze targets and identify key network infrastructure to test."""
    infrastructure = {
        'subnets': {},  # Use 'subnets' consistently
        'individual_hosts': [],
        'dns_servers': set(),
        'analysis_summary': {},
        'total_original_targets': 0
    }
    
    print(f"{Colors.OKGREEN}[+] Analyzing Target Infrastructure{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Processing {len(targets)} targets to identify network segments...{Colors.ENDC}")
    
    # Count original targets for reduction metrics
    total_original = 0
    for target in targets:
        try:
            if '/' in target:
                network = ipaddress.ip_network(target, strict=False)
                total_original += network.num_addresses - 2
            else:
                total_original += 1
        except ValueError:
            total_original += 1
    
    infrastructure['total_original_targets'] = total_original
    print(f"{Colors.OKBLUE}[*] Original scope: ~{total_original:,} potential targets{Colors.ENDC}")
    
    subnet_count = 0
    
    for target in targets:
        try:
            ip_obj = ipaddress.ip_address(target)
            
            if ip_obj.is_private:
                octets = str(ip_obj).split('.')
                
                if octets[0] == '10':
                    subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
                elif octets[0] == '172' and 16 <= int(octets[1]) <= 31:
                    subnet_key = f"{octets[0]}.{octets[1]}.0.0/16"
                elif octets[0] == '192' and octets[1] == '168':
                    subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
                else:
                    subnet_key = f"{octets[0]}.{octets[1]}.{octets[2]}.0/24"
            else:
                subnet_key = f"public_{str(ip_obj)}"
            
            if subnet_key not in infrastructure['subnets']:
                subnet_count += 1
                infrastructure['subnets'][subnet_key] = {
                    'network': subnet_key,
                    'targets': [],
                    'sample_targets': [],
                    'key_infrastructure': [],
                    'subnet_id': subnet_count
                }
                
                try:
                    network = ipaddress.ip_network(subnet_key, strict=False)
                    if network.is_private and network.num_addresses > 2:
                        base_ip = str(network.network_address).split('.')
                        key_ips = [
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.1",
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.254",
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.10",
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.53",
                            f"{base_ip[0]}.{base_ip[1]}.{base_ip[2]}.100",
                        ]
                        
                        for key_ip in key_ips:
                            try:
                                if ipaddress.ip_address(key_ip) in network:
                                    infrastructure['subnets'][subnet_key]['key_infrastructure'].append(key_ip)
                            except ValueError:
                                pass
                except ValueError:
                    pass
            
            infrastructure['subnets'][subnet_key]['targets'].append(str(ip_obj))
            
        except ValueError:
            if '/' in target:
                try:
                    network = ipaddress.ip_network(target, strict=False)
                    subnet_key = str(network)
                    
                    if subnet_key not in infrastructure['subnets']:
                        subnet_count += 1
                        infrastructure['subnets'][subnet_key] = {
                            'network': subnet_key,
                            'targets': [],
                            'sample_targets': [],
                            'key_infrastructure': [],
                            'subnet_id': subnet_count,
                            'is_full_network': True
                        }
                    
                    all_hosts = list(network.hosts())
                    if len(all_hosts) > 50:
                        sample_hosts = all_hosts[:10] + all_hosts[-10:] + all_hosts[len(all_hosts)//2:len(all_hosts)//2+10]
                        infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in sample_hosts[:30]]
                    else:
                        infrastructure['subnets'][subnet_key]['targets'] = [str(h) for h in all_hosts]
                    
                except ValueError:
                    infrastructure['individual_hosts'].append(target)
            else:
                infrastructure['individual_hosts'].append(target)
    
    # Generate sample targets for each subnet
    for subnet_key, subnet_data in infrastructure['subnets'].items():
        targets_in_subnet = subnet_data['targets']
        
        if len(targets_in_subnet) <= 5:
            subnet_data['sample_targets'] = targets_in_subnet.copy()
        else:
            sample_size = min(8, max(3, len(targets_in_subnet) // 20))
            
            samples = []
            samples.append(targets_in_subnet[0])
            if len(targets_in_subnet) > 1:
                samples.append(targets_in_subnet[-1])
            
            remaining = sample_size - len(samples)
            if remaining > 0 and len(targets_in_subnet) > 2:
                step = len(targets_in_subnet) // (remaining + 1)
                for i in range(remaining):
                    idx = (i + 1) * step
                    if idx < len(targets_in_subnet):
                        samples.append(targets_in_subnet[idx])
            
            subnet_data['sample_targets'] = list(set(samples))
        
        subnet_data['sample_targets'].extend(subnet_data['key_infrastructure'])
        subnet_data['sample_targets'] = list(set(subnet_data['sample_targets']))
    
    # Discover system DNS servers
    try:
        with open('/etc/resolv.conf', 'r') as f:
            for line in f:
                if line.startswith('nameserver'):
                    dns_ip = line.split()[1]
                    try:
                        ipaddress.ip_address(dns_ip)
                        infrastructure['dns_servers'].add(dns_ip)
                    except ValueError:
                        pass
    except FileNotFoundError:
        pass
    
    if not infrastructure['dns_servers']:
        infrastructure['dns_servers'].update(['8.8.8.8', '1.1.1.1'])
    
    # Generate summary
    infrastructure['analysis_summary'] = {
        'total_subnets': len(infrastructure['subnets']),
        'total_targets': sum(len(e['targets']) for e in infrastructure['subnets'].values()),
        'total_samples': sum(len(e['sample_targets']) for e in infrastructure['subnets'].values()),
        'individual_hosts': len(infrastructure['individual_hosts']),
        'dns_servers': len(infrastructure['dns_servers'])
    }
    
    print(f"{Colors.OKGREEN}[+] Infrastructure Analysis Complete{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Identified {len(infrastructure['subnets'])} network subnets:{Colors.ENDC}")
    
    for subnet_key, subnet_data in infrastructure['subnets'].items():
        subnet_id = subnet_data['subnet_id']
        target_count = len(subnet_data['targets'])
        sample_count = len(subnet_data['sample_targets'])
        
        print(f"{Colors.OKCYAN}    [{subnet_id}] {subnet_key}: {target_count} targets → {sample_count} samples{Colors.ENDC}")
        
        if debug:
            print(f"{Colors.OKCYAN}        Sample IPs: {', '.join(subnet_data['sample_targets'][:5])}{'...' if len(subnet_data['sample_targets']) > 5 else ''}{Colors.ENDC}")
    
    if infrastructure['individual_hosts']:
        print(f"{Colors.OKCYAN}    Individual hosts: {len(infrastructure['individual_hosts'])}{Colors.ENDC}")
    
    print(f"{Colors.OKBLUE}[*] Total testing targets: {infrastructure['analysis_summary']['total_samples']} (vs {infrastructure['analysis_summary']['total_targets']} original){Colors.ENDC}")
    
    return infrastructure

def perform_forward_dns_enumeration(targets, dns_servers, debug=False, timeout=3):
    """Perform forward DNS enumeration using common hostname patterns."""
    print(f"\n{Colors.OKBLUE}[*] Phase 4: Forward DNS Enumeration{Colors.ENDC}")
    
    forward_results = {
        'discovered_hosts': set(),
        'successful_queries': {},
        'domain_patterns': set()
    }
    
    # Common hostname patterns for internal networks
    common_hostnames = [
        'dc', 'dc1', 'dc2', 'dc01', 'dc02', 'domain', 'ad', 'ldap',
        'dns', 'dns1', 'dns2', 'ns', 'ns1', 'ns2',
        'mail', 'exchange', 'smtp', 'pop', 'imap',
        'web', 'www', 'intranet', 'portal', 'sharepoint',
        'db', 'database', 'sql', 'mysql', 'oracle',
        'file', 'files', 'fs', 'nas', 'share', 'fileserver',
        'backup', 'bkp', 'archive',
        'fw', 'firewall', 'gw', 'gateway', 'router',
        'monitor', 'nagios', 'zabbix', 'snmp',
        'print', 'printer', 'cups',
        'vm', 'vmware', 'vcenter', 'esxi',
        'admin', 'mgmt', 'management', 'console'
    ]
    
    # Extract potential domains from any hostname patterns we found
    potential_domains = set(['local', 'internal', 'corp', 'domain', 'ad'])
    
    # If we have DNS servers, try to extract domain from their configuration
    for dns_server in dns_servers[:3]:
        try:
            # Try to get the DNS server's domain
            cmd = f"dig @{dns_server} . NS +short +time=3"
            result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                for line in result.stdout.strip().split('\n'):
                    if '.' in line:
                        domain_parts = line.strip('.').split('.')
                        if len(domain_parts) >= 2:
                            potential_domains.add('.'.join(domain_parts[-2:]))
        except:
            pass
    
    print(f"{Colors.OKCYAN}    Testing {len(common_hostnames)} hostnames across {len(potential_domains)} domains...{Colors.ENDC}")
    
    queries_to_test = []
    for domain in potential_domains:
        for hostname in common_hostnames:
            fqdn = f"{hostname}.{domain}"
            queries_to_test.append(fqdn)
    
    # Limit total queries to reasonable number
    if len(queries_to_test) > 200:
        queries_to_test = queries_to_test[:200]
    
    with ThreadPoolExecutor(max_workers=20) as executor:
        future_to_query = {
            executor.submit(forward_dns_lookup, query, dns_servers[0] if dns_servers else '8.8.8.8', timeout): query 
            for query in queries_to_test
        }
        
        successful_count = 0
        for future in as_completed(future_to_query):
            query = future_to_query[future]
            try:
                ip = future.result()
                if ip:
                    forward_results['discovered_hosts'].add(ip)
                    forward_results['successful_queries'][query] = ip
                    forward_results['domain_patterns'].add(query.split('.', 1)[1])
                    successful_count += 1
                    
                    if debug:
                        print(f"{Colors.OKGREEN}      {query} -> {ip}{Colors.ENDC}")
                        
            except Exception:
                pass
    
    print(f"{Colors.OKGREEN}    → {successful_count} successful DNS queries, {len(forward_results['discovered_hosts'])} unique hosts{Colors.ENDC}")
    
    return forward_results

def reverse_dns_lookup(ip, dns_server, timeout=3):
    """Perform reverse DNS lookup for an IP address."""
    try:
        cmd = f"dig @{dns_server} -x {ip} +short +time={timeout}"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout+1)
        
        if result.returncode == 0 and result.stdout.strip():
            hostname = result.stdout.strip().split('\n')[0].rstrip('.')
            if hostname and not hostname.startswith(';'):
                return hostname
    except:
        pass
    
    return None

def forward_dns_lookup(hostname, dns_server, timeout=3):
    """Perform forward DNS lookup for a hostname."""
    try:
        cmd = f"dig @{dns_server} {hostname} A +short +time={timeout}"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout+1)
        
        if result.returncode == 0 and result.stdout.strip():
            ip = result.stdout.strip().split('\n')[0]
            # Validate it's actually an IP
            try:
                ipaddress.ip_address(ip)
                return ip
            except ValueError:
                pass
    except:
        pass
    
    return None

def compile_dns_intelligence(zone_results, reverse_results, forward_results, debug=False):
    """Compile all DNS intelligence into actionable target list."""
    print(f"\n{Colors.OKBLUE}[*] Phase 5: Compiling DNS Intelligence{Colors.ENDC}")
    
    intelligence = {
        'high_value_targets': set(),    # Hosts from DNS that likely exist
        'medium_value_targets': set(),  # Hosts from patterns/inference
        'discovered_domains': set(),
        'dns_summary': {},
        'recommendations': []
    }
    
    # Compile all discovered hosts
    all_discovered_hosts = set()
    
    # Zone transfer results (highest confidence)
    if zone_results['discovered_hosts']:
        all_discovered_hosts.update(zone_results['discovered_hosts'])
        intelligence['high_value_targets'].update(zone_results['discovered_hosts'])
        intelligence['recommendations'].append(f"Zone transfers revealed {len(zone_results['discovered_hosts'])} hosts")
    
    # Reverse DNS results (high confidence - these hosts have DNS records)
    if reverse_results['discovered_hosts']:
        all_discovered_hosts.update(reverse_results['discovered_hosts'])
        intelligence['high_value_targets'].update(reverse_results['discovered_hosts'])
        intelligence['recommendations'].append(f"Reverse DNS found {len(reverse_results['discovered_hosts'])} hosts")
    
    # Forward DNS results (high confidence - these hosts resolve)
    if forward_results['discovered_hosts']:
        all_discovered_hosts.update(forward_results['discovered_hosts'])
        intelligence['high_value_targets'].update(forward_results['discovered_hosts'])
        intelligence['recommendations'].append(f"Forward DNS enumeration found {len(forward_results['discovered_hosts'])} hosts")
    
    # Compile discovered domains
    intelligence['discovered_domains'].update(zone_results['discovered_domains'])
    intelligence['discovered_domains'].update(forward_results['domain_patterns'])
    
    # Generate summary
    intelligence['dns_summary'] = {
        'total_discovered_hosts': len(all_discovered_hosts),
        'zone_transfer_hosts': len(zone_results['discovered_hosts']),
        'reverse_dns_hosts': len(reverse_results['discovered_hosts']),
        'forward_dns_hosts': len(forward_results['discovered_hosts']),
        'discovered_domains': len(intelligence['discovered_domains']),
        'successful_zone_transfers': len(zone_results['successful_transfers'])
    }
    
    print(f"{Colors.OKGREEN}[+] DNS Intelligence Summary:{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Total hosts discovered via DNS: {len(all_discovered_hosts)}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] High-value targets (DNS confirmed): {len(intelligence['high_value_targets'])}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Domains discovered: {len(intelligence['discovered_domains'])}{Colors.ENDC}")
    
    if len(all_discovered_hosts) > 0:
        intelligence['recommendations'].append("Focus initial scanning on DNS-discovered hosts")
        print(f"{Colors.OKGREEN}[+] Recommendation: Prioritize the {len(all_discovered_hosts)} DNS-confirmed targets{Colors.ENDC}")
    else:
        intelligence['recommendations'].append("No DNS intelligence gathered - proceed with standard reachability testing")
        print(f"{Colors.WARNING}[!] No hosts discovered via DNS - proceeding with connectivity testing{Colors.ENDC}")
    
    return intelligence

def generate_dns_intelligence_reports(intelligence, dns_dir, timestamp, total_time):
    """Generate DNS intelligence reports."""
    
    # Main DNS targets file
    dns_targets_file = os.path.join(dns_dir, f"dns_discovered_targets_{timestamp}.txt")
    with open(dns_targets_file, 'w') as f:
        f.write(f"# DNS Intelligence Gathering Results\n")
        f.write(f"# Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"# Duration: {total_time:.2f} seconds\n")
        f.write(f"# Total DNS-confirmed targets: {len(intelligence['high_value_targets'])}\n\n")
        
        for target in sorted(intelligence['high_value_targets'], 
                           key=lambda x: ipaddress.ip_address(x) if x.replace('.','').isdigit() else x):
            f.write(f"{target}\n")
    
    # Detailed intelligence report
    intel_report = os.path.join(dns_dir, f"dns_intelligence_report_{timestamp}.txt")
    with open(intel_report, 'w') as f:
        f.write("=" * 70 + "\n")
        f.write("DNS INTELLIGENCE GATHERING REPORT\n")
        f.write("=" * 70 + "\n\n")
        f.write(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"Duration: {total_time:.2f} seconds\n\n")
        
        summary = intelligence['dns_summary']
        f.write("DISCOVERY SUMMARY:\n")
        f.write("-" * 17 + "\n")
        f.write(f"Total Hosts Discovered: {summary['total_discovered_hosts']}\n")
        f.write(f"Zone Transfer Hosts: {summary['zone_transfer_hosts']}\n")
        f.write(f"Reverse DNS Hosts: {summary['reverse_dns_hosts']}\n")
        f.write(f"Forward DNS Hosts: {summary['forward_dns_hosts']}\n")
        f.write(f"Domains Discovered: {summary['discovered_domains']}\n")
        f.write(f"Successful Zone Transfers: {summary['successful_zone_transfers']}\n\n")
        
        f.write("RECOMMENDATIONS:\n")
        f.write("-" * 15 + "\n")
        for rec in intelligence['recommendations']:
            f.write(f"• {rec}\n")
    
    print(f"{Colors.OKBLUE}[*] DNS targets file: {dns_targets_file}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Intelligence report: {intel_report}{Colors.ENDC}")

def run_smart_network_reachability_test(targets, base_dir=None, standalone=False, debug=False, 
                                       threads=None, timeout=None):
    """Run comprehensive reachability assessment: DNS intelligence + connectivity testing."""
    
    test_timeout = timeout if timeout is not None else REACHABILITY_TIMEOUT
    
    if base_dir:
        reachability_dir = os.path.join(base_dir, "scans", "reachability")
        os.makedirs(reachability_dir, exist_ok=True)
    else:
        reachability_dir = "./reachability_results"
        os.makedirs(reachability_dir, exist_ok=True)
    
    print(f"{Colors.OKGREEN}[+] Comprehensive Network Reachability Assessment{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Strategy: DNS intelligence + connectivity testing for maximum coverage{Colors.ENDC}")
    
    start_time = time.time()
    
    # Phase 1: DNS Intelligence Gathering
    print(f"\n{Colors.OKGREEN}=== PHASE 1: DNS INTELLIGENCE GATHERING ==={Colors.ENDC}")
    dns_intelligence = run_dns_intelligence_gathering(targets, base_dir, debug, test_timeout)
    
    # Phase 2: Infrastructure Analysis and Connectivity Testing
    print(f"\n{Colors.OKGREEN}=== PHASE 2: NETWORK CONNECTIVITY TESTING ==={Colors.ENDC}")
    infrastructure = analyze_network_infrastructure(targets, debug)
    subnet_results = test_subnet_reachability(infrastructure, debug, test_timeout)
    connectivity_targets, decision_log = generate_conservative_target_list(infrastructure, subnet_results, debug)
    
    # Phase 3: Combine and Validate Results
    print(f"\n{Colors.OKGREEN}=== PHASE 3: RESULTS INTEGRATION ==={Colors.ENDC}")
    final_results = integrate_dns_and_connectivity_results(
        dns_intelligence, connectivity_targets, debug, test_timeout
    )
    
    total_time = time.time() - start_time
    timestamp = time.strftime("%Y%m%d_%H%M%S")
    
    # Generate comprehensive reports
    return generate_comprehensive_reachability_reports(
        final_results, dns_intelligence, infrastructure, subnet_results, 
        decision_log, reachability_dir, timestamp, total_time, standalone, debug
    )

def integrate_dns_and_connectivity_results(dns_intelligence, connectivity_targets, debug=False, timeout=3):
    """Integrate DNS intelligence with connectivity test results."""
    print(f"{Colors.OKBLUE}[*] Integrating DNS intelligence with connectivity results...{Colors.ENDC}")
    
    results = {
        'dns_confirmed': list(dns_intelligence['high_value_targets']),
        'connectivity_confirmed': connectivity_targets,
        'validated_targets': [],  # DNS targets that also pass connectivity
        'dns_only_targets': [],   # DNS targets that don't respond to connectivity
        'connectivity_only_targets': [],  # Connectivity targets not in DNS
        'final_target_list': [],
        'validation_summary': {}
    }
    
    dns_targets = set(dns_intelligence['high_value_targets'])
    conn_targets = set(connectivity_targets)
    
    # Find overlaps and differences
    overlap_targets = dns_targets.intersection(conn_targets)
    dns_only = dns_targets - conn_targets
    connectivity_only = conn_targets - dns_targets
    
    print(f"{Colors.OKCYAN}    DNS-discovered targets: {len(dns_targets)}{Colors.ENDC}")
    print(f"{Colors.OKCYAN}    Connectivity-confirmed targets: {len(conn_targets)}{Colors.ENDC}")
    print(f"{Colors.OKCYAN}    Overlap (DNS + connectivity): {len(overlap_targets)}{Colors.ENDC}")
    print(f"{Colors.OKCYAN}    DNS-only targets: {len(dns_only)}{Colors.ENDC}")
    print(f"{Colors.OKCYAN}    Connectivity-only targets: {len(connectivity_only)}{Colors.ENDC}")
    
    # Validate DNS-only targets with quick connectivity test
    if dns_only:
        print(f"{Colors.OKBLUE}[*] Validating {len(dns_only)} DNS-only targets...{Colors.ENDC}")
        
        with ThreadPoolExecutor(max_workers=20) as executor:
            future_to_target = {
                executor.submit(test_basic_connectivity, target, timeout): target 
                for target in dns_only
            }
            
            validated_count = 0
            for future in as_completed(future_to_target):
                target = future_to_target[future]
                try:
                    is_reachable = future.result()
                    if is_reachable:
                        results['validated_targets'].append(target)
                        validated_count += 1
                        if debug:
                            print(f"{Colors.OKGREEN}      ✓ {target} (DNS + validated){Colors.ENDC}")
                    else:
                        results['dns_only_targets'].append(target)
                        if debug:
                            print(f"{Colors.WARNING}      - {target} (DNS only, no connectivity){Colors.ENDC}")
                except Exception:
                    results['dns_only_targets'].append(target)
        
        print(f"{Colors.OKGREEN}    → {validated_count}/{len(dns_only)} DNS targets validated via connectivity{Colors.ENDC}")
    
    # Build final target list with prioritization
    results['connectivity_confirmed'] = list(conn_targets)
    results['connectivity_only_targets'] = list(connectivity_only)
    
    # Priority order for final list:
    # 1. Overlap targets (DNS + connectivity confirmed) - HIGHEST priority
    # 2. Validated DNS targets (DNS + newly validated) - HIGH priority  
    # 3. Connectivity-only targets - MEDIUM priority
    # 4. DNS-only targets (DNS but no connectivity) - LOW priority
    
    final_targets = []
    
    # Add overlap targets (highest confidence)
    final_targets.extend(sorted(overlap_targets))
    
    # Add validated DNS targets
    final_targets.extend(sorted(results['validated_targets']))
    
    # Add connectivity-only targets
    final_targets.extend(sorted(connectivity_only))
    
    # Add DNS-only targets (might be offline but worth trying)
    final_targets.extend(sorted(results['dns_only_targets']))
    
    results['final_target_list'] = final_targets
    
    # Generate summary
    results['validation_summary'] = {
        'total_unique_targets': len(final_targets),
        'dns_discovered': len(dns_targets),
        'connectivity_confirmed': len(conn_targets),
        'high_confidence': len(overlap_targets) + len(results['validated_targets']),
        'medium_confidence': len(connectivity_only),
        'low_confidence': len(results['dns_only_targets']),
        'coverage_improvement': len(final_targets) - max(len(dns_targets), len(conn_targets))
    }
    
    print(f"\n{Colors.OKGREEN}[+] Integration Complete:{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Final target list: {len(final_targets)} unique targets{Colors.ENDC}")
    print(f"{Colors.OKGREEN}[*] High confidence: {results['validation_summary']['high_confidence']} targets (DNS + connectivity){Colors.ENDC}")
    print(f"{Colors.WARNING}[*] Medium confidence: {results['validation_summary']['medium_confidence']} targets (connectivity only){Colors.ENDC}")
    print(f"{Colors.OKCYAN}[*] Low confidence: {results['validation_summary']['low_confidence']} targets (DNS only){Colors.ENDC}")
    
    coverage_improvement = results['validation_summary']['coverage_improvement']
    if coverage_improvement > 0:
        print(f"{Colors.OKGREEN}[+] Combined approach found {coverage_improvement} additional targets vs single method{Colors.ENDC}")
    
    return results

def generate_comprehensive_reachability_reports(final_results, dns_intelligence, infrastructure, 
                                              subnet_results, decision_log, reachability_dir, 
                                              timestamp, total_time, standalone, debug):
    """Generate comprehensive reports combining DNS and connectivity intelligence."""
    
    final_targets = final_results['final_target_list']
    
    # 1. Main reachable targets file (prioritized)
    reachable_file = os.path.join(reachability_dir, f"reachable_targets_{timestamp}.txt")
    with open(reachable_file, 'w') as f:
        f.write(f"# Comprehensive Network Reachability Assessment Results\n")
        f.write(f"# Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"# Assessment Duration: {total_time:.2f} seconds\n")
        f.write(f"# Method: DNS Intelligence + Connectivity Testing\n")
        f.write(f"# Total Targets: {len(final_targets)}\n")
        f.write(f"# High Confidence: {final_results['validation_summary']['high_confidence']}\n")
        f.write(f"# Medium Confidence: {final_results['validation_summary']['medium_confidence']}\n")
        f.write(f"# Low Confidence: {final_results['validation_summary']['low_confidence']}\n\n")
        f.write(f"# Target Priority Order:\n")
        f.write(f"# 1. DNS + Connectivity confirmed (lines 1-{len(final_results['validated_targets']) + len(set(final_results['dns_confirmed']).intersection(set(final_results['connectivity_confirmed'])))})\n")
        f.write(f"# 2. Connectivity-only confirmed\n") 
        f.write(f"# 3. DNS-only targets\n\n")
        
        for target in final_targets:
            f.write(f"{target}\n")
    
    # 2. High confidence targets only
    high_confidence_file = os.path.join(reachability_dir, f"high_confidence_targets_{timestamp}.txt")
    high_conf_targets = (set(final_results['dns_confirmed']).intersection(set(final_results['connectivity_confirmed'])) | 
                        set(final_results['validated_targets']))
    
    with open(high_confidence_file, 'w') as f:
        f.write(f"# High Confidence Targets Only (DNS + Connectivity Confirmed)\n")
        f.write(f"# Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"# Count: {len(high_conf_targets)}\n\n")
        for target in sorted(high_conf_targets):
            f.write(f"{target}\n")
    
    # 3. Comprehensive assessment report
    assessment_report = os.path.join(reachability_dir, f"comprehensive_assessment_{timestamp}.txt")
    with open(assessment_report, 'w') as f:
        f.write("=" * 80 + "\n")
        f.write("COMPREHENSIVE NETWORK REACHABILITY ASSESSMENT\n")
        f.write("=" * 80 + "\n\n")
        f.write(f"Assessment Date: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"Total Duration: {total_time:.2f} seconds\n")
        f.write(f"Assessment Method: DNS Intelligence + Network Connectivity Testing\n\n")
        
        f.write("METHODOLOGY:\n")
        f.write("-" * 12 + "\n")
        f.write("1. DNS Intelligence Gathering:\n")
        f.write("   - Zone transfer attempts\n")
        f.write("   - Reverse DNS sweeps\n")
        f.write("   - Forward DNS enumeration\n")
        f.write("2. Network Connectivity Testing:\n")
        f.write("   - Subnet-based reachability\n")
        f.write("   - Infrastructure validation\n")
        f.write("3. Results Integration and Validation\n\n")
        
        f.write("DNS INTELLIGENCE RESULTS:\n")
        f.write("-" * 25 + "\n")
        dns_summary = dns_intelligence['dns_summary']
        f.write(f"Total DNS-discovered hosts: {dns_summary['total_discovered_hosts']}\n")
        f.write(f"Zone transfer hosts: {dns_summary['zone_transfer_hosts']}\n")
        f.write(f"Reverse DNS hosts: {dns_summary['reverse_dns_hosts']}\n")
        f.write(f"Forward DNS hosts: {dns_summary['forward_dns_hosts']}\n")
        f.write(f"Discovered domains: {dns_summary['discovered_domains']}\n\n")
        
        f.write("CONNECTIVITY TESTING RESULTS:\n")
        f.write("-" * 29 + "\n")
        f.write(f"Reachable subnets: {len(subnet_results['reachable_subnets'])}\n")
        f.write(f"Unreachable subnets: {len(subnet_results['unreachable_subnets'])}\n")
        f.write(f"Connectivity-confirmed targets: {len(final_results['connectivity_confirmed'])}\n\n")
        
        f.write("FINAL ASSESSMENT SUMMARY:\n")
        f.write("-" * 25 + "\n")
        summary = final_results['validation_summary']
        f.write(f"Total Unique Targets: {summary['total_unique_targets']}\n")
        f.write(f"High Confidence (DNS + Connectivity): {summary['high_confidence']}\n")
        f.write(f"Medium Confidence (Connectivity only): {summary['medium_confidence']}\n")
        f.write(f"Low Confidence (DNS only): {summary['low_confidence']}\n")
        f.write(f"Coverage Improvement: +{summary['coverage_improvement']} targets vs single method\n\n")
        
        f.write("RECOMMENDATIONS:\n")
        f.write("-" * 15 + "\n")
        f.write("1. Prioritize high-confidence targets for initial scanning\n")
        f.write("2. Use medium-confidence targets for comprehensive coverage\n")
        f.write("3. Test low-confidence targets last (may be offline)\n")
        if dns_summary['zone_transfer_hosts'] > 0:
            f.write("4. Zone transfers were successful - high intelligence value\n")
        if summary['coverage_improvement'] > 0:
            f.write(f"5. Combined approach provided {summary['coverage_improvement']} additional targets\n")
    
    # Print final summary
    print(f"\n{Colors.OKGREEN}{'='*70}{Colors.ENDC}")
    print(f"{Colors.OKGREEN}[+] Comprehensive Network Reachability Assessment Complete!{Colors.ENDC}")
    print(f"{Colors.OKGREEN}[+] Assessment Duration: {total_time:.2f} seconds{Colors.ENDC}")
    print(f"{Colors.OKGREEN}[+] Total Targets Found: {len(final_targets)}{Colors.ENDC}")
    print(f"{Colors.OKGREEN}[+] High Confidence Targets: {final_results['validation_summary']['high_confidence']}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] All targets: {reachable_file}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] High confidence only: {high_confidence_file}{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Full assessment report: {assessment_report}{Colors.ENDC}")
    print(f"{Colors.OKGREEN}{'='*70}{Colors.ENDC}")
    
    return final_targets, [], []  # Compatible with existing code

def run_nmap_discovery(base_dir, targets, stealth=False, quick=False, enhanced=False, debug=False):
    """Run comprehensive port scanning with enhanced options."""
    nmap_dir = os.path.join(base_dir, "scans", "nmap")
    target_file = os.path.join(nmap_dir, "targets.txt")
    
    # Write targets to file
    with open(target_file, 'w') as f:
        f.write('\n'.join(targets))
    
    print(f"{Colors.OKGREEN}[+] Starting {'Enhanced ' if enhanced else ''}Nmap Port Scanning{Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Targets already validated by reachability testing{Colors.ENDC}")
    
    # Use targets directly since they've been validated by reachability testing
    alive_hosts = targets
    
    # Write alive hosts file for consistency with existing structure
    alive_file = os.path.join(nmap_dir, "alive_hosts.txt")
    with open(alive_file, 'w') as f:
        f.write('\n'.join(alive_hosts))
    
    # Copy to targets directory for reference
    alive_copy = os.path.join(base_dir, "targets", "alive_hosts.txt")
    with open(alive_copy, 'w') as f:
        f.write('\n'.join(alive_hosts))
    
    print(f"{Colors.OKGREEN}[+] Scanning {len(alive_hosts)} validated targets{Colors.ENDC}")
    
    # For large target lists, break into smaller chunks to avoid timeouts
    chunk_size = 200 if len(alive_hosts) > 500 else len(alive_hosts)
    target_chunks = [alive_hosts[i:i + chunk_size] for i in range(0, len(alive_hosts), chunk_size)]
    
    if len(target_chunks) > 1:
        print(f"{Colors.OKBLUE}[*] Breaking {len(alive_hosts)} targets into {len(target_chunks)} chunks of ~{chunk_size} for efficiency{Colors.ENDC}")
    
    # Enhanced scanning modes
    timing = "-T2" if stealth else "-T3"
    
    # Adjust timeouts based on target count for efficiency
    if len(alive_hosts) > 500:
        # Very large scan - use shorter timeouts
        host_timeout = "120s"
        max_retries = "1"
        print(f"{Colors.OKBLUE}[*] Large target set detected - using shorter timeouts for efficiency{Colors.ENDC}")
    elif len(alive_hosts) > 200:
        # Medium scan - moderate timeouts
        host_timeout = "240s"
        max_retries = "1"
    else:
        # Small scan - normal timeouts
        host_timeout = "300s"
        max_retries = "2"
    
    if quick:
        # Quick mode: Low hanging fruit ports only, skip UDP for speed
        print(f"{Colors.OKBLUE}[*] Running Quick Mode: Low hanging fruit TCP ports (no UDP){Colors.ENDC}")
        
        # Focus on most common services for quick wins
        quick_ports = "21,22,23,25,53,80,110,135,139,143,443,993,995,1723,3306,3389,5432,5900,8080,8443"
        
        # Process chunks for quick scan
        for i, chunk in enumerate(target_chunks, 1):
            chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
            with open(chunk_file, 'w') as f:
                f.write('\n'.join(chunk))
                
            print(f"{Colors.OKCYAN}[*] Quick scan chunk {i}/{len(target_chunks)} ({len(chunk)} targets)...{Colors.ENDC}")
            tcp_cmd = f"nmap -sSV -Pn -n -p {quick_ports} {timing} --max-parallelism 50 --max-retries {max_retries} --host-timeout {host_timeout} -iL {chunk_file} -oA {os.path.join(nmap_dir, f'tcp_quick_chunk_{i}')}"
            
            result = run_command(tcp_cmd, debug=debug, stealth=stealth)
            if result is None:
                print(f"{Colors.WARNING}[!] Quick scan chunk {i} timed out, continuing with next chunk...{Colors.ENDC}")
        
    else:
        # Standard mode: Top 1000 TCP + 500 UDP, then enhanced scans
        print(f"{Colors.OKBLUE}[*] Running Standard Mode: Progressive scanning for fast results{Colors.ENDC}")
        
        # Phase 1: TCP top 1000 ports in chunks
        print(f"{Colors.OKBLUE}[*] Phase 1: Running TCP top 1000 ports...{Colors.ENDC}")
        for i, chunk in enumerate(target_chunks, 1):
            chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
            with open(chunk_file, 'w') as f:
                f.write('\n'.join(chunk))
                
            print(f"{Colors.OKCYAN}[*] TCP scan chunk {i}/{len(target_chunks)} ({len(chunk)} targets)...{Colors.ENDC}")
            tcp_cmd = f"nmap -sSV -Pn -n --top-ports 1000 {timing} --max-parallelism 50 --max-retries {max_retries} --host-timeout {host_timeout} -iL {chunk_file} -oA {os.path.join(nmap_dir, f'tcp_top1000_chunk_{i}')}"
            
            tcp_result = run_command(tcp_cmd, debug=debug, stealth=stealth)
            if tcp_result is None:
                print(f"{Colors.WARNING}[!] TCP chunk {i} timed out, continuing with next chunk...{Colors.ENDC}")
        
        # Phase 2: UDP top 500 ports in chunks
        print(f"{Colors.OKBLUE}[*] Phase 2: Running UDP top 500 ports...{Colors.ENDC}")
        for i, chunk in enumerate(target_chunks, 1):
            chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
            
            print(f"{Colors.OKCYAN}[*] UDP scan chunk {i}/{len(target_chunks)} ({len(chunk)} targets)...{Colors.ENDC}")
            udp_cmd = f"nmap -sU --top-ports 500 {timing} --max-parallelism 25 --max-retries 1 -iL {chunk_file} -oA {os.path.join(nmap_dir, f'udp_top500_chunk_{i}')}"
            
            udp_result = run_command(udp_cmd, debug=debug, stealth=stealth)
            if udp_result is None:
                print(f"{Colors.WARNING}[!] UDP chunk {i} timed out, continuing with next chunk...{Colors.ENDC}")
        
        # Phase 3: Enhanced scans if enhanced mode enabled
        if enhanced:
            print(f"{Colors.OKBLUE}[*] Phase 3: Running Enhanced Comprehensive Scans{Colors.ENDC}")
            
            for i, chunk in enumerate(target_chunks, 1):
                chunk_file = os.path.join(nmap_dir, f"chunk_{i}_targets.txt")
                
                # Full TCP port scan with comprehensive service detection
                print(f"{Colors.OKCYAN}[*] Enhanced TCP scan chunk {i}/{len(target_chunks)}...{Colors.ENDC}")
                tcp_full_cmd = f"nmap -sT -sV -sC -A --version-all {timing} -p- --max-parallelism 50 --max-retries {max_retries} --host-timeout 600s -iL {chunk_file} -oA {os.path.join(nmap_dir, f'tcp_full_enhanced_chunk_{i}')}"
                
                tcp_full_result = run_command(tcp_full_cmd, debug=debug, stealth=stealth)
                if tcp_full_result is None:
                    print(f"{Colors.WARNING}[!] Enhanced TCP chunk {i} timed out, continuing...{Colors.ENDC}")
                
                # Comprehensive UDP scan
                print(f"{Colors.OKCYAN}[*] Enhanced UDP scan chunk {i}/{len(target_chunks)}...{Colors.ENDC}")
                udp_enhanced_cmd = f"nmap -sU -sV --top-ports 1000 {timing} --max-parallelism 25 --max-retries 1 -iL {chunk_file} -oA {os.path.join(nmap_dir, f'udp_top1000_enhanced_chunk_{i}')}"
                
                udp_enhanced_result = run_command(udp_enhanced_cmd, debug=debug, stealth=stealth)
                if udp_enhanced_result is None:
                    print(f"{Colors.WARNING}[!] Enhanced UDP chunk {i} timed out, continuing...{Colors.ENDC}")
    
    # Phase 4: Additional scanning phases (post-discovery enumeration)
    print(f"{Colors.OKBLUE}[*] Phase 4: Post-Discovery Service Enumeration{Colors.ENDC}")
    
    # DNS enumeration
    print(f"{Colors.OKCYAN}[*] Running DNS enumeration...{Colors.ENDC}")
    run_enhanced_dns_enumeration(base_dir, alive_hosts, debug)
    
    # SMB enumeration  
    print(f"{Colors.OKCYAN}[*] Running SMB enumeration...{Colors.ENDC}")
    run_enhanced_smb_enumeration(base_dir, alive_hosts, debug)
    
    # Web enumeration
    print(f"{Colors.OKCYAN}[*] Running web enumeration...{Colors.ENDC}")
    run_enhanced_web_enumeration(base_dir, alive_hosts, stealth, debug)
    
    # Final summary
    print(f"\n{Colors.OKGREEN}[+] Nmap Discovery Phase Complete{Colors.ENDC}")
    if len(target_chunks) > 1:
        print(f"{Colors.OKBLUE}[*] Processed {len(target_chunks)} chunks covering {len(alive_hosts)} targets{Colors.ENDC}")
    
    # Count completed scan files
    scan_files = list(Path(nmap_dir).glob("*.gnmap"))
    print(f"{Colors.OKBLUE}[*] Generated {len(scan_files)} nmap result files{Colors.ENDC}")
    
    # Parse and summarize discovered services
    discovered_services = discover_services_from_nmap(base_dir)
    total_services = sum(len(services) for services in discovered_services.values())
    hosts_with_services = len(discovered_services)
    
    if hosts_with_services > 0:
        print(f"{Colors.OKGREEN}[*] Discovered {total_services} services across {hosts_with_services} responsive hosts{Colors.ENDC}")
        
        # Show top service types
        service_counts = {}
        for host_services in discovered_services.values():
            for service in host_services:
                service_name = service['service']
                service_counts[service_name] = service_counts.get(service_name, 0) + 1
        
        top_services = sorted(service_counts.items(), key=lambda x: x[1], reverse=True)[:5]
        if top_services:
            print(f"{Colors.OKBLUE}[*] Top services found: {', '.join([f'{svc}({count})' for svc, count in top_services])}{Colors.ENDC}")
    else:
        print(f"{Colors.WARNING}[!] No responsive hosts with open ports discovered{Colors.ENDC}")
        print(f"{Colors.OKCYAN}[*] This could be due to: firewalls, timeouts, or network filtering{Colors.ENDC}")
    
    return alive_hosts

def run_enhanced_web_enumeration(base_dir, targets, stealth=False, debug=False):
    """Enhanced web enumeration with multiple tools."""
    web_dir = os.path.join(base_dir, "scans", "web")
    
    print(f"{Colors.OKGREEN}[+] Starting Enhanced Web Enumeration{Colors.ENDC}")
    
    # Discover web services from nmap results
    services = discover_services_from_nmap(base_dir)
    web_services = []
    
    for ip, service_list in services.items():
        for service in service_list:
            if (service['service'] in ['http', 'https', 'http-proxy', 'ssl/http'] or 
                service['port'] in ['80', '443', '8080', '8443', '8000', '8888']):
                
                protocol = "https" if service['ssl'] or service['port'] in ['443', '8443'] else "http"
                web_services.append({
                    'ip': ip,
                    'port': service['port'],
                    'protocol': protocol,
                    'service': service['service']
                })
    
    if not web_services:
        print(f"{Colors.WARNING}[!] No web services identified{Colors.ENDC}")
        return
    
    print(f"{Colors.OKGREEN}[+] Found {len(web_services)} web services for enhanced enumeration{Colors.ENDC}")
    
    for web_service in web_services:
        ip = web_service['ip']
        port = web_service['port']
        protocol = web_service['protocol']
        base_url = f"{protocol}://{ip}:{port}"
        
        print(f"{Colors.OKCYAN}[*] Enhanced enumeration of {base_url}{Colors.ENDC}")
        
        # Create service-specific directory
        service_dir = os.path.join(web_dir, f"{protocol}_{port}")
        os.makedirs(service_dir, exist_ok=True)
        
        # 1. Basic HTTP Information
        print(f"{Colors.OKCYAN}[*] Gathering basic HTTP information...{Colors.ENDC}")
        
        # Curl for headers and basic info
        curl_cmd = f"curl -sSikL --max-time 10 {base_url}/"
        curl_output = os.path.join(service_dir, f"curl_headers_{ip}_{port}.txt")
        run_command(curl_cmd, curl_output, debug=debug, stealth=stealth)
        
        # Curl robots.txt
        robots_cmd = f"curl -sSik --max-time 10 {base_url}/robots.txt"
        robots_output = os.path.join(service_dir, f"robots_{ip}_{port}.txt")
        run_command(robots_cmd, robots_output, debug=debug, stealth=stealth)
        
        # 2. WhatWeb for technology identification
        which_whatweb = run_command("which whatweb", debug=debug)
        if which_whatweb and which_whatweb.returncode == 0:
            print(f"{Colors.OKCYAN}[*] Running WhatWeb technology identification...{Colors.ENDC}")
            whatweb_cmd = f"whatweb --color=never --no-errors -a 3 -v {base_url}"
            whatweb_output = os.path.join(service_dir, f"whatweb_{ip}_{port}.txt")
            run_command(whatweb_cmd, whatweb_output, debug=debug, stealth=stealth)
        
        # 3. Nikto vulnerability scanning
        which_nikto = run_command("which nikto", debug=debug)
        if which_nikto and which_nikto.returncode == 0:
            print(f"{Colors.OKCYAN}[*] Running Nikto vulnerability scan...{Colors.ENDC}")
            nikto_cmd = f"nikto -ask=no -h {base_url}"
            if stealth:
                nikto_cmd += " -T 2"
            nikto_output = os.path.join(service_dir, f"nikto_{ip}_{port}.txt")
            run_command(nikto_cmd, nikto_output, debug=debug, stealth=stealth)
        
        # 4. Directory brute forcing with gobuster (prioritized) and feroxbuster
        print(f"{Colors.OKCYAN}[*] Running directory enumeration...{Colors.ENDC}")
        
        # Primary wordlists to try (in order of preference)
        wordlists = [
            "/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt",
            "/usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt",
            "/usr/share/wordlists/dirb/common.txt",
            "/usr/share/seclists/Discovery/Web-Content/common.txt"
        ]
        
        # Find the first available wordlist
        wordlist_to_use = None
        for wordlist in wordlists:
            if os.path.exists(wordlist):
                wordlist_to_use = wordlist
                break
        
        if not wordlist_to_use:
            print(f"{Colors.WARNING}[!] No wordlists found for directory enumeration{Colors.ENDC}")
            wordlist_to_use = "/usr/share/wordlists/dirb/common.txt"  # fallback
        
        # Try directory enumeration tools in priority order
        dir_tools = [
            ("gobuster", f"gobuster dir -u {base_url}/ -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -t 10 -k --no-error -q -o"),
            ("feroxbuster", f"feroxbuster -u {base_url}/ -t 10 -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -v -k -q -o")
        ]
        
        # Adjust for stealth mode
        if stealth:
            dir_tools = [
                ("gobuster", f"gobuster dir -u {base_url}/ -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -t 5 -k --no-error -q --delay 200ms -o"),
                ("feroxbuster", f"feroxbuster -u {base_url}/ -t 5 -w {wordlist_to_use} -x txt,html,php,asp,aspx,jsp,xml,js,css,zip,tar,gz,bak,old,log -v -k -q --rate-limit 10 -o")
            ]
        
        for tool_name, tool_cmd in dir_tools:
            which_tool = run_command(f"which {tool_name}", debug=debug)
            if which_tool and which_tool.returncode == 0:
                print(f"{Colors.OKCYAN}[*] Running {tool_name} directory enumeration...{Colors.ENDC}")
                tool_output = os.path.join(service_dir, f"{tool_name}_{ip}_{port}.txt")
                full_cmd = f"{tool_cmd} {tool_output}"
                run_command(full_cmd, debug=debug, stealth=stealth)
                break
        
        # 5. SSL/TLS Analysis if HTTPS
        if protocol == "https":
            print(f"{Colors.OKCYAN}[*] Running SSL/TLS analysis...{Colors.ENDC}")
            
            # SSLScan
            which_sslscan = run_command("which sslscan", debug=debug)
            if which_sslscan and which_sslscan.returncode == 0:
                sslscan_cmd = f"sslscan --show-certificate --no-colour {ip}:{port}"
                sslscan_output = os.path.join(service_dir, f"sslscan_{ip}_{port}.txt")
                run_command(sslscan_cmd, sslscan_output, debug=debug, stealth=stealth)
            
            # TestSSL.sh if available
            which_testssl = run_command("which testssl.sh", debug=debug)
            if which_testssl and which_testssl.returncode == 0:
                testssl_cmd = f"testssl.sh {ip}:{port}"
                testssl_output = os.path.join(service_dir, f"testssl_{ip}_{port}.txt")
                run_command(testssl_cmd, testssl_output, debug=debug, stealth=stealth)
        
        # 6. Add manual commands for further testing
        manual_commands = [
            f"# Manual enumeration commands for {base_url}",
            f"wpscan --url {base_url}/ --enumerate vp,vt,tt,cb,dbe,u,m",
            f"cmsmap -t {base_url}/",
            f"python3 /opt/dirsearch/dirsearch.py -u {base_url}/ -e *",
            f"ffuf -u {base_url}/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt",
            f"gobuster dir -u {base_url}/ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -x txt,html,php,asp,aspx,jsp -t 50",
            f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} http-post-form '/login.php:username=^USER^&password=^PASS^:invalid'",
            f"sqlmap -u '{base_url}/?id=1' --batch --banner",
            f"nuclei -u {base_url} -t /root/nuclei-templates/"
        ]
        
        add_manual_command(base_dir, f"Web Service {base_url}", manual_commands)

def run_enhanced_smb_enumeration(base_dir, targets, debug=False):
    """Enhanced SMB enumeration with multiple tools and techniques."""
    smb_dir = os.path.join(base_dir, "scans", "smb")
    
    print(f"{Colors.OKGREEN}[+] Starting Enhanced SMB Enumeration{Colors.ENDC}")
    
    # Discover SMB services
    services = discover_services_from_nmap(base_dir)
    smb_targets = []
    
    for ip, service_list in services.items():
        for service in service_list:
            if (service['service'] in ['microsoft-ds', 'smb', 'netbios-ssn'] or 
                service['port'] in ['139', '445']):
                if ip not in smb_targets:
                    smb_targets.append(ip)
    
    if not smb_targets:
        print(f"{Colors.WARNING}[!] No SMB services identified{Colors.ENDC}")
        return
    
    print(f"{Colors.OKGREEN}[+] Found {len(smb_targets)} SMB targets for enhanced enumeration{Colors.ENDC}")
    
    for target in smb_targets:
        print(f"{Colors.OKCYAN}[*] Enhanced SMB enumeration of {target}{Colors.ENDC}")
        
        # 1. Enum4Linux comprehensive enumeration
        which_enum4linux = run_command("which enum4linux", debug=debug)
        if which_enum4linux and which_enum4linux.returncode == 0:
            print(f"{Colors.OKCYAN}[*] Running enum4linux comprehensive scan...{Colors.ENDC}")
            enum4linux_cmd = f"enum4linux -a -M -l -d {target}"
            enum4linux_output = os.path.join(smb_dir, f"enum4linux_{target}.txt")
            run_command(enum4linux_cmd, enum4linux_output, debug=debug)
        
        # 2. SMBClient share enumeration
        which_smbclient = run_command("which smbclient", debug=debug)
        if which_smbclient and which_smbclient.returncode == 0:
            print(f"{Colors.OKCYAN}[*] Running smbclient share enumeration...{Colors.ENDC}")
            smbclient_cmd = f"smbclient -L //{target} -N -I {target}"
            smbclient_output = os.path.join(smb_dir, f"smbclient_{target}.txt")
            run_command(smbclient_cmd, smbclient_output, debug=debug)
        
        # 3. SMBMap detailed enumeration
        which_smbmap = run_command("which smbmap", debug=debug)
        if which_smbmap and which_smbmap.returncode == 0:
            print(f"{Colors.OKCYAN}[*] Running smbmap detailed enumeration...{Colors.ENDC}")
            
            # Share permissions
            smbmap_cmd1 = f"smbmap -H {target} -u null -p ''"
            smbmap_output1 = os.path.join(smb_dir, f"smbmap_shares_{target}.txt")
            run_command(smbmap_cmd1, smbmap_output1, debug=debug)
            
            # Recursive listing
            smbmap_cmd2 = f"smbmap -H {target} -u null -p '' -r"
            smbmap_output2 = os.path.join(smb_dir, f"smbmap_recursive_{target}.txt")
            run_command(smbmap_cmd2, smbmap_output2, debug=debug)
        
        # 4. NBTScan NetBIOS enumeration
        which_nbtscan = run_command("which nbtscan", debug=debug)
        if which_nbtscan and which_nbtscan.returncode == 0:
            print(f"{Colors.OKCYAN}[*] Running nbtscan NetBIOS enumeration...{Colors.ENDC}")
            nbtscan_cmd = f"nbtscan -rvh {target}"
            nbtscan_output = os.path.join(smb_dir, f"nbtscan_{target}.txt")
            run_command(nbtscan_cmd, nbtscan_output, debug=debug)
        
        # 5. RPCClient enumeration
        which_rpcclient = run_command("which rpcclient", debug=debug)
        if which_rpcclient and which_rpcclient.returncode == 0:
            print(f"{Colors.OKCYAN}[*] Running rpcclient enumeration...{Colors.ENDC}")
            rpcclient_cmd = f'echo "enumdomusers; enumdomgroups; querydominfo; exit" | rpcclient -U "" {target}'
            rpcclient_output = os.path.join(smb_dir, f"rpcclient_{target}.txt")
            run_command(rpcclient_cmd, rpcclient_output, debug=debug)
        
        # 6. Advanced Nmap SMB scripts
        print(f"{Colors.OKCYAN}[*] Running advanced Nmap SMB scripts...{Colors.ENDC}")
        nmap_smb_cmd = f"nmap -p 139,445 --script 'smb-os-discovery,smb-security-mode,smb-enum-shares,smb-enum-users,smb-enum-domains,smb-enum-groups,smb-enum-processes,smb-enum-sessions,smb-server-stats' {target}"
        nmap_smb_output = os.path.join(smb_dir, f"nmap_smb_advanced_{target}.txt")
        run_command(nmap_smb_cmd, nmap_smb_output, debug=debug)
        
        # 7. Add manual commands
        manual_commands = [
            f"# Manual SMB enumeration commands for {target}",
            f"crackmapexec smb {target} --shares",
            f"crackmapexec smb {target} --users",
            f"crackmapexec smb {target} --groups",
            f"crackmapexec smb {target} --pass-pol",
            f"impacket-samrdump {target}",
            f"impacket-rpcdump {target}",
            f"smbclient //{target}/SHARE -U username%password",
            f"mount -t cifs //{target}/SHARE /mnt/smb -o username=,password=",
            f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {target} smb"
        ]
        
        add_manual_command(base_dir, f"SMB Service {target}", manual_commands)

def run_enhanced_database_enumeration(base_dir, targets, debug=False):
    """Enhanced database enumeration for various database services."""
    db_dir = os.path.join(base_dir, "scans", "databases")
    os.makedirs(db_dir, exist_ok=True)
    
    print(f"{Colors.OKGREEN}[+] Starting Enhanced Database Enumeration{Colors.ENDC}")
    
    # Discover database services
    services = discover_services_from_nmap(base_dir)
    db_services = {}
    
    for ip, service_list in services.items():
        for service in service_list:
            service_name = service['service'].lower()
            port = service['port']
            
            # Identify database services
            if any(db in service_name for db in ['mysql', 'mssql', 'postgresql', 'oracle', 'mongodb', 'redis']):
                if ip not in db_services:
                    db_services[ip] = []
                db_services[ip].append({
                    'service': service_name,
                    'port': port,
                    'protocol': service['protocol']
                })
    
    if not db_services:
        print(f"{Colors.WARNING}[!] No database services identified{Colors.ENDC}")
        return
    
    print(f"{Colors.OKGREEN}[+] Found database services on {len(db_services)} hosts{Colors.ENDC}")
    
    for ip, services in db_services.items():
        for service in services:
            service_name = service['service']
            port = service['port']
            
            print(f"{Colors.OKCYAN}[*] Enhanced enumeration of {service_name} on {ip}:{port}{Colors.ENDC}")
            
            # MySQL enumeration
            if 'mysql' in service_name:
                print(f"{Colors.OKCYAN}[*] Running MySQL enumeration...{Colors.ENDC}")
                
                # Nmap MySQL scripts
                mysql_nmap_cmd = f"nmap -p {port} --script 'mysql-audit,mysql-databases,mysql-dump-hashes,mysql-empty-password,mysql-enum,mysql-info,mysql-query,mysql-users,mysql-variables,mysql-vuln-cve2012-2122' {ip}"
                mysql_output = os.path.join(db_dir, f"mysql_nmap_{ip}_{port}.txt")
                run_command(mysql_nmap_cmd, mysql_output, debug=debug)
                
                # Manual commands
                mysql_manual = [
                    f"# MySQL enumeration for {ip}:{port}",
                    f"mysql -h {ip} -P {port} -u root -p",
                    f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} mysql",
                    f"ncrack -v --user root -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip}:{port}"
                ]
                add_manual_command(base_dir, f"MySQL {ip}:{port}", mysql_manual)
            
            # MSSQL enumeration
            elif 'mssql' in service_name or 'ms-sql' in service_name:
                print(f"{Colors.OKCYAN}[*] Running MSSQL enumeration...{Colors.ENDC}")
                
                # Nmap MSSQL scripts
                mssql_nmap_cmd = f"nmap -p {port} --script 'ms-sql-info,ms-sql-empty-password,ms-sql-xp-cmdshell,ms-sql-config,ms-sql-ntlm-info,ms-sql-tables,ms-sql-hasdbaccess,ms-sql-query' {ip}"
                mssql_output = os.path.join(db_dir, f"mssql_nmap_{ip}_{port}.txt")
                run_command(mssql_nmap_cmd, mssql_output, debug=debug)
                
                # Manual commands
                mssql_manual = [
                    f"# MSSQL enumeration for {ip}:{port}",
                    f"impacket-mssqlclient sa@{ip} -port {port}",
                    f"sqsh -S {ip}:{port} -U sa -P",
                    f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} mssql"
                ]
                add_manual_command(base_dir, f"MSSQL {ip}:{port}", mssql_manual)
            
            # PostgreSQL enumeration
            elif 'postgresql' in service_name:
                print(f"{Colors.OKCYAN}[*] Running PostgreSQL enumeration...{Colors.ENDC}")
                
                # Nmap PostgreSQL scripts
                pgsql_nmap_cmd = f"nmap -p {port} --script 'pgsql-brute' {ip}"
                pgsql_output = os.path.join(db_dir, f"postgresql_nmap_{ip}_{port}.txt")
                run_command(pgsql_nmap_cmd, pgsql_output, debug=debug)
                
                # Manual commands
                pgsql_manual = [
                    f"# PostgreSQL enumeration for {ip}:{port}",
                    f"psql -h {ip} -p {port} -U postgres",
                    f"hydra -L /usr/share/seclists/Usernames/top-usernames-shortlist.txt -P /usr/share/seclists/Passwords/darkweb2017-top100.txt {ip} postgres"
                ]
                add_manual_command(base_dir, f"PostgreSQL {ip}:{port}", pgsql_manual)
            
            # MongoDB enumeration
            elif 'mongodb' in service_name or 'mongod' in service_name:
                print(f"{Colors.OKCYAN}[*] Running MongoDB enumeration...{Colors.ENDC}")
                
                # Nmap MongoDB scripts
                mongo_nmap_cmd = f"nmap -p {port} --script 'mongodb-databases,mongodb-info' {ip}"
                mongo_output = os.path.join(db_dir, f"mongodb_nmap_{ip}_{port}.txt")
                run_command(mongo_nmap_cmd, mongo_output, debug=debug)
                
                # Manual commands
                mongo_manual = [
                    f"# MongoDB enumeration for {ip}:{port}",
                    f"mongo {ip}:{port}",
                    f"mongo {ip}:{port}/admin --eval 'db.runCommand(\"listCollections\")'",
                    f"mongo {ip}:{port} --eval 'show dbs'"
                ]
                add_manual_command(base_dir, f"MongoDB {ip}:{port}", mongo_manual)
            
            # Redis enumeration
            elif 'redis' in service_name:
                print(f"{Colors.OKCYAN}[*] Running Redis enumeration...{Colors.ENDC}")
                
                # Nmap Redis scripts
                redis_nmap_cmd = f"nmap -p {port} --script 'redis-info' {ip}"
                redis_output = os.path.join(db_dir, f"redis_nmap_{ip}_{port}.txt")
                run_command(redis_nmap_cmd, redis_output, debug=debug)
                
                # Redis-cli enumeration
                which_redis = run_command("which redis-cli", debug=debug)
                if which_redis and which_redis.returncode == 0:
                    redis_info_cmd = f"redis-cli -h {ip} -p {port} INFO"
                    redis_info_output = os.path.join(db_dir, f"redis_info_{ip}_{port}.txt")
                    run_command(redis_info_cmd, redis_info_output, debug=debug)
                
                # Manual commands
                redis_manual = [
                    f"# Redis enumeration for {ip}:{port}",
                    f"redis-cli -h {ip} -p {port}",
                    f"redis-cli -h {ip} -p {port} CONFIG GET '*'",
                    f"redis-cli -h {ip} -p {port} INFO",
                    f"redis-cli -h {ip} -p {port} CLIENT LIST"
                ]
                add_manual_command(base_dir, f"Redis {ip}:{port}", redis_manual)

def run_enhanced_dns_enumeration(base_dir, targets, debug=False):
    """Enhanced DNS enumeration with subdomain discovery and zone transfers."""
    dns_dir = os.path.join(base_dir, "scans", "dns")
    
    print(f"{Colors.OKGREEN}[+] Starting Enhanced DNS Enumeration{Colors.ENDC}")
    
    # Classify targets and discover domains
    rfc1918_networks, non_rfc1918_ips, hostnames = classify_network_ranges(targets)
    
    # Discover additional domains from reverse lookups
    discovered_domains = set(hostnames)
    
    # Parse DNS output files for additional domains
    for dns_file in Path(dns_dir).glob("*.txt"):
        try:
            with open(dns_file, 'r') as f:
                content = f.read()
                domain_patterns = [
                    r'([a-zA-Z0-9-]+\.(?:[a-zA-Z]{2,})+)',
                    r'([a-zA-Z0-9-]+\.(?:local|corp|internal|lan|domain|ad))',
                ]
                
                for pattern in domain_patterns:
                    matches = re.findall(pattern, content, re.IGNORECASE)
                    for match in matches:
                        if '.' in match and not match.startswith('.'):
                            domain = match.strip('.')
                            if len(domain.split('.')) >= 2:
                                discovered_domains.add(domain)
        except Exception as e:
            if debug:
                print(f"{Colors.WARNING}[!] Error parsing {dns_file}: {e}{Colors.ENDC}")
    
    # Enhanced subdomain enumeration
    if discovered_domains:
        print(f"{Colors.OKBLUE}[*] Enhanced subdomain enumeration for {len(discovered_domains)} domains{Colors.ENDC}")
        
        subdomain_dir = os.path.join(dns_dir, "subdomains")
        os.makedirs(subdomain_dir, exist_ok=True)
        
        for domain in discovered_domains:
            if len(domain.split('.')) >= 2:
                print(f"{Colors.OKCYAN}[*] Comprehensive subdomain enumeration for {domain}{Colors.ENDC}")
                safe_domain = domain.replace('.', '_')
                
                # Multiple subdomain enumeration techniques
                subdomain_tools = [
                    ("sublist3r", f"sublist3r -d {domain} -o"),
                    ("amass", f"amass enum -d {domain} -o"),
                    ("subfinder", f"subfinder -d {domain} -o"),
                    ("assetfinder", f"assetfinder --subs-only {domain}")
                ]
                
                for tool_name, tool_cmd in subdomain_tools:
                    which_tool = run_command(f"which {tool_name}", debug=debug)
                    if which_tool and which_tool.returncode == 0:
                        print(f"{Colors.OKCYAN}[*] Running {tool_name} subdomain enumeration...{Colors.ENDC}")
                        tool_output = os.path.join(subdomain_dir, f"{tool_name}_{safe_domain}.txt")
                        full_cmd = f"{tool_cmd} {tool_output}"
                        run_command(full_cmd, debug=debug)
                
                # Add manual subdomain commands
                subdomain_manual = [
                    f"# Advanced subdomain enumeration for {domain}",
                    f"gobuster dns -d {domain} -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 50",
                    f"python3 /opt/Sublist3r/sublist3r.py -d {domain} -b -t 100",
                    f"amass enum -passive -d {domain}",
                    f"curl -s 'https://dns.bufferover.run/dns?q=.{domain}' | jq -r .FDNS_A[]",
                    f"theHarvester -d {domain} -b all",
                    f"subfinder -d {domain} -all -recursive",
                    f"assetfinder --subs-only {domain} | sort -u"
                ]
                add_manual_command(base_dir, f"Subdomain Enumeration {domain}", subdomain_manual)

def run_quick_reconnaissance(base_dir, targets, debug=False):
    """Run quick reconnaissance scans for immediate results on pre-verified targets."""
    print(f"{Colors.OKGREEN}[+] Phase 1: Quick Reconnaissance (Fast Results){Colors.ENDC}")
    print(f"{Colors.OKBLUE}[*] Skipping connectivity checks - targets already verified by reachability testing{Colors.ENDC}")
    
    # Since targets are already verified as reachable, jump straight to port scanning
    # Fast top ports scan on verified hosts
    print(f"{Colors.OKBLUE}[*] Running fast top ports scan on {len(targets)} verified hosts...{Colors.ENDC}")
    
    # Limit to first 10 targets for quick results
    quick_targets = targets[:10] if len(targets) > 10 else targets
    
    fast_ports_cmd = ["nmap", "-n", "--top-ports", "100", "--max-retries", "1", 
                     "--max-rtt-timeout", "500ms", "--max-scan-delay", "5ms", "-Pn"] + quick_targets
    try:
        result = subprocess.run(fast_ports_cmd, capture_output=True, text=True, timeout=120)
        if result.returncode == 0:
            # Parse for open ports
            current_host = None
            quick_results = []
            for line in result.stdout.split('\n'):
                if "Nmap scan report for" in line:
                    current_host = line.split()[-1]
                    quick_results.append(current_host)
                elif "/tcp" in line and "open" in line:
                    port = line.split('/')[0]
                    service = line.split()[-1] if len(line.split()) > 2 else "unknown"
                    print(f"{Colors.OKGREEN}[+] Quick service found: {current_host}:{port} ({service}){Colors.ENDC}")
            return quick_results
    except subprocess.TimeoutExpired:
        print(f"{Colors.WARNING}[!] Fast port scan timed out, continuing...{Colors.ENDC}")
    
    return targets  # Return original targets if scan fails

def run_enhanced_enumeration(base_dir, targets, stealth=False, debug=False):
    """Run comprehensive enhanced enumeration across all discovered services."""
    print(f"{Colors.OKGREEN}[+] Starting Enhanced Service Enumeration{Colors.ENDC}")
    
    # Enhanced enumeration modules
    enumeration_modules = [
        ("Enhanced Web Enumeration", run_enhanced_web_enumeration),
        ("Enhanced SMB Enumeration", run_enhanced_smb_enumeration),
        ("Enhanced Database Enumeration", run_enhanced_database_enumeration),
        ("Enhanced DNS Enumeration", run_enhanced_dns_enumeration),
    ]
    
    for module_name, module_func in enumeration_modules:
        try:
            print(f"\n{Colors.OKBLUE}[*] Running {module_name}...{Colors.ENDC}")
            if module_name == "Enhanced DNS Enumeration":
                module_func(base_dir, targets, debug=debug)
            else:
                module_func(base_dir, targets, debug=debug)
        except Exception as e:
            print(f"{Colors.FAIL}[!] Error in {module_name}: {e}{Colors.ENDC}")
            if debug:
                import traceback
                traceback.print_exc()

def generate_summary_report(base_dir):
    """Generate a comprehensive summary report of all findings."""
    reports_dir = os.path.join(base_dir, "reports")
    report_file = os.path.join(reports_dir, "trashpanda_summary.txt")
    
    print(f"{Colors.OKGREEN}[+] Generating comprehensive summary report: {report_file}{Colors.ENDC}")
    
    with open(report_file, 'w') as f:
        f.write("=" * 80 + "\n")
        f.write("TRASHPANDA COMPREHENSIVE PENETRATION TESTING REPORT\n")
        f.write("=" * 80 + "\n\n")
        f.write(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"Operator: dmealey\n")
        f.write(f"Engagement Directory: {base_dir}\n\n")
        
        # Enhanced directory structure overview
        f.write("DIRECTORY STRUCTURE:\n")
        f.write("-" * 20 + "\n")
        main_dirs = ["tools", "scans", "logs", "loot", "payloads", "targets", 
                    "screenshots", "reports", "notes", "exploits", "wordlists", "pcaps"]
        
        for main_dir in main_dirs:
            dir_path = os.path.join(base_dir, main_dir)
            if os.path.exists(dir_path):
                file_count = len([f for f in os.listdir(dir_path) 
                                if os.path.isfile(os.path.join(dir_path, f))])
                subdir_count = len([d for d in os.listdir(dir_path) 
                                  if os.path.isdir(os.path.join(dir_path, d))])
                f.write(f"├── {main_dir:15} ({file_count} files, {subdir_count} subdirs)\n")
        
        f.write("\n")
        
        # Enhanced scan results summary
        f.write("SCAN RESULTS SUMMARY:\n")
        f.write("-" * 21 + "\n")
        scans_dir = os.path.join(base_dir, "scans")
        if os.path.exists(scans_dir):
            scan_types = ["nmap", "dns", "snmp", "smb", "web", "ssl", "vulns", 
                         "databases", "ldap", "ftp", "ssh", "reachability"]
            for scan_type in scan_types:
                scan_dir = os.path.join(scans_dir, scan_type)
                if os.path.exists(scan_dir):
                    file_count = len([f for f in os.listdir(scan_dir) 
                                    if os.path.isfile(os.path.join(scan_dir, f))])
                    f.write(f"├── {scan_type.upper():12} scans: {file_count} files\n")
        
        f.write("\n")
        
        # Service discovery summary
        services = discover_services_from_nmap(base_dir)
        if services:
            f.write("DISCOVERED SERVICES:\n")
            f.write("-" * 20 + "\n")
            for ip, service_list in services.items():
                f.write(f"Target: {ip}\n")
                for service in service_list:
                    ssl_indicator = " (SSL)" if service['ssl'] else ""
                    f.write(f"  ├── {service['protocol']}/{service['port']} - {service['service']}{ssl_indicator}\n")
                f.write("\n")
        
        # Key files inventory
        f.write("KEY FILES:\n")
        f.write("-" * 10 + "\n")
        
        key_files = [
            ("targets/alive_hosts.txt", "Live hosts discovered"),
            ("scans/reachability/reachable_targets_*.txt", "Network reachability results"),
            ("scans/nmap/*.gnmap", "Nmap scan results"),
            ("scans/dns/dns_enumeration_summary.txt", "DNS enumeration summary"),
            ("scans/snmp/snmp_enumeration_summary.txt", "SNMP enumeration summary"),
            ("scans/_manual_commands.txt", "Manual commands for further testing"),
            ("pcaps/capture_*.pcap", "Network traffic capture"),
            ("logs/engagement.log", "Engagement activity log")
        ]
        
        for file_pattern, description in key_files:
            file_path = os.path.join(base_dir, file_pattern.replace("*", ""))
            if "*" in file_pattern:
                import glob
                matches = glob.glob(os.path.join(base_dir, file_pattern))
                if matches:
                    f.write(f"✓ {description}: {len(matches)} file(s)\n")
                else:
                    f.write(f"✗ {description}: Not found\n")
            elif os.path.exists(file_path):
                f.write(f"✓ {description}: Available\n")
            else:
                f.write(f"✗ {description}: Not found\n")
        
        f.write("\n" + "=" * 80 + "\n")
        f.write("RECOMMENDED NEXT STEPS:\n")
        f.write("1. Review manual commands in scans/_manual_commands.txt\n")
        f.write("2. Analyze discovered services for vulnerabilities\n")
        f.write("3. Check web services for common web application vulnerabilities\n")
        f.write("4. Review SMB shares for sensitive information\n")
        f.write("5. Test discovered databases for default credentials\n")
        f.write("6. Perform credential stuffing attacks if usernames discovered\n")
        f.write("7. Document all findings in notes/ directory\n")
        f.write("8. Store any discovered credentials in loot/ directory\n")
        f.write("=" * 80 + "\n")

def main():
    parser = argparse.ArgumentParser(
        description="TrashPanda - Professional Penetration Testing Framework v2.4",
        epilog="""
Examples:
  %(prog)s targets.txt                    # Standard scan with reachability test
  %(prog)s -e targets.txt                 # Enhanced comprehensive enumeration
  %(prog)s -r targets.txt                 # Reachability testing only
  %(prog)s -c                             # Just create directory structure
  %(prog)s -n targets.txt                 # Only run Nmap scans
  %(prog)s targets.txt -s                 # Stealth mode scanning
  %(prog)s 192.168.1.0/24 -q             # Quick scan mode
        """,
        formatter_class=argparse.RawDescriptionHelpFormatter
    )
    
    # Target specification
    parser.add_argument("targets", nargs='?', help="Target file, IP, IP range, or CIDR")
    
    # Directory options
    parser.add_argument("-d", "--directory", help="Engagement directory name", default="/root/dmealey")
    parser.add_argument("-c", "--create-dirs", action="store_true", help="Only create directory structure and exit")
    
    # Scan modes
    parser.add_argument("-e", "--enhanced", action="store_true", help="Enable enhanced comprehensive enumeration mode")
    parser.add_argument("-s", "--stealth", action="store_true", help="Enable stealth mode")
    parser.add_argument("-q", "--quick", action="store_true", help="Quick mode")
    parser.add_argument("-f", "--full-tcp", action="store_true", help="Include full TCP port scan")
    
    # Reachability testing
    parser.add_argument("-r", "--reachability-only", action="store_true", help="Only run network reachability testing")
    parser.add_argument("--skip-reachability", action="store_true", help="Skip initial reachability testing")
    parser.add_argument("--reachability-threads", type=int, default=REACHABILITY_THREADS, help="Threads for reachability testing")
    parser.add_argument("--reachability-timeout", type=int, default=REACHABILITY_TIMEOUT, help="Timeout for reachability tests")
    
    # Individual module flags
    parser.add_argument("-n", "--nmap-only", action="store_true", help="Only run Nmap scans")
    parser.add_argument("--dns-only", action="store_true", help="Only run DNS enumeration")
    parser.add_argument("--snmp-only", action="store_true", help="Only run SNMP enumeration")
    parser.add_argument("--smb-only", action="store_true", help="Only run SMB enumeration")
    parser.add_argument("-w", "--web-only", action="store_true", help="Only run web enumeration")
    parser.add_argument("--ssl-only", action="store_true", help="Only run SSL enumeration")
    parser.add_argument("-v", "--vulns-only", action="store_true", help="Only run vulnerability scripts")
    
    # Module exclusions
    parser.add_argument("--no-dns", action="store_true", help="Skip DNS enumeration")
    parser.add_argument("--no-snmp", action="store_true", help="Skip SNMP enumeration")
    parser.add_argument("--no-smb", action="store_true", help="Skip SMB enumeration")
    parser.add_argument("--no-web", action="store_true", help="Skip web enumeration")
    parser.add_argument("--no-ssl", action="store_true", help="Skip SSL enumeration")
    parser.add_argument("--no-vulns", action="store_true", help="Skip vulnerability scripts")
    
    # Packet capture options
    parser.add_argument("--no-pcap", action="store_true", help="Skip tcpdump packet capture")
    parser.add_argument("-p", "--pcap-duration", type=int, default=TCPDUMP_DURATION, help=f"TCPDump capture duration in seconds")
    parser.add_argument("-i", "--pcap-interface", default="any", help="Network interface for packet capture")
    
    # Debug options
    parser.add_argument("--debug", action="store_true", help="Enable debug output")
    
    args = parser.parse_args()
    
    print_banner()
    
    # Create penetration testing structure
    base_dir = create_pentest_structure(args.directory)
    
    # Initialize comprehensive logging
    csv_log_file, verbose_log_file = setup_logging(base_dir)
    log_command.csv_file = csv_log_file  # Store for log_command function
    
    # Setup console output logging if not in debug mode
    if not args.debug:
        original_stdout = sys.stdout
        sys.stdout = LoggingPrint(original_stdout)
    
    log_verbose("TrashPanda session started", 'INFO')
    log_verbose(f"Arguments: {' '.join(sys.argv)}", 'INFO')
    
    # If only creating directories, exit here
    if args.create_dirs:
        print(f"{Colors.OKGREEN}[+] Directory structure created. Exiting as requested.{Colors.ENDC}")
        log_verbose("Directory creation only mode - exiting", 'INFO')
        sys.exit(0)
    
    # Parse targets
    if args.targets:
        print(f"{Colors.OKBLUE}[*] Parsing targets...{Colors.ENDC}")
        targets = parse_targets(args.targets)
    else:
        # Use default target file
        default_targets = os.path.join(base_dir, "targets", "targets.txt")
        if os.path.exists(default_targets):
            with open(default_targets, 'r') as f:
                content = [line.strip() for line in f if line.strip() and not line.startswith('#')]
            
            if content:
                print(f"{Colors.OKBLUE}[*] Using default target file: {default_targets}{Colors.ENDC}")
                targets = parse_targets(default_targets)
            else:
                print(f"{Colors.FAIL}[!] Default target file is empty{Colors.ENDC}")
                sys.exit(1)
        else:
            print(f"{Colors.FAIL}[!] No targets specified and no default target file found{Colors.ENDC}")
            sys.exit(1)
    
    if not targets:
        print(f"{Colors.FAIL}[!] No valid targets found{Colors.ENDC}")
        sys.exit(1)
    
    print(f"{Colors.OKGREEN}[+] Loaded {len(targets)} targets{Colors.ENDC}")
    
    # Apply public IP safety filter
    targets = filter_public_ips_from_targets(targets)
    
    if not targets:
        print(f"{Colors.FAIL}[!] No valid targets remaining after filtering{Colors.ENDC}")
        sys.exit(1)
    
    print(f"{Colors.OKGREEN}[+] Proceeding with {len(targets)} filtered targets{Colors.ENDC}")
    
    # Reachability-only mode
    if args.reachability_only:
        print(f"{Colors.OKBLUE}[*] Running standalone smart network reachability assessment{Colors.ENDC}")
        reachable, unreachable, detailed = run_smart_network_reachability_test(
            targets, base_dir, standalone=True, debug=args.debug,
            threads=args.reachability_threads, timeout=args.reachability_timeout
        )
        sys.exit(0)
    
    if args.enhanced:
        print(f"{Colors.WARNING}[!] Enhanced mode enabled - comprehensive enumeration will take significantly longer{Colors.ENDC}")
    if args.stealth:
        print(f"{Colors.WARNING}[!] Stealth mode enabled - scans will be slower and quieter{Colors.ENDC}")
    
    # Determine which modules to run
    modules_selected = any([
        args.nmap_only, args.dns_only, args.snmp_only, args.smb_only,
        args.web_only, args.ssl_only, args.vulns_only
    ])
    
    # Start tcpdump if requested
    tcpdump_info = None
    if not args.no_pcap and not modules_selected:
        tcpdump_info = start_tcpdump(base_dir, args.pcap_duration, args.pcap_interface)
    
    # Log start time and parameters
    start_time = time.time()
    engagement_log = os.path.join(base_dir, "logs", "engagement.log")
    
    with open(engagement_log, 'a') as f:
        f.write(f"\n=== SCAN SESSION ===\n")
        f.write(f"Start Time: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write(f"Target Count: {len(targets)}\n")
        f.write(f"Enhanced Mode: {args.enhanced}\n")
        f.write(f"Stealth Mode: {args.stealth}\n")
        f.write(f"Quick Mode: {args.quick}\n")
        f.write(f"Reachability Testing: {not args.skip_reachability}\n")
        f.write(f"Arguments: {' '.join(sys.argv)}\n\n")
    
    try:
        # Phase 0: Network Reachability Testing (unless skipped)
        if not args.skip_reachability:
            print(f"{Colors.OKBLUE}[*] Phase 0: Smart Network Reachability Assessment{Colors.ENDC}")
            reachable_targets, unreachable_targets, detailed_results = run_smart_network_reachability_test(
                targets, base_dir, standalone=False, debug=args.debug,
                threads=args.reachability_threads, timeout=args.reachability_timeout
            )
            
            if not reachable_targets:
                print(f"{Colors.FAIL}[!] No targets are reachable from current network position{Colors.ENDC}")
                print(f"{Colors.WARNING}[!] Check network connectivity or try from different location{Colors.ENDC}")
                sys.exit(1)
            
            # Use only reachable targets for further scanning
            targets = reachable_targets
            print(f"{Colors.OKGREEN}[+] Proceeding with {len(targets)} reachable targets{Colors.ENDC}")
        else:
            print(f"{Colors.WARNING}[!] Skipping reachability testing as requested{Colors.ENDC}")
        
        alive_hosts = targets  # Default to all targets
        
        # Run individual modules if specified
        if args.nmap_only:
            alive_hosts = run_nmap_discovery(base_dir, targets, args.stealth, args.quick, args.enhanced, args.debug)
        elif args.dns_only:
            run_enhanced_dns_enumeration(base_dir, targets, args.debug)
        elif args.snmp_only:
            print(f"{Colors.OKBLUE}[*] SNMP enumeration module not implemented yet{Colors.ENDC}")
        elif args.smb_only:
            run_enhanced_smb_enumeration(base_dir, targets, args.debug)
        elif args.web_only:
            run_enhanced_web_enumeration(base_dir, targets, args.stealth, args.debug)
        elif args.ssl_only:
            print(f"{Colors.OKBLUE}[*] SSL enumeration module not implemented yet{Colors.ENDC}")
        elif args.vulns_only:
            print(f"{Colors.OKBLUE}[*] Vulnerability scanning module not implemented yet{Colors.ENDC}")
        else:
            # Progressive scan mode - reorganized for quicker results
            scan_mode = "Enhanced" if args.enhanced else ("Quick" if args.quick else "Standard")
            print(f"{Colors.OKGREEN}[+] Starting {scan_mode} progressive enumeration scan{Colors.ENDC}")
            
            # Phase 1: Quick Reconnaissance (fast results first)
            quick_hits = run_quick_reconnaissance(base_dir, targets, args.debug)
            
            # Phase 2: Comprehensive Nmap Discovery and Port Scanning  
            alive_hosts = run_nmap_discovery(base_dir, targets, args.stealth, args.quick, args.enhanced, args.debug)
            
            # Phase 3: Enhanced enumeration if requested
            if args.enhanced:
                run_enhanced_enumeration(base_dir, alive_hosts, args.stealth, args.debug)
        
        # Stop tcpdump before generating report
        if tcpdump_info:
            print(f"{Colors.OKBLUE}[*] Stopping packet capture...{Colors.ENDC}")
            stop_tcpdump(tcpdump_info)
        
        # Generate comprehensive summary report
        generate_summary_report(base_dir)
        
        # Calculate runtime
        end_time = time.time()
        runtime = end_time - start_time
        hours = int(runtime // 3600)
        minutes = int((runtime % 3600) // 60)
        seconds = int(runtime % 60)
        
        # Log completion
        with open(engagement_log, 'a') as f:
            f.write(f"End Time: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"Total Runtime: {hours:02d}:{minutes:02d}:{seconds:02d}\n")
            f.write(f"Status: Completed Successfully\n")
        
        print(f"\n{Colors.OKGREEN}{'='*60}{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] TrashPanda enumeration completed!{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] Runtime: {hours:02d}:{minutes:02d}:{seconds:02d}{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] Results saved to: {base_dir}{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] Manual commands: {os.path.join(base_dir, 'scans', '_manual_commands.txt')}{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] Summary report: {os.path.join(base_dir, 'reports', 'trashpanda_summary.txt')}{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] CSV commands log: {csv_log_file}{Colors.ENDC}")
        print(f"{Colors.OKGREEN}[+] Verbose log: {verbose_log_file}{Colors.ENDC}")
        if tcpdump_info:
            print(f"{Colors.OKGREEN}[+] Packet capture: {tcpdump_info['pcap_file']}{Colors.ENDC}")
        print(f"{Colors.OKGREEN}{'='*60}{Colors.ENDC}")
        
        log_verbose(f"TrashPanda session completed successfully in {runtime:.2f} seconds", 'INFO')
        
    except KeyboardInterrupt:
        print(f"\n{Colors.WARNING}[!] Scan interrupted by user{Colors.ENDC}")
        log_verbose("Scan interrupted by user (KeyboardInterrupt)", 'WARNING')
        if 'tcpdump_info' in locals() and tcpdump_info:
            stop_tcpdump(tcpdump_info)
        generate_summary_report(base_dir)
        sys.exit(1)
        
    except Exception as e:
        print(f"\n{Colors.FAIL}[!] Unexpected error: {e}{Colors.ENDC}")
        log_verbose(f"Unexpected error: {e}", 'ERROR')
        if 'tcpdump_info' in locals() and tcpdump_info:
            stop_tcpdump(tcpdump_info)
        if args.debug:
            import traceback
            traceback.print_exc()
            log_verbose(f"Traceback: {traceback.format_exc()}", 'ERROR')
        sys.exit(1)

if __name__ == "__main__":
    main()