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
This commit is contained in:
Skyxim 2022-04-16 08:21:31 +08:00
parent 36a719e2f8
commit 80764217c2
4 changed files with 113 additions and 32 deletions

View File

@ -2,6 +2,7 @@ package sniffer
import ( import (
"errors" "errors"
"github.com/Dreamacro/clash/component/trie"
"net" "net"
CN "github.com/Dreamacro/clash/common/net" CN "github.com/Dreamacro/clash/common/net"
@ -12,48 +13,96 @@ import (
var ( var (
ErrorUnsupportedSniffer = errors.New("unsupported sniffer") ErrorUnsupportedSniffer = errors.New("unsupported sniffer")
ErrorSniffFailed = errors.New("all sniffer failed")
) )
var Dispatcher SnifferDispatcher var Dispatcher SnifferDispatcher
type SnifferDispatcher struct { type SnifferDispatcher struct {
enable bool enable bool
force bool force bool
sniffers []C.Sniffer 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) bufConn, ok := conn.(*CN.BufferedConn)
if !ok { if !ok {
return return
} }
if sd.force { sd.tcpHandler(bufConn, metadata)
sd.cover(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 { } else {
if metadata.Host != "" { metadata.DNSMode = C.DNSMapping
return
}
sd.cover(bufConn, metadata)
} }
resolver.InsertHostByIP(metadata.DstIP, host)
metadata.DstIP = nil
} }
func (sd *SnifferDispatcher) Enable() bool { func (sd *SnifferDispatcher) Enable() bool {
return sd.enable 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 { for _, sniffer := range sd.sniffers {
if sniffer.SupportNetwork() == C.TCP { if sniffer.SupportNetwork() == C.TCP {
_, err := conn.Peek(1) _, err := conn.Peek(1)
if err != nil { if err != nil {
return return "", err
} }
bufferedLen := conn.Buffered() bufferedLen := conn.Buffered()
bytes, err := conn.Peek(bufferedLen) bytes, err := conn.Peek(bufferedLen)
if err != nil { if err != nil {
log.Debugln("[Sniffer] the data lenght not enough") log.Debugln("[Sniffer] the data length not enough")
continue 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) log.Debugln("[Sniffer][%s] Sniff data failed %s", sniffer.Protocol(), metadata.DstIP)
continue 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{ dispatcher := SnifferDispatcher{
enable: true, enable: false,
force: force, }
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 { for _, snifferName := range needSniffer {
sniffer, err := NewSniffer(snifferName) sniffer, err := NewSniffer(snifferName)
if err != nil { if err != nil {
log.Errorln("Sniffer name[%s] is error", snifferName) log.Errorln("Sniffer name[%s] is error", snifferName)
return SnifferDispatcher{enable: false}, err return &SnifferDispatcher{enable: false}, err
} }
dispatcher.sniffers = append(dispatcher.sniffers, sniffer) 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) { func NewSniffer(name C.SnifferType) (C.Sniffer, error) {

View File

@ -124,6 +124,7 @@ type Sniffer struct {
Enable bool Enable bool
Force bool Force bool
Sniffers []C.SnifferType Sniffers []C.SnifferType
Reverses trie.DomainTrie[struct{}]
} }
// Experimental config // Experimental config
@ -218,6 +219,7 @@ type SnifferRaw struct {
Enable bool `yaml:"enable" json:"enable"` Enable bool `yaml:"enable" json:"enable"`
Force bool `yaml:"force" json:"force"` Force bool `yaml:"force" json:"force"`
Sniffing []string `yaml:"sniffing" json:"sniffing"` Sniffing []string `yaml:"sniffing" json:"sniffing"`
Reverse []string `yaml:"reverses" json:"reverses"`
} }
// Parse config // Parse config
@ -289,6 +291,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Enable: false, Enable: false,
Force: false, Force: false,
Sniffing: []string{}, Sniffing: []string{},
Reverse: []string{},
}, },
Profile: Profile{ Profile: Profile{
StoreSelected: true, StoreSelected: true,
@ -925,5 +928,12 @@ func parseSniffer(snifferRaw SnifferRaw) (*Sniffer, error) {
sniffer.Sniffers = append(sniffer.Sniffers, st) 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 return sniffer, nil
} }

View File

@ -222,12 +222,21 @@ func updateTun(tun *config.Tun, dns *config.DNS) {
func updateSniffer(sniffer *config.Sniffer) { func updateSniffer(sniffer *config.Sniffer) {
if sniffer.Enable { if sniffer.Enable {
var err error dispatcher, err := SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.Force, &sniffer.Reverses)
SNI.Dispatcher, err = SNI.NewSnifferDispatcher(sniffer.Sniffers, sniffer.Force)
if err != nil { if err != nil {
log.Warnln("initial sniffer failed, err:%v", err) log.Warnln("initial sniffer failed, err:%v", err)
} }
tunnel.UpdateSniffer(dispatcher)
log.Infoln("Sniffer is loaded and working") 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")
} }
} }

View File

@ -91,6 +91,12 @@ func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provid
configMux.Unlock() configMux.Unlock()
} }
func UpdateSniffer(dispatcher *sniffer.SnifferDispatcher) {
configMux.Lock()
sniffer.Dispatcher = *dispatcher
configMux.Unlock()
}
// Mode return current mode // Mode return current mode
func Mode() TunnelMode { func Mode() TunnelMode {
return mode return mode
@ -300,7 +306,7 @@ func handleTCPConn(connCtx C.ConnContext) {
} }
if sniffer.Dispatcher.Enable() { if sniffer.Dispatcher.Enable() {
sniffer.Dispatcher.Tcp(connCtx.Conn(), metadata) sniffer.Dispatcher.TCPSniff(connCtx.Conn(), metadata)
} }
proxy, rule, err := resolveMetadata(connCtx, metadata) proxy, rule, err := resolveMetadata(connCtx, metadata)