github.com/sagernet/sing-box@v1.9.0-rc.20/common/tls/utls_client.go (about) 1 //go:build with_utls 2 3 package tls 4 5 import ( 6 "context" 7 "crypto/tls" 8 "crypto/x509" 9 "math/rand" 10 "net" 11 "net/netip" 12 "os" 13 "strings" 14 15 "github.com/sagernet/sing-box/option" 16 E "github.com/sagernet/sing/common/exceptions" 17 "github.com/sagernet/sing/common/ntp" 18 utls "github.com/sagernet/utls" 19 20 "golang.org/x/net/http2" 21 ) 22 23 type UTLSClientConfig struct { 24 config *utls.Config 25 id utls.ClientHelloID 26 } 27 28 func (e *UTLSClientConfig) ServerName() string { 29 return e.config.ServerName 30 } 31 32 func (e *UTLSClientConfig) SetServerName(serverName string) { 33 e.config.ServerName = serverName 34 } 35 36 func (e *UTLSClientConfig) NextProtos() []string { 37 return e.config.NextProtos 38 } 39 40 func (e *UTLSClientConfig) SetNextProtos(nextProto []string) { 41 if len(nextProto) == 1 && nextProto[0] == http2.NextProtoTLS { 42 nextProto = append(nextProto, "http/1.1") 43 } 44 e.config.NextProtos = nextProto 45 } 46 47 func (e *UTLSClientConfig) Config() (*STDConfig, error) { 48 return nil, E.New("unsupported usage for uTLS") 49 } 50 51 func (e *UTLSClientConfig) Client(conn net.Conn) (Conn, error) { 52 return &utlsALPNWrapper{utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}, e.config.NextProtos}, nil 53 } 54 55 func (e *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) { 56 e.config.SessionIDGenerator = generator 57 } 58 59 func (e *UTLSClientConfig) Clone() Config { 60 return &UTLSClientConfig{ 61 config: e.config.Clone(), 62 id: e.id, 63 } 64 } 65 66 type utlsConnWrapper struct { 67 *utls.UConn 68 } 69 70 func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState { 71 state := c.Conn.ConnectionState() 72 return tls.ConnectionState{ 73 Version: state.Version, 74 HandshakeComplete: state.HandshakeComplete, 75 DidResume: state.DidResume, 76 CipherSuite: state.CipherSuite, 77 NegotiatedProtocol: state.NegotiatedProtocol, 78 NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual, 79 ServerName: state.ServerName, 80 PeerCertificates: state.PeerCertificates, 81 VerifiedChains: state.VerifiedChains, 82 SignedCertificateTimestamps: state.SignedCertificateTimestamps, 83 OCSPResponse: state.OCSPResponse, 84 TLSUnique: state.TLSUnique, 85 } 86 } 87 88 func (c *utlsConnWrapper) Upstream() any { 89 return c.UConn 90 } 91 92 type utlsALPNWrapper struct { 93 utlsConnWrapper 94 nextProtocols []string 95 } 96 97 func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error { 98 if len(c.nextProtocols) > 0 { 99 err := c.BuildHandshakeState() 100 if err != nil { 101 return err 102 } 103 for _, extension := range c.Extensions { 104 if alpnExtension, isALPN := extension.(*utls.ALPNExtension); isALPN { 105 alpnExtension.AlpnProtocols = c.nextProtocols 106 err = c.BuildHandshakeState() 107 if err != nil { 108 return err 109 } 110 break 111 } 112 } 113 } 114 return c.UConn.HandshakeContext(ctx) 115 } 116 117 func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (*UTLSClientConfig, error) { 118 var serverName string 119 if options.ServerName != "" { 120 serverName = options.ServerName 121 } else if serverAddress != "" { 122 if _, err := netip.ParseAddr(serverName); err != nil { 123 serverName = serverAddress 124 } 125 } 126 if serverName == "" && !options.Insecure { 127 return nil, E.New("missing server_name or insecure=true") 128 } 129 130 var tlsConfig utls.Config 131 tlsConfig.Time = ntp.TimeFuncFromContext(ctx) 132 if options.DisableSNI { 133 tlsConfig.ServerName = "127.0.0.1" 134 } else { 135 tlsConfig.ServerName = serverName 136 } 137 if options.Insecure { 138 tlsConfig.InsecureSkipVerify = options.Insecure 139 } else if options.DisableSNI { 140 return nil, E.New("disable_sni is unsupported in uTLS") 141 } 142 if len(options.ALPN) > 0 { 143 tlsConfig.NextProtos = options.ALPN 144 } 145 if options.MinVersion != "" { 146 minVersion, err := ParseTLSVersion(options.MinVersion) 147 if err != nil { 148 return nil, E.Cause(err, "parse min_version") 149 } 150 tlsConfig.MinVersion = minVersion 151 } 152 if options.MaxVersion != "" { 153 maxVersion, err := ParseTLSVersion(options.MaxVersion) 154 if err != nil { 155 return nil, E.Cause(err, "parse max_version") 156 } 157 tlsConfig.MaxVersion = maxVersion 158 } 159 if options.CipherSuites != nil { 160 find: 161 for _, cipherSuite := range options.CipherSuites { 162 for _, tlsCipherSuite := range tls.CipherSuites() { 163 if cipherSuite == tlsCipherSuite.Name { 164 tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID) 165 continue find 166 } 167 } 168 return nil, E.New("unknown cipher_suite: ", cipherSuite) 169 } 170 } 171 var certificate []byte 172 if len(options.Certificate) > 0 { 173 certificate = []byte(strings.Join(options.Certificate, "\n")) 174 } else if options.CertificatePath != "" { 175 content, err := os.ReadFile(options.CertificatePath) 176 if err != nil { 177 return nil, E.Cause(err, "read certificate") 178 } 179 certificate = content 180 } 181 if len(certificate) > 0 { 182 certPool := x509.NewCertPool() 183 if !certPool.AppendCertsFromPEM(certificate) { 184 return nil, E.New("failed to parse certificate:\n\n", certificate) 185 } 186 tlsConfig.RootCAs = certPool 187 } 188 id, err := uTLSClientHelloID(options.UTLS.Fingerprint) 189 if err != nil { 190 return nil, err 191 } 192 return &UTLSClientConfig{&tlsConfig, id}, nil 193 } 194 195 var ( 196 randomFingerprint utls.ClientHelloID 197 randomizedFingerprint utls.ClientHelloID 198 ) 199 200 func init() { 201 modernFingerprints := []utls.ClientHelloID{ 202 utls.HelloChrome_Auto, 203 utls.HelloFirefox_Auto, 204 utls.HelloEdge_Auto, 205 utls.HelloSafari_Auto, 206 utls.HelloIOS_Auto, 207 } 208 randomFingerprint = modernFingerprints[rand.Intn(len(modernFingerprints))] 209 210 weights := utls.DefaultWeights 211 weights.TLSVersMax_Set_VersionTLS13 = 1 212 weights.FirstKeyShare_Set_CurveP256 = 0 213 randomizedFingerprint = utls.HelloRandomized 214 randomizedFingerprint.Seed, _ = utls.NewPRNGSeed() 215 randomizedFingerprint.Weights = &weights 216 } 217 218 func uTLSClientHelloID(name string) (utls.ClientHelloID, error) { 219 switch name { 220 case "chrome", "": 221 return utls.HelloChrome_Auto, nil 222 case "chrome_psk": 223 return utls.HelloChrome_100_PSK, nil 224 case "chrome_psk_shuffle": 225 return utls.HelloChrome_112_PSK_Shuf, nil 226 case "chrome_padding_psk_shuffle": 227 return utls.HelloChrome_114_Padding_PSK_Shuf, nil 228 case "chrome_pq": 229 return utls.HelloChrome_115_PQ, nil 230 case "chrome_pq_psk": 231 return utls.HelloChrome_115_PQ_PSK, nil 232 case "firefox": 233 return utls.HelloFirefox_Auto, nil 234 case "edge": 235 return utls.HelloEdge_Auto, nil 236 case "safari": 237 return utls.HelloSafari_Auto, nil 238 case "360": 239 return utls.Hello360_Auto, nil 240 case "qq": 241 return utls.HelloQQ_Auto, nil 242 case "ios": 243 return utls.HelloIOS_Auto, nil 244 case "android": 245 return utls.HelloAndroid_11_OkHttp, nil 246 case "random": 247 return randomFingerprint, nil 248 case "randomized": 249 return randomizedFingerprint, nil 250 default: 251 return utls.ClientHelloID{}, E.New("unknown uTLS fingerprint: ", name) 252 } 253 }