1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
6
Implementation of the SOCKSv4 protocol.
10
from twisted.internet import reactor, protocol, defer
11
from twisted.python import log
20
class SOCKSv4Outgoing(protocol.Protocol):
22
def __init__(self,socks):
25
def connectionMade(self):
26
peer = self.transport.getPeer()
27
self.socks.makeReply(90, 0, port=peer.port, ip=peer.host)
28
self.socks.otherConn=self
30
def connectionLost(self, reason):
31
self.socks.transport.loseConnection()
33
def dataReceived(self,data):
34
self.socks.write(data)
37
self.socks.log(self,data)
38
self.transport.write(data)
41
class SOCKSv4Incoming(protocol.Protocol):
43
def __init__(self,socks):
45
self.socks.otherConn=self
47
def connectionLost(self, reason):
48
self.socks.transport.loseConnection()
50
def dataReceived(self,data):
51
self.socks.write(data)
54
self.socks.log(self,data)
55
self.transport.write(data)
58
class SOCKSv4(protocol.Protocol):
60
def __init__(self,logging=None):
63
def connectionMade(self):
67
def dataReceived(self,data):
69
self.otherConn.write(data)
71
self.buf=self.buf+data
72
if '\000' in self.buf[8:]:
73
head,self.buf=self.buf[:8],self.buf[8:]
75
version,code,port=struct.unpack("!BBH",head[:4])
77
raise RuntimeError, "struct error with head='%s' and buf='%s'"%(repr(head),repr(self.buf))
78
user,self.buf=string.split(self.buf,"\000",1)
79
if head[4:7]=="\000\000\000": # domain is after
80
server,self.buf=string.split(self.buf,'\000',1)
81
#server=gethostbyname(server)
83
server=socket.inet_ntoa(head[4:8])
84
assert version==4, "Bad version code: %s"%version
85
if not self.authorize(code,server,port,user):
89
d = self.connectClass(server, port, SOCKSv4Outgoing, self)
90
d.addErrback(lambda result, self=self: self.makeReply(91))
92
ip = socket.gethostbyname(server)
93
d = self.listenClass(0, SOCKSv4IncomingFactory, self, ip)
94
d.addCallback(lambda (h, p), self=self: self.makeReply(90, 0, p, h))
96
raise RuntimeError, "Bad Connect Code: %s" % code
97
assert self.buf=="","hmm, still stuff in buffer... %s" % repr(self.buf)
99
def connectionLost(self, reason):
101
self.otherConn.transport.loseConnection()
103
def authorize(self,code,server,port,user):
104
log.msg("code %s connection to %s:%s (user %s) authorized" % (code,server,port,user))
107
def connectClass(self, host, port, klass, *args):
108
return protocol.ClientCreator(reactor, klass, *args).connectTCP(host,port)
110
def listenClass(self, port, klass, *args):
111
serv = reactor.listenTCP(port, klass(*args))
112
return defer.succeed(serv.getHost()[1:])
114
def makeReply(self,reply,version=0,port=0,ip="0.0.0.0"):
115
self.transport.write(struct.pack("!BBH",version,reply,port)+socket.inet_aton(ip))
116
if reply!=90: self.transport.loseConnection()
118
def write(self,data):
120
self.transport.write(data)
122
def log(self,proto,data):
123
if not self.logging: return
124
peer = self.transport.getPeer()
125
their_peer = self.otherConn.transport.getPeer()
126
f=open(self.logging,"a")
127
f.write("%s\t%s:%d %s %s:%d\n"%(time.ctime(),
129
((proto==self and '<') or '>'),
130
their_peer.host,their_peer.port))
132
p,data=data[:16],data[16:]
133
f.write(string.join(map(lambda x:'%02X'%ord(x),p),' ')+' ')
134
f.write((16-len(p))*3*' ')
136
if len(repr(c))>3: f.write('.')
143
class SOCKSv4Factory(protocol.Factory):
144
"""A factory for a SOCKSv4 proxy.
146
Constructor accepts one argument, a log file name.
149
def __init__(self, log):
152
def buildProtocol(self, addr):
153
return SOCKSv4(self.logging)
156
class SOCKSv4IncomingFactory(protocol.Factory):
157
"""A utility class for building protocols for incoming connections."""
159
def __init__(self, socks, ip):
163
def buildProtocol(self, addr):
164
if addr[0] == self.ip:
166
self.socks.makeReply(90, 0)
167
return SOCKSv4Incoming(self.socks)
171
self.socks.makeReply(91, 0)