diff --git a/PROJECT.md b/PROJECT.md index 441fef2..3d314ed 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -22,6 +22,7 @@ Client -------> s5p -------> Hop 1 -------> Hop 2 -------> Target - **server.py** -- asyncio SOCKS5 server, chain builder, bidirectional relay - **proto.py** -- protocol handshake implementations (SOCKS5, SOCKS4/4a, HTTP CONNECT) - **config.py** -- YAML config loading, proxy URL parsing +- **source.py** -- dynamic proxy source (HTTP API fetch, cache, random selection) - **cli.py** -- argparse CLI, logging setup, cProfile support ## Deployment @@ -50,3 +51,4 @@ All other functionality uses Python stdlib (`asyncio`, `socket`, `struct`). - **Tor as a hop** -- no special Tor handling; it's just `socks5://127.0.0.1:9050` - **Graceful shutdown** -- SIGTERM/SIGINT handled in the event loop for clean container stops - **Config split** -- tracked example template, gitignored live config with real addresses +- **Proxy source** -- per-connection proxy rotation from HTTP API, cached with refresh diff --git a/README.md b/README.md index 1d8ada2..c68aac4 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ through configurable chains of SOCKS4, SOCKS5, and HTTP CONNECT proxies. - Per-hop authentication (username/password) - DNS leak prevention (domain names forwarded to proxies, never resolved locally) - Tor integration (Tor is just another SOCKS5 hop) +- Dynamic proxy source: fetch proxies from an HTTP API, rotate per-connection - Container-ready (Alpine-based, podman/docker) - Graceful shutdown (SIGTERM/SIGINT) - Pure Python, asyncio-based, minimal dependencies @@ -59,8 +60,12 @@ timeout: 10 chain: - socks5://127.0.0.1:9050 # Tor - - socks5://user:pass@proxy:1080 # post-Tor proxy - - http://proxy2:8080 # HTTP CONNECT proxy + +proxy_source: + url: http://10.200.1.250:8081/proxies + proto: socks5 # optional filter + limit: 1000 + refresh: 300 # cache refresh (seconds) ``` `config/s5p.yaml` is gitignored; `config/example.yaml` is the tracked template. @@ -68,12 +73,13 @@ chain: ## CLI Reference ``` -s5p [-c FILE] [-l [HOST:]PORT] [-C URL[,URL,...]] [-t SEC] [-v|-q] [--cprofile [FILE]] +s5p [-c FILE] [-l [HOST:]PORT] [-C URL[,URL,...]] [-S URL] [-t SEC] [-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) -v, --verbose Debug logging -q, --quiet Errors only @@ -84,9 +90,10 @@ Options: ## How Chaining Works ``` -Client -> s5p -> Hop1 -> Hop2 -> ... -> HopN -> Destination +Client -> s5p -> [static chain] -> [random proxy from source] -> Destination ``` s5p connects to Hop1 via TCP, negotiates the hop protocol (SOCKS5/4/HTTP), -then over that tunnel negotiates with Hop2, and so on. The final hop connects -to the actual destination. Each hop only sees its immediate neighbors. +then over that tunnel negotiates with Hop2, and so on. If a proxy source is +configured, a random proxy is appended to the chain per-connection. Each hop +only sees its immediate neighbors. diff --git a/ROADMAP.md b/ROADMAP.md index 590a9d4..26a6a6b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -10,6 +10,7 @@ - [x] Container deployment (Alpine + podman-compose) - [x] Graceful SIGTERM/SIGINT shutdown - [x] cProfile support +- [x] Dynamic proxy source API integration ## v0.2.0 diff --git a/TASKS.md b/TASKS.md index 320fe80..2dbfa5a 100644 --- a/TASKS.md +++ b/TASKS.md @@ -15,9 +15,9 @@ - [x] cProfile support (`--cprofile`) - [x] Config split (example.yaml tracked, s5p.yaml gitignored) -## Next +- [x] Dynamic proxy source API integration -- [ ] Gather working public proxy list for post-Tor chaining +## Next - [ ] Integration tests with mock proxy server - [ ] SOCKS5 server-side authentication - [ ] Tor control port integration diff --git a/docs/CHEATSHEET.md b/docs/CHEATSHEET.md index f39b6eb..fe076e8 100644 --- a/docs/CHEATSHEET.md +++ b/docs/CHEATSHEET.md @@ -11,6 +11,7 @@ s5p -l 0.0.0.0:9999 # custom listen address s5p -t 30 # 30s per-hop timeout s5p -v # debug logging s5p -q # errors only +s5p -S http://api:8081/proxies # proxy source API s5p --cprofile # profile to s5p.prof s5p --cprofile out.prof # profile to custom file ``` @@ -30,6 +31,16 @@ make down # podman-compose down cp config/example.yaml config/s5p.yaml # create live config (gitignored) ``` +## Proxy Source (config) + +```yaml +proxy_source: + url: http://10.200.1.250:8081/proxies + proto: socks5 # optional filter + limit: 1000 + refresh: 300 # seconds +``` + ## Proxy URLs ``` diff --git a/docs/USAGE.md b/docs/USAGE.md index e189e94..a21f0e1 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -18,6 +18,9 @@ s5p -l 0.0.0.0:9999 -C socks5://127.0.0.1:9050 # From config file s5p -c config/s5p.yaml +# With proxy source API (rotate exit proxy per-connection) +s5p -C socks5://127.0.0.1:9050 -S http://10.200.1.250:8081/proxies + # Debug mode s5p -v -C socks5://127.0.0.1:9050 ``` @@ -42,7 +45,13 @@ log_level: info chain: - socks5://127.0.0.1:9050 - - http://user:pass@proxy:8080 + +proxy_source: + url: http://10.200.1.250:8081/proxies + proto: socks5 # optional: filter by protocol + country: US # optional: filter by country + limit: 1000 # max proxies to fetch + refresh: 300 # cache refresh interval (seconds) ``` ## Proxy URL Format @@ -69,6 +78,29 @@ make down # stop and remove container Source (`./src`) and config (`./config/s5p.yaml`) are mounted read-only into the container. Edit locally, restart to pick up changes. +## Proxy Source + +Appends a random proxy from an HTTP API after the static chain on each +connection. Proxies are cached and refreshed at a configurable interval. + +```yaml +proxy_source: + url: http://10.200.1.250:8081/proxies + proto: socks5 # optional: only fetch this protocol + country: US # optional: only fetch this country + limit: 1000 # max proxies to fetch from API + refresh: 300 # re-fetch every 300 seconds +``` + +CLI shorthand (uses defaults for limit/refresh): + +```bash +s5p -C socks5://127.0.0.1:9050 -S http://10.200.1.250:8081/proxies +``` + +The API must return JSON: `{"proxies": [{"proto": "socks5", "proxy": "host:port"}, ...]}`. +Entries with `null` proto are skipped. + ## Profiling ```bash