fix(acl): error rule applied due to bad cache key

close: #1432
This commit is contained in:
Haruue 2025-09-10 15:00:04 +09:00
parent 5f3c47e6c3
commit 5ef85697f3
No known key found for this signature in database
GPG key ID: F6083B28CBCBC148
2 changed files with 57 additions and 6 deletions

View file

@ -19,6 +19,19 @@ const (
ProtocolUDP
)
func (p Protocol) String() string {
switch p {
case ProtocolBoth:
return "tcp+udp"
case ProtocolTCP:
return "tcp"
case ProtocolUDP:
return "udp"
default:
return fmt.Sprintf("Protocol(%d)", int(p))
}
}
type Outbound interface {
any
}
@ -63,12 +76,22 @@ type matchResult[O Outbound] struct {
type compiledRuleSetImpl[O Outbound] struct {
Rules []compiledRule[O]
Cache *lru.Cache[string, matchResult[O]] // key: HostInfo.String()
Cache *lru.Cache[matchResultCacheKey, matchResult[O]] // key: HostInfo.String()
}
type matchResultCacheKey struct {
Host string
Proto Protocol
Port uint16
}
func (s *compiledRuleSetImpl[O]) Match(host HostInfo, proto Protocol, port uint16) (O, net.IP) {
host.Name = strings.ToLower(host.Name) // Normalize host name to lower case
key := host.String()
key := matchResultCacheKey{
Host: host.String(),
Proto: proto,
Port: port,
}
if result, ok := s.Cache.Get(key); ok {
return result.Outbound, result.HijackAddress
}
@ -130,7 +153,7 @@ func Compile[O Outbound](rules []TextRule, outbounds map[string]O,
}
compiledRules[i] = compiledRule[O]{outbound, hm, proto, startPort, endPort, hijackAddress}
}
cache, err := lru.New[string, matchResult[O]](cacheSize)
cache, err := lru.New[matchResultCacheKey, matchResult[O]](cacheSize)
if err != nil {
return nil, err
}

View file

@ -1,6 +1,7 @@
package acl
import (
"fmt"
"net"
"testing"
@ -177,6 +178,30 @@ func TestCompile(t *testing.T) {
wantOutbound: ob1,
wantIP: net.ParseIP("2.2.2.2"),
},
{
host: HostInfo{
Name: "crap.v2ex.com",
},
proto: ProtocolTCP,
port: 81,
wantOutbound: 0,
},
{
host: HostInfo{
Name: "crap.v2ex.com",
},
proto: ProtocolUDP,
port: 80,
wantOutbound: ob3,
},
{
host: HostInfo{
Name: "crap.v2ex.com",
},
proto: ProtocolUDP,
port: 81,
wantOutbound: ob3,
},
{
host: HostInfo{
IPv4: net.ParseIP("210.140.92.187"),
@ -261,9 +286,12 @@ func TestCompile(t *testing.T) {
}
for _, test := range tests {
gotOutbound, gotIP := comp.Match(test.host, test.proto, test.port)
assert.Equal(t, test.wantOutbound, gotOutbound)
assert.Equal(t, test.wantIP, gotIP)
testName := fmt.Sprintf("%s#%s#%d", test.host, test.proto, test.port)
t.Run(testName, func(t *testing.T) {
gotOutbound, gotIP := comp.Match(test.host, test.proto, test.port)
assert.Equal(t, test.wantOutbound, gotOutbound)
assert.Equal(t, test.wantIP, gotIP)
})
}
// Test Invalid Port Range Rule