From 24ce6622a2960cdc6f84e4dde2ae0a74570415a4 Mon Sep 17 00:00:00 2001
From: fishg <1423545+fishg@users.noreply.github.com>
Date: Wed, 30 Mar 2022 23:54:52 +0800
Subject: [PATCH 1/5] Feature: add tls SNI sniffing (#68)

---
 common/snifer/tls/sniff.go      | 148 +++++++++++++++++++++++++++++
 common/snifer/tls/sniff_test.go | 159 ++++++++++++++++++++++++++++++++
 component/resolver/enhancer.go  |   7 ++
 dns/enhancer.go                 |   6 ++
 tunnel/statistic/tracker.go     |  17 ++++
 5 files changed, 337 insertions(+)
 create mode 100644 common/snifer/tls/sniff.go
 create mode 100644 common/snifer/tls/sniff_test.go

diff --git a/common/snifer/tls/sniff.go b/common/snifer/tls/sniff.go
new file mode 100644
index 00000000..1471fc68
--- /dev/null
+++ b/common/snifer/tls/sniff.go
@@ -0,0 +1,148 @@
+package tls
+
+import (
+	"encoding/binary"
+	"errors"
+	"strings"
+)
+
+var ErrNoClue = errors.New("not enough information for making a decision")
+
+type SniffHeader struct {
+	domain string
+}
+
+func (h *SniffHeader) Protocol() string {
+	return "tls"
+}
+
+func (h *SniffHeader) Domain() string {
+	return h.domain
+}
+
+var (
+	errNotTLS         = errors.New("not TLS header")
+	errNotClientHello = errors.New("not client hello")
+)
+
+func IsValidTLSVersion(major, minor byte) bool {
+	return major == 3
+}
+
+// ReadClientHello returns server name (if any) from TLS client hello message.
+// https://github.com/golang/go/blob/master/src/crypto/tls/handshake_messages.go#L300
+func ReadClientHello(data []byte, h *SniffHeader) error {
+	if len(data) < 42 {
+		return ErrNoClue
+	}
+	sessionIDLen := int(data[38])
+	if sessionIDLen > 32 || len(data) < 39+sessionIDLen {
+		return ErrNoClue
+	}
+	data = data[39+sessionIDLen:]
+	if len(data) < 2 {
+		return ErrNoClue
+	}
+	// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
+	// they are uint16s, the number must be even.
+	cipherSuiteLen := int(data[0])<<8 | int(data[1])
+	if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
+		return errNotClientHello
+	}
+	data = data[2+cipherSuiteLen:]
+	if len(data) < 1 {
+		return ErrNoClue
+	}
+	compressionMethodsLen := int(data[0])
+	if len(data) < 1+compressionMethodsLen {
+		return ErrNoClue
+	}
+	data = data[1+compressionMethodsLen:]
+
+	if len(data) == 0 {
+		return errNotClientHello
+	}
+	if len(data) < 2 {
+		return errNotClientHello
+	}
+
+	extensionsLength := int(data[0])<<8 | int(data[1])
+	data = data[2:]
+	if extensionsLength != len(data) {
+		return errNotClientHello
+	}
+
+	for len(data) != 0 {
+		if len(data) < 4 {
+			return errNotClientHello
+		}
+		extension := uint16(data[0])<<8 | uint16(data[1])
+		length := int(data[2])<<8 | int(data[3])
+		data = data[4:]
+		if len(data) < length {
+			return errNotClientHello
+		}
+
+		if extension == 0x00 { /* extensionServerName */
+			d := data[:length]
+			if len(d) < 2 {
+				return errNotClientHello
+			}
+			namesLen := int(d[0])<<8 | int(d[1])
+			d = d[2:]
+			if len(d) != namesLen {
+				return errNotClientHello
+			}
+			for len(d) > 0 {
+				if len(d) < 3 {
+					return errNotClientHello
+				}
+				nameType := d[0]
+				nameLen := int(d[1])<<8 | int(d[2])
+				d = d[3:]
+				if len(d) < nameLen {
+					return errNotClientHello
+				}
+				if nameType == 0 {
+					serverName := string(d[:nameLen])
+					// An SNI value may not include a
+					// trailing dot. See
+					// https://tools.ietf.org/html/rfc6066#section-3.
+					if strings.HasSuffix(serverName, ".") {
+						return errNotClientHello
+					}
+					h.domain = serverName
+					return nil
+				}
+				d = d[nameLen:]
+			}
+		}
+		data = data[length:]
+	}
+
+	return errNotTLS
+}
+
+func SniffTLS(b []byte) (*SniffHeader, error) {
+	if len(b) < 5 {
+		return nil, ErrNoClue
+	}
+
+	if b[0] != 0x16 /* TLS Handshake */ {
+		return nil, errNotTLS
+	}
+	if !IsValidTLSVersion(b[1], b[2]) {
+		return nil, errNotTLS
+	}
+	headerLen := int(binary.BigEndian.Uint16(b[3:5]))
+	if 5+headerLen > len(b) {
+		return nil, ErrNoClue
+	}
+
+	h := &SniffHeader{}
+	err := ReadClientHello(b[5:5+headerLen], h)
+	if err == nil {
+		return h, nil
+	}
+	return nil, err
+}
diff --git a/common/snifer/tls/sniff_test.go b/common/snifer/tls/sniff_test.go
new file mode 100644
index 00000000..26f5f1ee
--- /dev/null
+++ b/common/snifer/tls/sniff_test.go
@@ -0,0 +1,159 @@
+package tls
+
+import (
+	"testing"
+)
+
+func TestTLSHeaders(t *testing.T) {
+	cases := []struct {
+		input  []byte
+		domain string
+		err    bool
+	}{
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x00, 0xc8, 0x01, 0x00, 0x00,
+				0xc4, 0x03, 0x03, 0x1a, 0xac, 0xb2, 0xa8, 0xfe,
+				0xb4, 0x96, 0x04, 0x5b, 0xca, 0xf7, 0xc1, 0xf4,
+				0x2e, 0x53, 0x24, 0x6e, 0x34, 0x0c, 0x58, 0x36,
+				0x71, 0x97, 0x59, 0xe9, 0x41, 0x66, 0xe2, 0x43,
+				0xa0, 0x13, 0xb6, 0x00, 0x00, 0x20, 0x1a, 0x1a,
+				0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
+				0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
+				0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
+				0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
+				0x00, 0x7b, 0xba, 0xba, 0x00, 0x00, 0xff, 0x01,
+				0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+				0x14, 0x00, 0x00, 0x11, 0x63, 0x2e, 0x73, 0x2d,
+				0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+				0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17, 0x00,
+				0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
+				0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,
+				0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08,
+				0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00,
+				0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
+				0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c,
+				0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70,
+				0x2f, 0x31, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x02,
+				0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08,
+				0xaa, 0xaa, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,
+				0xaa, 0xaa, 0x00, 0x01, 0x00,
+			},
+			domain: "c.s-microsoft.com",
+			err:    false,
+		},
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x00, 0xee, 0x01, 0x00, 0x00,
+				0xea, 0x03, 0x03, 0xe7, 0x91, 0x9e, 0x93, 0xca,
+				0x78, 0x1b, 0x3c, 0xe0, 0x65, 0x25, 0x58, 0xb5,
+				0x93, 0xe1, 0x0f, 0x85, 0xec, 0x9a, 0x66, 0x8e,
+				0x61, 0x82, 0x88, 0xc8, 0xfc, 0xae, 0x1e, 0xca,
+				0xd7, 0xa5, 0x63, 0x20, 0xbd, 0x1c, 0x00, 0x00,
+				0x8b, 0xee, 0x09, 0xe3, 0x47, 0x6a, 0x0e, 0x74,
+				0xb0, 0xbc, 0xa3, 0x02, 0xa7, 0x35, 0xe8, 0x85,
+				0x70, 0x7c, 0x7a, 0xf0, 0x00, 0xdf, 0x4a, 0xea,
+				0x87, 0x01, 0x14, 0x91, 0x00, 0x20, 0xea, 0xea,
+				0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
+				0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
+				0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
+				0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
+				0x00, 0x81, 0x9a, 0x9a, 0x00, 0x00, 0xff, 0x01,
+				0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
+				0x16, 0x00, 0x00, 0x13, 0x77, 0x77, 0x77, 0x30,
+				0x37, 0x2e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x74,
+				0x61, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x00,
+				0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+				0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08,
+				0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05,
+				0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00,
+				0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,
+				0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74,
+				0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x75, 0x50,
+				0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+				0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a,
+				0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x8a, 0x8a,
+				0x00, 0x01, 0x00,
+			},
+			domain: "www07.clicktale.net",
+			err:    false,
+		},
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe2, 0x03, 0x03, 0x81, 0x47, 0xc1,
+				0x66, 0xd5, 0x1b, 0xfa, 0x4b, 0xb5, 0xe0, 0x2a, 0xe1, 0xa7, 0x87, 0x13, 0x1d, 0x11, 0xaa, 0xc6,
+				0xce, 0xfc, 0x7f, 0xab, 0x94, 0xc8, 0x62, 0xad, 0xc8, 0xab, 0x0c, 0xdd, 0xcb, 0x20, 0x6f, 0x9d,
+				0x07, 0xf1, 0x95, 0x3e, 0x99, 0xd8, 0xf3, 0x6d, 0x97, 0xee, 0x19, 0x0b, 0x06, 0x1b, 0xf4, 0x84,
+				0x0b, 0xb6, 0x8f, 0xcc, 0xde, 0xe2, 0xd0, 0x2d, 0x6b, 0x0c, 0x1f, 0x52, 0x53, 0x13, 0x00, 0x08,
+				0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0x00, 0xff, 0x01, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x0c,
+				0x00, 0x0a, 0x00, 0x00, 0x07, 0x64, 0x6f, 0x67, 0x66, 0x69, 0x73, 0x68, 0x00, 0x0b, 0x00, 0x04,
+				0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e,
+				0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
+				0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08,
+				0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
+				0x06, 0x01, 0x00, 0x2b, 0x00, 0x07, 0x06, 0x7f, 0x1c, 0x7f, 0x1b, 0x7f, 0x1a, 0x00, 0x2d, 0x00,
+				0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2f, 0x35, 0x0c,
+				0xb6, 0x90, 0x0a, 0xb7, 0xd5, 0xc4, 0x1b, 0x2f, 0x60, 0xaa, 0x56, 0x7b, 0x3f, 0x71, 0xc8, 0x01,
+				0x7e, 0x86, 0xd3, 0xb7, 0x0c, 0x29, 0x1a, 0x9e, 0x5b, 0x38, 0x3f, 0x01, 0x72,
+			},
+			domain: "dogfish",
+			err:    false,
+		},
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00,
+				0xff, 0x03, 0x03, 0x3d, 0x89, 0x52, 0x9e, 0xee,
+				0xbe, 0x17, 0x63, 0x75, 0xef, 0x29, 0xbd, 0x14,
+				0x6a, 0x49, 0xe0, 0x2c, 0x37, 0x57, 0x71, 0x62,
+				0x82, 0x44, 0x94, 0x8f, 0x6e, 0x94, 0x08, 0x45,
+				0x7f, 0xdb, 0xc1, 0x00, 0x00, 0x3e, 0xc0, 0x2c,
+				0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8,
+				0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e,
+				0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23,
+				0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14,
+				0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33,
+				0x00, 0x9d, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03,
+				0x13, 0x01, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35,
+				0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x00, 0x98,
+				0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00,
+				0x0b, 0x31, 0x30, 0x2e, 0x34, 0x32, 0x2e, 0x30,
+				0x2e, 0x32, 0x34, 0x33, 0x00, 0x0b, 0x00, 0x04,
+				0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0a,
+				0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19,
+				0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d,
+				0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03,
+				0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06,
+				0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03,
+				0x02, 0x01, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02,
+				0x06, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
+				0x00, 0x00, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x7f,
+				0x14, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00,
+				0x2d, 0x00, 0x03, 0x02, 0x01, 0x00, 0x00, 0x28,
+				0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
+				0x13, 0x7c, 0x6e, 0x97, 0xc4, 0xfd, 0x09, 0x2e,
+				0x70, 0x2f, 0x73, 0x5a, 0x9b, 0x57, 0x4d, 0x5f,
+				0x2b, 0x73, 0x2c, 0xa5, 0x4a, 0x98, 0x40, 0x3d,
+				0x75, 0x6e, 0xb4, 0x76, 0xf9, 0x48, 0x8f, 0x36,
+			},
+			domain: "10.42.0.243",
+			err:    false,
+		},
+	}
+
+	for _, test := range cases {
+		header, err := SniffTLS(test.input)
+		if test.err {
+			if err == nil {
+				t.Errorf("Exepct error but nil in test %v", test)
+			}
+		} else {
+			if err != nil {
+				t.Errorf("Expect no error but actually %s in test %v", err.Error(), test)
+			}
+			if header.Domain() != test.domain {
+				t.Error("expect domain ", test.domain, " but got ", header.Domain())
+			}
+		}
+	}
+}
diff --git a/component/resolver/enhancer.go b/component/resolver/enhancer.go
index 9df3f54b..77f18374 100644
--- a/component/resolver/enhancer.go
+++ b/component/resolver/enhancer.go
@@ -14,6 +14,7 @@ type Enhancer interface {
 	IsExistFakeIP(net.IP) bool
 	FindHostByIP(net.IP) (string, bool)
 	FlushFakeIP() error
+	InsertHostByIP(net.IP, string)
 }
 
 func FakeIPEnabled() bool {
@@ -56,6 +57,12 @@ func IsExistFakeIP(ip net.IP) bool {
 	return false
 }
 
+func InsertHostByIP(ip net.IP, host string) {
+	if mapper := DefaultHostMapper; mapper != nil {
+		mapper.InsertHostByIP(ip, host)
+	}
+}
+
 func FindHostByIP(ip net.IP) (string, bool) {
 	if mapper := DefaultHostMapper; mapper != nil {
 		return mapper.FindHostByIP(ip)
diff --git a/dns/enhancer.go b/dns/enhancer.go
index 9bf568c7..016ff02a 100644
--- a/dns/enhancer.go
+++ b/dns/enhancer.go
@@ -74,6 +74,12 @@ func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
 	return "", false
 }
 
+func (h *ResolverEnhancer) InsertHostByIP(ip net.IP, host string) {
+	if mapping := h.mapping; mapping != nil {
+		h.mapping.Set(ip.String(), host)
+	}
+}
+
 func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) {
 	if h.mapping != nil && o.mapping != nil {
 		o.mapping.CloneTo(h.mapping)
diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go
index 1f5f1f9c..f213ca61 100644
--- a/tunnel/statistic/tracker.go
+++ b/tunnel/statistic/tracker.go
@@ -1,10 +1,14 @@
 package statistic
 
 import (
+	"errors"
 	"net"
 	"time"
 
+	"github.com/Dreamacro/clash/common/snifer/tls"
+	"github.com/Dreamacro/clash/component/resolver"
 	C "github.com/Dreamacro/clash/constant"
+	"github.com/Dreamacro/clash/log"
 
 	"github.com/gofrs/uuid"
 	"go.uber.org/atomic"
@@ -48,7 +52,20 @@ func (tt *tcpTracker) Write(b []byte) (int, error) {
 	n, err := tt.Conn.Write(b)
 	upload := int64(n)
 	tt.manager.PushUploaded(upload)
+	if tt.UploadTotal.Load() < 128 && tt.Metadata.Host == "" && (tt.Metadata.DstPort == "443" || tt.Metadata.DstPort == "8443") {
+		header, err := tls.SniffTLS(b)
+		if err != nil {
+			// log.Errorln("Expect no error but actually %s %s:%s:%s", err.Error(), tt.Metadata.Host, tt.Metadata.DstIP.String(), tt.Metadata.DstPort)
+		} else {
+			resolver.InsertHostByIP(tt.Metadata.DstIP, header.Domain())
+			log.Warnln("use sni update host: %s ip: %s", header.Domain(), tt.Metadata.DstIP.String())
+			tt.manager.Leave(tt)
+			tt.Conn.Close()
+			return n, errors.New("sni update, break current link to avoid leaks")
+		}
+	}
 	tt.UploadTotal.Add(upload)
+
 	return n, err
 }
 

From e877b68179a735e5743a732b04e0de1dba238267 Mon Sep 17 00:00:00 2001
From: yaling888 <73897884+yaling888@users.noreply.github.com>
Date: Thu, 31 Mar 2022 21:20:46 +0800
Subject: [PATCH 2/5] Chore: revert "Feature: add tls SNI sniffing (#68)"

This reverts commit 24ce6622a2960cdc6f84e4dde2ae0a74570415a4.
---
 common/snifer/tls/sniff.go      | 148 -----------------------------
 common/snifer/tls/sniff_test.go | 159 --------------------------------
 component/resolver/enhancer.go  |   7 --
 dns/enhancer.go                 |   6 --
 tunnel/statistic/tracker.go     |  17 ----
 5 files changed, 337 deletions(-)
 delete mode 100644 common/snifer/tls/sniff.go
 delete mode 100644 common/snifer/tls/sniff_test.go

diff --git a/common/snifer/tls/sniff.go b/common/snifer/tls/sniff.go
deleted file mode 100644
index 1471fc68..00000000
--- a/common/snifer/tls/sniff.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package tls
-
-import (
-	"encoding/binary"
-	"errors"
-	"strings"
-)
-
-var ErrNoClue = errors.New("not enough information for making a decision")
-
-type SniffHeader struct {
-	domain string
-}
-
-func (h *SniffHeader) Protocol() string {
-	return "tls"
-}
-
-func (h *SniffHeader) Domain() string {
-	return h.domain
-}
-
-var (
-	errNotTLS         = errors.New("not TLS header")
-	errNotClientHello = errors.New("not client hello")
-)
-
-func IsValidTLSVersion(major, minor byte) bool {
-	return major == 3
-}
-
-// ReadClientHello returns server name (if any) from TLS client hello message.
-// https://github.com/golang/go/blob/master/src/crypto/tls/handshake_messages.go#L300
-func ReadClientHello(data []byte, h *SniffHeader) error {
-	if len(data) < 42 {
-		return ErrNoClue
-	}
-	sessionIDLen := int(data[38])
-	if sessionIDLen > 32 || len(data) < 39+sessionIDLen {
-		return ErrNoClue
-	}
-	data = data[39+sessionIDLen:]
-	if len(data) < 2 {
-		return ErrNoClue
-	}
-	// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
-	// they are uint16s, the number must be even.
-	cipherSuiteLen := int(data[0])<<8 | int(data[1])
-	if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
-		return errNotClientHello
-	}
-	data = data[2+cipherSuiteLen:]
-	if len(data) < 1 {
-		return ErrNoClue
-	}
-	compressionMethodsLen := int(data[0])
-	if len(data) < 1+compressionMethodsLen {
-		return ErrNoClue
-	}
-	data = data[1+compressionMethodsLen:]
-
-	if len(data) == 0 {
-		return errNotClientHello
-	}
-	if len(data) < 2 {
-		return errNotClientHello
-	}
-
-	extensionsLength := int(data[0])<<8 | int(data[1])
-	data = data[2:]
-	if extensionsLength != len(data) {
-		return errNotClientHello
-	}
-
-	for len(data) != 0 {
-		if len(data) < 4 {
-			return errNotClientHello
-		}
-		extension := uint16(data[0])<<8 | uint16(data[1])
-		length := int(data[2])<<8 | int(data[3])
-		data = data[4:]
-		if len(data) < length {
-			return errNotClientHello
-		}
-
-		if extension == 0x00 { /* extensionServerName */
-			d := data[:length]
-			if len(d) < 2 {
-				return errNotClientHello
-			}
-			namesLen := int(d[0])<<8 | int(d[1])
-			d = d[2:]
-			if len(d) != namesLen {
-				return errNotClientHello
-			}
-			for len(d) > 0 {
-				if len(d) < 3 {
-					return errNotClientHello
-				}
-				nameType := d[0]
-				nameLen := int(d[1])<<8 | int(d[2])
-				d = d[3:]
-				if len(d) < nameLen {
-					return errNotClientHello
-				}
-				if nameType == 0 {
-					serverName := string(d[:nameLen])
-					// An SNI value may not include a
-					// trailing dot. See
-					// https://tools.ietf.org/html/rfc6066#section-3.
-					if strings.HasSuffix(serverName, ".") {
-						return errNotClientHello
-					}
-					h.domain = serverName
-					return nil
-				}
-				d = d[nameLen:]
-			}
-		}
-		data = data[length:]
-	}
-
-	return errNotTLS
-}
-
-func SniffTLS(b []byte) (*SniffHeader, error) {
-	if len(b) < 5 {
-		return nil, ErrNoClue
-	}
-
-	if b[0] != 0x16 /* TLS Handshake */ {
-		return nil, errNotTLS
-	}
-	if !IsValidTLSVersion(b[1], b[2]) {
-		return nil, errNotTLS
-	}
-	headerLen := int(binary.BigEndian.Uint16(b[3:5]))
-	if 5+headerLen > len(b) {
-		return nil, ErrNoClue
-	}
-
-	h := &SniffHeader{}
-	err := ReadClientHello(b[5:5+headerLen], h)
-	if err == nil {
-		return h, nil
-	}
-	return nil, err
-}
diff --git a/common/snifer/tls/sniff_test.go b/common/snifer/tls/sniff_test.go
deleted file mode 100644
index 26f5f1ee..00000000
--- a/common/snifer/tls/sniff_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package tls
-
-import (
-	"testing"
-)
-
-func TestTLSHeaders(t *testing.T) {
-	cases := []struct {
-		input  []byte
-		domain string
-		err    bool
-	}{
-		{
-			input: []byte{
-				0x16, 0x03, 0x01, 0x00, 0xc8, 0x01, 0x00, 0x00,
-				0xc4, 0x03, 0x03, 0x1a, 0xac, 0xb2, 0xa8, 0xfe,
-				0xb4, 0x96, 0x04, 0x5b, 0xca, 0xf7, 0xc1, 0xf4,
-				0x2e, 0x53, 0x24, 0x6e, 0x34, 0x0c, 0x58, 0x36,
-				0x71, 0x97, 0x59, 0xe9, 0x41, 0x66, 0xe2, 0x43,
-				0xa0, 0x13, 0xb6, 0x00, 0x00, 0x20, 0x1a, 0x1a,
-				0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
-				0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
-				0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
-				0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
-				0x00, 0x7b, 0xba, 0xba, 0x00, 0x00, 0xff, 0x01,
-				0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
-				0x14, 0x00, 0x00, 0x11, 0x63, 0x2e, 0x73, 0x2d,
-				0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
-				0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17, 0x00,
-				0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
-				0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,
-				0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08,
-				0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00,
-				0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
-				0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c,
-				0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70,
-				0x2f, 0x31, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x02,
-				0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08,
-				0xaa, 0xaa, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,
-				0xaa, 0xaa, 0x00, 0x01, 0x00,
-			},
-			domain: "c.s-microsoft.com",
-			err:    false,
-		},
-		{
-			input: []byte{
-				0x16, 0x03, 0x01, 0x00, 0xee, 0x01, 0x00, 0x00,
-				0xea, 0x03, 0x03, 0xe7, 0x91, 0x9e, 0x93, 0xca,
-				0x78, 0x1b, 0x3c, 0xe0, 0x65, 0x25, 0x58, 0xb5,
-				0x93, 0xe1, 0x0f, 0x85, 0xec, 0x9a, 0x66, 0x8e,
-				0x61, 0x82, 0x88, 0xc8, 0xfc, 0xae, 0x1e, 0xca,
-				0xd7, 0xa5, 0x63, 0x20, 0xbd, 0x1c, 0x00, 0x00,
-				0x8b, 0xee, 0x09, 0xe3, 0x47, 0x6a, 0x0e, 0x74,
-				0xb0, 0xbc, 0xa3, 0x02, 0xa7, 0x35, 0xe8, 0x85,
-				0x70, 0x7c, 0x7a, 0xf0, 0x00, 0xdf, 0x4a, 0xea,
-				0x87, 0x01, 0x14, 0x91, 0x00, 0x20, 0xea, 0xea,
-				0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
-				0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
-				0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
-				0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
-				0x00, 0x81, 0x9a, 0x9a, 0x00, 0x00, 0xff, 0x01,
-				0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
-				0x16, 0x00, 0x00, 0x13, 0x77, 0x77, 0x77, 0x30,
-				0x37, 0x2e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x74,
-				0x61, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x00,
-				0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
-				0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08,
-				0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05,
-				0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00,
-				0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,
-				0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,
-				0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74,
-				0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x75, 0x50,
-				0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
-				0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a,
-				0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x8a, 0x8a,
-				0x00, 0x01, 0x00,
-			},
-			domain: "www07.clicktale.net",
-			err:    false,
-		},
-		{
-			input: []byte{
-				0x16, 0x03, 0x01, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe2, 0x03, 0x03, 0x81, 0x47, 0xc1,
-				0x66, 0xd5, 0x1b, 0xfa, 0x4b, 0xb5, 0xe0, 0x2a, 0xe1, 0xa7, 0x87, 0x13, 0x1d, 0x11, 0xaa, 0xc6,
-				0xce, 0xfc, 0x7f, 0xab, 0x94, 0xc8, 0x62, 0xad, 0xc8, 0xab, 0x0c, 0xdd, 0xcb, 0x20, 0x6f, 0x9d,
-				0x07, 0xf1, 0x95, 0x3e, 0x99, 0xd8, 0xf3, 0x6d, 0x97, 0xee, 0x19, 0x0b, 0x06, 0x1b, 0xf4, 0x84,
-				0x0b, 0xb6, 0x8f, 0xcc, 0xde, 0xe2, 0xd0, 0x2d, 0x6b, 0x0c, 0x1f, 0x52, 0x53, 0x13, 0x00, 0x08,
-				0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0x00, 0xff, 0x01, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x0c,
-				0x00, 0x0a, 0x00, 0x00, 0x07, 0x64, 0x6f, 0x67, 0x66, 0x69, 0x73, 0x68, 0x00, 0x0b, 0x00, 0x04,
-				0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e,
-				0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
-				0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08,
-				0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
-				0x06, 0x01, 0x00, 0x2b, 0x00, 0x07, 0x06, 0x7f, 0x1c, 0x7f, 0x1b, 0x7f, 0x1a, 0x00, 0x2d, 0x00,
-				0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2f, 0x35, 0x0c,
-				0xb6, 0x90, 0x0a, 0xb7, 0xd5, 0xc4, 0x1b, 0x2f, 0x60, 0xaa, 0x56, 0x7b, 0x3f, 0x71, 0xc8, 0x01,
-				0x7e, 0x86, 0xd3, 0xb7, 0x0c, 0x29, 0x1a, 0x9e, 0x5b, 0x38, 0x3f, 0x01, 0x72,
-			},
-			domain: "dogfish",
-			err:    false,
-		},
-		{
-			input: []byte{
-				0x16, 0x03, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00,
-				0xff, 0x03, 0x03, 0x3d, 0x89, 0x52, 0x9e, 0xee,
-				0xbe, 0x17, 0x63, 0x75, 0xef, 0x29, 0xbd, 0x14,
-				0x6a, 0x49, 0xe0, 0x2c, 0x37, 0x57, 0x71, 0x62,
-				0x82, 0x44, 0x94, 0x8f, 0x6e, 0x94, 0x08, 0x45,
-				0x7f, 0xdb, 0xc1, 0x00, 0x00, 0x3e, 0xc0, 0x2c,
-				0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8,
-				0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e,
-				0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23,
-				0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14,
-				0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33,
-				0x00, 0x9d, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03,
-				0x13, 0x01, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35,
-				0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x00, 0x98,
-				0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00,
-				0x0b, 0x31, 0x30, 0x2e, 0x34, 0x32, 0x2e, 0x30,
-				0x2e, 0x32, 0x34, 0x33, 0x00, 0x0b, 0x00, 0x04,
-				0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0a,
-				0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19,
-				0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d,
-				0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03,
-				0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06,
-				0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03,
-				0x02, 0x01, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02,
-				0x06, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
-				0x00, 0x00, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x7f,
-				0x14, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00,
-				0x2d, 0x00, 0x03, 0x02, 0x01, 0x00, 0x00, 0x28,
-				0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
-				0x13, 0x7c, 0x6e, 0x97, 0xc4, 0xfd, 0x09, 0x2e,
-				0x70, 0x2f, 0x73, 0x5a, 0x9b, 0x57, 0x4d, 0x5f,
-				0x2b, 0x73, 0x2c, 0xa5, 0x4a, 0x98, 0x40, 0x3d,
-				0x75, 0x6e, 0xb4, 0x76, 0xf9, 0x48, 0x8f, 0x36,
-			},
-			domain: "10.42.0.243",
-			err:    false,
-		},
-	}
-
-	for _, test := range cases {
-		header, err := SniffTLS(test.input)
-		if test.err {
-			if err == nil {
-				t.Errorf("Exepct error but nil in test %v", test)
-			}
-		} else {
-			if err != nil {
-				t.Errorf("Expect no error but actually %s in test %v", err.Error(), test)
-			}
-			if header.Domain() != test.domain {
-				t.Error("expect domain ", test.domain, " but got ", header.Domain())
-			}
-		}
-	}
-}
diff --git a/component/resolver/enhancer.go b/component/resolver/enhancer.go
index 77f18374..9df3f54b 100644
--- a/component/resolver/enhancer.go
+++ b/component/resolver/enhancer.go
@@ -14,7 +14,6 @@ type Enhancer interface {
 	IsExistFakeIP(net.IP) bool
 	FindHostByIP(net.IP) (string, bool)
 	FlushFakeIP() error
-	InsertHostByIP(net.IP, string)
 }
 
 func FakeIPEnabled() bool {
@@ -57,12 +56,6 @@ func IsExistFakeIP(ip net.IP) bool {
 	return false
 }
 
-func InsertHostByIP(ip net.IP, host string) {
-	if mapper := DefaultHostMapper; mapper != nil {
-		mapper.InsertHostByIP(ip, host)
-	}
-}
-
 func FindHostByIP(ip net.IP) (string, bool) {
 	if mapper := DefaultHostMapper; mapper != nil {
 		return mapper.FindHostByIP(ip)
diff --git a/dns/enhancer.go b/dns/enhancer.go
index 016ff02a..9bf568c7 100644
--- a/dns/enhancer.go
+++ b/dns/enhancer.go
@@ -74,12 +74,6 @@ func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
 	return "", false
 }
 
-func (h *ResolverEnhancer) InsertHostByIP(ip net.IP, host string) {
-	if mapping := h.mapping; mapping != nil {
-		h.mapping.Set(ip.String(), host)
-	}
-}
-
 func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) {
 	if h.mapping != nil && o.mapping != nil {
 		o.mapping.CloneTo(h.mapping)
diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go
index f213ca61..1f5f1f9c 100644
--- a/tunnel/statistic/tracker.go
+++ b/tunnel/statistic/tracker.go
@@ -1,14 +1,10 @@
 package statistic
 
 import (
-	"errors"
 	"net"
 	"time"
 
-	"github.com/Dreamacro/clash/common/snifer/tls"
-	"github.com/Dreamacro/clash/component/resolver"
 	C "github.com/Dreamacro/clash/constant"
-	"github.com/Dreamacro/clash/log"
 
 	"github.com/gofrs/uuid"
 	"go.uber.org/atomic"
@@ -52,20 +48,7 @@ func (tt *tcpTracker) Write(b []byte) (int, error) {
 	n, err := tt.Conn.Write(b)
 	upload := int64(n)
 	tt.manager.PushUploaded(upload)
-	if tt.UploadTotal.Load() < 128 && tt.Metadata.Host == "" && (tt.Metadata.DstPort == "443" || tt.Metadata.DstPort == "8443") {
-		header, err := tls.SniffTLS(b)
-		if err != nil {
-			// log.Errorln("Expect no error but actually %s %s:%s:%s", err.Error(), tt.Metadata.Host, tt.Metadata.DstIP.String(), tt.Metadata.DstPort)
-		} else {
-			resolver.InsertHostByIP(tt.Metadata.DstIP, header.Domain())
-			log.Warnln("use sni update host: %s ip: %s", header.Domain(), tt.Metadata.DstIP.String())
-			tt.manager.Leave(tt)
-			tt.Conn.Close()
-			return n, errors.New("sni update, break current link to avoid leaks")
-		}
-	}
 	tt.UploadTotal.Add(upload)
-
 	return n, err
 }
 

From c495d314d4d94db15153cf1f558db35b0d5cc9a6 Mon Sep 17 00:00:00 2001
From: fishg <1423545+fishg@users.noreply.github.com>
Date: Wed, 30 Mar 2022 23:54:52 +0800
Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0tls=20sni=20?=
 =?UTF-8?q?=E5=97=85=E6=8E=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

# Conflicts:
#	tunnel/statistic/tracker.go
#	tunnel/tunnel.go
---
 common/snifer/tls/sniff.go      | 148 +++++++++++++++++++++++++++++
 common/snifer/tls/sniff_test.go | 159 ++++++++++++++++++++++++++++++++
 component/resolver/enhancer.go  |   7 ++
 dns/enhancer.go                 |   6 ++
 tunnel/statistic/tracker.go     |  18 ++++
 5 files changed, 338 insertions(+)
 create mode 100644 common/snifer/tls/sniff.go
 create mode 100644 common/snifer/tls/sniff_test.go

diff --git a/common/snifer/tls/sniff.go b/common/snifer/tls/sniff.go
new file mode 100644
index 00000000..1471fc68
--- /dev/null
+++ b/common/snifer/tls/sniff.go
@@ -0,0 +1,148 @@
+package tls
+
+import (
+	"encoding/binary"
+	"errors"
+	"strings"
+)
+
+var ErrNoClue = errors.New("not enough information for making a decision")
+
+type SniffHeader struct {
+	domain string
+}
+
+func (h *SniffHeader) Protocol() string {
+	return "tls"
+}
+
+func (h *SniffHeader) Domain() string {
+	return h.domain
+}
+
+var (
+	errNotTLS         = errors.New("not TLS header")
+	errNotClientHello = errors.New("not client hello")
+)
+
+func IsValidTLSVersion(major, minor byte) bool {
+	return major == 3
+}
+
+// ReadClientHello returns server name (if any) from TLS client hello message.
+// https://github.com/golang/go/blob/master/src/crypto/tls/handshake_messages.go#L300
+func ReadClientHello(data []byte, h *SniffHeader) error {
+	if len(data) < 42 {
+		return ErrNoClue
+	}
+	sessionIDLen := int(data[38])
+	if sessionIDLen > 32 || len(data) < 39+sessionIDLen {
+		return ErrNoClue
+	}
+	data = data[39+sessionIDLen:]
+	if len(data) < 2 {
+		return ErrNoClue
+	}
+	// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
+	// they are uint16s, the number must be even.
+	cipherSuiteLen := int(data[0])<<8 | int(data[1])
+	if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
+		return errNotClientHello
+	}
+	data = data[2+cipherSuiteLen:]
+	if len(data) < 1 {
+		return ErrNoClue
+	}
+	compressionMethodsLen := int(data[0])
+	if len(data) < 1+compressionMethodsLen {
+		return ErrNoClue
+	}
+	data = data[1+compressionMethodsLen:]
+
+	if len(data) == 0 {
+		return errNotClientHello
+	}
+	if len(data) < 2 {
+		return errNotClientHello
+	}
+
+	extensionsLength := int(data[0])<<8 | int(data[1])
+	data = data[2:]
+	if extensionsLength != len(data) {
+		return errNotClientHello
+	}
+
+	for len(data) != 0 {
+		if len(data) < 4 {
+			return errNotClientHello
+		}
+		extension := uint16(data[0])<<8 | uint16(data[1])
+		length := int(data[2])<<8 | int(data[3])
+		data = data[4:]
+		if len(data) < length {
+			return errNotClientHello
+		}
+
+		if extension == 0x00 { /* extensionServerName */
+			d := data[:length]
+			if len(d) < 2 {
+				return errNotClientHello
+			}
+			namesLen := int(d[0])<<8 | int(d[1])
+			d = d[2:]
+			if len(d) != namesLen {
+				return errNotClientHello
+			}
+			for len(d) > 0 {
+				if len(d) < 3 {
+					return errNotClientHello
+				}
+				nameType := d[0]
+				nameLen := int(d[1])<<8 | int(d[2])
+				d = d[3:]
+				if len(d) < nameLen {
+					return errNotClientHello
+				}
+				if nameType == 0 {
+					serverName := string(d[:nameLen])
+					// An SNI value may not include a
+					// trailing dot. See
+					// https://tools.ietf.org/html/rfc6066#section-3.
+					if strings.HasSuffix(serverName, ".") {
+						return errNotClientHello
+					}
+					h.domain = serverName
+					return nil
+				}
+				d = d[nameLen:]
+			}
+		}
+		data = data[length:]
+	}
+
+	return errNotTLS
+}
+
+func SniffTLS(b []byte) (*SniffHeader, error) {
+	if len(b) < 5 {
+		return nil, ErrNoClue
+	}
+
+	if b[0] != 0x16 /* TLS Handshake */ {
+		return nil, errNotTLS
+	}
+	if !IsValidTLSVersion(b[1], b[2]) {
+		return nil, errNotTLS
+	}
+	headerLen := int(binary.BigEndian.Uint16(b[3:5]))
+	if 5+headerLen > len(b) {
+		return nil, ErrNoClue
+	}
+
+	h := &SniffHeader{}
+	err := ReadClientHello(b[5:5+headerLen], h)
+	if err == nil {
+		return h, nil
+	}
+	return nil, err
+}
diff --git a/common/snifer/tls/sniff_test.go b/common/snifer/tls/sniff_test.go
new file mode 100644
index 00000000..26f5f1ee
--- /dev/null
+++ b/common/snifer/tls/sniff_test.go
@@ -0,0 +1,159 @@
+package tls
+
+import (
+	"testing"
+)
+
+func TestTLSHeaders(t *testing.T) {
+	cases := []struct {
+		input  []byte
+		domain string
+		err    bool
+	}{
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x00, 0xc8, 0x01, 0x00, 0x00,
+				0xc4, 0x03, 0x03, 0x1a, 0xac, 0xb2, 0xa8, 0xfe,
+				0xb4, 0x96, 0x04, 0x5b, 0xca, 0xf7, 0xc1, 0xf4,
+				0x2e, 0x53, 0x24, 0x6e, 0x34, 0x0c, 0x58, 0x36,
+				0x71, 0x97, 0x59, 0xe9, 0x41, 0x66, 0xe2, 0x43,
+				0xa0, 0x13, 0xb6, 0x00, 0x00, 0x20, 0x1a, 0x1a,
+				0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
+				0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
+				0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
+				0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
+				0x00, 0x7b, 0xba, 0xba, 0x00, 0x00, 0xff, 0x01,
+				0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+				0x14, 0x00, 0x00, 0x11, 0x63, 0x2e, 0x73, 0x2d,
+				0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+				0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17, 0x00,
+				0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
+				0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,
+				0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08,
+				0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00,
+				0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
+				0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c,
+				0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70,
+				0x2f, 0x31, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x02,
+				0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08,
+				0xaa, 0xaa, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,
+				0xaa, 0xaa, 0x00, 0x01, 0x00,
+			},
+			domain: "c.s-microsoft.com",
+			err:    false,
+		},
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x00, 0xee, 0x01, 0x00, 0x00,
+				0xea, 0x03, 0x03, 0xe7, 0x91, 0x9e, 0x93, 0xca,
+				0x78, 0x1b, 0x3c, 0xe0, 0x65, 0x25, 0x58, 0xb5,
+				0x93, 0xe1, 0x0f, 0x85, 0xec, 0x9a, 0x66, 0x8e,
+				0x61, 0x82, 0x88, 0xc8, 0xfc, 0xae, 0x1e, 0xca,
+				0xd7, 0xa5, 0x63, 0x20, 0xbd, 0x1c, 0x00, 0x00,
+				0x8b, 0xee, 0x09, 0xe3, 0x47, 0x6a, 0x0e, 0x74,
+				0xb0, 0xbc, 0xa3, 0x02, 0xa7, 0x35, 0xe8, 0x85,
+				0x70, 0x7c, 0x7a, 0xf0, 0x00, 0xdf, 0x4a, 0xea,
+				0x87, 0x01, 0x14, 0x91, 0x00, 0x20, 0xea, 0xea,
+				0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
+				0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,
+				0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
+				0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
+				0x00, 0x81, 0x9a, 0x9a, 0x00, 0x00, 0xff, 0x01,
+				0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
+				0x16, 0x00, 0x00, 0x13, 0x77, 0x77, 0x77, 0x30,
+				0x37, 0x2e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x74,
+				0x61, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x00,
+				0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+				0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08,
+				0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05,
+				0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00,
+				0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,
+				0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,
+				0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74,
+				0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x75, 0x50,
+				0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+				0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a,
+				0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x8a, 0x8a,
+				0x00, 0x01, 0x00,
+			},
+			domain: "www07.clicktale.net",
+			err:    false,
+		},
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe2, 0x03, 0x03, 0x81, 0x47, 0xc1,
+				0x66, 0xd5, 0x1b, 0xfa, 0x4b, 0xb5, 0xe0, 0x2a, 0xe1, 0xa7, 0x87, 0x13, 0x1d, 0x11, 0xaa, 0xc6,
+				0xce, 0xfc, 0x7f, 0xab, 0x94, 0xc8, 0x62, 0xad, 0xc8, 0xab, 0x0c, 0xdd, 0xcb, 0x20, 0x6f, 0x9d,
+				0x07, 0xf1, 0x95, 0x3e, 0x99, 0xd8, 0xf3, 0x6d, 0x97, 0xee, 0x19, 0x0b, 0x06, 0x1b, 0xf4, 0x84,
+				0x0b, 0xb6, 0x8f, 0xcc, 0xde, 0xe2, 0xd0, 0x2d, 0x6b, 0x0c, 0x1f, 0x52, 0x53, 0x13, 0x00, 0x08,
+				0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0x00, 0xff, 0x01, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x0c,
+				0x00, 0x0a, 0x00, 0x00, 0x07, 0x64, 0x6f, 0x67, 0x66, 0x69, 0x73, 0x68, 0x00, 0x0b, 0x00, 0x04,
+				0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e,
+				0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
+				0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08,
+				0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,
+				0x06, 0x01, 0x00, 0x2b, 0x00, 0x07, 0x06, 0x7f, 0x1c, 0x7f, 0x1b, 0x7f, 0x1a, 0x00, 0x2d, 0x00,
+				0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2f, 0x35, 0x0c,
+				0xb6, 0x90, 0x0a, 0xb7, 0xd5, 0xc4, 0x1b, 0x2f, 0x60, 0xaa, 0x56, 0x7b, 0x3f, 0x71, 0xc8, 0x01,
+				0x7e, 0x86, 0xd3, 0xb7, 0x0c, 0x29, 0x1a, 0x9e, 0x5b, 0x38, 0x3f, 0x01, 0x72,
+			},
+			domain: "dogfish",
+			err:    false,
+		},
+		{
+			input: []byte{
+				0x16, 0x03, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00,
+				0xff, 0x03, 0x03, 0x3d, 0x89, 0x52, 0x9e, 0xee,
+				0xbe, 0x17, 0x63, 0x75, 0xef, 0x29, 0xbd, 0x14,
+				0x6a, 0x49, 0xe0, 0x2c, 0x37, 0x57, 0x71, 0x62,
+				0x82, 0x44, 0x94, 0x8f, 0x6e, 0x94, 0x08, 0x45,
+				0x7f, 0xdb, 0xc1, 0x00, 0x00, 0x3e, 0xc0, 0x2c,
+				0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8,
+				0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e,
+				0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23,
+				0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14,
+				0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33,
+				0x00, 0x9d, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03,
+				0x13, 0x01, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35,
+				0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x00, 0x98,
+				0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00,
+				0x0b, 0x31, 0x30, 0x2e, 0x34, 0x32, 0x2e, 0x30,
+				0x2e, 0x32, 0x34, 0x33, 0x00, 0x0b, 0x00, 0x04,
+				0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0a,
+				0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19,
+				0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d,
+				0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03,
+				0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06,
+				0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03,
+				0x02, 0x01, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02,
+				0x06, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,
+				0x00, 0x00, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x7f,
+				0x14, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00,
+				0x2d, 0x00, 0x03, 0x02, 0x01, 0x00, 0x00, 0x28,
+				0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,
+				0x13, 0x7c, 0x6e, 0x97, 0xc4, 0xfd, 0x09, 0x2e,
+				0x70, 0x2f, 0x73, 0x5a, 0x9b, 0x57, 0x4d, 0x5f,
+				0x2b, 0x73, 0x2c, 0xa5, 0x4a, 0x98, 0x40, 0x3d,
+				0x75, 0x6e, 0xb4, 0x76, 0xf9, 0x48, 0x8f, 0x36,
+			},
+			domain: "10.42.0.243",
+			err:    false,
+		},
+	}
+
+	for _, test := range cases {
+		header, err := SniffTLS(test.input)
+		if test.err {
+			if err == nil {
+				t.Errorf("Exepct error but nil in test %v", test)
+			}
+		} else {
+			if err != nil {
+				t.Errorf("Expect no error but actually %s in test %v", err.Error(), test)
+			}
+			if header.Domain() != test.domain {
+				t.Error("expect domain ", test.domain, " but got ", header.Domain())
+			}
+		}
+	}
+}
diff --git a/component/resolver/enhancer.go b/component/resolver/enhancer.go
index 9df3f54b..77f18374 100644
--- a/component/resolver/enhancer.go
+++ b/component/resolver/enhancer.go
@@ -14,6 +14,7 @@ type Enhancer interface {
 	IsExistFakeIP(net.IP) bool
 	FindHostByIP(net.IP) (string, bool)
 	FlushFakeIP() error
+	InsertHostByIP(net.IP, string)
 }
 
 func FakeIPEnabled() bool {
@@ -56,6 +57,12 @@ func IsExistFakeIP(ip net.IP) bool {
 	return false
 }
 
+func InsertHostByIP(ip net.IP, host string) {
+	if mapper := DefaultHostMapper; mapper != nil {
+		mapper.InsertHostByIP(ip, host)
+	}
+}
+
 func FindHostByIP(ip net.IP) (string, bool) {
 	if mapper := DefaultHostMapper; mapper != nil {
 		return mapper.FindHostByIP(ip)
diff --git a/dns/enhancer.go b/dns/enhancer.go
index 9bf568c7..016ff02a 100644
--- a/dns/enhancer.go
+++ b/dns/enhancer.go
@@ -74,6 +74,12 @@ func (h *ResolverEnhancer) FindHostByIP(ip net.IP) (string, bool) {
 	return "", false
 }
 
+func (h *ResolverEnhancer) InsertHostByIP(ip net.IP, host string) {
+	if mapping := h.mapping; mapping != nil {
+		h.mapping.Set(ip.String(), host)
+	}
+}
+
 func (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) {
 	if h.mapping != nil && o.mapping != nil {
 		o.mapping.CloneTo(h.mapping)
diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go
index 1f5f1f9c..77713da2 100644
--- a/tunnel/statistic/tracker.go
+++ b/tunnel/statistic/tracker.go
@@ -1,11 +1,15 @@
 package statistic
 
 import (
+	"errors"
 	"net"
 	"time"
 
+	"github.com/Dreamacro/clash/common/snifer/tls"
+	"github.com/Dreamacro/clash/component/resolver"
 	C "github.com/Dreamacro/clash/constant"
 
+	"github.com/Dreamacro/clash/log"
 	"github.com/gofrs/uuid"
 	"go.uber.org/atomic"
 )
@@ -48,7 +52,21 @@ func (tt *tcpTracker) Write(b []byte) (int, error) {
 	n, err := tt.Conn.Write(b)
 	upload := int64(n)
 	tt.manager.PushUploaded(upload)
+	if tt.UploadTotal.Load() < 128 && tt.Metadata.Host == "" && (tt.Metadata.DstPort == "443" || tt.Metadata.DstPort == "8443") {
+		header, err := tls.SniffTLS(b)
+		if err != nil {
+			// log.Errorln("Expect no error but actually %s %s:%s:%s", err.Error(), tt.Metadata.Host, tt.Metadata.DstIP.String(), tt.Metadata.DstPort)
+		} else {
+			tt.Metadata.Host = header.Domain()
+			resolver.InsertHostByIP(tt.Metadata.DstIP, tt.Metadata.Host)
+			log.Errorln("sni %s %s", tt.Metadata.Host, tt.Metadata.DstIP.String())
+			tt.manager.Leave(tt)
+			tt.Conn.Close()
+			return n, errors.New("sni update")
+		}
+	}
 	tt.UploadTotal.Add(upload)
+
 	return n, err
 }
 

From afdcb6cfc7b8be71f2ebb7ba38629becbaab1e86 Mon Sep 17 00:00:00 2001
From: fishg <1423545+fishg@users.noreply.github.com>
Date: Thu, 31 Mar 2022 11:41:40 +0800
Subject: [PATCH 4/5] fix: log level ajust and lint fix

---
 tunnel/statistic/tracker.go | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go
index 77713da2..f213ca61 100644
--- a/tunnel/statistic/tracker.go
+++ b/tunnel/statistic/tracker.go
@@ -8,8 +8,8 @@ import (
 	"github.com/Dreamacro/clash/common/snifer/tls"
 	"github.com/Dreamacro/clash/component/resolver"
 	C "github.com/Dreamacro/clash/constant"
-
 	"github.com/Dreamacro/clash/log"
+
 	"github.com/gofrs/uuid"
 	"go.uber.org/atomic"
 )
@@ -57,12 +57,11 @@ func (tt *tcpTracker) Write(b []byte) (int, error) {
 		if err != nil {
 			// log.Errorln("Expect no error but actually %s %s:%s:%s", err.Error(), tt.Metadata.Host, tt.Metadata.DstIP.String(), tt.Metadata.DstPort)
 		} else {
-			tt.Metadata.Host = header.Domain()
-			resolver.InsertHostByIP(tt.Metadata.DstIP, tt.Metadata.Host)
-			log.Errorln("sni %s %s", tt.Metadata.Host, tt.Metadata.DstIP.String())
+			resolver.InsertHostByIP(tt.Metadata.DstIP, header.Domain())
+			log.Warnln("use sni update host: %s ip: %s", header.Domain(), tt.Metadata.DstIP.String())
 			tt.manager.Leave(tt)
 			tt.Conn.Close()
-			return n, errors.New("sni update")
+			return n, errors.New("sni update, break current link to avoid leaks")
 		}
 	}
 	tt.UploadTotal.Add(upload)

From 13012a9f897adf8a7acc49c4b1f31e3fe629bc0b Mon Sep 17 00:00:00 2001
From: fishg <1423545+fishg@users.noreply.github.com>
Date: Sat, 2 Apr 2022 16:03:53 +0800
Subject: [PATCH 5/5] fix: dns over proxy may due to cancel request, but proxy
 live status is fine

---
 adapter/adapter.go | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/adapter/adapter.go b/adapter/adapter.go
index 23dc304a..f4087241 100644
--- a/adapter/adapter.go
+++ b/adapter/adapter.go
@@ -7,6 +7,7 @@ import (
 	"net"
 	"net/http"
 	"net/url"
+	"strings"
 	"time"
 
 	"github.com/Dreamacro/clash/common/queue"
@@ -37,7 +38,11 @@ func (p *Proxy) Dial(metadata *C.Metadata) (C.Conn, error) {
 // DialContext implements C.ProxyAdapter
 func (p *Proxy) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
 	conn, err := p.ProxyAdapter.DialContext(ctx, metadata, opts...)
-	p.alive.Store(err == nil)
+	wasCancel := false
+	if err != nil {
+		wasCancel = strings.Contains(err.Error(), "operation was canceled")
+	}
+	p.alive.Store(err == nil || wasCancel)
 	return conn, err
 }