user 29b4a36863 feat: named proxy pools with per-listener assignment
Add proxy_pools: top-level config (dict of name -> pool config) so
listeners can draw from different proxy sources. Each pool has
independent sources, health testing, state persistence, and refresh
cycles.

- PoolSourceConfig gains mitm: bool|None for API ?mitm=0/1 filtering
- ListenerConfig gains pool_name for named pool assignment
- ProxyPool gains name param with prefixed log messages and
  per-name state file derivation (pool-{name}.json)
- server.py replaces single proxy_pool with proxy_pools dict,
  validates listener pool references at startup, per-listener closure
- API /pool merges all pools (with pool field on multi-pool entries),
  /status and /config expose per-pool summaries
- Backward compat: singular proxy_pool: registers as "default"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 11:33:53 +01:00
2026-02-16 19:07:10 +01:00

s5p

SOCKS5 proxy server with Tor and proxy-chain support. Routes connections through configurable chains of SOCKS4, SOCKS5, and HTTP CONNECT proxies.

Features

  • SOCKS5 server (RFC 1928)
  • Proxy chaining: tunnel through multiple hops in sequence
  • Supported hop protocols: SOCKS5, SOCKS4/4a, HTTP CONNECT
  • Per-hop authentication (username/password)
  • DNS leak prevention (domain names forwarded to proxies, never resolved locally)
  • Tor integration (SOCKS5 hop + control port NEWNYM for circuit rotation)
  • Multi-listener: different ports with different chain depths (Tor-only, Tor+1, Tor+2)
  • Managed proxy pool: multiple sources (API + file), health-tested, weighted selection
  • Per-proxy failure backoff (60s cooldown), stale proxy expiry, chain pre-flight
  • Fast warm start (seconds on restart vs minutes on cold start)
  • Connection retry with proxy rotation (configurable attempts)
  • Dead proxy reporting to upstream API (optional report_url)
  • SIGHUP hot reload (timeout, retries, log_level, pool config)
  • Connection metrics with pool stats (logged periodically and on shutdown)
  • Concurrent connection limit with backpressure (max_connections)
  • Async HTTP client for proxy source fetching (parallel, no threads)
  • First-hop TCP connection pool (pre-warmed, stale-evicted)
  • Built-in control API (runtime metrics, pool state, config reload via HTTP)
  • Container-ready (Alpine-based, podman/docker)
  • Graceful shutdown (SIGTERM/SIGINT)
  • Pure Python, asyncio-based, minimal dependencies

Quick Start

# Install locally
cd ~/git/s5p
python -m venv .venv && source .venv/bin/activate
pip install -e .

# Run with Tor
s5p -C socks5://127.0.0.1:9050

# Run with config file
cp config/example.yaml config/s5p.yaml  # edit with your proxies
s5p -c config/s5p.yaml

# Test it
curl --proxy socks5h://127.0.0.1:1080 https://check.torproject.org/api/ip

Container

make build   # podman-compose build
make up      # podman-compose up -d
make logs    # podman-compose logs -f
make down    # podman-compose down

Source, config, and data are bind-mounted, not baked into the image. Pool state and profile output persist in ~/.cache/s5p/ (/data inside container).

Configuration

Copy the example and edit with your proxy chain:

cp config/example.yaml config/s5p.yaml
timeout: 10
retries: 3
max_connections: 256                  # concurrent connection limit
pool_size: 8                          # pre-warmed connections to first hop
api_listen: 127.0.0.1:1081           # control API (disabled by default)

# Multi-listener: each port gets a different chain depth
listeners:
  - listen: 0.0.0.0:1080
    chain:
      - socks5://127.0.0.1:9050
      - pool                           # Tor + 2 pool proxies
      - pool
  - listen: 0.0.0.0:1081
    chain:
      - socks5://127.0.0.1:9050
      - pool                           # Tor + 1 pool proxy
  - listen: 0.0.0.0:1082
    chain:
      - socks5://127.0.0.1:9050       # Tor only

# Old single-listener format still works:
# listen: 127.0.0.1:1080
# chain:
#   - socks5://127.0.0.1:9050

tor:
  control_port: 9051                  # Tor control port (NEWNYM)
  password: ""                        # or cookie_file for auth
  newnym_interval: 0                  # periodic circuit rotation (0 = manual)

proxy_pool:
  sources:
    - url: http://10.200.1.250:8081/proxies
      proto: socks5
    - file: /etc/s5p/proxies.txt     # one proxy URL per line
  refresh: 300                        # re-fetch interval (seconds)
  test_interval: 120                  # health test cycle (seconds)
  max_fails: 3                        # evict after N consecutive failures

config/s5p.yaml is gitignored; config/example.yaml is the tracked template.

CLI Reference

s5p [-c FILE] [-l [HOST:]PORT] [-C URL[,URL,...]] [-S URL] [-t SEC] [-r N] [-m N] [--api [HOST:]PORT] [-v|-q]

Options:
  -c, --config FILE        YAML config file
  -l, --listen [HOST:]PORT Listen address (default: 127.0.0.1:1080)
  -C, --chain URL[,URL]    Comma-separated proxy chain
  -S, --proxy-source URL   Proxy source API URL
  -t, --timeout SEC        Per-hop timeout (default: 10)
  -r, --retries N          Max attempts per connection (default: 3, proxy_source only)
  -m, --max-connections N  Max concurrent connections (default: 256)
  --api [HOST:]PORT        Enable control API (e.g. 127.0.0.1:1081)
  -v, --verbose            Debug logging
  -q, --quiet              Errors only
  --cprofile [FILE]        Enable cProfile, dump to FILE (default: s5p.prof)
  --tracemalloc [N]        Enable tracemalloc, show top N allocators on exit (default: 10)
  -V, --version            Show version

How Chaining Works

:1080  Client -> s5p -> Tor -> [pool] -> [pool] -> Destination  (2 pool hops)
:1081  Client -> s5p -> Tor -> [pool] -> Destination           (1 pool hop)
:1082  Client -> s5p -> Tor -> Destination                     (0 pool hops)

s5p connects to Hop1 via TCP, negotiates the hop protocol (SOCKS5/4/HTTP), then over that tunnel negotiates with Hop2, and so on. If a proxy pool is configured, alive proxies are appended per-connection (one per pool entry), weighted toward those with the most recent successful health test. Each hop only sees its immediate neighbors.

Description
No description provided
Readme 769 KiB
Languages
Python 99.6%
Makefile 0.2%
Dockerfile 0.2%