Skip to content

Commit bc5b606

Browse files
committed
🚮 dns: fix cache eviction
The resolver was missing cache eviction. Implement it using the new LRU cache.
1 parent ebf0933 commit bc5b606

File tree

3 files changed

+32
-17
lines changed

3 files changed

+32
-17
lines changed

dns/dns.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"sync"
1414
"time"
1515

16+
"github.com/database64128/shadowsocks-go/cache"
1617
"github.com/database64128/shadowsocks-go/conn"
1718
"github.com/database64128/shadowsocks-go/netio"
1819
"github.com/database64128/shadowsocks-go/zerocopy"
@@ -26,6 +27,8 @@ const (
2627
maxDNSPacketSize = 1232
2728

2829
lookupTimeout = 20 * time.Second
30+
31+
defaultCacheSize = 1024
2932
)
3033

3134
var (
@@ -42,9 +45,8 @@ type ResolverConfig struct {
4245

4346
// Type is the resolver type.
4447
//
45-
// Available values:
46-
// - "plain": Resolve names by sending cleartext DNS queries to the configured upstream server.
47-
// - "system": Use the system resolver. This does not support custom server addresses or clients.
48+
// - "plain": Resolve names by sending cleartext DNS queries to the configured upstream server.
49+
// - "system": Use the system resolver. This does not support custom server addresses or clients.
4850
//
4951
// The default value is "plain".
5052
Type string `json:"type,omitzero"`
@@ -59,6 +61,12 @@ type ResolverConfig struct {
5961
// UDPClientName is the name of the UDPClient to use.
6062
// Leave empty to disable UDP.
6163
UDPClientName string `json:"udpClientName,omitzero"`
64+
65+
// CacheSize is the size of the DNS cache.
66+
//
67+
// If zero, the default cache size is 1024.
68+
// If negative, the cache will be unbounded.
69+
CacheSize int `json:"cacheSize,omitzero"`
6270
}
6371

6472
// NewSimpleResolver creates a new [NewSimpleResolver] from the config.
@@ -101,7 +109,12 @@ func (rc *ResolverConfig) NewSimpleResolver(tcpClientMap map[string]netio.Stream
101109
}
102110
}
103111

104-
return NewResolver(rc.Name, rc.AddrPort, tcpClient, udpClient, logger), nil
112+
cacheSize := rc.CacheSize
113+
if cacheSize == 0 {
114+
cacheSize = defaultCacheSize
115+
}
116+
117+
return NewResolver(rc.Name, cacheSize, rc.AddrPort, tcpClient, udpClient, logger), nil
105118
}
106119

107120
// Result represents the result of name resolution.
@@ -120,11 +133,11 @@ type Resolver struct {
120133
// name stores the resolver's name to make its log messages more useful.
121134
name string
122135

123-
// mu protects the DNS cache map.
124-
mu sync.RWMutex
136+
// mu protects the DNS cache.
137+
mu sync.Mutex
125138

126-
// cache is the DNS cache map.
127-
cache map[string]Result
139+
// cache is the DNS cache.
140+
cache cache.BoundedCache[string, Result]
128141

129142
// serverAddr is the upstream server's address and port.
130143
serverAddr conn.Addr
@@ -142,10 +155,10 @@ type Resolver struct {
142155
logger *zap.Logger
143156
}
144157

145-
func NewResolver(name string, serverAddrPort netip.AddrPort, tcpClient netio.StreamClient, udpClient zerocopy.UDPClient, logger *zap.Logger) *Resolver {
158+
func NewResolver(name string, cacheSize int, serverAddrPort netip.AddrPort, tcpClient netio.StreamClient, udpClient zerocopy.UDPClient, logger *zap.Logger) *Resolver {
146159
return &Resolver{
147160
name: name,
148-
cache: make(map[string]Result),
161+
cache: *cache.NewBoundedCache[string, Result](cacheSize),
149162
serverAddr: conn.AddrFromIPPort(serverAddrPort),
150163
serverAddrPort: serverAddrPort,
151164
tcpClient: tcpClient,
@@ -156,9 +169,9 @@ func NewResolver(name string, serverAddrPort netip.AddrPort, tcpClient netio.Str
156169

157170
func (r *Resolver) lookup(ctx context.Context, name string) (Result, error) {
158171
// Lookup cache first.
159-
r.mu.RLock()
160-
result, ok := r.cache[name]
161-
r.mu.RUnlock()
172+
r.mu.Lock()
173+
result, ok := r.cache.Get(name)
174+
r.mu.Unlock()
162175

163176
if ok && !result.HasExpired() {
164177
if ce := r.logger.Check(zap.DebugLevel, "DNS lookup got result from cache"); ce != nil {
@@ -294,7 +307,7 @@ func (r *Resolver) sendQueries(ctx context.Context, nameString string) (result R
294307
// Add result to cache if TTL hasn't expired.
295308
if !result.HasExpired() {
296309
r.mu.Lock()
297-
r.cache[nameString] = result
310+
r.cache.Set(nameString, result)
298311
r.mu.Unlock()
299312
}
300313

dns/dns_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
func testResolver(t *testing.T, name string, serverAddrPort netip.AddrPort, tcpClient netio.StreamClient, udpClient zerocopy.UDPClient, logger *zap.Logger) {
16-
r := NewResolver(name, serverAddrPort, tcpClient, udpClient, logger)
16+
r := NewResolver(name, defaultCacheSize, serverAddrPort, tcpClient, udpClient, logger)
1717
ctx := t.Context()
1818

1919
// Uncached lookup.

docs/config.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,15 @@
573573
"name": "cf-v6",
574574
"addrPort": "[2606:4700:4700::1111]:53",
575575
"tcpClientName": "ss-2022-a",
576-
"udpClientName": "ss-2022-a"
576+
"udpClientName": "ss-2022-a",
577+
"cacheSize": 1024
577578
},
578579
{
579580
"name": "systemd-resolved",
580581
"addrPort": "127.0.0.53:53",
581582
"tcpClientName": "direct",
582-
"udpClientName": "direct"
583+
"udpClientName": "direct",
584+
"cacheSize": 32
583585
},
584586
{
585587
"name": "system",

0 commit comments

Comments
 (0)