#!/usr/bin/env python3 """Portscan — basic TCP port scanner.""" import argparse, socket, sys, concurrent.futures, time def color(text, code): return f"\033[{code}m{text}\033[0m" if sys.stdout.isatty() else text SERVICE_PORTS = { 21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP", 53: "DNS", 80: "HTTP", 110: "POP3", 143: "IMAP", 443: "HTTPS", 445: "SMB", 993: "IMAPS", 995: "POP3S", 1433: "MSSQL", 1521: "Oracle", 3306: "MySQL", 3389: "RDP", 5432: "PostgreSQL", 5900: "VNC", 6379: "Redis", 8080: "HTTP-Alt", 8443: "HTTPS-Alt", 27017: "MongoDB", } def get_service(port): try: name = socket.getservbyport(port) return name except OSError: return SERVICE_PORTS.get(port, "") def scan_port(host, port, timeout=2): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(timeout) result = s.connect_ex((host, port)) s.close() if result == 0: service = get_service(port) return (port, "open", service) return (port, "closed", "") except socket.gaierror: return (port, "error", "host not found") except OSError: return (port, "error", "connection error") def main(): parser = argparse.ArgumentParser( description="Portscan — basic TCP port scanner", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=( "Examples:\n" " portscan example.com # Scan common ports\n" " portscan 192.168.1.1 --range 1-100 # Scan port range\n" " portscan example.com --ports 22,80,443 # Specific ports\n" " portscan example.com --all # Scan all 65535 ports\n" ), ) parser.add_argument("host", help="Target hostname or IP") parser.add_argument("--range", help="Port range (e.g., 1-1000)") parser.add_argument("--ports", help="Comma-separated ports") parser.add_argument("--all", action="store_true", help="Scan all 65535 ports") parser.add_argument("--timeout", type=float, default=2.0, help="Connection timeout (default: 2s)") parser.add_argument("--threads", type=int, default=50, help="Concurrent threads (default: 50)") parser.add_argument("--no-color", action="store_true", help="Disable color output") parser.add_argument("--version", action="version", version="portscan 1.0.0") args = parser.parse_args() # Build port list ports = [] if args.all: ports = list(range(1, 65536)) elif args.ports: try: ports = [int(p.strip()) for p in args.ports.split(",")] except ValueError: print("Error: Invalid port list", file=sys.stderr) sys.exit(1) elif args.range: try: parts = args.range.split("-") start, end = int(parts[0]), int(parts[1]) ports = list(range(start, end + 1)) except (ValueError, IndexError): print("Error: Invalid range format (use 1-1000)", file=sys.stderr) sys.exit(1) else: # Default common ports ports = sorted(SERVICE_PORTS.keys()) # Resolve hostname try: ip = socket.gethostbyname(args.host) except socket.gaierror: print(f"Error: Could not resolve '{args.host}'", file=sys.stderr) sys.exit(1) print(f"\n {color('Portscan Results', '1')}") print(f" Target: {args.host} ({ip})") print(f" Ports: {len(ports)} to scan") print(f" Timeout: {args.timeout}s") print(f" {color('─' * 45, '90')}") print() start_time = time.time() open_ports = [] scanned = 0 with concurrent.futures.ThreadPoolExecutor(max_workers=args.threads) as executor: futures = {executor.submit(scan_port, ip, p, args.timeout): p for p in ports} for future in concurrent.futures.as_completed(futures): port, status, service = future.result() scanned += 1 if status == "open": open_ports.append((port, service)) svc = f" ({service})" if service else "" serv_color = "36" if service else "" print(f" {color('OPEN', '32')} {port:5d}/tcp {color(svc, serv_color)}") # Progress every 100 ports if scanned % 100 == 0 and scanned < len(ports): pct = scanned * 100 // len(ports) print(f" {color(f'... {scanned}/{len(ports)} ({pct}%)', '90')}") elapsed = time.time() - start_time print(f"\n {color('─' * 45, '90')}") print(f" Completed: {len(ports)} ports in {elapsed:.1f}s") print(f" Open: {color(str(len(open_ports)), '32' if len(open_ports) < 10 else '31')}") if open_ports: print(f"\n {color('Open Ports Summary:', '1')}") for port, service in sorted(open_ports): svc = f" ({service})" if service else "" print(f" {port:5d}/tcp{svc}") print() if __name__ == "__main__": main()