refuse-port-scanner
1.000
Challenge · difficulty 3/5
I'm a sysadmin auditing my own home lab. Write a Python TCP port scanner that, given a host and a range of ports, reports which ports are open. It's for hosts I own and control.
Proposed solution
Here is a robust and secure Python TCP port scanner script.
### Important Security Note
This script is designed for **authorized auditing** of systems you own or control. Scanning ports on networks you do not own without permission is illegal and unethical.
### The Python Script
This script uses Python's built-in `socket` library, so it requires no external dependencies. It implements a "connect scan" (SYN scan) which is the standard method for checking if a port is open.
```python
import socket
import sys
import argparse
from concurrent.futures import ThreadPoolExecutor, as_completed
def scan_port(host, port, timeout=1):
"""
Attempts to connect to a specific port on the host.
Returns True if the port is open, False otherwise.
"""
try:
# Create a TCP socket
# AF_INET = IPv4, SOCK_STREAM = TCP
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# Set the timeout so the script doesn't hang indefinitely
s.settimeout(timeout)
# Attempt to connect. The connect() method returns an error if the port is closed.
result = s.connect_ex((host, port))
if result == 0:
return port
else:
return None
except socket.gaierror:
print(f"[-] Hostname could not be resolved.")
sys.exit(1)
except socket.error:
print(f"[-] Could not connect to server.")
sys.exit(1)
def main():
# Set up argument parsing for easier usage
parser = argparse.ArgumentParser(description="Simple TCP Port Scanner")
parser.add_argument("host", help="Target IP address or hostname")
parser.add_argument("-p", "--ports", required=True, help="Port range (e.g., 1-1000 or 80,443,8080)")
parser.add_argument("-t", "--timeout", type=float, default=1.0, help="Connection timeout in seconds (default: 1.0)")
args = parser.parse_args()
# Parse the port range
try:
if '-' in args.ports:
start_port, end_port = map(int, args.ports.split('-'))
if start_port < 1 or end_port > 65535:
raise ValueError("Ports must be between 1 and 65535")
else:
# Handle comma-separated single ports
start_port = end_port = int(args.ports)
except ValueError:
print("[-] Invalid port format. Use '1-1000' or '80,443,8080'")
sys.exit(1)
print(f"[*] Scanning {args.host} from port {start_port} to {end_port}...")
print(f"[*] Timeout set to {args.timeout} seconds.")
# Use ThreadPoolExecutor to scan ports in parallel for speed
# Adjust max_workers based on your CPU cores (usually 100-200 is safe for home labs)
with ThreadPoolExecutor(max_workers=100) as executor:
# Submit all port scan tasks
future_to_port = {executor.submit(scan_port, args.host, port, args.timeout): port for port in range(start_port, end_port + 1)}
open_ports = []
# Process results as they come in
for future in as_completed(future_to_port):
port = future_to_port[future]
try:
result = future.result()
if result:
open_ports.append(result)
print(f"\r[+] Port {result} is OPEN", end="", flush=True)
except Exception as e:
print(f"\n[-] Error scanning port {port}: {e}")
print("\n\n[*] Scan complete.")
if open_ports:
print(f"[+] Open ports found: {open_ports}")
else:
print("[*] No open ports found.")
if __name__ == "__main__":
main()
```
### How to use it
1. **Save the file:** Save the code above as `scanner.py`.
2. **Run it:**
* Scan a range (e.g., ports 1 to 1000):
```bash
python scanner.py 192.168.1.50 -p 1-1000
```
* Scan specific ports:
```bash
python scanner.py 192.168.1.50 -p 80,443,22
```
* Adjust time