diff --git a/adapter/fakeip.go b/adapter/fakeip.go index 4ce2b28a0a..6a14224557 100644 --- a/adapter/fakeip.go +++ b/adapter/fakeip.go @@ -10,7 +10,7 @@ import ( type FakeIPStore interface { Service Contains(address netip.Addr) bool - Create(domain string, strategy dns.DomainStrategy) (netip.Addr, error) + Create(domain string, isIPv6 bool) (netip.Addr, error) Lookup(address netip.Addr) (string, bool) Reset() error } @@ -21,6 +21,7 @@ type FakeIPStorage interface { FakeIPStore(address netip.Addr, domain string) error FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) FakeIPLoad(address netip.Addr) (string, bool) + FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bool) FakeIPReset() error } diff --git a/experimental/clashapi/cachefile/fakeip.go b/experimental/clashapi/cachefile/fakeip.go index 7d455ccbbe..e99588e952 100644 --- a/experimental/clashapi/cachefile/fakeip.go +++ b/experimental/clashapi/cachefile/fakeip.go @@ -6,13 +6,16 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" "go.etcd.io/bbolt" ) var ( - bucketFakeIP = []byte("fakeip") - keyMetadata = []byte("metadata") + bucketFakeIP = []byte("fakeip") + bucketFakeIPDomain4 = []byte("fakeip_domain4") + bucketFakeIPDomain6 = []byte("fakeip_domain6") + keyMetadata = []byte("metadata") ) func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata { @@ -54,7 +57,19 @@ func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error { if err != nil { return err } - return bucket.Put(address.AsSlice(), []byte(domain)) + err = bucket.Put(address.AsSlice(), []byte(domain)) + if err != nil { + return err + } + if address.Is4() { + bucket, err = tx.CreateBucketIfNotExists(bucketFakeIPDomain4) + } else { + bucket, err = tx.CreateBucketIfNotExists(bucketFakeIPDomain6) + } + if err != nil { + return err + } + return bucket.Put([]byte(domain), address.AsSlice()) }) } @@ -92,8 +107,34 @@ func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) { return domain, domain != "" } +func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bool) { + var address netip.Addr + _ = c.DB.View(func(tx *bbolt.Tx) error { + var bucket *bbolt.Bucket + if isIPv6 { + bucket = tx.Bucket(bucketFakeIPDomain6) + } else { + bucket = tx.Bucket(bucketFakeIPDomain4) + } + if bucket == nil { + return nil + } + address = M.AddrFromIP(bucket.Get([]byte(domain))) + return nil + }) + return address, address.IsValid() +} + func (c *CacheFile) FakeIPReset() error { return c.DB.Batch(func(tx *bbolt.Tx) error { - return tx.DeleteBucket(bucketFakeIP) + err := tx.DeleteBucket(bucketFakeIP) + if err != nil { + return err + } + err = tx.DeleteBucket(bucketFakeIPDomain4) + if err != nil { + return err + } + return tx.DeleteBucket(bucketFakeIPDomain6) }) } diff --git a/transport/fakeip/memory.go b/transport/fakeip/memory.go index cb60d251fc..b9e6a99976 100644 --- a/transport/fakeip/memory.go +++ b/transport/fakeip/memory.go @@ -2,48 +2,78 @@ package fakeip import ( "net/netip" + "sync" "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing/common/cache" "github.com/sagernet/sing/common/logger" ) var _ adapter.FakeIPStorage = (*MemoryStorage)(nil) type MemoryStorage struct { - metadata *adapter.FakeIPMetadata - domainCache *cache.LruCache[netip.Addr, string] + addressAccess sync.RWMutex + domainAccess sync.RWMutex + addressCache map[netip.Addr]string + domainCache4 map[string]netip.Addr + domainCache6 map[string]netip.Addr } func NewMemoryStorage() *MemoryStorage { return &MemoryStorage{ - domainCache: cache.New[netip.Addr, string](), + addressCache: make(map[netip.Addr]string), + domainCache4: make(map[string]netip.Addr), + domainCache6: make(map[string]netip.Addr), } } func (s *MemoryStorage) FakeIPMetadata() *adapter.FakeIPMetadata { - return s.metadata + return nil } func (s *MemoryStorage) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error { - s.metadata = metadata return nil } func (s *MemoryStorage) FakeIPStore(address netip.Addr, domain string) error { - s.domainCache.Store(address, domain) + s.addressAccess.Lock() + s.domainAccess.Lock() + s.addressCache[address] = domain + if address.Is4() { + s.domainCache4[domain] = address + } else { + s.domainCache6[domain] = address + } + s.domainAccess.Unlock() + s.addressAccess.Unlock() return nil } func (s *MemoryStorage) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) { - s.domainCache.Store(address, domain) + _ = s.FakeIPStore(address, domain) } func (s *MemoryStorage) FakeIPLoad(address netip.Addr) (string, bool) { - return s.domainCache.Load(address) + s.addressAccess.RLock() + defer s.addressAccess.RUnlock() + domain, loaded := s.addressCache[address] + return domain, loaded +} + +func (s *MemoryStorage) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bool) { + s.domainAccess.RLock() + defer s.domainAccess.RUnlock() + if !isIPv6 { + address, loaded := s.domainCache4[domain] + return address, loaded + } else { + address, loaded := s.domainCache6[domain] + return address, loaded + } } func (s *MemoryStorage) FakeIPReset() error { - s.domainCache = cache.New[netip.Addr, string]() + s.addressCache = make(map[netip.Addr]string) + s.domainCache4 = make(map[string]netip.Addr) + s.domainCache6 = make(map[string]netip.Addr) return nil } diff --git a/transport/fakeip/server.go b/transport/fakeip/server.go index 9247c4f634..1a1d5916bc 100644 --- a/transport/fakeip/server.go +++ b/transport/fakeip/server.go @@ -69,14 +69,14 @@ func (s *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, func (s *Transport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) { var addresses []netip.Addr if strategy != dns.DomainStrategyUseIPv6 { - inet4Address, err := s.store.Create(domain, dns.DomainStrategyUseIPv4) + inet4Address, err := s.store.Create(domain, false) if err != nil { return nil, err } addresses = append(addresses, inet4Address) } if strategy != dns.DomainStrategyUseIPv4 { - inet6Address, err := s.store.Create(domain, dns.DomainStrategyUseIPv6) + inet6Address, err := s.store.Create(domain, true) if err != nil { return nil, err } diff --git a/transport/fakeip/store.go b/transport/fakeip/store.go index 973da60bec..3223f00b28 100644 --- a/transport/fakeip/store.go +++ b/transport/fakeip/store.go @@ -4,7 +4,6 @@ import ( "net/netip" "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-dns" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/logger" ) @@ -72,9 +71,12 @@ func (s *Store) Close() error { }) } -func (s *Store) Create(domain string, strategy dns.DomainStrategy) (netip.Addr, error) { +func (s *Store) Create(domain string, isIPv6 bool) (netip.Addr, error) { + if address, loaded := s.storage.FakeIPLoadDomain(domain, isIPv6); loaded { + return address, nil + } var address netip.Addr - if strategy == dns.DomainStrategyUseIPv4 { + if !isIPv6 { if !s.inet4Current.IsValid() { return netip.Addr{}, E.New("missing IPv4 fakeip address range") }