From c7fc93df3757207f57fae47419087b2f902b6a88 Mon Sep 17 00:00:00 2001 From: laburaps Date: Thu, 12 Dec 2024 19:02:34 +0800 Subject: [PATCH] fix: the TLS Sniffer fails when the length of the ClientHello packet exceeds the TCP MSS (#1711) * chore: add uniformly formatted debug info to sniffDomain * fix: when data is not enough, attempt to peek more data and retry * chore: reduce debug info of sniffDomain --- component/sniffer/dispatcher.go | 26 ++++++++++++++++++++++---- component/sniffer/tls_sniffer.go | 19 ++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index f198c4ae..ada43176 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -145,6 +145,10 @@ func (sd *Dispatcher) Enable() bool { } func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) { + //defer func(start time.Time) { + // log.Debugln("[Sniffer] [%s] Sniffing took %s", metadata.DstIP, time.Since(start)) + //}(time.Now()) + for s := range sd.sniffers { if s.SupportNetwork() == C.TCP && s.SupportPort(metadata.DstPort) { _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) @@ -154,7 +158,7 @@ func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (s _, ok := err.(*net.OpError) if ok { sd.cacheSniffFailed(metadata) - log.Errorln("[Sniffer] [%s] may not have any sent data, Consider adding skip", metadata.DstIP.String()) + log.Errorln("[Sniffer] [%s] [%s] may not have any sent data, Consider adding skip", metadata.DstIP, s.Protocol()) _ = conn.Close() } @@ -164,22 +168,36 @@ func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (s bufferedLen := conn.Buffered() bytes, err := conn.Peek(bufferedLen) if err != nil { - log.Debugln("[Sniffer] the data length not enough") + log.Debugln("[Sniffer] [%s] [%s] the data length not enough, error: %v", metadata.DstIP, s.Protocol(), err) continue } host, err := s.SniffData(bytes) + var e *errNeedAtLeastData + if errors.As(err, &e) { + //log.Debugln("[Sniffer] [%s] [%s] %v, got length: %d", metadata.DstIP, s.Protocol(), e, len(bytes)) + _ = conn.SetReadDeadline(time.Now().Add(1 * time.Second)) + bytes, err = conn.Peek(e.length) + _ = conn.SetReadDeadline(time.Time{}) + //log.Debugln("[Sniffer] [%s] [%s] try again, got length: %d", metadata.DstIP, s.Protocol(), len(bytes)) + if err != nil { + log.Debugln("[Sniffer] [%s] [%s] the data length not enough, error: %v", metadata.DstIP, s.Protocol(), err) + continue + } + host, err = s.SniffData(bytes) + } if err != nil { - //log.Debugln("[Sniffer] [%s] Sniff data failed %s", s.Protocol(), metadata.DstIP) + //log.Debugln("[Sniffer] [%s] [%s] Sniff data failed, error: %v", metadata.DstIP, s.Protocol(), err) continue } _, err = netip.ParseAddr(host) if err == nil { - //log.Debugln("[Sniffer] [%s] Sniff data failed %s", s.Protocol(), metadata.DstIP) + //log.Debugln("[Sniffer] [%s] [%s] Sniff data failed, got host [%s]", metadata.DstIP, s.Protocol(), host) continue } + //log.Debugln("[Sniffer] [%s] [%s] Sniffed [%s]", metadata.DstIP, s.Protocol(), host) return host, nil } } diff --git a/component/sniffer/tls_sniffer.go b/component/sniffer/tls_sniffer.go index 974df79a..b57f36ec 100644 --- a/component/sniffer/tls_sniffer.go +++ b/component/sniffer/tls_sniffer.go @@ -3,6 +3,7 @@ package sniffer import ( "encoding/binary" "errors" + "fmt" "strings" "github.com/metacubex/mihomo/common/utils" @@ -15,6 +16,19 @@ var ( errNotClientHello = errors.New("not client hello") ) +type errNeedAtLeastData struct { + length int + err error +} + +func (e *errNeedAtLeastData) Error() string { + return fmt.Sprintf("%v, need at least length: %d", e.err, e.length) +} + +func (e *errNeedAtLeastData) Unwrap() error { + return e.err +} + var _ sniffer.Sniffer = (*TLSSniffer)(nil) type TLSSniffer struct { @@ -160,7 +174,10 @@ func SniffTLS(b []byte) (*string, error) { } headerLen := int(binary.BigEndian.Uint16(b[3:5])) if 5+headerLen > len(b) { - return nil, ErrNoClue + return nil, &errNeedAtLeastData{ + length: 5 + headerLen, + err: ErrNoClue, + } } domain, err := ReadClientHello(b[5 : 5+headerLen])