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 (
|
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) {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user