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 }