mirror of
https://github.com/go-gost/x.git
synced 2026-10-19 16:16:32 +00:00
Merge pull request #129 from iBug/bypass
This commit is contained in:
+22
-37
@@ -44,13 +44,13 @@ var ErrBypass = errors.New("bypass")
|
||||
type options struct {
|
||||
// whitelist toggles between blacklist and whitelist mode.
|
||||
// When false (default): matching addresses bypass the proxy.
|
||||
// When true: only matching addresses bypass the proxy.
|
||||
// When true: matching addresses use the proxy and all others bypass it.
|
||||
whitelist bool
|
||||
|
||||
// network restricts this bypass to a specific network protocol.
|
||||
// When set, the incoming network must match before address matchers
|
||||
// are evaluated. Recognized values include "tcp" and "udp".
|
||||
// An empty value disables the network check.
|
||||
// When set, the incoming network must match as part of the base rule.
|
||||
// Recognized values include "tcp" and "udp". An empty value
|
||||
// disables the network check.
|
||||
network string
|
||||
|
||||
// matchers holds static patterns provided at construction time
|
||||
@@ -89,8 +89,8 @@ func WhitelistOption(whitelist bool) Option {
|
||||
}
|
||||
|
||||
// NetworkOption restricts this bypass to a specific network protocol.
|
||||
// When set, Contains returns false when the incoming network does not
|
||||
// match, regardless of address matchers. Accepts values like "tcp" or "udp".
|
||||
// Network and address predicates are combined before whitelist mode is
|
||||
// applied to the resulting match. Accepts values like "tcp" or "udp".
|
||||
func NetworkOption(network string) Option {
|
||||
return func(opts *options) {
|
||||
opts.network = network
|
||||
@@ -427,43 +427,28 @@ func (p *localBypass) Contains(ctx context.Context, network, addr string, opts .
|
||||
return decision == decisionBypass
|
||||
}
|
||||
|
||||
// decide returns the bypass decision for the given address, applying the
|
||||
// whitelist or blacklist mode to the pattern match result.
|
||||
// decide returns the bypass decision for the given address. Network and
|
||||
// address predicates form one base match, then whitelist mode is applied once
|
||||
// to that combined result.
|
||||
func (p *localBypass) decide(network string, addr string) bypassDecision {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if p.options.network != "" && p.options.network != network {
|
||||
matched := p.options.network == "" || p.options.network == network
|
||||
if p.patterns != nil {
|
||||
matched = matched && p.patterns.matchAny(addr)
|
||||
} else if p.options.network == "" {
|
||||
// A bypass with no predicates has no base match.
|
||||
matched = false
|
||||
}
|
||||
|
||||
// Whitelist is a top-level negation of the normal blacklist decision:
|
||||
// blacklist mode proxies a failed combined match, while whitelist mode
|
||||
// proxies a successful combined match.
|
||||
if matched == p.options.whitelist {
|
||||
return decisionProxy
|
||||
}
|
||||
|
||||
if p.patterns == nil {
|
||||
if p.options.network != "" {
|
||||
if p.options.whitelist {
|
||||
return decisionProxy
|
||||
}
|
||||
return decisionBypass
|
||||
}
|
||||
|
||||
return decisionProxy
|
||||
}
|
||||
|
||||
matched := p.patterns.matchAny(addr)
|
||||
|
||||
if p.options.whitelist {
|
||||
// Whitelist mode: the pattern set specifies addresses that MUST use the proxy.
|
||||
// Matched addresses go through the proxy; unmatched addresses bypass.
|
||||
if matched {
|
||||
return decisionProxy
|
||||
}
|
||||
return decisionBypass
|
||||
}
|
||||
// Blacklist mode: the pattern set specifies addresses that should bypass the proxy.
|
||||
// Matched addresses bypass; unmatched addresses go through the proxy.
|
||||
if matched {
|
||||
return decisionBypass
|
||||
}
|
||||
return decisionProxy
|
||||
return decisionBypass
|
||||
}
|
||||
|
||||
func (p *localBypass) IsWhitelist() bool {
|
||||
|
||||
+41
-8
@@ -765,7 +765,40 @@ func TestContains_NetworkMatch_WhitelistBypass(t *testing.T) {
|
||||
assert.True(t, b.Contains(context.Background(), "udp", "10.0.0.1"))
|
||||
}
|
||||
|
||||
func TestContains_NetworkMismatch_ReturnsFalse(t *testing.T) {
|
||||
func TestContains_WhitelistNegatesCombinedMatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
whitelist bool
|
||||
network string
|
||||
addr string
|
||||
wantProxy bool
|
||||
}{
|
||||
{name: "blacklist both match", network: "tcp", addr: "192.168.1.1", wantProxy: false},
|
||||
{name: "blacklist network fails", network: "udp", addr: "192.168.1.1", wantProxy: true},
|
||||
{name: "blacklist matcher fails", network: "tcp", addr: "10.0.0.1", wantProxy: true},
|
||||
{name: "blacklist both fail", network: "udp", addr: "10.0.0.1", wantProxy: true},
|
||||
{name: "whitelist both match", whitelist: true, network: "tcp", addr: "192.168.1.1", wantProxy: true},
|
||||
{name: "whitelist network fails", whitelist: true, network: "udp", addr: "192.168.1.1", wantProxy: false},
|
||||
{name: "whitelist matcher fails", whitelist: true, network: "tcp", addr: "10.0.0.1", wantProxy: false},
|
||||
{name: "whitelist both fail", whitelist: true, network: "udp", addr: "10.0.0.1", wantProxy: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b := newSyncedBypass(
|
||||
WhitelistOption(tt.whitelist),
|
||||
NetworkOption("tcp"),
|
||||
MatchersOption([]string{"192.168.1.1"}),
|
||||
)
|
||||
defer b.Close()
|
||||
|
||||
gotProxy := !b.Contains(context.Background(), tt.network, tt.addr)
|
||||
assert.Equal(t, tt.wantProxy, gotProxy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContains_NetworkMismatch_BlacklistReturnsFalse(t *testing.T) {
|
||||
b := newSyncedBypass(
|
||||
NetworkOption("tcp"),
|
||||
MatchersOption([]string{"192.168.1.1"}),
|
||||
@@ -789,7 +822,7 @@ func TestContains_NetworkMismatch_EmptyAddr(t *testing.T) {
|
||||
assert.False(t, b.Contains(context.Background(), "tcp", ""))
|
||||
}
|
||||
|
||||
func TestContains_NetworkMismatch_WhitelistReturnsFalse(t *testing.T) {
|
||||
func TestContains_NetworkMismatch_WhitelistBypasses(t *testing.T) {
|
||||
b := newSyncedBypass(
|
||||
WhitelistOption(true),
|
||||
NetworkOption("tcp"),
|
||||
@@ -797,9 +830,9 @@ func TestContains_NetworkMismatch_WhitelistReturnsFalse(t *testing.T) {
|
||||
)
|
||||
defer b.Close()
|
||||
|
||||
// Network does not match → bypass returns false even in whitelist mode
|
||||
assert.False(t, b.Contains(context.Background(), "udp", "10.0.0.1"))
|
||||
assert.False(t, b.Contains(context.Background(), "udp", "192.168.1.1"))
|
||||
// Network fails the combined predicate, then whitelist mode negates it.
|
||||
assert.True(t, b.Contains(context.Background(), "udp", "10.0.0.1"))
|
||||
assert.True(t, b.Contains(context.Background(), "udp", "192.168.1.1"))
|
||||
}
|
||||
|
||||
func TestContains_PatternSetPassesThroughNetworkFilter(t *testing.T) {
|
||||
@@ -846,15 +879,15 @@ func TestNetworkOnly_Whitelist_ProxiesMatchingNetwork(t *testing.T) {
|
||||
assert.False(t, b.Contains(context.Background(), "tcp", "10.0.0.1"))
|
||||
}
|
||||
|
||||
func TestNetworkOnly_Whitelist_NonMatchingNotAffected(t *testing.T) {
|
||||
func TestNetworkOnly_Whitelist_NonMatchingBypasses(t *testing.T) {
|
||||
b := newSyncedBypass(
|
||||
WhitelistOption(true),
|
||||
NetworkOption("tcp"),
|
||||
)
|
||||
defer b.Close()
|
||||
|
||||
// network: tcp 是作用域限定,非 TCP 流量不受影响
|
||||
assert.False(t, b.Contains(context.Background(), "udp", "192.168.1.1"))
|
||||
// The network predicate fails, then whitelist mode negates it.
|
||||
assert.True(t, b.Contains(context.Background(), "udp", "192.168.1.1"))
|
||||
}
|
||||
|
||||
func TestNetworkOnly_NoNetwork_BehavesAsBefore(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user