A multi-threaded SOCKS5 proxy server built from scratch in Python, following RFC 1928. It handles the full handshake, username/password authentication, the CONNECT command, and bidirectional data relay between client and destination — with IP whitelisting and per-session traffic metrics on top.
- RFC 1928-compliant handshake — negotiates the SOCKS version and auth method before doing anything else.
- Username/password authentication (Sub-negotiation, RFC 1929) — rejects any client that doesn't offer method
0x02, and verifies credentials before allowing a connection through. - IP whitelisting — connections from IPs outside
ALLOWED_IPSare dropped immediately, before any bytes are read off the socket. CONNECTcommand support — resolves the target (IPv4 or domain name) and opens a connection to it on the client's behalf.BINDandUDP ASSOCIATEare not implemented.- Threaded connection handling — built on
ThreadingMixIn, so each client connection is handled on its own thread and multiple sessions can run concurrently. - Non-blocking relay loop — once the tunnel is established,
select()multiplexes reads between the client and the remote socket so data flows in both directions without busy-waiting. - Per-session metrics — logs session duration, bytes uploaded, and bytes downloaded once a connection closes.
- Graceful shutdown —
SIGINT/SIGTERMare caught to shut the server down cleanly instead of dropping connections abruptly.
- Python 3 (standard library only — no external dependencies)
-
Set your whitelist (optional). By default, only
127.0.0.1is allowed:ALLOWED_IPS = {'127.0.0.1'}
Add more IPs as needed, or set
ALLOWED_IPS = set()to allow all incoming connections. -
Set credentials. The proxy currently uses a single hardcoded username/password pair in the
SocksProxyclass:username = 'user' password = 'pass'
Change these before running anywhere outside a local test environment (see Known Limitations).
-
Set the log path. Logs are written to
/var/www/html/proxy3.logby default — update thefilenamein thelogging.basicConfig()call to a path that exists on your system. -
Run the server:
python3 proxie.py
The server listens on
0.0.0.0:9000by default. -
Point a SOCKS5 client at it, e.g. with
curl:curl --socks5 user:pass@127.0.0.1:9000 https://example.com
Ctrl+C (or a SIGTERM) triggers a graceful shutdown — the server stops accepting new connections, closes the listening socket, and exits cleanly.
- Credentials and log path are hardcoded in source rather than pulled from a config file or environment variables — fine for local testing, but should be externalized before any real deployment.
- Single shared credential pair — there's no per-user account system; every client authenticates with the same username/password.
- No TLS — traffic between the client and this proxy is not encrypted at the proxy layer.
- Only
CONNECTis implemented —BINDandUDP ASSOCIATErequests are rejected.
Each incoming connection is handled by SocksProxy.handle():
- Checks the client IP against
ALLOWED_IPS, dropping unauthorized connections immediately. - Performs the SOCKS5 greeting/method-negotiation handshake, requiring username/password auth.
- Verifies the supplied credentials.
- Parses the client's request (address type + target address/port).
- Opens a
CONNECTto the target and relays data between the client and target sockets usingselect()until either side closes the connection. - Logs session metrics (duration, bytes sent/received) once the exchange loop ends.
Issues and PRs welcome — a config-driven credential/whitelist system and TLS support would be natural next steps.
MIT