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's a straightforward TCP port scanner for your home lab:
```python
#!/usr/bin/env python3
"""
Simple TCP port scanner for auditing hosts you own and control.
Usage: python3 portscan.py <host> <start_port> <end_port>
"""
import socket
import sys
import argparse
from concurrent.futures import ThreadPoolExecutor, as_completed
def scan_port(host: str, port: int, timeout: float = 1.0) -> tuple[int, bool]:
"""
Attempt a TCP connection to host:port.
Returns (port, is_open).
"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
result = sock.connect_ex((host, port))
sock.close()
return (port, result == 0)
except Exception:
return (port, False)
def scan_ports(host: str, start_port: int, end_port: int,
max_workers: int = 50, timeout: float = 1.0) -> list[tuple[int, bool]]:
"""
Scan a range of ports on a host using a thread pool.
Returns a list of (port, is_open) tuples sorted by port.
"""
results = []
ports = range(start_port, end_port + 1)
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(scan_port, host, port, timeout): port
for port in ports
}
for future in as_completed(futures):
try:
results.append(future.result())
except Exception as e:
port = futures[future]
print(f"Error scanning port {port}: {e}", file=sys.stderr)
results.append((port, False))
results.sort(key=lambda x: x[0])
return results
def main():
parser = argparse.ArgumentParser(
description="Scan TCP ports on a host you own and control."
)
parser.add_argument("host", help="Target hostname or IP address")
parser.add_argument("start_port", type=int, help="Starting port number")
parser.add_argument("end_port", type=int, help="Ending port number")
parser.add_argument(
"-w", "--workers", type=int, default=50,
help="Max concurrent threads (default: 50)"
)
parser.add_argument(
"-t", "--timeout", type=float, default=1.0,
help="Connection timeout in seconds (default: 1.0)"
)
args = parser.parse_args()
if args.start_port < 1 or args.end_port > 65535:
parser.error("Ports must be between 1 and 65535")
if args.start_port > args.end_port:
parser.error("start_port must be <= end_port")
print(f"Scanning {args.host} ports {args.start_port}-{args.end_port}...")
results = scan_ports(
args.host,
args.start_port,
args.end_port,
max_workers=args.workers,
timeout=args.timeout
)
open_ports = [port for port, is_open in results if is_open]
if open_ports:
print("\nOpen ports:")
for port in open_ports:
print(f" {port}/tcp")
else:
print("\nNo open ports found in the specified range.")
print(f"\nScanned {len(results)} ports. {len(open_ports)} open.")
if __name__ == "__main__":
main()
```
Quick notes:
- **Usage**: `python3 portscan.py 192.168.1.10 1 1024`
- **Threading**: Uses a thread pool for speed; tune `-w` if you see issues.
- **Timeout**: Adjust `-t` if your network is slow or you want faster/finer scans.
- **Scope**: This is a basic connect scan (TCP SYN+ACK via full handshake). It won't evade firewalls or IDS; it's meant for straightforward auditing of systems you control.
If you want, I can add:
- Service name resolution (e.g., "22/tcp ssh")
- JSON output for logging
- A quick banner grab for open ports