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