github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/transport/internet/tls/tls.go (about) 1 package tls 2 3 import ( 4 "context" 5 "crypto/rand" 6 "crypto/tls" 7 "math/big" 8 "time" 9 10 utls "github.com/refraction-networking/utls" 11 "github.com/xmplusdev/xmcore/common/buf" 12 "github.com/xmplusdev/xmcore/common/net" 13 ) 14 15 //go:generate go run github.com/xmplusdev/xmcore/common/errors/errorgen 16 17 type Interface interface { 18 net.Conn 19 HandshakeContext(ctx context.Context) error 20 VerifyHostname(host string) error 21 NegotiatedProtocol() (name string, mutual bool) 22 } 23 24 var _ buf.Writer = (*Conn)(nil) 25 26 type Conn struct { 27 *tls.Conn 28 } 29 30 const tlsCloseTimeout = 250 * time.Millisecond 31 32 func (c *Conn) Close() error { 33 timer := time.AfterFunc(tlsCloseTimeout, func() { 34 c.Conn.NetConn().Close() 35 }) 36 defer timer.Stop() 37 return c.Conn.Close() 38 } 39 40 func (c *Conn) WriteMultiBuffer(mb buf.MultiBuffer) error { 41 mb = buf.Compact(mb) 42 mb, err := buf.WriteMultiBuffer(c, mb) 43 buf.ReleaseMulti(mb) 44 return err 45 } 46 47 func (c *Conn) HandshakeAddressContext(ctx context.Context) net.Address { 48 if err := c.HandshakeContext(ctx); err != nil { 49 return nil 50 } 51 state := c.ConnectionState() 52 if state.ServerName == "" { 53 return nil 54 } 55 return net.ParseAddress(state.ServerName) 56 } 57 58 func (c *Conn) NegotiatedProtocol() (name string, mutual bool) { 59 state := c.ConnectionState() 60 return state.NegotiatedProtocol, state.NegotiatedProtocolIsMutual 61 } 62 63 // Client initiates a TLS client handshake on the given connection. 64 func Client(c net.Conn, config *tls.Config) net.Conn { 65 tlsConn := tls.Client(c, config) 66 return &Conn{Conn: tlsConn} 67 } 68 69 // Server initiates a TLS server handshake on the given connection. 70 func Server(c net.Conn, config *tls.Config) net.Conn { 71 tlsConn := tls.Server(c, config) 72 return &Conn{Conn: tlsConn} 73 } 74 75 type UConn struct { 76 *utls.UConn 77 } 78 79 func (c *UConn) Close() error { 80 timer := time.AfterFunc(tlsCloseTimeout, func() { 81 c.Conn.NetConn().Close() 82 }) 83 defer timer.Stop() 84 return c.Conn.Close() 85 } 86 87 func (c *UConn) HandshakeAddressContext(ctx context.Context) net.Address { 88 if err := c.HandshakeContext(ctx); err != nil { 89 return nil 90 } 91 state := c.ConnectionState() 92 if state.ServerName == "" { 93 return nil 94 } 95 return net.ParseAddress(state.ServerName) 96 } 97 98 // WebsocketHandshake basically calls UConn.Handshake inside it but it will only send 99 // http/1.1 in its ALPN. 100 func (c *UConn) WebsocketHandshakeContext(ctx context.Context) error { 101 // Build the handshake state. This will apply every variable of the TLS of the 102 // fingerprint in the UConn 103 if err := c.BuildHandshakeState(); err != nil { 104 return err 105 } 106 // Iterate over extensions and check for utls.ALPNExtension 107 hasALPNExtension := false 108 for _, extension := range c.Extensions { 109 if alpn, ok := extension.(*utls.ALPNExtension); ok { 110 hasALPNExtension = true 111 alpn.AlpnProtocols = []string{"http/1.1"} 112 break 113 } 114 } 115 if !hasALPNExtension { // Append extension if doesn't exists 116 c.Extensions = append(c.Extensions, &utls.ALPNExtension{AlpnProtocols: []string{"http/1.1"}}) 117 } 118 // Rebuild the client hello and do the handshake 119 if err := c.BuildHandshakeState(); err != nil { 120 return err 121 } 122 return c.HandshakeContext(ctx) 123 } 124 125 func (c *UConn) NegotiatedProtocol() (name string, mutual bool) { 126 state := c.ConnectionState() 127 return state.NegotiatedProtocol, state.NegotiatedProtocolIsMutual 128 } 129 130 func UClient(c net.Conn, config *tls.Config, fingerprint *utls.ClientHelloID) net.Conn { 131 utlsConn := utls.UClient(c, copyConfig(config), *fingerprint) 132 return &UConn{UConn: utlsConn} 133 } 134 135 func copyConfig(c *tls.Config) *utls.Config { 136 return &utls.Config{ 137 RootCAs: c.RootCAs, 138 ServerName: c.ServerName, 139 InsecureSkipVerify: c.InsecureSkipVerify, 140 VerifyPeerCertificate: c.VerifyPeerCertificate, 141 KeyLogWriter: c.KeyLogWriter, 142 } 143 } 144 145 func init() { 146 bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(ModernFingerprints)))) 147 stopAt := int(bigInt.Int64()) 148 i := 0 149 for _, v := range ModernFingerprints { 150 if i == stopAt { 151 PresetFingerprints["random"] = v 152 break 153 } 154 i++ 155 } 156 weights := utls.DefaultWeights 157 weights.TLSVersMax_Set_VersionTLS13 = 1 158 weights.FirstKeyShare_Set_CurveP256 = 0 159 randomized := utls.HelloRandomized 160 randomized.Seed, _ = utls.NewPRNGSeed() 161 randomized.Weights = &weights 162 PresetFingerprints["randomized"] = &randomized 163 } 164 165 func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) { 166 if name == "" { 167 return 168 } 169 if fingerprint = PresetFingerprints[name]; fingerprint != nil { 170 return 171 } 172 if fingerprint = ModernFingerprints[name]; fingerprint != nil { 173 return 174 } 175 if fingerprint = OtherFingerprints[name]; fingerprint != nil { 176 return 177 } 178 return 179 } 180 181 var PresetFingerprints = map[string]*utls.ClientHelloID{ 182 // Recommended preset options in GUI clients 183 "chrome": &utls.HelloChrome_Auto, 184 "firefox": &utls.HelloFirefox_Auto, 185 "safari": &utls.HelloSafari_Auto, 186 "ios": &utls.HelloIOS_Auto, 187 "android": &utls.HelloAndroid_11_OkHttp, 188 "edge": &utls.HelloEdge_Auto, 189 "360": &utls.Hello360_Auto, 190 "qq": &utls.HelloQQ_Auto, 191 "random": nil, 192 "randomized": nil, 193 } 194 195 var ModernFingerprints = map[string]*utls.ClientHelloID{ 196 // One of these will be chosen as `random` at startup 197 "hellofirefox_99": &utls.HelloFirefox_99, 198 "hellofirefox_102": &utls.HelloFirefox_102, 199 "hellofirefox_105": &utls.HelloFirefox_105, 200 "hellochrome_83": &utls.HelloChrome_83, 201 "hellochrome_87": &utls.HelloChrome_87, 202 "hellochrome_96": &utls.HelloChrome_96, 203 "hellochrome_100": &utls.HelloChrome_100, 204 "hellochrome_102": &utls.HelloChrome_102, 205 "hellochrome_106_shuffle": &utls.HelloChrome_106_Shuffle, 206 "helloios_13": &utls.HelloIOS_13, 207 "helloios_14": &utls.HelloIOS_14, 208 "helloedge_85": &utls.HelloEdge_85, 209 "helloedge_106": &utls.HelloEdge_106, 210 "hellosafari_16_0": &utls.HelloSafari_16_0, 211 "hello360_11_0": &utls.Hello360_11_0, 212 "helloqq_11_1": &utls.HelloQQ_11_1, 213 } 214 215 var OtherFingerprints = map[string]*utls.ClientHelloID{ 216 // Golang, randomized, auto, and fingerprints that are too old 217 "hellogolang": &utls.HelloGolang, 218 "hellorandomized": &utls.HelloRandomized, 219 "hellorandomizedalpn": &utls.HelloRandomizedALPN, 220 "hellorandomizednoalpn": &utls.HelloRandomizedNoALPN, 221 "hellofirefox_auto": &utls.HelloFirefox_Auto, 222 "hellofirefox_55": &utls.HelloFirefox_55, 223 "hellofirefox_56": &utls.HelloFirefox_56, 224 "hellofirefox_63": &utls.HelloFirefox_63, 225 "hellofirefox_65": &utls.HelloFirefox_65, 226 "hellochrome_auto": &utls.HelloChrome_Auto, 227 "hellochrome_58": &utls.HelloChrome_58, 228 "hellochrome_62": &utls.HelloChrome_62, 229 "hellochrome_70": &utls.HelloChrome_70, 230 "hellochrome_72": &utls.HelloChrome_72, 231 "helloios_auto": &utls.HelloIOS_Auto, 232 "helloios_11_1": &utls.HelloIOS_11_1, 233 "helloios_12_1": &utls.HelloIOS_12_1, 234 "helloandroid_11_okhttp": &utls.HelloAndroid_11_OkHttp, 235 "helloedge_auto": &utls.HelloEdge_Auto, 236 "hellosafari_auto": &utls.HelloSafari_Auto, 237 "hello360_auto": &utls.Hello360_Auto, 238 "hello360_7_5": &utls.Hello360_7_5, 239 "helloqq_auto": &utls.HelloQQ_Auto, 240 }