From 3d5681cffd371ecdb9b340fd963872e8416d32e9 Mon Sep 17 00:00:00 2001 From: Dreamacro Date: Mon, 11 Oct 2021 20:48:58 +0800 Subject: [PATCH] Feature: persistence fakeip (#1662) --- component/fakeip/cachefile.go | 49 +++++++++ component/fakeip/memory.go | 60 +++++++++++ component/fakeip/pool.go | 93 ++++++++++------- component/fakeip/pool_test.go | 147 +++++++++++++++++++++++---- component/profile/cachefile/cache.go | 75 +++++++++++--- config/config.go | 13 ++- dns/enhancer.go | 2 +- dns/middleware.go | 2 +- 8 files changed, 366 insertions(+), 75 deletions(-) create mode 100644 component/fakeip/cachefile.go create mode 100644 component/fakeip/memory.go diff --git a/component/fakeip/cachefile.go b/component/fakeip/cachefile.go new file mode 100644 index 0000000000..7ee889812a --- /dev/null +++ b/component/fakeip/cachefile.go @@ -0,0 +1,49 @@ +package fakeip + +import ( + "net" + + "github.com/Dreamacro/clash/component/profile/cachefile" +) + +type cachefileStore struct { + cache *cachefile.CacheFile +} + +// GetByHost implements store.GetByHost +func (c *cachefileStore) GetByHost(host string) (net.IP, bool) { + elm := c.cache.GetFakeip([]byte(host)) + if elm == nil { + return nil, false + } + return net.IP(elm), true +} + +// PutByHost implements store.PutByHost +func (c *cachefileStore) PutByHost(host string, ip net.IP) { + c.cache.PutFakeip([]byte(host), ip) +} + +// GetByIP implements store.GetByIP +func (c *cachefileStore) GetByIP(ip net.IP) (string, bool) { + elm := c.cache.GetFakeip(ip.To4()) + if elm == nil { + return "", false + } + return string(elm), true +} + +// PutByIP implements store.PutByIP +func (c *cachefileStore) PutByIP(ip net.IP, host string) { + c.cache.PutFakeip(ip.To4(), []byte(host)) +} + +// Exist implements store.Exist +func (c *cachefileStore) Exist(ip net.IP) bool { + _, exist := c.GetByIP(ip) + return exist +} + +// CloneTo implements store.CloneTo +// already persistence +func (c *cachefileStore) CloneTo(store store) {} diff --git a/component/fakeip/memory.go b/component/fakeip/memory.go new file mode 100644 index 0000000000..75d4a3b243 --- /dev/null +++ b/component/fakeip/memory.go @@ -0,0 +1,60 @@ +package fakeip + +import ( + "net" + + "github.com/Dreamacro/clash/common/cache" +) + +type memoryStore struct { + cache *cache.LruCache +} + +// GetByHost implements store.GetByHost +func (m *memoryStore) GetByHost(host string) (net.IP, bool) { + if elm, exist := m.cache.Get(host); exist { + ip := elm.(net.IP) + + // ensure ip --> host on head of linked list + m.cache.Get(ipToUint(ip.To4())) + return ip, true + } + + return nil, false +} + +// PutByHost implements store.PutByHost +func (m *memoryStore) PutByHost(host string, ip net.IP) { + m.cache.Set(host, ip) +} + +// GetByIP implements store.GetByIP +func (m *memoryStore) GetByIP(ip net.IP) (string, bool) { + if elm, exist := m.cache.Get(ipToUint(ip.To4())); exist { + host := elm.(string) + + // ensure host --> ip on head of linked list + m.cache.Get(host) + return host, true + } + + return "", false +} + +// PutByIP implements store.PutByIP +func (m *memoryStore) PutByIP(ip net.IP, host string) { + m.cache.Set(ipToUint(ip.To4()), host) +} + +// Exist implements store.Exist +func (m *memoryStore) Exist(ip net.IP) bool { + return m.cache.Exist(ipToUint(ip.To4())) +} + +// CloneTo implements store.CloneTo +// only for memoryStore to memoryStore +func (m *memoryStore) CloneTo(store store) { + if ms, ok := store.(*memoryStore); ok { + m.cache.CloneTo(ms.cache) + } +} diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 97d812ac3f..180d5eb189 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -6,9 +6,19 @@ import ( "sync" "github.com/Dreamacro/clash/common/cache" + "github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/trie" ) +type store interface { + GetByHost(host string) (net.IP, bool) + PutByHost(host string, ip net.IP) + GetByIP(ip net.IP) (string, bool) + PutByIP(ip net.IP, host string) + Exist(ip net.IP) bool + CloneTo(store) +} + // Pool is a implementation about fake ip generator without storage type Pool struct { max uint32 @@ -18,25 +28,19 @@ type Pool struct { mux sync.Mutex host *trie.DomainTrie ipnet *net.IPNet - cache *cache.LruCache + store store } // Lookup return a fake ip with host func (p *Pool) Lookup(host string) net.IP { p.mux.Lock() defer p.mux.Unlock() - if elm, exist := p.cache.Get(host); exist { - ip := elm.(net.IP) - - // ensure ip --> host on head of linked list - n := ipToUint(ip.To4()) - offset := n - p.min + 1 - p.cache.Get(offset) + if ip, exist := p.store.GetByHost(host); exist { return ip } ip := p.get(host) - p.cache.Set(host, ip) + p.store.PutByHost(host, ip) return ip } @@ -49,22 +53,11 @@ func (p *Pool) LookBack(ip net.IP) (string, bool) { return "", false } - n := ipToUint(ip.To4()) - offset := n - p.min + 1 - - if elm, exist := p.cache.Get(offset); exist { - host := elm.(string) - - // ensure host --> ip on head of linked list - p.cache.Get(host) - return host, true - } - - return "", false + return p.store.GetByIP(ip) } -// LookupHost return if domain in host -func (p *Pool) LookupHost(domain string) bool { +// ShouldSkipped return if domain should be skipped +func (p *Pool) ShouldSkipped(domain string) bool { if p.host == nil { return false } @@ -80,9 +73,7 @@ func (p *Pool) Exist(ip net.IP) bool { return false } - n := ipToUint(ip.To4()) - offset := n - p.min + 1 - return p.cache.Exist(offset) + return p.store.Exist(ip) } // Gateway return gateway ip @@ -95,9 +86,9 @@ func (p *Pool) IPNet() *net.IPNet { return p.ipnet } -// PatchFrom clone cache from old pool -func (p *Pool) PatchFrom(o *Pool) { - o.cache.CloneTo(p.cache) +// CloneFrom clone cache from old pool +func (p *Pool) CloneFrom(o *Pool) { + o.store.CloneTo(p.store) } func (p *Pool) get(host string) net.IP { @@ -109,12 +100,13 @@ func (p *Pool) get(host string) net.IP { break } - if !p.cache.Exist(p.offset) { + ip := uintToIP(p.min + p.offset - 1) + if !p.store.Exist(ip) { break } } ip := uintToIP(p.min + p.offset - 1) - p.cache.Set(p.offset, host) + p.store.PutByIP(ip, host) return ip } @@ -130,11 +122,24 @@ func uintToIP(v uint32) net.IP { return net.IP{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } +type Options struct { + IPNet *net.IPNet + Host *trie.DomainTrie + + // Size sets the maximum number of entries in memory + // and does not work if Persistence is true + Size int + + // Persistence will save the data to disk. + // Size will not work and record will be fully stored. + Persistence bool +} + // New return Pool instance -func New(ipnet *net.IPNet, size int, host *trie.DomainTrie) (*Pool, error) { - min := ipToUint(ipnet.IP) + 2 +func New(options Options) (*Pool, error) { + min := ipToUint(options.IPNet.IP) + 2 - ones, bits := ipnet.Mask.Size() + ones, bits := options.IPNet.Mask.Size() total := 1<