github.com/yaling888/clash@v1.53.0/tunnel/statistic/sniffing.go (about) 1 package statistic 2 3 import ( 4 "errors" 5 "strings" 6 7 "github.com/phuslu/log" 8 "go.uber.org/atomic" 9 10 "github.com/yaling888/clash/common/snifer/tls" 11 "github.com/yaling888/clash/component/resolver" 12 C "github.com/yaling888/clash/constant" 13 ) 14 15 type sniffing struct { 16 C.Conn 17 18 metadata *C.Metadata 19 totalWrite *atomic.Uint64 20 allowBreak bool 21 } 22 23 func (r *sniffing) Read(b []byte) (int, error) { 24 return r.Conn.Read(b) 25 } 26 27 func (r *sniffing) Write(b []byte) (int, error) { 28 if r.totalWrite.Load() < 128 && r.metadata.Host == "" && 29 (r.metadata.DstPort == 443 || r.metadata.DstPort == 8443 || r.metadata.DstPort == 993 || 30 r.metadata.DstPort == 465 || r.metadata.DstPort == 995) { 31 header, err := tls.SniffTLS(b) 32 if err == nil && strings.Index(header.Domain(), ".") > 0 { 33 log.Debug(). 34 Str("host", header.Domain()). 35 NetIPAddr("ip", r.metadata.DstIP). 36 Msg("[Sniffer] update sni") 37 38 resolver.InsertHostByIP(r.metadata.DstIP, header.Domain()) 39 40 if r.allowBreak { 41 _ = r.Conn.Close() 42 return 0, errors.New("sni update, break current link to avoid leaks") 43 } else { 44 r.metadata.Host = header.Domain() 45 } 46 } 47 } 48 49 n, err := r.Conn.Write(b) 50 r.totalWrite.Add(uint64(n)) 51 52 return n, err 53 } 54 55 func (r *sniffing) Close() error { 56 return r.Conn.Close() 57 } 58 59 func NewSniffing(conn C.Conn, metadata *C.Metadata, rule C.Rule) C.Conn { 60 return &sniffing{ 61 Conn: conn, 62 metadata: metadata, 63 totalWrite: atomic.NewUint64(0), 64 allowBreak: rule != nil, 65 } 66 }