Skip to content

Commit 14eaa35

Browse files
committed
Implement remote DNS
This commit implements remote DNS. It introduces two new dependencies: ttlcache and dns. Remote DNS implements a UDP listener DNS A record queries on port 53. It replies with an unused IP address from an address pool, 198.18.0.0/15 by default. When obtaining a new address from the pool, tun2socks needs to memorize which name the address belongs to, so that when a client connects to the address, it can instruct the proxy to connect to the FQDN. To implement this IP to name mapping, the FakeIP module from clash is used.
1 parent 63f71e0 commit 14eaa35

File tree

21 files changed

+1385
-5
lines changed

21 files changed

+1385
-5
lines changed

common/cache/lrucache.go

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package cache
2+
3+
// Modified by https://github.com/die-net/lrucache
4+
5+
import (
6+
"container/list"
7+
"sync"
8+
"time"
9+
)
10+
11+
// Option is part of Functional Options Pattern
12+
type Option func(*LruCache)
13+
14+
// EvictCallback is used to get a callback when a cache entry is evicted
15+
type EvictCallback = func(key any, value any)
16+
17+
// WithEvict set the evict callback
18+
func WithEvict(cb EvictCallback) Option {
19+
return func(l *LruCache) {
20+
l.onEvict = cb
21+
}
22+
}
23+
24+
// WithUpdateAgeOnGet update expires when Get element
25+
func WithUpdateAgeOnGet() Option {
26+
return func(l *LruCache) {
27+
l.updateAgeOnGet = true
28+
}
29+
}
30+
31+
// WithAge defined element max age (second)
32+
func WithAge(maxAge int64) Option {
33+
return func(l *LruCache) {
34+
l.maxAge = maxAge
35+
}
36+
}
37+
38+
// WithSize defined max length of LruCache
39+
func WithSize(maxSize int) Option {
40+
return func(l *LruCache) {
41+
l.maxSize = maxSize
42+
}
43+
}
44+
45+
// WithStale decide whether Stale return is enabled.
46+
// If this feature is enabled, element will not get Evicted according to `WithAge`.
47+
func WithStale(stale bool) Option {
48+
return func(l *LruCache) {
49+
l.staleReturn = stale
50+
}
51+
}
52+
53+
// LruCache is a thread-safe, in-memory lru-cache that evicts the
54+
// least recently used entries from memory when (if set) the entries are
55+
// older than maxAge (in seconds). Use the New constructor to create one.
56+
type LruCache struct {
57+
maxAge int64
58+
maxSize int
59+
mu sync.Mutex
60+
cache map[any]*list.Element
61+
lru *list.List // Front is least-recent
62+
updateAgeOnGet bool
63+
staleReturn bool
64+
onEvict EvictCallback
65+
}
66+
67+
// New creates an LruCache
68+
func New(options ...Option) *LruCache {
69+
lc := &LruCache{
70+
lru: list.New(),
71+
cache: make(map[any]*list.Element),
72+
}
73+
74+
for _, option := range options {
75+
option(lc)
76+
}
77+
78+
return lc
79+
}
80+
81+
// Get returns the any representation of a cached response and a bool
82+
// set to true if the key was found.
83+
func (c *LruCache) Get(key any) (any, bool) {
84+
entry := c.get(key)
85+
if entry == nil {
86+
return nil, false
87+
}
88+
value := entry.value
89+
90+
return value, true
91+
}
92+
93+
// GetWithExpire returns the any representation of a cached response,
94+
// a time.Time Give expected expires,
95+
// and a bool set to true if the key was found.
96+
// This method will NOT check the maxAge of element and will NOT update the expires.
97+
func (c *LruCache) GetWithExpire(key any) (any, time.Time, bool) {
98+
entry := c.get(key)
99+
if entry == nil {
100+
return nil, time.Time{}, false
101+
}
102+
103+
return entry.value, time.Unix(entry.expires, 0), true
104+
}
105+
106+
// Exist returns if key exist in cache but not put item to the head of linked list
107+
func (c *LruCache) Exist(key any) bool {
108+
c.mu.Lock()
109+
defer c.mu.Unlock()
110+
111+
_, ok := c.cache[key]
112+
return ok
113+
}
114+
115+
// Set stores the any representation of a response for a given key.
116+
func (c *LruCache) Set(key any, value any) {
117+
expires := int64(0)
118+
if c.maxAge > 0 {
119+
expires = time.Now().Unix() + c.maxAge
120+
}
121+
c.SetWithExpire(key, value, time.Unix(expires, 0))
122+
}
123+
124+
// SetWithExpire stores the any representation of a response for a given key and given expires.
125+
// The expires time will round to second.
126+
func (c *LruCache) SetWithExpire(key any, value any, expires time.Time) {
127+
c.mu.Lock()
128+
defer c.mu.Unlock()
129+
130+
if le, ok := c.cache[key]; ok {
131+
c.lru.MoveToBack(le)
132+
e := le.Value.(*entry)
133+
e.value = value
134+
e.expires = expires.Unix()
135+
} else {
136+
e := &entry{key: key, value: value, expires: expires.Unix()}
137+
c.cache[key] = c.lru.PushBack(e)
138+
139+
if c.maxSize > 0 {
140+
if len := c.lru.Len(); len > c.maxSize {
141+
c.deleteElement(c.lru.Front())
142+
}
143+
}
144+
}
145+
146+
c.maybeDeleteOldest()
147+
}
148+
149+
// CloneTo clone and overwrite elements to another LruCache
150+
func (c *LruCache) CloneTo(n *LruCache) {
151+
c.mu.Lock()
152+
defer c.mu.Unlock()
153+
154+
n.mu.Lock()
155+
defer n.mu.Unlock()
156+
157+
n.lru = list.New()
158+
n.cache = make(map[any]*list.Element)
159+
160+
for e := c.lru.Front(); e != nil; e = e.Next() {
161+
elm := e.Value.(*entry)
162+
n.cache[elm.key] = n.lru.PushBack(elm)
163+
}
164+
}
165+
166+
func (c *LruCache) get(key any) *entry {
167+
c.mu.Lock()
168+
defer c.mu.Unlock()
169+
170+
le, ok := c.cache[key]
171+
if !ok {
172+
return nil
173+
}
174+
175+
if !c.staleReturn && c.maxAge > 0 && le.Value.(*entry).expires <= time.Now().Unix() {
176+
c.deleteElement(le)
177+
c.maybeDeleteOldest()
178+
179+
return nil
180+
}
181+
182+
c.lru.MoveToBack(le)
183+
entry := le.Value.(*entry)
184+
if c.maxAge > 0 && c.updateAgeOnGet {
185+
entry.expires = time.Now().Unix() + c.maxAge
186+
}
187+
return entry
188+
}
189+
190+
// Delete removes the value associated with a key.
191+
func (c *LruCache) Delete(key any) {
192+
c.mu.Lock()
193+
194+
if le, ok := c.cache[key]; ok {
195+
c.deleteElement(le)
196+
}
197+
198+
c.mu.Unlock()
199+
}
200+
201+
func (c *LruCache) maybeDeleteOldest() {
202+
if !c.staleReturn && c.maxAge > 0 {
203+
now := time.Now().Unix()
204+
for le := c.lru.Front(); le != nil && le.Value.(*entry).expires <= now; le = c.lru.Front() {
205+
c.deleteElement(le)
206+
}
207+
}
208+
}
209+
210+
func (c *LruCache) deleteElement(le *list.Element) {
211+
c.lru.Remove(le)
212+
e := le.Value.(*entry)
213+
delete(c.cache, e.key)
214+
if c.onEvict != nil {
215+
c.onEvict(e.key, e.value)
216+
}
217+
}
218+
219+
type entry struct {
220+
key any
221+
value any
222+
expires int64
223+
}

0 commit comments

Comments
 (0)