import nmap
import os

# Set the path to nmap executable
os.environ["PATH"] += os.pathsep + "C:\\Program Files (x86)\\Nmap"

# Function to scan a target host for open ports and services using Nmap
# target: The IP address or hostname to scan (e.g., '192.168.1.1' or 'example.com')
# ports: (Optional) A string specifying ports to scan (e.g., '22,80,443' or '1-1024'). If None, scans default ports.
def scan_host(target, ports=None):
    """
    Scans the specified target for open ports and service information using Nmap.
    Args:
        target (str): The IP address or hostname to scan.
        ports (str, optional): Ports to scan (e.g., '22,80,443' or '1-1024').
    Returns:
        dict: A dictionary containing scan results, including open ports and service details.
    """
    # Initialize the Nmap PortScanner object
    nm = nmap.PortScanner()
    # Set the nmap path explicitly
    nm.scan_command = "C:\\Program Files (x86)\\Nmap\\nmap.exe"
    try:
        # Build the arguments for the scan
        scan_args = {}
        if ports:
            scan_args['ports'] = ports
        # Perform the scan with service detection
        # nm.scan() returns a dictionary with scan results
        # Use service detection arguments for better results
        nm.scan(hosts=target, arguments='-sV', **scan_args)
  # -sV: Service/version detection

        # Prepare results
        results = {
            'target': target,
            'scan': []  # List of open ports and their details
        }
        # nm.all_hosts() returns a list of scanned hosts (should be one in this case)
        for host in nm.all_hosts():
            # For each protocol (usually 'tcp', sometimes 'udp')
            for proto in nm[host].all_protocols():
                lport = nm[host][proto].keys()  # List of ports
                for port in lport:
                    port_info = nm[host][proto][port]
                    # Each port_info contains details like state, name, product, version, etc.
                    results['scan'].append({
                        'port': port,
                        'protocol': proto,
                        'state': port_info.get('state'),
                        'name': port_info.get('name'),
                        'product': port_info.get('product'),
                        'version': port_info.get('version'),
                        'extrainfo': port_info.get('extrainfo'),
                    })
        return results
    except nmap.PortScannerError as e:
        # Handle errors (e.g., Nmap not installed, permission issues)
        print(f"Nmap scan error: {e}")
        return {'error': str(e)}
    except Exception as e:
        # Handle any other exceptions
        print(f"Unexpected error: {e}")
        return {'error': str(e)}

# Example usage (uncomment to test directly)
# if __name__ == "__main__":
#     target = "scanme.nmap.org"
#     print(scan_host(target, ports="22,80,443"))

# scanme.nmap.org