-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtunnel.py
104 lines (90 loc) · 3.54 KB
/
tunnel.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
99
100
101
102
103
104
#!/usr/bin/python3
import asyncio
import socket
import sys
class Tunnel:
TUNNEL_DATA_SIZE = 65536
def __init__(self, c_reader, c_writer, r_reader, r_writer, header: bytes):
self.client_reader: asyncio.StreamReader = c_reader
self.client_writer: asyncio.StreamWriter = c_writer
self.remote_reader: asyncio.StreamReader = r_reader
self.remote_writer: asyncio.StreamWriter = r_writer
self.header_data = header
self.is_running = True
self.event = asyncio.Event()
async def tunnel(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, header: bytes=None):
if header:
try:
data = await reader.read(self.TUNNEL_DATA_SIZE)
writer.write(header + data)
await writer.drain()
except Exception:
self.is_running = False
self.event.set()
return
while self.is_running:
if reader.at_eof():
await asyncio.sleep(1)
continue
try:
data = await reader.read(self.TUNNEL_DATA_SIZE)
writer.write(data)
await writer.drain()
except Exception:
self.is_running = False
self.event.set()
async def close_tunnel(self):
await self.event.wait()
self.client_writer.close()
await self.client_writer.wait_closed()
self.remote_writer.close()
await self.remote_writer.wait_closed()
async def run(self):
try:
await asyncio.gather(
self.tunnel(self.client_reader, self.remote_writer, self.header_data),
self.tunnel(self.remote_reader, self.client_writer),
self.close_tunnel()
)
except Exception as e:
print("[-] gather:", str(e))
async def handle_connect(remote_addr: tuple, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
header = None
# # filter unwanted data
# header = await reader.read(4)
# if header.startswith(b"GET ") or header.startswith(b"POST ") \
# or header.startswith(b"HEAD"):
# writer.close()
# await writer.wait_closed()
# return
try:
remote_ip, remote_port = socket.getaddrinfo(remote_addr[0], remote_addr[1])[0][4]
remote_reader, remote_writer = await asyncio.open_connection(
remote_ip, remote_port
)
except Exception as e:
print("[-] connect remote failed:", str(e))
writer.close()
await writer.wait_closed()
return
print(f"[+] Connected: [{writer.get_extra_info('peername')}] -> [{remote_writer.get_extra_info('peername')}]")
tunnel = Tunnel(reader, writer, remote_reader, remote_writer, header)
await tunnel.run()
async def main(listening_port: int, remote_addr: tuple):
server = await asyncio.start_server(
lambda reader, writer: handle_connect(
remote_addr, reader, writer
), "0.0.0.0", listening_port,
)
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f"Listening on {addrs}")
async with server:
await server.serve_forever()
if __name__ == '__main__':
if len(sys.argv) < 4:
print('Usage: %s listening_port remote_host remote_port' % sys.argv[0])
exit(-1)
listening_port = int(sys.argv[1])
remote_host = sys.argv[2]
remote_port = int(sys.argv[3])
asyncio.run(main(listening_port, (remote_host, remote_port)))