From 6c56a3b80ef9d013ca267d9b97d395cf4af66bfb Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Sat, 16 Apr 2022 08:21:31 +0800 Subject: [PATCH] feat: add domain list for sniffer, reverse force logic when force is false, if domain in the list, will force replace when force is true, if sniff domain in the list, will skip it --- component/sniffer/dispatcher.go | 114 ++++++++++++++++++++++++-------- config/config.go | 10 +++ hub/executor/executor.go | 13 +++- tunnel/tunnel.go | 8 ++- 4 files changed, 113 insertions(+), 32 deletions(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index fa5f0c44..11d255bd 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -2,6 +2,7 @@ package sniffer import ( "errors" + "github.com/Dreamacro/clash/component/trie" "net" CN "github.com/Dreamacro/clash/common/net" @@ -12,48 +13,96 @@ import ( var ( ErrorUnsupportedSniffer = errors.New("unsupported sniffer") + ErrorSniffFailed = errors.New("all sniffer failed") ) var Dispatcher SnifferDispatcher type SnifferDispatcher struct { - enable bool - force bool - sniffers []C.Sniffer + enable bool + force bool + sniffers []C.Sniffer + reverseDomainTree *trie.DomainTrie[struct{}] + tcpHandler func(conn *CN.BufferedConn, metadata *C.Metadata) } -func (sd *SnifferDispatcher) Tcp(conn net.Conn, metadata *C.Metadata) { +func (sd *SnifferDispatcher) forceReplace(conn *CN.BufferedConn, metadata *C.Metadata) { + host, err := sd.sniffDomain(conn, metadata) + if err != nil { + log.Debugln("[Sniffer]All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.DstIP, metadata.DstPort) + return + } else { + if sd.force && sd.inReverse(host) { + log.Debugln("[Sniffer]Skip replace host:%s", host) + return + } + } + + sd.replaceDomain(metadata, host) +} + +func (sd *SnifferDispatcher) replace(conn *CN.BufferedConn, metadata *C.Metadata) { + if metadata.Host != "" && sd.inReverse(metadata.Host) { + log.Debugln("[Sniffer]Skip Sniff domain:%s", metadata.Host) + return + } + + host, err := sd.sniffDomain(conn, metadata) + if err != nil { + log.Debugln("[Sniffer]All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.DstIP, metadata.DstPort) + return + } + + sd.replaceDomain(metadata, host) +} + +func (sd *SnifferDispatcher) TCPSniff(conn net.Conn, metadata *C.Metadata) { bufConn, ok := conn.(*CN.BufferedConn) if !ok { return } - if sd.force { - sd.cover(bufConn, metadata) + sd.tcpHandler(bufConn, metadata) +} + +func (sd *SnifferDispatcher) inReverse(host string) bool { + return sd.reverseDomainTree != nil && sd.reverseDomainTree.Search(host) != nil +} + +func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string) { + log.Debugln("[Sniffer]Sniff TCP [%s:%s]-->[%s:%s] success, replace domain [%s]-->[%s]", + metadata.SrcIP, metadata.SrcPort, + metadata.DstIP, metadata.DstPort, + metadata.Host, host) + + metadata.AddrType = C.AtypDomainName + metadata.Host = host + if resolver.FakeIPEnabled() { + metadata.DNSMode = C.DNSFakeIP } else { - if metadata.Host != "" { - return - } - sd.cover(bufConn, metadata) + metadata.DNSMode = C.DNSMapping } + + resolver.InsertHostByIP(metadata.DstIP, host) + metadata.DstIP = nil } func (sd *SnifferDispatcher) Enable() bool { return sd.enable } -func (sd *SnifferDispatcher) cover(conn *CN.BufferedConn, metadata *C.Metadata) { +func (sd *SnifferDispatcher) sniffDomain(conn *CN.BufferedConn, metadata *C.Metadata) (string, error) { for _, sniffer := range sd.sniffers { if sniffer.SupportNetwork() == C.TCP { _, err := conn.Peek(1) if err != nil { - return + return "", err } bufferedLen := conn.Buffered() bytes, err := conn.Peek(bufferedLen) if err != nil { - log.Debugln("[Sniffer] the data lenght not enough") + log.Debugln("[Sniffer] the data length not enough") continue } @@ -62,39 +111,46 @@ func (sd *SnifferDispatcher) cover(conn *CN.BufferedConn, metadata *C.Metadata) log.Debugln("[Sniffer][%s] Sniff data failed %s", sniffer.Protocol(), metadata.DstIP) continue } - metadata.Host = host - metadata.AddrType = C.AtypDomainName - log.Debugln("[Sniffer][%s] %s --> %s", sniffer.Protocol(), metadata.DstIP, metadata.Host) - if resolver.FakeIPEnabled() { - metadata.DNSMode = C.DNSFakeIP - } else { - metadata.DNSMode = C.DNSMapping - } - resolver.InsertHostByIP(metadata.DstIP, host) - metadata.DstIP = nil - break + return host, nil } } + + return "", ErrorSniffFailed } -func NewSnifferDispatcher(needSniffer []C.SnifferType, force bool) (SnifferDispatcher, error) { +func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) { dispatcher := SnifferDispatcher{ - enable: true, - force: force, + enable: false, + } + + return &dispatcher, nil +} + +func NewSnifferDispatcher(needSniffer []C.SnifferType, force bool, reverses *trie.DomainTrie[struct{}]) (*SnifferDispatcher, error) { + dispatcher := SnifferDispatcher{ + enable: true, + force: force, + reverseDomainTree: reverses, } for _, snifferName := range needSniffer { sniffer, err := NewSniffer(snifferName) if err != nil { log.Errorln("Sniffer name[%s] is error", snifferName) - return SnifferDispatcher{enable: false}, err + return &SnifferDispatcher{enable: false}, err } dispatcher.sniffers = append(dispatcher.sniffers, sniffer) } - return dispatcher, nil + if force { + dispatcher.tcpHandler = dispatcher.forceReplace + } else { + dispatcher.tcpHandler = dispatcher.replace + } + + return &dispatcher, nil } func NewSniffer(name C.SnifferType) (C.Sniffer, error) { diff --git a/config/config.go b/config/config.go index 677b2238..18099a27 100644 --- a/config/config.go +++ b/config/config.go @@ -124,6 +124,7 @@ type Sniffer struct { Enable bool Force bool Sniffers []C.SnifferType + Reverses trie.DomainTrie[struct{}] } // Experimental config @@ -218,6 +219,7 @@ type SnifferRaw struct { Enable bool `yaml:"enable" json:"enable"` Force bool `yaml:"force" json:"force"` Sniffing []string `yaml:"sniffing" json:"sniffing"` + Reverse []string `yaml:"reverses" json:"reverses"` } // Parse config @@ -289,6 +291,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { Enable: false, Force: false, Sniffing: []string{}, + Reverse: []string{}, }, Profile: Profile{ StoreSelected: true, @@ -925,5 +928,12 @@ func parseSniffer(snifferRaw SnifferRaw) (*Sniffer, error) { sniffer.Sniffers = append(sniffer.Sniffers, st) } + for _, domain := range snifferRaw.Reverse { + err := sniffer.Reverses.Insert(domain, struct{}{}) + if err != nil { + return nil, fmt.Errorf("error domian[%s], error:%v", domain, err) + } + } + return sniffer, nil } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index b9785c6c..92984ff1 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -222,12 +222,21 @@ func updateTun(tun *config.Tun, dns *config.DNS) { func updateSniffer(sniffer *config.Sniffer) { if sniffer.Enable { - var err error - SNI.Dispatcher, err = SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.Force) + dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.Force, &sniffer.Reverses) if err != nil { log.Warnln("initial sniffer failed, err:%v", err) } + + tunnel.UpdateSniffer(dispatcher) log.Infoln("Sniffer is loaded and working") + } else { + dispatcher, err := SNI.NewCloseSnifferDispatcher() + if err != nil { + log.Warnln("initial sniffer failed, err:%v", err) + } + + tunnel.UpdateSniffer(dispatcher) + log.Infoln("Sniffer is closed") } } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index b7d8fd69..efacea01 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -91,6 +91,12 @@ func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provid configMux.Unlock() } +func UpdateSniffer(dispatcher *sniffer.SnifferDispatcher) { + configMux.Lock() + sniffer.Dispatcher = *dispatcher + configMux.Unlock() +} + // Mode return current mode func Mode() TunnelMode { return mode @@ -300,7 +306,7 @@ func handleTCPConn(connCtx C.ConnContext) { } if sniffer.Dispatcher.Enable() { - sniffer.Dispatcher.Tcp(connCtx.Conn(), metadata) + sniffer.Dispatcher.TCPSniff(connCtx.Conn(), metadata) } proxy, rule, err := resolveMetadata(connCtx, metadata)