1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
# -*- test-case-name: twisted.test.test_socks -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Implementation of the SOCKSv4 protocol.
"""
import socket
import string
# python imports
import struct
import time
# twisted imports
from twisted.internet import defer, protocol, reactor
from twisted.python import log
class SOCKSv4Outgoing(protocol.Protocol):
def __init__(self, socks):
self.socks = socks
def connectionMade(self):
peer = self.transport.getPeer()
self.socks.makeReply(90, 0, port=peer.port, ip=peer.host)
self.socks.otherConn = self
def connectionLost(self, reason):
self.socks.transport.loseConnection()
def dataReceived(self, data):
self.socks.write(data)
def write(self, data):
self.socks.log(self, data)
self.transport.write(data)
class SOCKSv4Incoming(protocol.Protocol):
def __init__(self, socks):
self.socks = socks
self.socks.otherConn = self
def connectionLost(self, reason):
self.socks.transport.loseConnection()
def dataReceived(self, data):
self.socks.write(data)
def write(self, data):
self.socks.log(self, data)
self.transport.write(data)
class SOCKSv4(protocol.Protocol):
"""
An implementation of the SOCKSv4 protocol.
@type logging: L{str} or L{None}
@ivar logging: If not L{None}, the name of the logfile to which connection
information will be written.
@type reactor: object providing L{twisted.internet.interfaces.IReactorTCP}
@ivar reactor: The reactor used to create connections.
@type buf: L{str}
@ivar buf: Part of a SOCKSv4 connection request.
@type otherConn: C{SOCKSv4Incoming}, C{SOCKSv4Outgoing} or L{None}
@ivar otherConn: Until the connection has been established, C{otherConn} is
L{None}. After that, it is the proxy-to-destination protocol instance
along which the client's connection is being forwarded.
"""
def __init__(self, logging=None, reactor=reactor):
self.logging = logging
self.reactor = reactor
def connectionMade(self):
self.buf = b""
self.otherConn = None
def dataReceived(self, data):
"""
Called whenever data is received.
@type data: L{bytes}
@param data: Part or all of a SOCKSv4 packet.
"""
if self.otherConn:
self.otherConn.write(data)
return
self.buf = self.buf + data
completeBuffer = self.buf
if b"\000" in self.buf[8:]:
head, self.buf = self.buf[:8], self.buf[8:]
version, code, port = struct.unpack("!BBH", head[:4])
user, self.buf = self.buf.split(b"\000", 1)
if head[4:7] == b"\000\000\000" and head[7:8] != b"\000":
# An IP address of the form 0.0.0.X, where X is non-zero,
# signifies that this is a SOCKSv4a packet.
# If the complete packet hasn't been received, restore the
# buffer and wait for it.
if b"\000" not in self.buf:
self.buf = completeBuffer
return
server, self.buf = self.buf.split(b"\000", 1)
d = self.reactor.resolve(server)
d.addCallback(self._dataReceived2, user, version, code, port)
d.addErrback(lambda result, self=self: self.makeReply(91))
return
else:
server = socket.inet_ntoa(head[4:8])
self._dataReceived2(server, user, version, code, port)
def _dataReceived2(self, server, user, version, code, port):
"""
The second half of the SOCKS connection setup. For a SOCKSv4 packet this
is after the server address has been extracted from the header. For a
SOCKSv4a packet this is after the host name has been resolved.
@type server: L{str}
@param server: The IP address of the destination, represented as a
dotted quad.
@type user: L{str}
@param user: The username associated with the connection.
@type version: L{int}
@param version: The SOCKS protocol version number.
@type code: L{int}
@param code: The command code. 1 means establish a TCP/IP stream
connection, and 2 means establish a TCP/IP port binding.
@type port: L{int}
@param port: The port number associated with the connection.
"""
assert version == 4, "Bad version code: %s" % version
if not self.authorize(code, server, port, user):
self.makeReply(91)
return
if code == 1: # CONNECT
d = self.connectClass(server, port, SOCKSv4Outgoing, self)
d.addErrback(lambda result, self=self: self.makeReply(91))
elif code == 2: # BIND
d = self.listenClass(0, SOCKSv4IncomingFactory, self, server)
d.addCallback(lambda x, self=self: self.makeReply(90, 0, x[1], x[0]))
else:
raise RuntimeError(f"Bad Connect Code: {code}")
assert self.buf == b"", "hmm, still stuff in buffer... %s" % repr(self.buf)
def connectionLost(self, reason):
if self.otherConn:
self.otherConn.transport.loseConnection()
def authorize(self, code, server, port, user):
log.msg(
"code %s connection to %s:%s (user %s) authorized"
% (code, server, port, user)
)
return 1
def connectClass(self, host, port, klass, *args):
return protocol.ClientCreator(reactor, klass, *args).connectTCP(host, port)
def listenClass(self, port, klass, *args):
serv = reactor.listenTCP(port, klass(*args))
return defer.succeed(serv.getHost()[1:])
def makeReply(self, reply, version=0, port=0, ip="0.0.0.0"):
self.transport.write(
struct.pack("!BBH", version, reply, port) + socket.inet_aton(ip)
)
if reply != 90:
self.transport.loseConnection()
def write(self, data):
self.log(self, data)
self.transport.write(data)
def log(self, proto, data):
if not self.logging:
return
peer = self.transport.getPeer()
their_peer = self.otherConn.transport.getPeer()
f = open(self.logging, "a")
f.write(
"%s\t%s:%d %s %s:%d\n"
% (
time.ctime(),
peer.host,
peer.port,
((proto == self and "<") or ">"),
their_peer.host,
their_peer.port,
)
)
while data:
p, data = data[:16], data[16:]
f.write(string.join(map(lambda x: "%02X" % ord(x), p), " ") + " ")
f.write((16 - len(p)) * 3 * " ")
for c in p:
if len(repr(c)) > 3:
f.write(".")
else:
f.write(c)
f.write("\n")
f.write("\n")
f.close()
class SOCKSv4Factory(protocol.Factory):
"""
A factory for a SOCKSv4 proxy.
Constructor accepts one argument, a log file name.
"""
def __init__(self, log):
self.logging = log
def buildProtocol(self, addr):
return SOCKSv4(self.logging, reactor)
class SOCKSv4IncomingFactory(protocol.Factory):
"""
A utility class for building protocols for incoming connections.
"""
def __init__(self, socks, ip):
self.socks = socks
self.ip = ip
def buildProtocol(self, addr):
if addr[0] == self.ip:
self.ip = ""
self.socks.makeReply(90, 0)
return SOCKSv4Incoming(self.socks)
elif self.ip == "":
return None
else:
self.socks.makeReply(91, 0)
self.ip = ""
return None
|