An asynchronous proxy server written in pure Python, supporting SOCKS4, SOCKS4a, SOCKS5, and SOCKS5h protocols. The project is designed for educational purposes and has no external dependencies.
This project is actively developed on a periodic basis and is maintained as a hobby. Contributions and feedback are welcome. Feel free to reach out via my Telegram account @shpaker.
- Asynchronous: Built with asyncio for high performance
- No Dependencies: Pure Python implementation without external libraries
- Multiple Protocols: Support for SOCKS4, SOCKS4a, SOCKS5, and SOCKS5h
- Flexible Configuration: Custom authentication and domain resolution
- Rule System: Access control based on IP addresses and domains
- CLI and API: Use via command line or programmatic interface
- Type Safety: Full type hints support
- Python >= 3.14
pip install soxyproxy- Create a configuration file
config.toml:
[proxy]
protocol = "socks5"
transport = "tcp"
[transport]
host = "127.0.0.1"
port = 1080
[[ruleset.connecting.allow]]
from = "127.0.0.1"
[[ruleset.proxying.allow]]
from = "127.0.0.1"
to = "0.0.0.0/0"- Start the server:
# Logs to terminal
soxy config.toml
# Logs to file
soxy config.toml --logfile logs.txt
# or
soxy config.toml -l logs.txt- Test the connection:
# SOCKS5 (without authentication)
curl -x "socks5://127.0.0.1:1080" https://google.com -v
# SOCKS5h (with domain resolution on proxy side)
curl -x "socks5h://127.0.0.1:1080" https://google.com -v
# SOCKS5 with authentication (if configured)
curl -x "socks5://alice:password123@127.0.0.1:1080" https://google.com -vimport asyncio
import logging
from ipaddress import IPv4Address, IPv4Network
from socket import gethostbyname
import soxy
logging.basicConfig(level=logging.INFO)
def auther(username: str, password: str) -> bool:
"""Simple authentication function."""
return username == "user" and password == "pass"
def resolver(domain_name: str) -> IPv4Address:
"""Domain name resolver."""
return IPv4Address(gethostbyname(domain_name))
async def main() -> None:
async with soxy.Proxy(
protocol=soxy.Socks5(
auther=auther,
resolver=resolver, # Required for SOCKS5h and SOCKS4a
),
transport=soxy.TcpTransport(host="127.0.0.1", port=1080),
ruleset=soxy.Ruleset(
allow_connecting_rules=[
soxy.ConnectingRule(
from_addresses=IPv4Address("127.0.0.1"),
)
],
allow_proxying_rules=[
soxy.ProxyingRule(
from_addresses=IPv4Address("127.0.0.1"),
to_addresses=IPv4Network("0.0.0.0/0"),
),
],
),
) as app:
await app.serve_forever()
if __name__ == "__main__":
asyncio.run(main())socks5:
curl -x "socks5://top:secret@127.0.0.1:1080" https://google.com -vsocks5h:
curl -x "socks5h://top:secret@127.0.0.1:1080" https://google.com -vConfiguration is specified in TOML format and consists of three main sections:
Proxy server settings:
protocol(string): SOCKS protocol ("socks4","socks4a","socks5","socks5h")transport(string): Transport protocol (currently only"tcp")
Authentication settings (optional - if omitted, proxy works without authentication):
- For SOCKS5/SOCKS5h: Dictionary mapping
usernametopassword - For SOCKS4/SOCKS4a: Dictionary with usernames as keys (values are ignored, only presence of username is checked)
Example:
[proxy.auth]
alice = "password123"
bob = "secret456"Transport layer settings:
host(string): IP address to listen on (e.g.,"127.0.0.1"or"0.0.0.0")port(number): Port to listen on (e.g.,1080)
Access control rules:
connecting.allow: List of rules allowing client connectionsconnecting.block: List of rules blocking client connectionsproxying.allow: List of rules allowing request proxyingproxying.block: List of rules blocking request proxying
Each rule contains:
from: Source IP address or network (IPv4/IPv6 address or CIDR)to: Destination IP address, network, or domain name (only for proxying rules)
[proxy]
protocol = "socks5"
transport = "tcp"
# Optional authentication (omit this section for no authentication)
[proxy.auth]
alice = "password123"
bob = "secret456"
charlie = "mypass789"
[transport]
host = "0.0.0.0"
port = 1080
# Connection rules
[[ruleset.connecting.allow]]
from = "127.0.0.1"
[[ruleset.connecting.allow]]
from = "192.168.1.0/24"
[[ruleset.connecting.block]]
from = "10.0.0.1"
# Proxying rules
[[ruleset.proxying.allow]]
from = "127.0.0.1"
to = "0.0.0.0/0"
[[ruleset.proxying.allow]]
from = "192.168.1.0/24"
to = "8.8.8.8"
[[ruleset.proxying.block]]
from = "127.0.0.1"
to = "10.0.0.0/8"The simplest way to configure authentication is through the configuration file:
[proxy]
protocol = "socks5"
transport = "tcp"
# Authentication dictionary: username -> password
[proxy.auth]
alice = "password123"
bob = "secret456"
charlie = "mypass789"
[transport]
host = "127.0.0.1"
port = 1080
[ruleset]
connecting = { allow = [], block = [] }
proxying = { allow = [], block = [] }For SOCKS4/SOCKS4a (username only, no password):
[proxy]
protocol = "socks4"
[proxy.auth]
user1 = "" # Value doesn't matter, only username presence is checked
user2 = ""
admin = ""Note: Authentication is optional. If [proxy.auth] section is omitted, the proxy works without authentication.
For SOCKS5 and SOCKS4 protocols, you can configure custom authentication programmatically:
async def async_auther(username: str, password: str) -> bool:
# Asynchronous database check
# ...
return True
# Or synchronous function
def sync_auther(username: str, password: str) -> bool:
return username in allowed_users and check_password(username, password)
protocol = soxy.Socks5(auther=async_auther) # or sync_autherFor working with domain names (SOCKS5h, SOCKS4a), a resolver is required:
async def custom_resolver(domain_name: str) -> IPv4Address | None:
# Custom resolution logic
# For example, using DNS-over-HTTPS
# ...
return IPv4Address("1.2.3.4")
protocol = soxy.Socks5(
resolver=custom_resolver,
)The rule system allows flexible access control:
ruleset = soxy.Ruleset(
# Allow connections only from local address
allow_connecting_rules=[
soxy.ConnectingRule(
from_addresses=IPv4Address("127.0.0.1"),
),
],
# Allow proxying to any addresses
allow_proxying_rules=[
soxy.ProxyingRule(
from_addresses=IPv4Address("127.0.0.1"),
to_addresses=IPv4Network("0.0.0.0/0"),
),
],
# Block proxying to internal networks
block_proxying_rules=[
soxy.ProxyingRule(
from_addresses=IPv4Network("0.0.0.0/0"),
to_addresses=IPv4Network("10.0.0.0/8"),
),
soxy.ProxyingRule(
from_addresses=IPv4Network("0.0.0.0/0"),
to_addresses=IPv4Network("192.168.0.0/16"),
),
],
)# SOCKS4
protocol = soxy.Socks4()
# SOCKS4a (with domain name support)
protocol = soxy.Socks4(resolver=resolver)
# SOCKS5 (without authentication)
protocol = soxy.Socks5()
# SOCKS5 with authentication
protocol = soxy.Socks5(auther=auther)
# SOCKS5h (with domain resolution on proxy side)
protocol = soxy.Socks5(resolver=resolver, auther=auther)git clone https://github.com/shpaker/soxyproxy.git
cd soxyproxy
pip install -e ".[dev]"pytest# Linting
ruff check .
# Type checking
mypy soxyThis project is licensed under the GPL-3.0 license.