mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-11-16 11:42:43 +08:00
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:
parent
36a719e2f8
commit
80764217c2
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user