diff --git a/component/sniffer/base_sniffer.go b/component/sniffer/base_sniffer.go index 55f51c5003..04e8bc6386 100644 --- a/component/sniffer/base_sniffer.go +++ b/component/sniffer/base_sniffer.go @@ -9,8 +9,9 @@ import ( ) type SnifferConfig struct { - OverrideDest bool - Ports utils.IntRanges[uint16] + OverrideDest bool + Ports utils.IntRanges[uint16] + SkipThreshold uint8 // max sniff-failure count before a dst is skipped (255 = never skip) } type BaseSniffer struct { diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 1dafcf1f27..3a8c02d73c 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -2,6 +2,7 @@ package sniffer import ( "errors" + "math" "net" "net/netip" "time" @@ -106,11 +107,15 @@ func (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool if sd.shouldOverride(metadata) { inWhitelist := false overrideDest := false + skipThreshold := uint8(5) for sniffer, config := range sd.sniffers { if sniffer.SupportNetwork() == C.TCP || sniffer.SupportNetwork() == C.ALLNet { inWhitelist = sniffer.SupportPort(metadata.DstPort) if inWhitelist { overrideDest = config.OverrideDest + if config.SkipThreshold > 0 { + skipThreshold = config.SkipThreshold + } break } } @@ -123,7 +128,7 @@ func (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool dst := metadata.AddrPort() if !forceSniffer { - if count, ok := sd.skipList.Get(dst); ok && count > 5 { + if count, ok := sd.skipList.Get(dst); ok && count > skipThreshold { log.Debugln("[Sniffer] Skip sniffing[%s] due to multiple failures", dst) return false } @@ -245,7 +250,7 @@ func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (s func (sd *Dispatcher) cacheSniffFailed(metadata *C.Metadata) { dst := metadata.AddrPort() sd.skipList.Compute(dst, func(oldValue uint8, loaded bool) (newValue uint8, delete bool) { - if oldValue <= 5 { + if oldValue < math.MaxUint8 { oldValue++ } return oldValue, false diff --git a/config/config.go b/config/config.go index 8959dda922..6d90cb840f 100644 --- a/config/config.go +++ b/config/config.go @@ -378,13 +378,15 @@ type RawSniffer struct { Ports []string `yaml:"port-whitelist" json:"port-whitelist"` ForceDnsMapping bool `yaml:"force-dns-mapping" json:"force-dns-mapping"` ParsePureIp bool `yaml:"parse-pure-ip" json:"parse-pure-ip"` + SkipThreshold int `yaml:"skip-threshold" json:"skip-threshold"` Sniff map[string]RawSniffingConfig `yaml:"sniff" json:"sniff"` } type RawSniffingConfig struct { - Ports []string `yaml:"ports" json:"ports"` - OverrideDest *bool `yaml:"override-destination" json:"override-destination"` + Ports []string `yaml:"ports" json:"ports"` + OverrideDest *bool `yaml:"override-destination" json:"override-destination"` + SkipThreshold *int `yaml:"skip-threshold" json:"skip-threshold"` } type RawTLS struct { @@ -590,6 +592,7 @@ func DefaultRawConfig() *RawConfig { ForceDnsMapping: true, ParsePureIp: true, OverrideDest: true, + SkipThreshold: 5, }, ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip", ExternalControllerCors: RawCors{ @@ -1770,12 +1773,17 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]P.RuleProvider if sniffConfig.OverrideDest != nil { overrideDest = *sniffConfig.OverrideDest } + skipThreshold := snifferRaw.SkipThreshold + if sniffConfig.SkipThreshold != nil { + skipThreshold = *sniffConfig.SkipThreshold + } for _, snifferType := range snifferTypes.List { if snifferType.String() == strings.ToUpper(sniffType) { find = true loadSniffer[snifferType] = sniffer.SnifferConfig{ - Ports: ports, - OverrideDest: overrideDest, + Ports: ports, + OverrideDest: overrideDest, + SkipThreshold: uint8(skipThreshold), } } } @@ -1800,8 +1808,9 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]P.RuleProvider if snifferType.String() == strings.ToUpper(snifferName) { find = true loadSniffer[snifferType] = sniffer.SnifferConfig{ - Ports: globalPorts, - OverrideDest: snifferRaw.OverrideDest, + Ports: globalPorts, + OverrideDest: snifferRaw.OverrideDest, + SkipThreshold: uint8(snifferRaw.SkipThreshold), } } } diff --git a/docs/config.yaml b/docs/config.yaml index bbe2ec7162..c333733ce1 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -201,6 +201,10 @@ sniffer: # 是否使用嗅探结果作为实际访问,默认 true # 全局配置,优先级低于 sniffer.sniff 实际配置 override-destination: false + # 同一目标嗅探失败超过该次数后,在缓存有效期内跳过嗅探,默认 5 + # 取值 uint8(0-255),设为 255(-1) 表示永不跳过 + # 全局配置,优先级低于 sniffer.sniff 实际配置 + # skip-threshold: 5 sniff: # TLS 和 QUIC 默认如果不配置 ports 默认嗅探 443 QUIC: # ports: [ 443 ] @@ -212,6 +216,8 @@ sniffer: ports: [80, 8080-8880] # 可覆盖 sniffer.override-destination override-destination: true + # 可覆盖 sniffer.skip-threshold + # skip-threshold: 5 force-domain: - +.v2ex.com # skip-src-address: # 对于来源ip跳过嗅探