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 (
"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) {

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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)