Last active
December 16, 2015 22:59
-
-
Save sfan5/5510472 to your computer and use it in GitHub Desktop.
A SOCKS 5 proxy server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# SOCKS 5 server | |
# -*- coding: utf-8 -*- | |
import socket, struct, time, select, os | |
from thread import start_new_thread, allocate_lock | |
# Address to listen on | |
HOST = "" | |
# Port to listen on | |
PORT = 12345 | |
# Buffer size | |
BUFSIZE = 128 * 1024 | |
# Logfile | |
LOGFILE = "" | |
# Domain blacklist | |
# Note that most clients only send the IP Address they are connecting to | |
# which means that this blacklist is likely not effective | |
DOMAIN_BLACKLIST = [] | |
# IP blacklist | |
IP_BLACKLIST = [] | |
# Port blacklist | |
PORT_BLACKLIST = [] | |
# Authentication method (none or password) | |
AUTHMETHOD = "none" | |
# User and password for "password" authentication method: | |
AM_PSW_USER = "" | |
AM_PSW_PASS = "" | |
client_cnt = 0 | |
connections = [] | |
lock = allocate_lock() | |
class MyError(): | |
pass | |
def log(st, c=-1): | |
cstr = "" | |
if c != -1: | |
cstr = " [Connection %d]" % c | |
lock.acquire() | |
if LOGFILE != '': | |
LOGFILE.write(time.strftime("[%c]") + cstr + " " + st + "\n") | |
print(time.strftime("[%c]") + cstr + " " + st) | |
lock.release() | |
def hcwrapper(sock, addr): | |
log("INFO: Client from %s connected" % addr[0]) | |
handleconnect(sock, addr) | |
sock.close() | |
def socketpipe(si, so, cid): | |
global lock, connections | |
while True: | |
try: | |
select.select([si.fileno()], [], []) | |
buf = si.recv(BUFSIZE) | |
if not buf: | |
raise MyError | |
so.sendall(buf) | |
except MyError: | |
lock.acquire() | |
connections[cid]["open"] = False | |
si.close() | |
so.close() | |
lock.release() | |
log("INFO: Client closed connection", cid) | |
break | |
except socket.error: | |
break | |
def handleconnect(sock, addr): | |
global client_cnt, lock, connections | |
lock.acquire() | |
cid = client_cnt | |
connections.append({"open": False, "src_addr": addr[0]}) | |
client_cnt += 1 | |
lock.release() | |
version = ord(sock.recv(1)) | |
if version != 5: | |
log("ERR: Unknown version %d" % version, cid) | |
return sock.close() | |
numauthmethods = ord(sock.recv(1)) | |
if numauthmethods <= 0: | |
log("ERR: Invalid number of authentication methods", cid) | |
return sock.close() | |
authsuccess = False | |
for i in range(0, numauthmethods): | |
authmet = ord(sock.recv(1)) | |
print(authmet) | |
if authmet == 0 and AUTHMETHOD == "none": | |
authsuccess = True | |
elif authmet == 2 and AUTHMETHOD == "password": | |
authsuccess = True | |
if not authsuccess: | |
log("ERR: No suitable auth method found", cid) | |
sock.sendall(chr(5) + chr(0xFF)) | |
return sock.close() | |
if AUTHMETHOD == "password": | |
authversion = ord(sock.recv(1)) | |
if authversion != 1: | |
log("ERR: Unknown password authentication version %d" % version, cid) | |
return sock.close() | |
unamelen = ord(sock.recv(1)) | |
uname = sock.recv(unamelen) | |
pswlen = ord(sock.recv(1)) | |
psw = sock.recv(pswlen) | |
if uname == AM_PSW_USER and psw == AM_PSW_PASS: | |
sock.sendall(chr(1) + chr(0)) | |
connections[cid]["auth_user"] = uname | |
else: | |
sock.sendall(chr(1) + chr(255)) | |
log("WARN: Tried to authenticate with wrong username/password %r/%r" % uname, psw, cid) | |
return sock.close() | |
connections[cid]["authmethod"] = AUTHMETHOD | |
log("INFO: Authentication succeeded", cid) | |
sock.sendall(chr(5) + chr(0)) | |
version = ord(sock.recv(1)) | |
if version != 5: | |
log("ERR: Unknown version %d" % version, cid) | |
return sock.close() | |
cmd = ord(sock.recv(1)) | |
if cmd != 1: | |
log("ERR: Unknown command %d" % version, cid) | |
sock.sendall(chr(5) + chr(7)) | |
return sock.close() | |
dummy_pf = chr(0) + chr(1) + chr(255) + chr(255) + chr(255) + chr(255) + chr(0) + chr(0) | |
sock.recv(1) | |
atype = ord(sock.recv(1)) | |
remote_addr = "" | |
if atype == 1: | |
remote_addr = "%d.%d.%d.%d" % (ord(sock.recv(1)), ord(sock.recv(1)), ord(sock.recv(1)), ord(sock.recv(1))) | |
if remote_addr in IP_BLACKLIST: | |
log("WARN: Tried to connect to blacklisted IP: %s" % remote_addr, cid) | |
sock.sendall(chr(5) + chr(2) + dummy_pf) | |
return sock.close() | |
hname = "" | |
elif atype == 3: | |
l = ord(sock.recv(1)) | |
hname = sock.recv(l) | |
if hname in DOMAIN_BLACKLIST: | |
log("WARN: Tried to connect to blacklisted domain: %s" % hname, cid) | |
sock.sendall(chr(5) + chr(2) + dummy_pf) | |
return sock.close() | |
try: | |
remote_addr = socket.getaddrinfo(hname, 80)[0][4][0] | |
if remote_addr in IP_BLACKLIST: | |
log("WARN: Tried to connect to blacklisted IP: %s" % remote_addr, cid) | |
sock.sendall(chr(5) + chr(2) + dummy_pf) | |
return sock.close() | |
except: | |
log("ERR: Could not resolve '%s'" % hname, cid) | |
sock.sendall(chr(5) + chr(4) + dummy_pf) | |
return sock.close() | |
else: | |
log("ERR: Unknown address type", cid) | |
sock.sendall(chr(5) + chr(8) + dummy_pf) | |
return sock.close() | |
connections[cid]["remote_addr"] = remote_addr | |
remote_port = struct.unpack("!H", sock.recv(2))[0] | |
if remote_port == 0: | |
log("ERR: Requested connection to port 0", cid) | |
sock.sendall(chr(5) + chr(1) + dummy_pf) | |
return sock.close() | |
if remote_port in PORT_BLACKLIST: | |
log("WARN: Tried to connect to blacklisted port: %d" % remote_port, cid) | |
sock.sendall(chr(5) + chr(2) + dummy_pf) | |
return sock.close() | |
connections[cid]["remote_port"] = remote_port | |
remote_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
remote_sock.connect((remote_addr, remote_port)) | |
if not remote_sock: | |
log("ERR: Connection to remote failed", cid) | |
sock.sendall(chr(5) + chr(4) + dummy_pf) | |
return sock.close() | |
sock.sendall(chr(5) + chr(0) + dummy_pf) | |
if hname != "": | |
hname = " (" + hname + ")" | |
log("INFO: Successfully connected to %s:%d%s" % (remote_addr, remote_port, hname), cid) | |
connections[cid]["open"] = True | |
start_new_thread(socketpipe, (remote_sock, sock, cid)) | |
start_new_thread(socketpipe, (sock, remote_sock, cid)) | |
while True: | |
time.sleep(30) | |
if AUTHMETHOD != "none" and AUTHMETHOD != "password": | |
log("ERR: AUTHMETHOD must be either 'none' or 'password'") | |
if LOGFILE.strip() != '': | |
LOGFILE = open(logfile, 'w') | |
else: | |
LOGFILE = '' | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
s.bind((HOST, PORT)) | |
s.listen(1) | |
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
log("SOCKS5 server ready.") | |
while True: | |
try: | |
conn, addr = s.accept() | |
except KeyboardInterrupt: | |
break | |
start_new_thread(hcwrapper, (conn, addr,)) | |
s.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment