fix(sub): don't project public inbounds through a fallback master

A standalone inbound bound to a public/wildcard listen that still carried a stale inbound_fallbacks row had its share/subscription link rewritten with the master's port + Reality/TLS settings (keeping only its own transport), producing an unusable link that silently fails - the client connects but no traffic flows. The leak hit every backend link surface: subscription URL, JSON sub, Clash sub, and the panel Client Information link.

Gate projectThroughFallbackMaster on reachability: only project a child that is not directly reachable on its own listen (loopback or a unix-domain socket). A public or wildcard inbound advertises its own port + security regardless of any fallback row. Legit loopback/socket fallback children still project as before.

Closes #4987
This commit is contained in:
MHSanaei
2026-06-06 02:13:39 +02:00
parent 75bc6e8076
commit 2b4e199a97
2 changed files with 45 additions and 0 deletions

View File

@@ -61,6 +61,25 @@ func TestIsRoutableHost(t *testing.T) {
}
}
func TestListenIsInternalOnly(t *testing.T) {
// Reachable only from the same host -> a fallback child here must be
// projected through its master.
internalOnly := []string{"127.0.0.1", "127.0.0.2", "::1", "[::1]", "@fallback", "/run/x.sock"}
for _, v := range internalOnly {
if !listenIsInternalOnly(v) {
t.Fatalf("listenIsInternalOnly(%q) = false, want true", v)
}
}
// Directly reachable on its own port -> never projected, even if a stale
// fallback rule names it as a child (#4987).
reachable := []string{"", "0.0.0.0", "::", "::0", "1.2.3.4", "10.0.0.5", "192.168.1.10", "vpn.example.com"}
for _, v := range reachable {
if listenIsInternalOnly(v) {
t.Fatalf("listenIsInternalOnly(%q) = true, want false", v)
}
}
}
func TestResolveInboundAddress(t *testing.T) {
const reqHost = "sub.example.com"