Skip to content

Instantly share code, notes, and snippets.

@SteveHere
Forked from zengxs/proxy_socks2http.py
Last active October 30, 2023 03:25
Show Gist options
  • Save SteveHere/f702ed857a529c87a5914725c6e52e64 to your computer and use it in GitHub Desktop.
Save SteveHere/f702ed857a529c87a5914725c6e52e64 to your computer and use it in GitHub Desktop.
Prettification + update to current asyncio syntax (Python 3.11+)
import logging
import socks # use pysocks
import asyncio
from datetime import datetime
from itertools import cycle
logging.basicConfig(level=logging.INFO)
socks_router_loop = cycle(( # simple round-robin router to socks proxies
('127.2.0.0', 9050, None, None), # address, port, username, password
# ('127.3.0.0', 9050, "proxy", "passwordpassword"),
# ('127.4.0.0', 9050, "proxy", "passwordpassword"),
))
proxy_address, proxy_port = '127.1.0.0', 9051
def now_time() -> str:
return str(datetime.now().time())
async def dial(client_read: asyncio.StreamReader, client_write: asyncio.StreamWriter, server_read: asyncio.StreamReader, server_write: asyncio.StreamWriter):
async def io_copy(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
try:
while data := await reader.read(8192):
writer.write(data)
writer.close()
except (OSError, ConnectionResetError) as e:
logging.error(f"{now_time()}: {e}")
asyncio.ensure_future(io_copy(client_read, server_write))
asyncio.ensure_future(io_copy(server_read, client_write))
async def open_socks5(host: str, port: int, socks_address: str, socks_port: int,
socks_username: str | None = None, socks_password: str | None = None,
limit=2 ** 16, aio_loop=None):
s = None
while s is None:
try:
s = socks.socksocket()
s.set_proxy(socks.SOCKS5, socks_address, socks_port, username=socks_username, password=socks_password)
s.connect((host, port))
except (socks.GeneralProxyError, OSError):
logging.error(f"{now_time()}: Connection error with {socks_address}:{socks_port} - Retrying")
s = None
if not aio_loop:
aio_loop = asyncio.get_event_loop()
reader = asyncio.StreamReader(limit=limit, loop=aio_loop)
protocol = asyncio.StreamReaderProtocol(reader, loop=aio_loop)
transport, _ = await aio_loop.create_connection(lambda: protocol, sock=s)
return reader, asyncio.StreamWriter(transport, protocol, reader, aio_loop)
def get_connection_properties(headers: list) -> (str, int, bool):
method, url, version = headers[0].decode().split(' ', 2)
if is_connect := (method.upper() == 'CONNECT'):
host, port = url.split(':', 1)
else:
hosts = [hl[5:].lstrip() for header in headers[1:] if (hl := header.decode())[:5].lower() == 'host:']
if not hosts: # If empty
raise ValueError("No http host line")
host, port = (host_text, "80") if ':' not in (host_text := hosts[0].strip("\r\n")) else host_text.split(':', 1)
return host, int(port), is_connect
async def handle_connection(client_read: asyncio.StreamReader, client_write: asyncio.StreamWriter):
http_header_list = []
try:
while (line := await client_read.readline()) != b'\r\n':
http_header_list.append(line)
http_header_list.append(line)
host, port, is_connect = get_connection_properties(http_header_list)
except (IOError, ValueError) as e:
logging.error(f"{now_time()}: {e}")
client_write.close()
return
s_address, s_port, s_username, s_password = next(socks_router_loop)
server_read, server_write = await open_socks5(
host=host, port=port,
socks_address=s_address, socks_port=s_port,
socks_username=s_username, socks_password=s_password
)
if is_connect:
client_write.write(b'HTTP/1.0 200 Connection Established\r\n\r\n')
else:
server_write.write(b''.join(http_header_list))
asyncio.ensure_future(dial(client_read, client_write, server_read, server_write))
async def main():
server = await asyncio.start_server(handle_connection, host=proxy_address, port=proxy_port)
try:
await server.start_serving()
print("Socks proxy open: ", proxy_address, proxy_port)
await server.serve_forever()
except (KeyboardInterrupt, asyncio.exceptions.CancelledError):
print("Keyboard Interrupt made: Shutting down")
finally:
server.close()
print("Server fully shut down")
if __name__ == '__main__':
asyncio.run(main())
@raffaem
Copy link

raffaem commented Dec 26, 2021

Uhm I cannot get it to work with wget:

$ ./proxy_socks2http_stevehere.py 
./proxy_socks2http_stevehere.py:75: DeprecationWarning: There is no current event loop
  loop, server = get_event_loop(), start_server(handle_connection, host=proxy_address, port=proxy_port)
Socks proxy open:  127.0.0.1 9000
18:19:45.375158 - [Errno 104] Connection reset by peer
18:20:00.878676 - [Errno 104] Connection reset by peer

@SteveHere
Copy link
Author

@raffaem
Sorry for the late response: The updated gist should work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment