AdguardTeam / AdguardTeam/AdGuardCLI
SOCKS5 proxy unusable in auto mode for ports within filtered_ports ('Route loop detected'); IPv6 literal targets silently dropped
- Langage dominant
- Aucune donnée de langage
- Étoiles
- 160
- Forks
- 6
- Métriques de merge des PR
- Aucune PR mergée en 30 j
Description
## Pre-checks
- [x] Filters were updated (I ran `adguard-cli check-update` before filing)
- [x] I checked the [Knowledge Base](https://adguard.com/kb/adguard-for-linux/) and/or [FAQ](https://adguard.com/kb/adguard-for-linux/solving-problems/)
- [x] I searched existing issues and found none covering this case
## App version
1.4.13
## OS version
Ubuntu 24.04.4 LTS (kernel 7.0.0-30-generic), dual-stack (IPv4 + IPv6), GNOME desktop
## Issue Details
**Setup:** transparent interception mode (`proxy_mode: auto`, default `filtered_ports: '80:5221,5300:49151'`), HTTPS filtering enabled for browsers only, DNS filtering disabled, listeners on 127.0.0.1 (HTTP 3129 / SOCKS5 1081).
In `auto` mode, the SOCKS5 proxy (127.0.0.1:1081) refuses to connect to **any destination whose port is inside `filtered_ports`**, returning SOCKS5 reply code 5 ("connection refused"). The same destinations work when the port is outside the range. This makes SOCKS5 unusable for web browsing (ports 80/443) — while the HTTP proxy on 3129 works fine for the same destinations.
### Steps to reproduce
1. With AdGuard CLI running in default auto mode:
```bash
curl -v --socks5-hostname 127.0.0.1:1081 http://1.1.1.1/
# curl: (97) Can't complete SOCKS5 connection to 1.1.1.1. (5)
```
2. The corresponding `proxy.log` entry (IPv4 case):
```
WARN AGFDVSocketFactory onConnectEvent: [1.1.1.1:80]: Route loop detected for outgoing connection to 1.1.1.1, local address: 192.168.1.x:55745
```
The same failure occurs when the outgoing socket binds to the machine's global IPv6 address:
```
WARN AGFDVSocketFactory onConnectEvent: [example.com:80]: Route loop detected for outgoing connection to example.com, local address: [2001:db8::x]:54675
```
(LAN/global addresses redacted; they are the machine's normal non-loopback addresses.)
3. Same destination host, port **outside** `filtered_ports` — connection is granted:
```bash
curl -v --socks5-hostname 127.0.0.1:1081 http://1.1.1.1:53/ # SOCKS5 request granted, tunnel established
curl -v --socks5-hostname 127.0.0.1:1081 http://1.1.1.1:49152/ # SOCKS5 request granted, then normal remote timeout
```
4. Control: the HTTP proxy handles the same destinations without issue:
```bash
curl -x http://127.0.0.1:3129 https://example.com # 200 OK
```
So with the destination host and AdGuard state held constant, only the destination **port** determines success — which points at the `filtered_ports` check, not at an actual routing loop.
## Expected Behavior
The SOCKS5 upstream path should be able to reach destinations on any port. The process's own outgoing traffic is already excluded from the transparent redirect (the main engine path works: `ss` shows ESTAB upstream connections from the same PID to real destinations on port 443 — a filtered port — from the non-loopback local addresses). The SOCKS5 path should get the same self-traffic exclusion (e.g. via fwmark/cgroup matching) instead of a pre-connect heuristic that assumes a loop.
Additionally, when a SOCKS5 request target is given as an IPv6 literal (SOCKS5 ATYP=4), the connection is closed with no SOCKS5 error reply at all — clients see "empty reply" and nothing is logged (details below). It should either work or return a proper SOCKS5 failure code.
## Actual Behavior
1. Any SOCKS5 CONNECT to a destination port inside `80:5221,5300:49151` fails with reply code 5 and logs `Route loop detected` (see step 2 above), on both IPv4 and IPv6 local addresses.
2. IPv6-literal targets are silently dropped:
```bash
curl -v --socks5-hostname 127.0.0.1:1081 http://[2606:4700:4700::1111]/
# curl: (52) Empty reply from server
```
Nothing appears in `proxy.log` or `access.log` for this attempt (grep for the target address: 0 results). Minor related inconsistency: `curl --socks5-hostname 127.0.0.1:1081 http://example.com:8080/` also returns (52) with no log entry even though 8080 is inside the first filtered range.
3. The documented loop-avoidance config does **not** fix it:
```yaml
apps:
- name: 'adguard-cli'
action: 'bypass'
skip_outbound_proxy: true
```
With this rule, after a hot reload the pre-connect refusal goes away and the SOCKS5 handshake completes — but the connection is then silently dropped (curl exit 52/35, zero log entries). After a full restart the "Route loop detected" refusal comes back. The check appears to be hard-coded (fires whenever dest port ∈ filtered_ports and the local socket address would be non-loopback) and does not consult app rules; the differing post-hot-reload behavior is its own defect.
## Additional information
### Analysis
- The warning comes from `AGFDVSocketFactory` (`onConnectEvent`) — i.e., the socket factory used for the SOCKS5 listener's outgoing connections.
- Correlation in `access.log`: each WARN is paired 1:1 with a "adguard-cli" TCP entry of 0 bytes for the same destination — AdGuard intercepting its own outgoing connection attempt and then refusing it.
- Topology (inferred via `ss`, `iptables-save` needs root): the transparent REDIRECT sends intercepted traffic to 127.0.0.1: / [::1]: (auto port changes every start: 38341 → 32777 → 41575); `ss` shows connections to the auto port whose peers are the machine's own global addresses — including AdGuard's own process as the origin. REDIRECT preserves the original source address, which is exactly what the "local address: " in the WARN reflects.
- The main transparent-engine path demonstrably has working self-exclusion (same PID, ESTAB to e.g. `35.190.46.17:443` and `[2606:4700::6812:199f]:443`); `AGFDVSocketFactory` appears to lack it and instead runs this conservative pre-connect check that produces a false positive on every filtered port.
- Practical consequence: in auto mode, SOCKS5 on 1081 only serves ports outside `filtered_ports` (53, 5222–5299, 49152+, …). For web (80/443) it is unusable; users must fall back to the HTTP proxy on 3129.
### Environment
- AdGuard CLI: v1.4.13, running under a systemd user unit (`adguard-cli start --no-fork --log-to-file`); the same failures occur with a plain interactive `adguard-cli start`, so the unit is not a factor.
- Related existing issues I found and checked (none covers this): #50, #112 (older loop-detection problems, fixed in v1.0 / v1.2.1), #95, #139.
- KB pages consulted: [AdGuard CLI and VPN interaction](https://adguard.com/kb/adguard-for-linux/vpn-cli-interaction/), [CLI exclusions and filters](https://adguard.com/kb/adguard-for-linux/cli-exclusions-and-filters/), [CLI auto-start](https://adguard.com/kb/adguard-for-linux/solving-problems/cli-auto-start/).
Happy to provide additional logs or test a fix build.
Guide de contribution
Aucun guide de contribution indexé pour ce dépôt
Évaluation
Cette issue n'a pas encore été évaluée.