diff --git a/Makefile b/Makefile index 847a7e37..f450b2b2 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST) releases: $(gz_releases) $(zip_releases) vet: - go vet ./... + go test ./... lint: golangci-lint run ./... diff --git a/README.md b/README.md index a6b65d0d..5df2420b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash Support resolve ip with a proxy tunnel. Support `geosite` with `fallback-filter`. + +Use curl -X POST controllerip:port/cache/fakeip/flush to flush persistence fakeip ```yaml dns: enable: true diff --git a/common/cache/lrucache.go b/common/cache/lrucache.go index 25a1b48e..0bea06f6 100644 --- a/common/cache/lrucache.go +++ b/common/cache/lrucache.go @@ -216,6 +216,15 @@ func (c *LruCache) deleteElement(le *list.Element) { } } +func (c *LruCache) Clear() error { + c.mu.Lock() + + c.cache = make(map[any]*list.Element) + + c.mu.Unlock() + return nil +} + type entry struct { key any value any diff --git a/component/fakeip/cachefile.go b/component/fakeip/cachefile.go index 04bdc65c..9e5f22f4 100644 --- a/component/fakeip/cachefile.go +++ b/component/fakeip/cachefile.go @@ -53,3 +53,8 @@ func (c *cachefileStore) Exist(ip net.IP) bool { // CloneTo implements store.CloneTo // already persistence func (c *cachefileStore) CloneTo(store store) {} + +// FlushFakeIP implements store.FlushFakeIP +func (c *cachefileStore) FlushFakeIP() error { + return c.cache.FlushFakeIP() +} diff --git a/component/fakeip/memory.go b/component/fakeip/memory.go index c6c6873d..a7ff3708 100644 --- a/component/fakeip/memory.go +++ b/component/fakeip/memory.go @@ -67,3 +67,8 @@ func (m *memoryStore) CloneTo(store store) { m.cache.CloneTo(ms.cache) } } + +// FlushFakeIP implements store.FlushFakeIP +func (m *memoryStore) FlushFakeIP() error { + return m.cache.Clear() +} diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 68484899..e93873c9 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -18,6 +18,7 @@ type store interface { DelByIP(ip net.IP) Exist(ip net.IP) bool CloneTo(store) + FlushFakeIP() error } // Pool is a implementation about fake ip generator without storage @@ -120,6 +121,10 @@ func (p *Pool) get(host string) net.IP { return ip } +func (p *Pool) FlushFakeIP() error { + return p.store.FlushFakeIP() +} + func ipToUint(ip net.IP) uint32 { v := uint32(ip[0]) << 24 v += uint32(ip[1]) << 16 diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index 1e30bd94..86e80a2d 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -193,3 +193,59 @@ func TestPool_Error(t *testing.T) { assert.Error(t, err) } + +func TestPool_FlushFileCache(t *testing.T) { + _, ipnet, _ := net.ParseCIDR("192.168.0.1/28") + pools, tempfile, err := createPools(Options{ + IPNet: ipnet, + Size: 10, + }) + assert.Nil(t, err) + defer os.Remove(tempfile) + + for _, pool := range pools { + foo := pool.Lookup("foo.com") + bar := pool.Lookup("baz.com") + bax := pool.Lookup("baz.com") + fox := pool.Lookup("foo.com") + + err = pool.FlushFakeIP() + assert.Nil(t, err) + + baz := pool.Lookup("foo.com") + next := pool.Lookup("baz.com") + nero := pool.Lookup("foo.com") + + assert.Equal(t, foo, fox) + assert.NotEqual(t, foo, baz) + assert.Equal(t, bar, bax) + assert.NotEqual(t, bar, next) + assert.Equal(t, baz, nero) + } +} + +func TestPool_FlushMemoryCache(t *testing.T) { + _, ipnet, _ := net.ParseCIDR("192.168.0.1/28") + pool, _ := New(Options{ + IPNet: ipnet, + Size: 10, + }) + + foo := pool.Lookup("foo.com") + bar := pool.Lookup("baz.com") + bax := pool.Lookup("baz.com") + fox := pool.Lookup("foo.com") + + err := pool.FlushFakeIP() + assert.Nil(t, err) + + baz := pool.Lookup("foo.com") + next := pool.Lookup("baz.com") + nero := pool.Lookup("foo.com") + + assert.Equal(t, foo, fox) + assert.NotEqual(t, foo, baz) + assert.Equal(t, bar, bax) + assert.NotEqual(t, bar, next) + assert.Equal(t, baz, nero) +} diff --git a/component/profile/cachefile/cache.go b/component/profile/cachefile/cache.go index 71113567..3d2dd1de 100644 --- a/component/profile/cachefile/cache.go +++ b/component/profile/cachefile/cache.go @@ -132,6 +132,17 @@ func (c *CacheFile) GetFakeip(key []byte) []byte { return bucket.Get(key) } +func (c *CacheFile) FlushFakeIP() error { + err := c.DB.Batch(func(t *bbolt.Tx) error { + bucket := t.Bucket(bucketFakeip) + if bucket == nil { + return nil + } + return t.DeleteBucket(bucketFakeip) + }) + return err +} + func (c *CacheFile) Close() error { return c.DB.Close() } diff --git a/component/resolver/enhancer.go b/component/resolver/enhancer.go index 68f1d5d1..9df3f54b 100644 --- a/component/resolver/enhancer.go +++ b/component/resolver/enhancer.go @@ -13,6 +13,7 @@ type Enhancer interface { IsFakeBroadcastIP(net.IP) bool IsExistFakeIP(net.IP) bool FindHostByIP(net.IP) (string, bool) + FlushFakeIP() error } func FakeIPEnabled() bool { @@ -62,3 +63,10 @@ func FindHostByIP(ip net.IP) (string, bool) { return "", false } + +func FlushFakeIP() error { + if mapper := DefaultHostMapper; mapper != nil { + return mapper.FlushFakeIP() + } + return nil +} diff --git a/dns/enhancer.go b/dns/enhancer.go index f42fa268..9bf568c7 100644 --- a/dns/enhancer.go +++ b/dns/enhancer.go @@ -84,6 +84,13 @@ func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) { } } +func (h *ResolverEnhancer) FlushFakeIP() error { + if h.fakePool != nil { + return h.fakePool.FlushFakeIP() + } + return nil +} + func NewEnhancer(cfg Config) *ResolverEnhancer { var fakePool *fakeip.Pool var mapping *cache.LruCache diff --git a/hub/route/cache.go b/hub/route/cache.go new file mode 100644 index 00000000..bdfd2e35 --- /dev/null +++ b/hub/route/cache.go @@ -0,0 +1,26 @@ +package route + +import ( + "net/http" + + "github.com/Dreamacro/clash/component/resolver" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/render" +) + +func cacheRouter() http.Handler { + r := chi.NewRouter() + r.Post("/fakeip/flush", flushFakeIPPool) + return r +} + +func flushFakeIPPool(w http.ResponseWriter, r *http.Request) { + err := resolver.FlushFakeIP() + if err != nil { + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, newError(err.Error())) + return + } + render.NoContent(w, r) +} diff --git a/hub/route/server.go b/hub/route/server.go index a6632071..440406aa 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -71,6 +71,7 @@ func Start(addr string, secret string) { r.Mount("/rules", ruleRouter()) r.Mount("/connections", connectionRouter()) r.Mount("/providers/proxies", proxyProviderRouter()) + r.Mount("/cache", cacheRouter()) }) if uiPath != "" { @@ -130,7 +131,7 @@ func authentication(next http.Handler) http.Handler { } func hello(w http.ResponseWriter, r *http.Request) { - render.JSON(w, r, render.M{"hello": "clash"}) + render.JSON(w, r, render.M{"hello": "clash with tun"}) } func traffic(w http.ResponseWriter, r *http.Request) {