feat: support fake-ip-range6 in dns module

This commit is contained in:
wwqgtxx 2025-10-28 11:40:00 +08:00
parent ff62386f6b
commit c8af92a01f
13 changed files with 234 additions and 153 deletions

View File

@ -50,6 +50,10 @@ func (c *cachefileStore) FlushFakeIP() error {
return c.cache.FlushFakeIP() return c.cache.FlushFakeIP()
} }
func newCachefileStore(cache *cachefile.CacheFile) *cachefileStore { func newCachefileStore(cache *cachefile.CacheFile, prefix netip.Prefix) *cachefileStore {
return &cachefileStore{cache.FakeIpStore()} if prefix.Addr().Is6() {
return &cachefileStore{cache.FakeIpStore6()}
} else {
return &cachefileStore{cache.FakeIpStore()}
}
} }

View File

@ -7,7 +7,6 @@ import (
"sync" "sync"
"github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/profile/cachefile"
C "github.com/metacubex/mihomo/constant"
"go4.org/netipx" "go4.org/netipx"
) )
@ -36,8 +35,6 @@ type Pool struct {
offset netip.Addr offset netip.Addr
cycle bool cycle bool
mux sync.Mutex mux sync.Mutex
host []C.DomainMatcher
mode C.FilterMode
ipnet netip.Prefix ipnet netip.Prefix
store store store store
} }
@ -66,24 +63,6 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) {
return p.store.GetByIP(ip) return p.store.GetByIP(ip)
} }
// ShouldSkipped return if domain should be skipped
func (p *Pool) ShouldSkipped(domain string) bool {
should := p.shouldSkipped(domain)
if p.mode == C.FilterWhiteList {
return !should
}
return should
}
func (p *Pool) shouldSkipped(domain string) bool {
for _, matcher := range p.host {
if matcher.MatchDomain(domain) {
return true
}
}
return false
}
// Exist returns if given ip exists in fake-ip pool // Exist returns if given ip exists in fake-ip pool
func (p *Pool) Exist(ip netip.Addr) bool { func (p *Pool) Exist(ip netip.Addr) bool {
p.mux.Lock() p.mux.Lock()
@ -166,8 +145,6 @@ func (p *Pool) restoreState() {
type Options struct { type Options struct {
IPNet netip.Prefix IPNet netip.Prefix
Host []C.DomainMatcher
Mode C.FilterMode
// Size sets the maximum number of entries in memory // Size sets the maximum number of entries in memory
// and does not work if Persistence is true // and does not work if Persistence is true
@ -197,12 +174,10 @@ func New(options Options) (*Pool, error) {
last: last, last: last,
offset: first.Prev(), offset: first.Prev(),
cycle: false, cycle: false,
host: options.Host,
mode: options.Mode,
ipnet: options.IPNet, ipnet: options.IPNet,
} }
if options.Persistence { if options.Persistence {
pool.store = newCachefileStore(cachefile.Cache()) pool.store = newCachefileStore(cachefile.Cache(), options.IPNet)
} else { } else {
pool.store = newMemoryStore(options.Size) pool.store = newMemoryStore(options.Size)
} }

View File

@ -8,8 +8,6 @@ import (
"time" "time"
"github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/profile/cachefile"
"github.com/metacubex/mihomo/component/trie"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/bbolt" "github.com/metacubex/bbolt"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -43,7 +41,7 @@ func createCachefileStore(options Options) (*Pool, string, error) {
return nil, "", err return nil, "", err
} }
pool.store = newCachefileStore(&cachefile.CacheFile{DB: db}) pool.store = newCachefileStore(&cachefile.CacheFile{DB: db}, options.IPNet)
return pool, f.Name(), nil return pool, f.Name(), nil
} }
@ -146,47 +144,6 @@ func TestPool_CycleUsed(t *testing.T) {
} }
} }
func TestPool_Skip(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/29")
tree := trie.New[struct{}]()
assert.NoError(t, tree.Insert("example.com", struct{}{}))
assert.False(t, tree.IsEmpty())
pools, tempfile, err := createPools(Options{
IPNet: ipnet,
Size: 10,
Host: []C.DomainMatcher{tree.NewDomainSet()},
})
assert.Nil(t, err)
defer os.Remove(tempfile)
for _, pool := range pools {
assert.True(t, pool.ShouldSkipped("example.com"))
assert.False(t, pool.ShouldSkipped("foo.com"))
assert.False(t, pool.shouldSkipped("baz.com"))
}
}
func TestPool_SkipWhiteList(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/29")
tree := trie.New[struct{}]()
assert.NoError(t, tree.Insert("example.com", struct{}{}))
assert.False(t, tree.IsEmpty())
pools, tempfile, err := createPools(Options{
IPNet: ipnet,
Size: 10,
Host: []C.DomainMatcher{tree.NewDomainSet()},
Mode: C.FilterWhiteList,
})
assert.Nil(t, err)
defer os.Remove(tempfile)
for _, pool := range pools {
assert.False(t, pool.ShouldSkipped("example.com"))
assert.True(t, pool.ShouldSkipped("foo.com"))
assert.True(t, pool.ShouldSkipped("baz.com"))
}
}
func TestPool_MaxCacheSize(t *testing.T) { func TestPool_MaxCacheSize(t *testing.T) {
ipnet := netip.MustParsePrefix("192.168.0.1/24") ipnet := netip.MustParsePrefix("192.168.0.1/24")
pool, _ := New(Options{ pool, _ := New(Options{

View File

@ -0,0 +1,28 @@
package fakeip
import (
C "github.com/metacubex/mihomo/constant"
)
type Skipper struct {
Host []C.DomainMatcher
Mode C.FilterMode
}
// ShouldSkipped return if domain should be skipped
func (p *Skipper) ShouldSkipped(domain string) bool {
should := p.shouldSkipped(domain)
if p.Mode == C.FilterWhiteList {
return !should
}
return should
}
func (p *Skipper) shouldSkipped(domain string) bool {
for _, matcher := range p.Host {
if matcher.MatchDomain(domain) {
return true
}
}
return false
}

View File

@ -0,0 +1,35 @@
package fakeip
import (
"testing"
"github.com/metacubex/mihomo/component/trie"
C "github.com/metacubex/mihomo/constant"
"github.com/stretchr/testify/assert"
)
func TestSkipper_BlackList(t *testing.T) {
tree := trie.New[struct{}]()
assert.NoError(t, tree.Insert("example.com", struct{}{}))
assert.False(t, tree.IsEmpty())
skipper := &Skipper{
Host: []C.DomainMatcher{tree.NewDomainSet()},
}
assert.True(t, skipper.ShouldSkipped("example.com"))
assert.False(t, skipper.ShouldSkipped("foo.com"))
assert.False(t, skipper.shouldSkipped("baz.com"))
}
func TestSkipper_WhiteList(t *testing.T) {
tree := trie.New[struct{}]()
assert.NoError(t, tree.Insert("example.com", struct{}{}))
assert.False(t, tree.IsEmpty())
skipper := &Skipper{
Host: []C.DomainMatcher{tree.NewDomainSet()},
Mode: C.FilterWhiteList,
}
assert.False(t, skipper.ShouldSkipped("example.com"))
assert.True(t, skipper.ShouldSkipped("foo.com"))
assert.True(t, skipper.ShouldSkipped("baz.com"))
}

View File

@ -19,6 +19,7 @@ var (
bucketSelected = []byte("selected") bucketSelected = []byte("selected")
bucketFakeip = []byte("fakeip") bucketFakeip = []byte("fakeip")
bucketFakeip6 = []byte("fakeip6")
bucketETag = []byte("etag") bucketETag = []byte("etag")
bucketSubscriptionInfo = []byte("subscriptioninfo") bucketSubscriptionInfo = []byte("subscriptioninfo")
) )

View File

@ -10,10 +10,15 @@ import (
type FakeIpStore struct { type FakeIpStore struct {
*CacheFile *CacheFile
bucketName []byte
} }
func (c *CacheFile) FakeIpStore() *FakeIpStore { func (c *CacheFile) FakeIpStore() *FakeIpStore {
return &FakeIpStore{c} return &FakeIpStore{c, bucketFakeip}
}
func (c *CacheFile) FakeIpStore6() *FakeIpStore {
return &FakeIpStore{c, bucketFakeip6}
} }
func (c *FakeIpStore) GetByHost(host string) (ip netip.Addr, exist bool) { func (c *FakeIpStore) GetByHost(host string) (ip netip.Addr, exist bool) {
@ -21,7 +26,7 @@ func (c *FakeIpStore) GetByHost(host string) (ip netip.Addr, exist bool) {
return return
} }
c.DB.View(func(t *bbolt.Tx) error { c.DB.View(func(t *bbolt.Tx) error {
if bucket := t.Bucket(bucketFakeip); bucket != nil { if bucket := t.Bucket(c.bucketName); bucket != nil {
if v := bucket.Get([]byte(host)); v != nil { if v := bucket.Get([]byte(host)); v != nil {
ip, exist = netip.AddrFromSlice(v) ip, exist = netip.AddrFromSlice(v)
} }
@ -36,7 +41,7 @@ func (c *FakeIpStore) PutByHost(host string, ip netip.Addr) {
return return
} }
err := c.DB.Batch(func(t *bbolt.Tx) error { err := c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := t.CreateBucketIfNotExists(bucketFakeip) bucket, err := t.CreateBucketIfNotExists(c.bucketName)
if err != nil { if err != nil {
return err return err
} }
@ -52,7 +57,7 @@ func (c *FakeIpStore) GetByIP(ip netip.Addr) (host string, exist bool) {
return return
} }
c.DB.View(func(t *bbolt.Tx) error { c.DB.View(func(t *bbolt.Tx) error {
if bucket := t.Bucket(bucketFakeip); bucket != nil { if bucket := t.Bucket(c.bucketName); bucket != nil {
if v := bucket.Get(ip.AsSlice()); v != nil { if v := bucket.Get(ip.AsSlice()); v != nil {
host, exist = string(v), true host, exist = string(v), true
} }
@ -67,7 +72,7 @@ func (c *FakeIpStore) PutByIP(ip netip.Addr, host string) {
return return
} }
err := c.DB.Batch(func(t *bbolt.Tx) error { err := c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := t.CreateBucketIfNotExists(bucketFakeip) bucket, err := t.CreateBucketIfNotExists(c.bucketName)
if err != nil { if err != nil {
return err return err
} }
@ -85,7 +90,7 @@ func (c *FakeIpStore) DelByIP(ip netip.Addr) {
addr := ip.AsSlice() addr := ip.AsSlice()
err := c.DB.Batch(func(t *bbolt.Tx) error { err := c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := t.CreateBucketIfNotExists(bucketFakeip) bucket, err := t.CreateBucketIfNotExists(c.bucketName)
if err != nil { if err != nil {
return err return err
} }
@ -105,11 +110,11 @@ func (c *FakeIpStore) DelByIP(ip netip.Addr) {
func (c *FakeIpStore) FlushFakeIP() error { func (c *FakeIpStore) FlushFakeIP() error {
err := c.DB.Batch(func(t *bbolt.Tx) error { err := c.DB.Batch(func(t *bbolt.Tx) error {
bucket := t.Bucket(bucketFakeip) bucket := t.Bucket(c.bucketName)
if bucket == nil { if bucket == nil {
return nil return nil
} }
return t.DeleteBucket(bucketFakeip) return t.DeleteBucket(c.bucketName)
}) })
return err return err
} }

View File

@ -157,7 +157,11 @@ type DNS struct {
DefaultNameserver []dns.NameServer DefaultNameserver []dns.NameServer
CacheAlgorithm string CacheAlgorithm string
CacheMaxSize int CacheMaxSize int
FakeIPRange *fakeip.Pool FakeIPRange netip.Prefix
FakeIPPool *fakeip.Pool
FakeIPRange6 netip.Prefix
FakeIPPool6 *fakeip.Pool
FakeIPSkipper *fakeip.Skipper
NameServerPolicy []dns.Policy NameServerPolicy []dns.Policy
ProxyServerNameserver []dns.NameServer ProxyServerNameserver []dns.NameServer
DirectNameServer []dns.NameServer DirectNameServer []dns.NameServer
@ -221,6 +225,7 @@ type RawDNS struct {
Listen string `yaml:"listen" json:"listen"` Listen string `yaml:"listen" json:"listen"`
EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"`
FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"`
FakeIPRange6 string `yaml:"fake-ip-range6" json:"fake-ip-range6"`
FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"`
FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"`
DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"`
@ -686,7 +691,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
} }
config.DNS = dnsCfg config.DNS = dnsCfg
err = parseTun(rawCfg.Tun, config.General) err = parseTun(rawCfg.Tun, dnsCfg, config.General)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1407,13 +1412,21 @@ func parseDNS(rawCfg *RawConfig, ruleProviders map[string]providerTypes.RuleProv
} }
} }
fakeIPRange, err := netip.ParsePrefix(cfg.FakeIPRange) if cfg.FakeIPRange != "" {
T.SetFakeIPRange(fakeIPRange) dnsCfg.FakeIPRange, err = netip.ParsePrefix(cfg.FakeIPRange)
if cfg.EnhancedMode == C.DNSFakeIP {
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
if cfg.FakeIPRange6 != "" {
dnsCfg.FakeIPRange6, err = netip.ParsePrefix(cfg.FakeIPRange6)
if err != nil {
return nil, err
}
}
if cfg.EnhancedMode == C.DNSFakeIP {
var fakeIPTrie *trie.DomainTrie[struct{}] var fakeIPTrie *trie.DomainTrie[struct{}]
if len(dnsCfg.Fallback) != 0 { if len(dnsCfg.Fallback) != 0 {
fakeIPTrie = trie.New[struct{}]() fakeIPTrie = trie.New[struct{}]()
@ -1431,18 +1444,39 @@ func parseDNS(rawCfg *RawConfig, ruleProviders map[string]providerTypes.RuleProv
return nil, err return nil, err
} }
pool, err := fakeip.New(fakeip.Options{ skipper := &fakeip.Skipper{
IPNet: fakeIPRange, Host: host,
Size: 1000, Mode: cfg.FakeIPFilterMode,
Host: host, }
Mode: cfg.FakeIPFilterMode, dnsCfg.FakeIPSkipper = skipper
Persistence: rawCfg.Profile.StoreFakeIP,
}) if dnsCfg.FakeIPRange.IsValid() {
if err != nil { pool, err := fakeip.New(fakeip.Options{
return nil, err IPNet: dnsCfg.FakeIPRange,
Size: 1000,
Persistence: rawCfg.Profile.StoreFakeIP,
})
if err != nil {
return nil, err
}
dnsCfg.FakeIPPool = pool
} }
dnsCfg.FakeIPRange = pool if dnsCfg.FakeIPRange6.IsValid() {
pool6, err := fakeip.New(fakeip.Options{
IPNet: dnsCfg.FakeIPRange6,
Size: 1000,
Persistence: rawCfg.Profile.StoreFakeIP,
})
if err != nil {
return nil, err
}
dnsCfg.FakeIPPool6 = pool6
}
if dnsCfg.FakeIPPool == nil && dnsCfg.FakeIPPool6 == nil {
return nil, errors.New("disallow `fake-ip-range` and `fake-ip-range6` both empty with fake-ip mode")
}
} }
if len(cfg.Fallback) != 0 { if len(cfg.Fallback) != 0 {
@ -1504,9 +1538,9 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser {
return users return users
} }
func parseTun(rawTun RawTun, general *General) error { func parseTun(rawTun RawTun, dns *DNS, general *General) error {
tunAddressPrefix := T.FakeIPRange() tunAddressPrefix := dns.FakeIPRange
if !tunAddressPrefix.IsValid() { if !tunAddressPrefix.IsValid() || !tunAddressPrefix.Addr().Is4() {
tunAddressPrefix = netip.MustParsePrefix("198.18.0.1/16") tunAddressPrefix = netip.MustParsePrefix("198.18.0.1/16")
} }
tunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30) tunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30)

View File

@ -9,10 +9,12 @@ import (
) )
type ResolverEnhancer struct { type ResolverEnhancer struct {
mode C.DNSMode mode C.DNSMode
fakePool *fakeip.Pool fakeIPPool *fakeip.Pool
mapping *lru.LruCache[netip.Addr, string] fakeIPPool6 *fakeip.Pool
useHosts bool fakeIPSkipper *fakeip.Skipper
mapping *lru.LruCache[netip.Addr, string]
useHosts bool
} }
func (h *ResolverEnhancer) FakeIPEnabled() bool { func (h *ResolverEnhancer) FakeIPEnabled() bool {
@ -28,10 +30,14 @@ func (h *ResolverEnhancer) IsExistFakeIP(ip netip.Addr) bool {
return false return false
} }
if pool := h.fakePool; pool != nil { if pool := h.fakeIPPool; pool != nil {
return pool.Exist(ip) return pool.Exist(ip)
} }
if pool6 := h.fakeIPPool6; pool6 != nil {
return pool6.Exist(ip)
}
return false return false
} }
@ -40,10 +46,14 @@ func (h *ResolverEnhancer) IsFakeIP(ip netip.Addr) bool {
return false return false
} }
if pool := h.fakePool; pool != nil { if pool := h.fakeIPPool; pool != nil {
return pool.IPNet().Contains(ip) && ip != pool.Gateway() && ip != pool.Broadcast() return pool.IPNet().Contains(ip) && ip != pool.Gateway() && ip != pool.Broadcast()
} }
if pool6 := h.fakeIPPool6; pool6 != nil {
return pool6.IPNet().Contains(ip) && ip != pool6.Gateway() && ip != pool6.Broadcast()
}
return false return false
} }
@ -52,20 +62,30 @@ func (h *ResolverEnhancer) IsFakeBroadcastIP(ip netip.Addr) bool {
return false return false
} }
if pool := h.fakePool; pool != nil { if pool := h.fakeIPPool; pool != nil {
return pool.Broadcast() == ip return pool.Broadcast() == ip
} }
if pool6 := h.fakeIPPool6; pool6 != nil {
return pool6.Broadcast() == ip
}
return false return false
} }
func (h *ResolverEnhancer) FindHostByIP(ip netip.Addr) (string, bool) { func (h *ResolverEnhancer) FindHostByIP(ip netip.Addr) (string, bool) {
if pool := h.fakePool; pool != nil { if pool := h.fakeIPPool; pool != nil {
if host, existed := pool.LookBack(ip); existed { if host, existed := pool.LookBack(ip); existed {
return host, true return host, true
} }
} }
if pool6 := h.fakeIPPool6; pool6 != nil {
if host, existed := pool6.LookBack(ip); existed {
return host, true
}
}
if mapping := h.mapping; mapping != nil { if mapping := h.mapping; mapping != nil {
if host, existed := h.mapping.Get(ip); existed { if host, existed := h.mapping.Get(ip); existed {
return host, true return host, true
@ -82,9 +102,12 @@ func (h *ResolverEnhancer) InsertHostByIP(ip netip.Addr, host string) {
} }
func (h *ResolverEnhancer) FlushFakeIP() error { func (h *ResolverEnhancer) FlushFakeIP() error {
if pool := h.fakePool; pool != nil { if pool := h.fakeIPPool; pool != nil {
return pool.FlushFakeIP() return pool.FlushFakeIP()
} }
if pool6 := h.fakeIPPool6; pool6 != nil {
return pool6.FlushFakeIP()
}
return nil return nil
} }
@ -93,36 +116,48 @@ func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) {
o.mapping.CloneTo(h.mapping) o.mapping.CloneTo(h.mapping)
} }
if h.fakePool != nil && o.fakePool != nil { if h.fakeIPPool != nil && o.fakeIPPool != nil {
h.fakePool.CloneFrom(o.fakePool) h.fakeIPPool.CloneFrom(o.fakeIPPool)
}
if h.fakeIPPool6 != nil && o.fakeIPPool6 != nil {
h.fakeIPPool6.CloneFrom(o.fakeIPPool6)
} }
} }
func (h *ResolverEnhancer) StoreFakePoolState() { func (h *ResolverEnhancer) StoreFakePoolState() {
if h.fakePool != nil { if h.fakeIPPool != nil {
h.fakePool.StoreState() h.fakeIPPool.StoreState()
}
if h.fakeIPPool6 != nil {
h.fakeIPPool6.StoreState()
} }
} }
type EnhancerConfig struct { type EnhancerConfig struct {
EnhancedMode C.DNSMode IPv6 bool
Pool *fakeip.Pool EnhancedMode C.DNSMode
UseHosts bool FakeIPPool *fakeip.Pool
FakeIPPool6 *fakeip.Pool
FakeIPSkipper *fakeip.Skipper
UseHosts bool
} }
func NewEnhancer(cfg EnhancerConfig) *ResolverEnhancer { func NewEnhancer(cfg EnhancerConfig) *ResolverEnhancer {
var fakePool *fakeip.Pool e := &ResolverEnhancer{
var mapping *lru.LruCache[netip.Addr, string]
if cfg.EnhancedMode != C.DNSNormal {
fakePool = cfg.Pool
mapping = lru.New(lru.WithSize[netip.Addr, string](4096))
}
return &ResolverEnhancer{
mode: cfg.EnhancedMode, mode: cfg.EnhancedMode,
fakePool: fakePool,
mapping: mapping,
useHosts: cfg.UseHosts, useHosts: cfg.UseHosts,
} }
if cfg.EnhancedMode != C.DNSNormal {
e.fakeIPPool = cfg.FakeIPPool
if cfg.IPv6 {
e.fakeIPPool6 = cfg.FakeIPPool6
}
e.fakeIPSkipper = cfg.FakeIPSkipper
e.mapping = lru.New(lru.WithSize[netip.Addr, string](4096))
}
return e
} }

View File

@ -67,8 +67,7 @@ func withHosts(mapping *lru.LruCache[netip.Addr, string]) middleware {
} else if q.Qtype == D.TypeAAAA { } else if q.Qtype == D.TypeAAAA {
rr := &D.AAAA{} rr := &D.AAAA{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: 10} rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: 10}
ip := ipAddr.As16() rr.AAAA = ipAddr.AsSlice()
rr.AAAA = ip[:]
msg.Answer = append(msg.Answer, rr) msg.Answer = append(msg.Answer, rr)
if mapping != nil { if mapping != nil {
mapping.SetWithExpire(ipAddr, host, time.Now().Add(time.Second*10)) mapping.SetWithExpire(ipAddr, host, time.Now().Add(time.Second*10))
@ -147,29 +146,42 @@ func withMapping(mapping *lru.LruCache[netip.Addr, string]) middleware {
} }
} }
func withFakeIP(fakePool *fakeip.Pool) middleware { func withFakeIP(skipper *fakeip.Skipper, fakePool *fakeip.Pool, fakePool6 *fakeip.Pool) middleware {
return func(next handler) handler { return func(next handler) handler {
return func(ctx *icontext.DNSContext, r *D.Msg) (*D.Msg, error) { return func(ctx *icontext.DNSContext, r *D.Msg) (*D.Msg, error) {
q := r.Question[0] q := r.Question[0]
host := strings.TrimRight(q.Name, ".") host := strings.TrimRight(q.Name, ".")
if fakePool.ShouldSkipped(host) { if skipper.ShouldSkipped(host) {
return next(ctx, r) return next(ctx, r)
} }
var rr D.RR
switch q.Qtype { switch q.Qtype {
case D.TypeAAAA, D.TypeSVCB, D.TypeHTTPS: case D.TypeA:
if fakePool == nil {
return handleMsgWithEmptyAnswer(r), nil
}
ip := fakePool.Lookup(host)
rr = &D.A{
Hdr: D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL},
A: ip.AsSlice(),
}
case D.TypeAAAA:
if fakePool6 == nil {
return handleMsgWithEmptyAnswer(r), nil
}
ip := fakePool6.Lookup(host)
rr = &D.AAAA{
Hdr: D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: dnsDefaultTTL},
AAAA: ip.AsSlice(),
}
case D.TypeSVCB, D.TypeHTTPS:
return handleMsgWithEmptyAnswer(r), nil return handleMsgWithEmptyAnswer(r), nil
} default:
if q.Qtype != D.TypeA {
return next(ctx, r) return next(ctx, r)
} }
rr := &D.A{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
ip := fakePool.Lookup(host)
rr.A = ip.AsSlice()
msg := r.Copy() msg := r.Copy()
msg.Answer = []D.RR{rr} msg.Answer = []D.RR{rr}
@ -226,7 +238,7 @@ func newHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {
} }
if mapper.mode == C.DNSFakeIP { if mapper.mode == C.DNSFakeIP {
middlewares = append(middlewares, withFakeIP(mapper.fakePool)) middlewares = append(middlewares, withFakeIP(mapper.fakeIPSkipper, mapper.fakeIPPool, mapper.fakeIPPool6))
} }
if mapper.mode != C.DNSNormal { if mapper.mode != C.DNSNormal {

View File

@ -261,6 +261,7 @@ dns:
enhanced-mode: fake-ip # or redir-host enhanced-mode: fake-ip # or redir-host
fake-ip-range: 198.18.0.1/16 # fake-ip 池设置 fake-ip-range: 198.18.0.1/16 # fake-ip 池设置
# fake-ip-range6: fdfe:dcba:9876::1/64 # fake-ip6 池设置
# 配置不使用 fake-ip 的域名 # 配置不使用 fake-ip 的域名
fake-ip-filter: fake-ip-filter:

View File

@ -247,10 +247,11 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
return return
} }
ipv6 := c.IPv6 && generalIPv6
r := dns.NewResolver(dns.Config{ r := dns.NewResolver(dns.Config{
Main: c.NameServer, Main: c.NameServer,
Fallback: c.Fallback, Fallback: c.Fallback,
IPv6: c.IPv6 && generalIPv6, IPv6: ipv6,
IPv6Timeout: c.IPv6Timeout, IPv6Timeout: c.IPv6Timeout,
FallbackIPFilter: c.FallbackIPFilter, FallbackIPFilter: c.FallbackIPFilter,
FallbackDomainFilter: c.FallbackDomainFilter, FallbackDomainFilter: c.FallbackDomainFilter,
@ -263,9 +264,12 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
CacheMaxSize: c.CacheMaxSize, CacheMaxSize: c.CacheMaxSize,
}) })
m := dns.NewEnhancer(dns.EnhancerConfig{ m := dns.NewEnhancer(dns.EnhancerConfig{
EnhancedMode: c.EnhancedMode, IPv6: ipv6,
Pool: c.FakeIPRange, EnhancedMode: c.EnhancedMode,
UseHosts: c.UseHosts, FakeIPPool: c.FakeIPPool,
FakeIPPool6: c.FakeIPPool6,
FakeIPSkipper: c.FakeIPSkipper,
UseHosts: c.UseHosts,
}) })
// reuse cache of old host mapper // reuse cache of old host mapper

View File

@ -61,8 +61,6 @@ var (
findProcessMode = atomic.NewInt32Enum(P.FindProcessStrict) findProcessMode = atomic.NewInt32Enum(P.FindProcessStrict)
fakeIPRange netip.Prefix
snifferDispatcher *sniffer.Dispatcher snifferDispatcher *sniffer.Dispatcher
sniffingEnable = false sniffingEnable = false
@ -142,14 +140,6 @@ func Status() TunnelStatus {
return status.Load() return status.Load()
} }
func SetFakeIPRange(p netip.Prefix) {
fakeIPRange = p
}
func FakeIPRange() netip.Prefix {
return fakeIPRange
}
func SetSniffing(b bool) { func SetSniffing(b bool) {
if snifferDispatcher.Enable() { if snifferDispatcher.Enable() {
configMux.Lock() configMux.Lock()
@ -563,7 +553,7 @@ func handleTCPConn(connCtx C.ConnContext) {
dialMetadata := metadata dialMetadata := metadata
if len(metadata.Host) > 0 { if len(metadata.Host) > 0 {
if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok { if node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok {
if dstIp, _ := node.RandIP(); !FakeIPRange().Contains(dstIp) { if dstIp, _ := node.RandIP(); !resolver.IsFakeIP(dstIp) {
dialMetadata.DstIP = dstIp dialMetadata.DstIP = dstIp
dialMetadata.DNSMode = C.DNSHosts dialMetadata.DNSMode = C.DNSHosts
dialMetadata = dialMetadata.Pure() dialMetadata = dialMetadata.Pure()