github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/cachefile/fakeip.go (about)

     1  package cachefile
     2  
     3  import (
     4  	"net/netip"
     5  	"os"
     6  	"time"
     7  
     8  	"github.com/sagernet/bbolt"
     9  	"github.com/sagernet/sing-box/adapter"
    10  	C "github.com/sagernet/sing-box/constant"
    11  	"github.com/sagernet/sing/common/logger"
    12  	M "github.com/sagernet/sing/common/metadata"
    13  )
    14  
    15  const fakeipBucketPrefix = "fakeip_"
    16  
    17  var (
    18  	bucketFakeIP        = []byte(fakeipBucketPrefix + "address")
    19  	bucketFakeIPDomain4 = []byte(fakeipBucketPrefix + "domain4")
    20  	bucketFakeIPDomain6 = []byte(fakeipBucketPrefix + "domain6")
    21  	keyMetadata         = []byte(fakeipBucketPrefix + "metadata")
    22  )
    23  
    24  func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata {
    25  	var metadata adapter.FakeIPMetadata
    26  	err := c.DB.Batch(func(tx *bbolt.Tx) error {
    27  		bucket := tx.Bucket(bucketFakeIP)
    28  		if bucket == nil {
    29  			return os.ErrNotExist
    30  		}
    31  		metadataBinary := bucket.Get(keyMetadata)
    32  		if len(metadataBinary) == 0 {
    33  			return os.ErrInvalid
    34  		}
    35  		err := bucket.Delete(keyMetadata)
    36  		if err != nil {
    37  			return err
    38  		}
    39  		return metadata.UnmarshalBinary(metadataBinary)
    40  	})
    41  	if err != nil {
    42  		return nil
    43  	}
    44  	return &metadata
    45  }
    46  
    47  func (c *CacheFile) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
    48  	return c.DB.Batch(func(tx *bbolt.Tx) error {
    49  		bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
    50  		if err != nil {
    51  			return err
    52  		}
    53  		metadataBinary, err := metadata.MarshalBinary()
    54  		if err != nil {
    55  			return err
    56  		}
    57  		return bucket.Put(keyMetadata, metadataBinary)
    58  	})
    59  }
    60  
    61  func (c *CacheFile) FakeIPSaveMetadataAsync(metadata *adapter.FakeIPMetadata) {
    62  	if c.saveMetadataTimer == nil {
    63  		c.saveMetadataTimer = time.AfterFunc(C.FakeIPMetadataSaveInterval, func() {
    64  			_ = c.FakeIPSaveMetadata(metadata)
    65  		})
    66  	} else {
    67  		c.saveMetadataTimer.Reset(C.FakeIPMetadataSaveInterval)
    68  	}
    69  }
    70  
    71  func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
    72  	return c.DB.Batch(func(tx *bbolt.Tx) error {
    73  		bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
    74  		if err != nil {
    75  			return err
    76  		}
    77  		oldDomain := bucket.Get(address.AsSlice())
    78  		err = bucket.Put(address.AsSlice(), []byte(domain))
    79  		if err != nil {
    80  			return err
    81  		}
    82  		if address.Is4() {
    83  			bucket, err = tx.CreateBucketIfNotExists(bucketFakeIPDomain4)
    84  		} else {
    85  			bucket, err = tx.CreateBucketIfNotExists(bucketFakeIPDomain6)
    86  		}
    87  		if err != nil {
    88  			return err
    89  		}
    90  		if oldDomain != nil {
    91  			if err := bucket.Delete(oldDomain); err != nil {
    92  				return err
    93  			}
    94  		}
    95  		return bucket.Put([]byte(domain), address.AsSlice())
    96  	})
    97  }
    98  
    99  func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
   100  	c.saveFakeIPAccess.Lock()
   101  	if oldDomain, loaded := c.saveDomain[address]; loaded {
   102  		if address.Is4() {
   103  			delete(c.saveAddress4, oldDomain)
   104  		} else {
   105  			delete(c.saveAddress6, oldDomain)
   106  		}
   107  	}
   108  	c.saveDomain[address] = domain
   109  	if address.Is4() {
   110  		c.saveAddress4[domain] = address
   111  	} else {
   112  		c.saveAddress6[domain] = address
   113  	}
   114  	c.saveFakeIPAccess.Unlock()
   115  	go func() {
   116  		err := c.FakeIPStore(address, domain)
   117  		if err != nil {
   118  			logger.Warn("save FakeIP cache: ", err)
   119  		}
   120  		c.saveFakeIPAccess.Lock()
   121  		delete(c.saveDomain, address)
   122  		if address.Is4() {
   123  			delete(c.saveAddress4, domain)
   124  		} else {
   125  			delete(c.saveAddress6, domain)
   126  		}
   127  		c.saveFakeIPAccess.Unlock()
   128  	}()
   129  }
   130  
   131  func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
   132  	c.saveFakeIPAccess.RLock()
   133  	cachedDomain, cached := c.saveDomain[address]
   134  	c.saveFakeIPAccess.RUnlock()
   135  	if cached {
   136  		return cachedDomain, true
   137  	}
   138  	var domain string
   139  	_ = c.DB.View(func(tx *bbolt.Tx) error {
   140  		bucket := tx.Bucket(bucketFakeIP)
   141  		if bucket == nil {
   142  			return nil
   143  		}
   144  		domain = string(bucket.Get(address.AsSlice()))
   145  		return nil
   146  	})
   147  	return domain, domain != ""
   148  }
   149  
   150  func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bool) {
   151  	var (
   152  		cachedAddress netip.Addr
   153  		cached        bool
   154  	)
   155  	c.saveFakeIPAccess.RLock()
   156  	if !isIPv6 {
   157  		cachedAddress, cached = c.saveAddress4[domain]
   158  	} else {
   159  		cachedAddress, cached = c.saveAddress6[domain]
   160  	}
   161  	c.saveFakeIPAccess.RUnlock()
   162  	if cached {
   163  		return cachedAddress, true
   164  	}
   165  	var address netip.Addr
   166  	_ = c.DB.View(func(tx *bbolt.Tx) error {
   167  		var bucket *bbolt.Bucket
   168  		if isIPv6 {
   169  			bucket = tx.Bucket(bucketFakeIPDomain6)
   170  		} else {
   171  			bucket = tx.Bucket(bucketFakeIPDomain4)
   172  		}
   173  		if bucket == nil {
   174  			return nil
   175  		}
   176  		address = M.AddrFromIP(bucket.Get([]byte(domain)))
   177  		return nil
   178  	})
   179  	return address, address.IsValid()
   180  }
   181  
   182  func (c *CacheFile) FakeIPReset() error {
   183  	return c.DB.Batch(func(tx *bbolt.Tx) error {
   184  		err := tx.DeleteBucket(bucketFakeIP)
   185  		if err != nil {
   186  			return err
   187  		}
   188  		err = tx.DeleteBucket(bucketFakeIPDomain4)
   189  		if err != nil {
   190  			return err
   191  		}
   192  		return tx.DeleteBucket(bucketFakeIPDomain6)
   193  	})
   194  }