mirror of
https://github.com/wweir/sower.git
synced 2026-10-19 16:14:33 +00:00
fix(sower): guard the direct dial path against self-targeting too
DialSmart can reach directDial without passing DialProxyOnly (a direct rule match, or an access probe that marks the name reachable), and a direct dial back into this client's own proxy listener is a self-sustaining loop that never leaves the host. Guard both entries; the second check is a cache hit.
This commit is contained in:
@@ -223,6 +223,14 @@ func (r *Router) DialSmart(network, domain string, port uint16) (net.Conn, error
|
||||
ctx := context.Background()
|
||||
addr := net.JoinHostPort(domain, strconv.FormatUint(uint64(port), 10))
|
||||
|
||||
// Guarded here as well as in DialProxyOnly: this path reaches directDial
|
||||
// without passing it (rule-based and probe-based direct decisions), and
|
||||
// dialing a listener this client itself forwards on re-enters the proxy
|
||||
// either way — locally, in that case, since no tunnel is involved.
|
||||
if ip, self := r.selfTarget(domain, port); self {
|
||||
return nil, selfProxyError(domain, port, ip)
|
||||
}
|
||||
|
||||
// 1. rule_based( block > direct > proxy )
|
||||
// 2. detect_based( CN IP || access site )
|
||||
// 3. fallback( proxy )
|
||||
|
||||
@@ -108,3 +108,29 @@ func TestDialProxyOnlyAllowsTargetThatFailsToResolve(t *testing.T) {
|
||||
t.Fatalf("dial after a resolution failure: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DialSmart reaches directDial without passing DialProxyOnly, so the guard has
|
||||
// to sit on that path too: a name a direct rule matches, or one the access
|
||||
// probe marks reachable, would otherwise dial this client's own listener in a
|
||||
// loop that never leaves the host.
|
||||
func TestDialSmartRefusesDirectSelfTarget(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newSelfTargetRouter(t, []string{"100.64.1.1:80"}, func(context.Context, string) ([]net.IP, error) {
|
||||
return []net.IP{net.ParseIP("100.64.1.1")}, nil
|
||||
})
|
||||
r.DirectRule.Add("self.example.com")
|
||||
|
||||
if _, err := r.DialSmart("tcp", "self.example.com", 80); !errors.Is(err, ErrSelfProxyTarget) {
|
||||
t.Fatalf("direct-routed self target err = %v, want ErrSelfProxyTarget", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialSmartRefusesSelfTargetLiteral(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newSelfTargetRouter(t, []string{"100.64.1.1:443"}, nil)
|
||||
if _, err := r.DialSmart("tcp", "100.64.1.1", 443); !errors.Is(err, ErrSelfProxyTarget) {
|
||||
t.Fatalf("literal self target on the smart path err = %v, want ErrSelfProxyTarget", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user