| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 | |
| 5 """ | |
| 6 Implementation of the SOCKSv4 protocol. | |
| 7 """ | |
| 8 | |
| 9 # twisted imports | |
| 10 from twisted.internet import reactor, protocol, defer | |
| 11 from twisted.python import log | |
| 12 | |
| 13 # python imports | |
| 14 import struct | |
| 15 import string | |
| 16 import socket | |
| 17 import time | |
| 18 | |
| 19 | |
| 20 class SOCKSv4Outgoing(protocol.Protocol): | |
| 21 | |
| 22 def __init__(self,socks): | |
| 23 self.socks=socks | |
| 24 | |
| 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 | |
| 29 | |
| 30 def connectionLost(self, reason): | |
| 31 self.socks.transport.loseConnection() | |
| 32 | |
| 33 def dataReceived(self,data): | |
| 34 self.socks.write(data) | |
| 35 | |
| 36 def write(self,data): | |
| 37 self.socks.log(self,data) | |
| 38 self.transport.write(data) | |
| 39 | |
| 40 | |
| 41 class SOCKSv4Incoming(protocol.Protocol): | |
| 42 | |
| 43 def __init__(self,socks): | |
| 44 self.socks=socks | |
| 45 self.socks.otherConn=self | |
| 46 | |
| 47 def connectionLost(self, reason): | |
| 48 self.socks.transport.loseConnection() | |
| 49 | |
| 50 def dataReceived(self,data): | |
| 51 self.socks.write(data) | |
| 52 | |
| 53 def write(self,data): | |
| 54 self.socks.log(self,data) | |
| 55 self.transport.write(data) | |
| 56 | |
| 57 | |
| 58 class SOCKSv4(protocol.Protocol): | |
| 59 | |
| 60 def __init__(self,logging=None): | |
| 61 self.logging=logging | |
| 62 | |
| 63 def connectionMade(self): | |
| 64 self.buf="" | |
| 65 self.otherConn=None | |
| 66 | |
| 67 def dataReceived(self,data): | |
| 68 if self.otherConn: | |
| 69 self.otherConn.write(data) | |
| 70 return | |
| 71 self.buf=self.buf+data | |
| 72 if '\000' in self.buf[8:]: | |
| 73 head,self.buf=self.buf[:8],self.buf[8:] | |
| 74 try: | |
| 75 version,code,port=struct.unpack("!BBH",head[:4]) | |
| 76 except struct.error: | |
| 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) | |
| 82 else: | |
| 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): | |
| 86 self.makeReply(91) | |
| 87 return | |
| 88 if code==1: # CONNECT | |
| 89 d = self.connectClass(server, port, SOCKSv4Outgoing, self) | |
| 90 d.addErrback(lambda result, self=self: self.makeReply(91)) | |
| 91 elif code==2: # BIND | |
| 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)) | |
| 95 else: | |
| 96 raise RuntimeError, "Bad Connect Code: %s" % code | |
| 97 assert self.buf=="","hmm, still stuff in buffer... %s" % repr(self.b
uf) | |
| 98 | |
| 99 def connectionLost(self, reason): | |
| 100 if self.otherConn: | |
| 101 self.otherConn.transport.loseConnection() | |
| 102 | |
| 103 def authorize(self,code,server,port,user): | |
| 104 log.msg("code %s connection to %s:%s (user %s) authorized" % (code,serve
r,port,user)) | |
| 105 return 1 | |
| 106 | |
| 107 def connectClass(self, host, port, klass, *args): | |
| 108 return protocol.ClientCreator(reactor, klass, *args).connectTCP(host,por
t) | |
| 109 | |
| 110 def listenClass(self, port, klass, *args): | |
| 111 serv = reactor.listenTCP(port, klass(*args)) | |
| 112 return defer.succeed(serv.getHost()[1:]) | |
| 113 | |
| 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() | |
| 117 | |
| 118 def write(self,data): | |
| 119 self.log(self,data) | |
| 120 self.transport.write(data) | |
| 121 | |
| 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(), | |
| 128 peer.host,peer.port, | |
| 129 ((proto==self and '<') or '>'), | |
| 130 their_peer.host,their_peer.port)) | |
| 131 while data: | |
| 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*' ') | |
| 135 for c in p: | |
| 136 if len(repr(c))>3: f.write('.') | |
| 137 else: f.write(c) | |
| 138 f.write('\n') | |
| 139 f.write('\n') | |
| 140 f.close() | |
| 141 | |
| 142 | |
| 143 class SOCKSv4Factory(protocol.Factory): | |
| 144 """A factory for a SOCKSv4 proxy. | |
| 145 | |
| 146 Constructor accepts one argument, a log file name. | |
| 147 """ | |
| 148 | |
| 149 def __init__(self, log): | |
| 150 self.logging = log | |
| 151 | |
| 152 def buildProtocol(self, addr): | |
| 153 return SOCKSv4(self.logging) | |
| 154 | |
| 155 | |
| 156 class SOCKSv4IncomingFactory(protocol.Factory): | |
| 157 """A utility class for building protocols for incoming connections.""" | |
| 158 | |
| 159 def __init__(self, socks, ip): | |
| 160 self.socks = socks | |
| 161 self.ip = ip | |
| 162 | |
| 163 def buildProtocol(self, addr): | |
| 164 if addr[0] == self.ip: | |
| 165 self.ip = "" | |
| 166 self.socks.makeReply(90, 0) | |
| 167 return SOCKSv4Incoming(self.socks) | |
| 168 elif self.ip == "": | |
| 169 return None | |
| 170 else: | |
| 171 self.socks.makeReply(91, 0) | |
| 172 self.ip = "" | |
| 173 return None | |
| OLD | NEW |