github.com/yaling888/clash@v1.53.0/component/fakeip/pool.go (about)

     1  package fakeip
     2  
     3  import (
     4  	"errors"
     5  	"net/netip"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/yaling888/clash/common/nnip"
    10  	"github.com/yaling888/clash/component/profile/cachefile"
    11  	"github.com/yaling888/clash/component/trie"
    12  )
    13  
    14  const (
    15  	offsetKey = "key-offset-fake-ip"
    16  	cycleKey  = "key-cycle-fake-ip"
    17  )
    18  
    19  type store interface {
    20  	GetByHost(host string) (netip.Addr, bool)
    21  	PutByHost(host string, ip netip.Addr)
    22  	GetByIP(ip netip.Addr) (string, bool)
    23  	PutByIP(ip netip.Addr, host string)
    24  	DelByIP(ip netip.Addr)
    25  	Exist(ip netip.Addr) bool
    26  	CloneTo(store)
    27  	FlushFakeIP() error
    28  }
    29  
    30  // Pool is an implementation about fake ip generator without storage
    31  type Pool struct {
    32  	gateway netip.Addr
    33  	first   netip.Addr
    34  	last    netip.Addr
    35  	offset  netip.Addr
    36  	cycle   bool
    37  	mux     sync.Mutex
    38  	host    *trie.DomainTrie[bool]
    39  	ipnet   *netip.Prefix
    40  	store   store
    41  }
    42  
    43  // Lookup return a fake ip with host
    44  func (p *Pool) Lookup(host string) netip.Addr {
    45  	p.mux.Lock()
    46  	defer p.mux.Unlock()
    47  	host = strings.ToLower(host)
    48  	if ip, exist := p.store.GetByHost(host); exist {
    49  		return ip
    50  	}
    51  
    52  	ip := p.get(host)
    53  	p.store.PutByHost(host, ip)
    54  	return ip
    55  }
    56  
    57  // LookBack return host with the fake ip
    58  func (p *Pool) LookBack(ip netip.Addr) (string, bool) {
    59  	p.mux.Lock()
    60  	defer p.mux.Unlock()
    61  
    62  	return p.store.GetByIP(ip)
    63  }
    64  
    65  // ShouldSkipped return if domain should be skipped
    66  func (p *Pool) ShouldSkipped(domain string) bool {
    67  	if p.host == nil {
    68  		return false
    69  	}
    70  	return p.host.Search(domain) != nil
    71  }
    72  
    73  // Exist returns if given ip exists in fake-ip pool
    74  func (p *Pool) Exist(ip netip.Addr) bool {
    75  	p.mux.Lock()
    76  	defer p.mux.Unlock()
    77  
    78  	return p.store.Exist(ip)
    79  }
    80  
    81  // Gateway return gateway ip
    82  func (p *Pool) Gateway() netip.Addr {
    83  	return p.gateway
    84  }
    85  
    86  // Broadcast return the last ip
    87  func (p *Pool) Broadcast() netip.Addr {
    88  	return p.last
    89  }
    90  
    91  // IPNet return raw ipnet
    92  func (p *Pool) IPNet() *netip.Prefix {
    93  	return p.ipnet
    94  }
    95  
    96  // CloneFrom clone cache from old pool
    97  func (p *Pool) CloneFrom(o *Pool) {
    98  	o.store.CloneTo(p.store)
    99  }
   100  
   101  func (p *Pool) get(host string) netip.Addr {
   102  	p.offset = p.offset.Next()
   103  
   104  	if !p.offset.Less(p.last) {
   105  		p.cycle = true
   106  		p.offset = p.first
   107  	}
   108  
   109  	if p.cycle || p.store.Exist(p.offset) {
   110  		p.store.DelByIP(p.offset)
   111  	}
   112  
   113  	p.store.PutByIP(p.offset, host)
   114  	return p.offset
   115  }
   116  
   117  func (p *Pool) FlushFakeIP() error {
   118  	err := p.store.FlushFakeIP()
   119  	if err == nil {
   120  		p.cycle = false
   121  		p.offset = p.first.Prev()
   122  	}
   123  	return err
   124  }
   125  
   126  func (p *Pool) StoreState() {
   127  	if s, ok := p.store.(*cachefileStore); ok {
   128  		s.PutByHost(offsetKey, p.offset)
   129  		if p.cycle {
   130  			s.PutByHost(cycleKey, p.offset)
   131  		}
   132  	}
   133  }
   134  
   135  func (p *Pool) restoreState() {
   136  	if s, ok := p.store.(*cachefileStore); ok {
   137  		if _, exist := s.GetByHost(cycleKey); exist {
   138  			p.cycle = true
   139  		}
   140  
   141  		if offset, exist := s.GetByHost(offsetKey); exist {
   142  			if p.ipnet.Contains(offset) {
   143  				p.offset = offset
   144  			} else {
   145  				_ = p.FlushFakeIP()
   146  			}
   147  		} else if s.Exist(p.first) {
   148  			_ = p.FlushFakeIP()
   149  		}
   150  	}
   151  }
   152  
   153  type Options struct {
   154  	IPNet *netip.Prefix
   155  	Host  *trie.DomainTrie[bool]
   156  
   157  	// Size sets the maximum number of entries in memory
   158  	// and does not work if Persistence is true
   159  	Size int
   160  
   161  	// Persistence will save the data to disk.
   162  	// Size will not work and record will be fully stored.
   163  	Persistence bool
   164  }
   165  
   166  // New return Pool instance
   167  func New(options Options) (*Pool, error) {
   168  	var (
   169  		hostAddr = options.IPNet.Masked().Addr()
   170  		gateway  = hostAddr.Next()
   171  		first    = gateway.Next().Next()
   172  		last     = nnip.UnMasked(*options.IPNet)
   173  	)
   174  
   175  	if !options.IPNet.IsValid() || !first.IsValid() || !first.Less(last) || first.Is6() {
   176  		return nil, errors.New("ipnet don't have valid ip")
   177  	}
   178  
   179  	pool := &Pool{
   180  		gateway: gateway,
   181  		first:   first,
   182  		last:    last,
   183  		offset:  first.Prev(),
   184  		cycle:   false,
   185  		host:    options.Host,
   186  		ipnet:   options.IPNet,
   187  	}
   188  	if options.Persistence {
   189  		pool.store = &cachefileStore{
   190  			cache: cachefile.Cache(),
   191  		}
   192  	} else {
   193  		pool.store = newMemoryStore(options.Size)
   194  	}
   195  
   196  	pool.restoreState()
   197  
   198  	return pool, nil
   199  }