from twisted.internet import protocol, reactor
import struct
STATE_INITIAL = 0
STATE_AUTH = 1
STATE_REQUEST = 2
STATE_READY = 3
STATE_AUTH_USERPASS = 4
STATE_LAST = 5
STATE_CONNECT_PENDING = STATE_LAST + 1
SOCKS5_VER = 0x05
ADDR_IPV4 = 0x01
ADDR_DOMAINNAME = 0x03
ADDR_IPV6 = 0x04
CMD_CONNECT = 0x01
CMD_BIND = 0x02
CMD_UDPASSOC = 0x03
AUTHMECH_ANON = 0x00
AUTHMECH_USERPASS = 0x02
AUTHMECH_INVALID = 0xFF
REPLY_SUCCESS = 0x00
REPLY_GENERAL_FAILUR = 0x01
REPLY_CONN_NOT_ALLOWED = 0x02
REPLY_NETWORK_UNREACHABLE = 0x03
REPLY_HOST_UNREACHABLE = 0x04
REPLY_CONN_REFUSED = 0x05
REPLY_TTL_EXPIRED = 0x06
REPLY_CMD_NOT_SUPPORTED = 0x07
REPLY_ADDR_NOT_SUPPORTED = 0x08
class SOCKSv5Outgoing(protocol.Protocol):
def __init__(self, peersock):
self.peersock = peersock
self.peersock.peersock = self
def connectionMade(self):
_invalid_, hostname, port = self.transport.getPeer()
self.peersock.connectCompleted(hostname, port)
def connectionLost(self, reason):
self.peersock.transport.loseConnection()
self.peersock.peersock = None
self.peersock = None
def dataReceived(self, buf):
self.peersock.transport.write(buf)
class SOCKSv5(protocol.Protocol):
def __init__(self):
self.state = STATE_INITIAL
self.buf = ""
self.supportedAuthMechs = [ AUTHMECH_USERPASS ]
self.supportedAddrs = [ ADDR_IPV4, ADDR_DOMAINNAME ]
self.enabledCommands = [ CMD_CONNECT, CMD_BIND ]
self.peersock = None
self.addressType = 0
self.requestType = 0
def _parseNegotiation(self):
try:
ver, nmethod = struct.unpack('!BB', self.buf[:2])
methods = struct.unpack('%dB' % nmethod, self.buf[2:nmethod+2])
if ver != 5:
self.transport.write(struct.pack('!BB', SOCKS5_VER, AUTHMECH_INVALID))
self.transport.loseConnection()
return
self.buf = self.buf[nmethod+2:]
for m in self.supportedAuthMechs:
if m in methods:
if m == AUTHMECH_ANON:
self.state = STATE_REQUEST
elif m == AUTHMECH_USERPASS:
self.state = STATE_AUTH_USERPASS
self.transport.write(struct.pack('!BB', SOCKS5_VER, m))
return
self.transport.write(struct.pack('!BB', SOCKS5_VER, AUTHMECH_INVALID))
self.transport.loseConnection()
except struct.error:
pass
def _parseUserPass(self):
try:
ver, ulen = struct.unpack('BB', self.buf[:2])
uname, = struct.unpack('%ds' % ulen, self.buf[2:ulen + 2])
plen, = struct.unpack('B', self.buf[ulen + 2])
password, = struct.unpack('%ds' % plen, self.buf[ulen + 3:ulen + 3 + plen])
self.buf = self.buf[3 + ulen + plen:]
if self.authenticateUserPass(uname, password):
self.state = STATE_REQUEST
self.transport.write(struct.pack('!BB', SOCKS5_VER, 0x00))
else:
self.transport.write(struct.pack('!BB', SOCKS5_VER, 0x01))
self.transport.loseConnection()
except struct.error:
pass
def sendErrorReply(self, errorcode):
result = struct.pack('!BBBBIH', SOCKS5_VER, errorcode, 0, 1, 0, 0)
self.transport.write(result)
self.transport.loseConnection()
def _parseRequest(self):
try:
ver, cmd, rsvd, self.addressType = struct.unpack('!BBBB', self.buf[:4])
if self.addressType not in self.supportedAddrs:
self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED)
return
if self.addressType == ADDR_IPV4:
addr, port = struct.unpack('!IH', self.buf[4:10])
self.buf = self.buf[10:]
elif self.addressType == ADDR_DOMAINNAME:
nlen = ord(self.buf[4])
addr, port = struct.unpack('!%dsH' % nlen, self.buf[5:])
self.buf = self.buf[7 + len(addr):]
else:
self.sendErrorReply(REPLY_ADDR_NOT_SUPPORTED)
return
if cmd not in self.enabledCommands:
self.sendErrorReply(REPLY_CMD_NOT_SUPPORTED)
return
if cmd == CMD_CONNECT:
self.connectRequested(addr, port)
elif cmd == CMD_BIND:
self.bindRequested(addr, port)
else:
self.sendErrorReply(REPLY_CMD_NOT_SUPPORTED)
except struct.error, why:
return None
def connectRequested(self, addr, port):
self.transport.stopReading()
self.state = STATE_CONNECT_PENDING
protocol.ClientCreator(reactor, SOCKSv5Outgoing, self).connectTCP(addr, port)
def connectCompleted(self, remotehost, remoteport):
if self.addressType == ADDR_IPV4:
result = struct.pack('!BBBBIH', SOCKS5_VER, REPLY_SUCCESS, 0, 1, remotehost, remoteport)
elif self.addressType == ADDR_DOMAINNAME:
result = struct.pack('!BBBBB%dsH' % len(remotehost), SOCKS5_VER, REPLY_SUCCESS, 0,
ADDR_DOMAINNAME, len(remotehost), remotehost, remoteport)
self.transport.write(result)
self.state = STATE_READY
self.transport.startReading()
def bindRequested(self, addr, port):
pass
def authenticateUserPass(self, user, passwd):
print "User/pass: ", user, passwd
return True
def dataReceived(self, buf):
if self.state == STATE_READY:
self.peersock.transport.write(buf)
return
self.buf = self.buf + buf
if self.state == STATE_INITIAL:
self._parseNegotiation()
if self.state == STATE_AUTH_USERPASS:
self._parseUserPass()
if self.state == STATE_REQUEST:
self._parseRequest()
factory = protocol.Factory()
factory.protocol = SOCKSv5
if __name__ == "__main__":
reactor.listenTCP(8888, factory)
reactor.run()