github.com/igoogolx/clash@v1.19.8/component/fakeip/pool.go (about)

     1  package fakeip
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/igoogolx/clash/common/cache"
    10  	"github.com/igoogolx/clash/component/profile/cachefile"
    11  	"github.com/igoogolx/clash/component/trie"
    12  )
    13  
    14  type store interface {
    15  	GetByHost(host string) (net.IP, bool)
    16  	PutByHost(host string, ip net.IP)
    17  	GetByIP(ip net.IP) (string, bool)
    18  	PutByIP(ip net.IP, host string)
    19  	DelByIP(ip net.IP)
    20  	Exist(ip net.IP) bool
    21  	CloneTo(store)
    22  }
    23  
    24  // Pool is an implementation about fake ip generator without storage
    25  type Pool struct {
    26  	max     uint32
    27  	min     uint32
    28  	gateway uint32
    29  	offset  uint32
    30  	mux     sync.Mutex
    31  	host    *trie.DomainTrie
    32  	ipnet   *net.IPNet
    33  	store   store
    34  }
    35  
    36  // Lookup return a fake ip with host
    37  func (p *Pool) Lookup(host string) net.IP {
    38  	p.mux.Lock()
    39  	defer p.mux.Unlock()
    40  
    41  	// RFC4343: DNS Case Insensitive, we SHOULD return result with all cases.
    42  	host = strings.ToLower(host)
    43  	if ip, exist := p.store.GetByHost(host); exist {
    44  		return ip
    45  	}
    46  
    47  	ip := p.get(host)
    48  	p.store.PutByHost(host, ip)
    49  	return ip
    50  }
    51  
    52  // LookBack return host with the fake ip
    53  func (p *Pool) LookBack(ip net.IP) (string, bool) {
    54  	p.mux.Lock()
    55  	defer p.mux.Unlock()
    56  
    57  	if ip = ip.To4(); ip == nil {
    58  		return "", false
    59  	}
    60  
    61  	return p.store.GetByIP(ip)
    62  }
    63  
    64  // ShouldSkipped return if domain should be skipped
    65  func (p *Pool) ShouldSkipped(domain string) bool {
    66  	if p.host == nil {
    67  		return false
    68  	}
    69  	return p.host.Search(domain) != nil
    70  }
    71  
    72  // Exist returns if given ip exists in fake-ip pool
    73  func (p *Pool) Exist(ip net.IP) bool {
    74  	p.mux.Lock()
    75  	defer p.mux.Unlock()
    76  
    77  	if ip = ip.To4(); ip == nil {
    78  		return false
    79  	}
    80  
    81  	return p.store.Exist(ip)
    82  }
    83  
    84  // Gateway return gateway ip
    85  func (p *Pool) Gateway() net.IP {
    86  	return uintToIP(p.gateway)
    87  }
    88  
    89  // IPNet return raw ipnet
    90  func (p *Pool) IPNet() *net.IPNet {
    91  	return p.ipnet
    92  }
    93  
    94  // CloneFrom clone cache from old pool
    95  func (p *Pool) CloneFrom(o *Pool) {
    96  	o.store.CloneTo(p.store)
    97  }
    98  
    99  func (p *Pool) get(host string) net.IP {
   100  	current := p.offset
   101  	for {
   102  		ip := uintToIP(p.min + p.offset)
   103  		if !p.store.Exist(ip) {
   104  			break
   105  		}
   106  
   107  		p.offset = (p.offset + 1) % (p.max - p.min)
   108  		// Avoid infinite loops
   109  		if p.offset == current {
   110  			p.offset = (p.offset + 1) % (p.max - p.min)
   111  			ip := uintToIP(p.min + p.offset)
   112  			p.store.DelByIP(ip)
   113  			break
   114  		}
   115  	}
   116  	ip := uintToIP(p.min + p.offset)
   117  	p.store.PutByIP(ip, host)
   118  	return ip
   119  }
   120  
   121  func ipToUint(ip net.IP) uint32 {
   122  	v := uint32(ip[0]) << 24
   123  	v += uint32(ip[1]) << 16
   124  	v += uint32(ip[2]) << 8
   125  	v += uint32(ip[3])
   126  	return v
   127  }
   128  
   129  func uintToIP(v uint32) net.IP {
   130  	return net.IP{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}
   131  }
   132  
   133  type Options struct {
   134  	IPNet *net.IPNet
   135  	Host  *trie.DomainTrie
   136  
   137  	// Size sets the maximum number of entries in memory
   138  	// and does not work if Persistence is true
   139  	Size int
   140  
   141  	// Persistence will save the data to disk.
   142  	// Size will not work and record will be fully stored.
   143  	Persistence bool
   144  }
   145  
   146  // New return Pool instance
   147  func New(options Options) (*Pool, error) {
   148  	min := ipToUint(options.IPNet.IP) + 2
   149  
   150  	ones, bits := options.IPNet.Mask.Size()
   151  	total := 1<<uint(bits-ones) - 2
   152  
   153  	if total <= 0 {
   154  		return nil, errors.New("ipnet don't have valid ip")
   155  	}
   156  
   157  	max := min + uint32(total) - 1
   158  	pool := &Pool{
   159  		min:     min,
   160  		max:     max,
   161  		gateway: min - 1,
   162  		host:    options.Host,
   163  		ipnet:   options.IPNet,
   164  	}
   165  	if options.Persistence {
   166  		pool.store = &cachefileStore{
   167  			cache: cachefile.Cache(),
   168  		}
   169  	} else {
   170  		pool.store = &memoryStore{
   171  			cache: cache.New(cache.WithSize(options.Size * 2)),
   172  		}
   173  	}
   174  
   175  	return pool, nil
   176  }