github.com/metacubex/mihomo@v1.18.5/component/sniffer/tls_sniffer.go (about) 1 package sniffer 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "strings" 7 8 "github.com/metacubex/mihomo/common/utils" 9 C "github.com/metacubex/mihomo/constant" 10 "github.com/metacubex/mihomo/constant/sniffer" 11 ) 12 13 var ( 14 errNotTLS = errors.New("not TLS header") 15 errNotClientHello = errors.New("not client hello") 16 ) 17 18 var _ sniffer.Sniffer = (*TLSSniffer)(nil) 19 20 type TLSSniffer struct { 21 *BaseSniffer 22 } 23 24 func NewTLSSniffer(snifferConfig SnifferConfig) (*TLSSniffer, error) { 25 ports := snifferConfig.Ports 26 if len(ports) == 0 { 27 ports = utils.IntRanges[uint16]{utils.NewRange[uint16](443, 443)} 28 } 29 return &TLSSniffer{ 30 BaseSniffer: NewBaseSniffer(ports, C.TCP), 31 }, nil 32 } 33 34 func (tls *TLSSniffer) Protocol() string { 35 return "tls" 36 } 37 38 func (tls *TLSSniffer) SupportNetwork() C.NetWork { 39 return C.TCP 40 } 41 42 func (tls *TLSSniffer) SniffData(bytes []byte) (string, error) { 43 domain, err := SniffTLS(bytes) 44 if err == nil { 45 return *domain, nil 46 } else { 47 return "", err 48 } 49 } 50 51 func IsValidTLSVersion(major, minor byte) bool { 52 return major == 3 53 } 54 55 // ReadClientHello returns server name (if any) from TLS client hello message. 56 // https://github.com/golang/go/blob/master/src/crypto/tls/handshake_messages.go#L300 57 func ReadClientHello(data []byte) (*string, error) { 58 if len(data) < 42 { 59 return nil, ErrNoClue 60 } 61 sessionIDLen := int(data[38]) 62 if sessionIDLen > 32 || len(data) < 39+sessionIDLen { 63 return nil, ErrNoClue 64 } 65 data = data[39+sessionIDLen:] 66 if len(data) < 2 { 67 return nil, ErrNoClue 68 } 69 // cipherSuiteLen is the number of bytes of cipher suite numbers. Since 70 // they are uint16s, the number must be even. 71 cipherSuiteLen := int(data[0])<<8 | int(data[1]) 72 if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { 73 return nil, errNotClientHello 74 } 75 data = data[2+cipherSuiteLen:] 76 if len(data) < 1 { 77 return nil, ErrNoClue 78 } 79 compressionMethodsLen := int(data[0]) 80 if len(data) < 1+compressionMethodsLen { 81 return nil, ErrNoClue 82 } 83 data = data[1+compressionMethodsLen:] 84 85 if len(data) == 0 { 86 return nil, errNotClientHello 87 } 88 if len(data) < 2 { 89 return nil, errNotClientHello 90 } 91 92 extensionsLength := int(data[0])<<8 | int(data[1]) 93 data = data[2:] 94 if extensionsLength != len(data) { 95 return nil, errNotClientHello 96 } 97 98 for len(data) != 0 { 99 if len(data) < 4 { 100 return nil, errNotClientHello 101 } 102 extension := uint16(data[0])<<8 | uint16(data[1]) 103 length := int(data[2])<<8 | int(data[3]) 104 data = data[4:] 105 if len(data) < length { 106 return nil, errNotClientHello 107 } 108 109 if extension == 0x00 { /* extensionServerName */ 110 d := data[:length] 111 if len(d) < 2 { 112 return nil, errNotClientHello 113 } 114 namesLen := int(d[0])<<8 | int(d[1]) 115 d = d[2:] 116 if len(d) != namesLen { 117 return nil, errNotClientHello 118 } 119 for len(d) > 0 { 120 if len(d) < 3 { 121 return nil, errNotClientHello 122 } 123 nameType := d[0] 124 nameLen := int(d[1])<<8 | int(d[2]) 125 d = d[3:] 126 if len(d) < nameLen { 127 return nil, errNotClientHello 128 } 129 if nameType == 0 { 130 serverName := string(d[:nameLen]) 131 // An SNI value may not include a 132 // trailing dot. See 133 // https://tools.ietf.org/html/rfc6066#section-3. 134 if strings.HasSuffix(serverName, ".") { 135 return nil, errNotClientHello 136 } 137 138 return &serverName, nil 139 } 140 141 d = d[nameLen:] 142 } 143 } 144 data = data[length:] 145 } 146 147 return nil, errNotTLS 148 } 149 150 func SniffTLS(b []byte) (*string, error) { 151 if len(b) < 5 { 152 return nil, ErrNoClue 153 } 154 155 if b[0] != 0x16 /* TLS Handshake */ { 156 return nil, errNotTLS 157 } 158 if !IsValidTLSVersion(b[1], b[2]) { 159 return nil, errNotTLS 160 } 161 headerLen := int(binary.BigEndian.Uint16(b[3:5])) 162 if 5+headerLen > len(b) { 163 return nil, ErrNoClue 164 } 165 166 domain, err := ReadClientHello(b[5 : 5+headerLen]) 167 if err == nil { 168 return domain, nil 169 } 170 return nil, err 171 }