Merge pull request #129 from iBug/bypass

This commit is contained in:
ginuerzh
2026-10-07 01:21:06 +00:00
2 changed files with 63 additions and 45 deletions
+22 -37
View File
@@ -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
View File
@@ -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) {