-
Notifications
You must be signed in to change notification settings - Fork 2
/
protocol.py
99 lines (72 loc) · 2.63 KB
/
protocol.py
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
import auth
from exceptions import InvalidRequest, WrongProtocol
import asyncio
class Socks5ProtocolState:
INIT = 0
NEGOTIATED = 1
AUTHORIZED = 2
CONNECTED = 3
class Socks5Protocol(asyncio.Protocol)
def __init__(self):
self.transport = None
self.state = Socks5Protocol.INIT
def _next_state(self, skips=0):
""" Move protocol state forward by one plus skips"""
self.state += 1 + skips
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('Receive connection from {}.'.format(peername))
self.transport = transport
def data_received(self, data):
if self.state == Socks5ProtocolState.INIT:
self._negotiate_auth_method(data)
elif self.state == Socks5ProtocolState.NEGOTIATED:
raise NotImplemented
elif self.state == Socks5ProtocolState.AUTHORIZED:
self._connect(data)
def _negotiate_auth_method(self, data):
if len(data) <= 2:
raise InvalidRequest
if data[0] != b'\x05':
raise WrongProtocol
auth_method_count = int(data[1], 16)
auth_method_codes = list(data[2:])
if len(auth_method_codes) < auth_method_count:
raise InvalidRequest
# no auth emthod proposed by client is accept
accepted_code = '\xff'
for auth_method_code in auth_method_codes:
if auth_method_code in auth.acceptable_auth_method_codes:
accepted_code = auth_method_code
break
respone = '\x05' + accepted_code
self.transport.write(response)
self.state = self.state
# skip the auth phase when not required
self._next_state(self.accepted_code == auth.NoAuthRequired.method_code)
def _connect(self, data):
version = data[:1]
assert version == '\x05'
cmd = data[1:2]
assert int(cmd, 16) == 1
addr_type = int(data[3:4], 16)
assert addr_type in (1, 3, 4)
if addr_type == 1:
addr = data[4:8]
addr = ':'.join([for byte in addr])
elif addr_type == 3:
addr_octets_count = int(data[4], 16)
addr = data[5:5+addr_octets_count]
else:
addr = data[4:20]
def _channeling(self, data):
loop = asyncio.get_event_loop()
coro = loop.create_server(Socks5Protocol, '127.0.0.1', 1080)
server = loop.run_until_complete(coro)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()