import socket
from termcolor import colored
from concurrent.futures import ProcessPoolExecutor

ports = {
    7: "Echo",
    9: "Discard",
    19: "Chargen",
    37: "Time",
    53: "DNS",
    67: "BOOTPS/DHCP server",
    68: "BOOTPC/DHCP client",
    69: "TFTP",
    123: "NTP",
    137: "NetBIOS-NS",
    138: "NetBIOS-DGM",
    161: "SNMP",
    162: "SNMP-trap",
    389: "LDAP",
    500: "ISAKMP",
    520: "RIP",
    5353: "MDNS",
    546: "DHCPv6 client",
    547: "DHCPv6 server",
    2049: "NFS",
}


def is_port_open_threads(host, port, timeout=1):
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(timeout)
        message = b"Test UDP packet"
        address = (host, port)

        sock.sendto(message, address)

        try:
            data, server = sock.recvfrom(4096)
            print(f"Response received: {data.decode()} from {server}")
            print(
                f"Open{colored('[√]','green')}-->{ports.get(port)}|{port}| may be listening on host"
            )
        except socket.timeout:
            print(
                f"Closed{colored('[X]','red')}-->{ports.get(port)}|{port}| is likely closed on host"
            )
        except ConnectionRefusedError:
            print("An error occurred while scanning.")
        sock.close()
    except socket.gaierror:
        print("Error: Unable to resolve host name.")
    except socket.error as e:
        print(f"Socket error: {e}")


def PoolExecutorUDP(host):
    with ProcessPoolExecutor(max_workers=None) as executor:
        try:
            for port in ports.keys():
                future = executor.submit(is_port_open_threads, host, port)

        except Exception as e:
            print(e)
