feat: add bypass rules, weighted pool selection, integration tests
Per-listener bypass rules skip the chain for local/private destinations (CIDR, exact IP/hostname, domain suffix). Weighted multi-candidate pool selection biases toward pools with more alive proxies. End-to-end integration tests validate the full client->s5p->hop->target path using mock SOCKS5 proxies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -206,10 +206,28 @@ listeners:
|
||||
| `pool:name` | Named pool `name` (case-sensitive) |
|
||||
| `pool:` | Same as bare `pool` (empty name = default) |
|
||||
| `Pool:name` | Prefix is case-insensitive; name is case-sensitive |
|
||||
| `[pool:a, pool:b]` | Random choice from candidates `a` or `b` per connection |
|
||||
|
||||
The `pool` keyword in a chain means "append a random alive proxy from the
|
||||
assigned pool". Multiple `pool` entries = multiple pool hops (deeper chaining).
|
||||
|
||||
### Multi-candidate pool hops
|
||||
|
||||
Use a YAML list to randomly pick from a set of candidate pools at each hop.
|
||||
On each connection, one candidate is chosen at random per hop (independently).
|
||||
|
||||
```yaml
|
||||
listeners:
|
||||
- listen: 0.0.0.0:1080
|
||||
chain:
|
||||
- socks5://10.200.1.13:9050
|
||||
- [pool:clean, pool:mitm] # hop 1: random choice
|
||||
- [pool:clean, pool:mitm] # hop 2: random choice
|
||||
```
|
||||
|
||||
Single-element pool references (`pool`, `pool:name`) and multi-candidate
|
||||
lists can be mixed freely in the same chain. All existing syntax is unchanged.
|
||||
|
||||
When `pool:` is omitted on a listener with pool hops, it defaults to
|
||||
`"default"`. A listener referencing an unknown pool name causes a fatal
|
||||
error at startup. Listeners without pool hops ignore the `pool:` key.
|
||||
@@ -224,6 +242,41 @@ error at startup. Listeners without pool hops ignore the `pool:` key.
|
||||
| FirstHopPool | per unique first hop | Listeners with same first hop share it |
|
||||
| Chain + pool_hops | per listener | Each listener has its own chain depth |
|
||||
|
||||
## Bypass Rules
|
||||
|
||||
Per-listener rules to skip the chain for specific destinations. When a target
|
||||
matches a bypass rule, s5p connects directly (no chain, no pool hops).
|
||||
|
||||
```yaml
|
||||
listeners:
|
||||
- listen: 0.0.0.0:1080
|
||||
bypass:
|
||||
- 127.0.0.0/8 # CIDR: loopback
|
||||
- 10.0.0.0/8 # CIDR: RFC 1918
|
||||
- 192.168.0.0/16 # CIDR: RFC 1918
|
||||
- fc00::/7 # CIDR: IPv6 ULA
|
||||
- localhost # exact hostname
|
||||
- .local # domain suffix (matches *.local and local)
|
||||
chain:
|
||||
- socks5://127.0.0.1:9050
|
||||
- pool
|
||||
```
|
||||
|
||||
### Rule syntax
|
||||
|
||||
| Pattern | Type | Matches |
|
||||
|---------|------|---------|
|
||||
| `10.0.0.0/8` | CIDR | Any IP in the network |
|
||||
| `127.0.0.1` | Exact IP | That IP only |
|
||||
| `localhost` | Exact hostname | String-equal match |
|
||||
| `.local` | Domain suffix | `*.local` and `local` itself |
|
||||
|
||||
CIDR rules only match IP addresses, not hostnames. Domain suffix rules only
|
||||
match hostnames, not IPs. Exact rules match both (string compare for hostnames,
|
||||
parsed compare for IPs).
|
||||
|
||||
When bypass is active, retries are disabled (direct connections are not retried).
|
||||
|
||||
### Backward compatibility
|
||||
|
||||
When no `listeners:` key is present, the old `listen`/`chain` format creates
|
||||
|
||||
Reference in New Issue
Block a user