作业题目
实现localProxy和remoteProxy分离式代理。
支持SOCKS5代理和HTTPS代理(基于#2作业的成果)。
localProxy收到的每个TCP连接单独建立代理TCP连接。
# localProxy.py
import asyncio
import argparse
import sys
from struct import unpack, pack
async def msg_send(reader, writer, host):
while reader.at_eof:
try:
data = await reader.read(1024 * 64)
if not data:
writer.close()
break
except (ConnectionAbortedError, ConnectionResetError) as e:
writer.close()
print(f"{host} exit exceptionally {repr(e)}")
break
try:
writer.write(data)
await writer.drain()
except (ConnectionAbortedError, ConnectionResetError) as e:
writer.close()
print(f"{host} exit exceptionally {repr(e)}")
break
print(f"{host} exit")
async def handle(reader, writer):
data = await reader.read(1024 * 64)
if (data[0] == 5):
proxy_type = "socks5"
else:
proxy_type = "https"
if (proxy_type == "socks5"):
addr = writer.get_extra_info('peername')
print(f"connect from {addr!r}")
# message at least 3 bytes
if len(data) < 3:
print('message is too short')
writer.close()
writer.write(b'\x05\x00')
await writer.drain()
data = await reader.read(1024 * 64)
try:
reader_remote, writer_remote = await asyncio.open_connection(args.remote_host, args.remote_port)
print(f"connect to remote proxy successful")
except:
print(f"connect to remote proxy failed")
writer.close()
return
writer_remote.write(data)
client_to_remote = msg_send(reader, writer_remote, addr[0])
remote_to_client = msg_send(reader_remote, writer, "remote proxy")
await asyncio.gather(client_to_remote, remote_to_client)
else:
addr = writer.get_extra_info('peername')
print(f"connect from {addr!r}")
try:
reader_remote, writer_remote = await asyncio.open_connection(args.remote_host, args.remote_port)
print(f"connect to remote proxy successful")
except:
print(f"connect to remote proxy failed")
writer.close()
return
writer_remote.write(data)
client_to_remote = msg_send(reader, writer_remote, addr[0])
remote_to_client = msg_send(reader_remote, writer, "remote proxy")
await asyncio.gather(client_to_remote, remote_to_client)
async def main_logic():
server = await asyncio.start_server(handle, '127.0.0.1', args.listen_port)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(main_logic())
if __name__ == "__main__":
_parser = argparse.ArgumentParser(description='local socks5 https dual proxy server')
_parser.add_argument('--port', dest='listen_port', metavar='listen_port', required=True,
help='local proxy listen port')
_parser.add_argument('--remote_host', dest='remote_host', metavar='remote_host', required=True,
help='remote proxy listen port')
_parser.add_argument('--remote_port', dest='remote_port', metavar='remote_port', required=True,
help='remote proxy listen port')
_parser.add_argument('--user_name', dest='user_name', metavar='user_name', required=True,
help='remote proxy user name')
_parser.add_argument('--pass_word', dest='pass_word', metavar='pass_word', required=True,
help='remote proxy pass word')
args = _parser.parse_args()
main()
#remoteProxy.py
import asyncio
import argparse
import sys
import ipaddress
from struct import unpack, pack
async def msg_send(reader, writer, host):
while reader.at_eof:
try:
data = await reader.read(1024 * 64)
if not data:
writer.close()
break
except (ConnectionAbortedError, ConnectionResetError) as e:
writer.close()
print(f"{host} exit exceptionally {repr(e)}")
break
try:
writer.write(data)
await writer.drain()
except (ConnectionAbortedError, ConnectionResetError) as e:
writer.close()
print(f"{host} exit exceptionally {repr(e)}")
break
print(f"{host} exit")
async def handle(reader, writer):
data = await reader.read(1024 * 64)
if (data[0] == 5):
proxy_type = "socks5"
else:
proxy_type = "https"
if(proxy_type == "socks5"):
addr = writer.get_extra_info('peername')
print(f"connect from {addr!r}")
info = unpack('!4B', data[:4])
ver, cmd, atyp = info[0], info[1], info[3]
# domain name
if ver == 5 and cmd == 1 and atyp == 3:
addr_len = unpack('!B', data[4:5])[0]
dst_addr = data[5:addr_len + 5].decode()
dst_port = unpack('!H', data[addr_len + 5: addr_len + 7])[0]
print(f'destination domain name {dst_addr},destination port {dst_port}')
try:
reader_remote, writer_remote = await asyncio.open_connection(dst_addr, dst_port)
writer.write(pack('!5B', 5, 0, 0, 3, addr_len) + dst_addr.encode() + pack('!H', dst_port))
await writer.drain()
print(f'connect to {dst_addr} successful ')
except (TimeoutError, ConnectionRefusedError, OSError) as e:
print(f'connect to {dst_addr} failed ')
writer.write(pack('!5B', 5, 3, 0, 3, addr_len) + dst_addr.encode() + pack('!H', dst_port))
await writer.drain()
writer.close()
return
client_to_remote = msg_send(reader, writer_remote, addr[0])
remote_to_client = msg_send(reader_remote, writer, dst_addr)
await asyncio.gather(client_to_remote, remote_to_client)
# ipv4
elif ver == 5 and cmd == 1 and atyp == 1:
dst_address = str(ipaddress.ip_address(data[4:8]))
dst_port = unpack('H', data[8:10])[0]
print(f'destination address {dst_address},destination port {dst_port}')
try:
reader_remote, writer_remote = await asyncio.open_connection(dst_address, dst_port)
writer.write(pack('!8B', 5, 0, 0, 1, *unpack('!BBBB', data[4:8])) + pack('!H', dst_port))
await writer.drain()
print(f'connect to {dst_addr} successful ')
except (TimeoutError, ConnectionRefusedError, OSError) as e:
print(f'connect to {dst_addr} failed ')
writer.write(pack('!8B', 5, 3, 0, 1, *unpack('!BBBB', data[4:8])) + pack('!H', dst_port))
await writer.drain()
writer.close()
return
client_to_remote = msg_send(reader, writer_remote, addr[0])
remote_to_client = msg_send(reader_remote, writer, dst_address)
await asyncio.gather(client_to_remote, remote_to_client)
# ipv6
elif ver == 5 and cmd == 1 and atyp == 4:
dst_address = str(ipaddress.ip_address(data[4:20]))
dst_port = unpack('H', data[20:22])[0]
print(f'destination address {dst_address},destination port {dst_port}')
try:
reader_remote, writer_remote = await asyncio.open_connection(dst_address, dst_port)
writer.write(pack('!20B', 5, 0, 0, 1, *unpack('!16B', data[4:20])) + pack('!H', dst_port))
await writer.drain()
print(f'connect to {dst_addr} successful ')
except (TimeoutError, ConnectionRefusedError, OSError) as e:
print(f'connect to {dst_addr} failed ')
writer.write(pack('!20B', 5, 3, 0, 1, *unpack('!16B', data[4:20])) + pack('!H', dst_port))
await writer.drain()
writer.close()
return
client_to_remote = msg_send(reader, writer_remote, addr[0])
remote_to_client = msg_send(reader_remote, writer, dst_address)
await asyncio.gather(client_to_remote, remote_to_client)
else:
print('unsuppoted request')
writer.close()
return
else:
addr = writer.get_extra_info('peername')
print(f"connect from {addr!r}")
info = data.decode().split("\r\n")
request = info[0].split(" ")
method = request[0]
host = request[1].split(":")[0]
port = request[1].split(":")[1]
ver = request[2]
print(f'destination domain name {host},destination port {port}')
if method.upper() == 'CONNECT':
try:
reader_remote, writer_remote = await asyncio.open_connection(host, port)
reply = ver + " 200 Connection established\r\n"
reply += "Proxy-agent: wyx\r\n\r\n"
writer.write(reply.encode())
await writer.drain()
print(f'connect to {host} successful ')
except (TimeoutError, ConnectionRefusedError) as e:
print(f'connect to {host} failed ')
writer.close()
return
client_to_remote = msg_send(reader, writer_remote, addr[0])
remote_to_client = msg_send(reader_remote, writer, host)
await asyncio.gather(client_to_remote, remote_to_client)
async def main_logic():
server = await asyncio.start_server(handle, args.listen_host, args.listen_port)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(main_logic())
if __name__ == "__main__":
_parser = argparse.ArgumentParser(description='remote socks5 https dual proxy server')
_parser.add_argument('--host', dest='listen_host', metavar='listen_host', required=True,
help='remote proxy listen host')
_parser.add_argument('--port', dest='listen_port', metavar='listen_port', required=True,
help='remote proxy listen port')
args = _parser.parse_args()
main()