Files
sing-box-extended-mirror/common/tls/utls_client.go
T

271 lines
7.5 KiB
Go
Raw Normal View History

2022-09-10 10:27:00 +08:00
//go:build with_utls
package tls
import (
2023-05-09 17:39:50 +08:00
"context"
2022-09-10 10:27:00 +08:00
"crypto/tls"
"crypto/x509"
2023-02-28 21:10:11 +08:00
"math/rand"
2022-09-10 10:27:00 +08:00
"net"
"os"
2023-09-19 19:59:07 +08:00
"strings"
2025-06-12 08:58:07 +08:00
"time"
2022-09-10 10:27:00 +08:00
2025-01-08 10:34:45 +08:00
"github.com/sagernet/sing-box/adapter"
2025-06-12 08:58:07 +08:00
"github.com/sagernet/sing-box/common/tlsfragment"
2022-09-10 10:27:00 +08:00
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
2023-08-29 13:43:42 +08:00
"github.com/sagernet/sing/common/ntp"
2023-02-28 21:16:31 +08:00
2025-05-03 11:35:53 +08:00
utls "github.com/metacubex/utls"
2023-02-28 21:16:31 +08:00
"golang.org/x/net/http2"
2022-09-10 10:27:00 +08:00
)
2022-11-09 11:43:03 +08:00
type UTLSClientConfig struct {
2025-06-12 08:58:07 +08:00
ctx context.Context
config *utls.Config
id utls.ClientHelloID
fragment bool
fragmentFallbackDelay time.Duration
recordFragment bool
2022-09-10 10:27:00 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) ServerName() string {
return c.config.ServerName
2022-11-09 11:43:03 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) SetServerName(serverName string) {
c.config.ServerName = serverName
2022-11-09 11:43:03 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) NextProtos() []string {
return c.config.NextProtos
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) SetNextProtos(nextProto []string) {
2023-02-28 21:16:31 +08:00
if len(nextProto) == 1 && nextProto[0] == http2.NextProtoTLS {
nextProto = append(nextProto, "http/1.1")
}
2025-06-12 09:13:23 +08:00
c.config.NextProtos = nextProto
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) Config() (*STDConfig, error) {
2022-09-10 10:27:00 +08:00
return nil, E.New("unsupported usage for uTLS")
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) Client(conn net.Conn) (Conn, error) {
if c.recordFragment {
conn = tf.NewConn(conn, c.ctx, c.fragment, c.recordFragment, c.fragmentFallbackDelay)
2025-06-12 08:58:07 +08:00
}
2025-06-12 09:13:23 +08:00
return &utlsALPNWrapper{utlsConnWrapper{utls.UClient(conn, c.config.Clone(), c.id)}, c.config.NextProtos}, nil
2022-09-10 10:27:00 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {
c.config.SessionIDGenerator = generator
2023-02-20 14:08:13 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) Clone() Config {
return &UTLSClientConfig{
2025-06-12 09:13:23 +08:00
c.ctx, c.config.Clone(), c.id, c.fragment, c.fragmentFallbackDelay, c.recordFragment,
}
}
2025-06-12 09:13:23 +08:00
func (c *UTLSClientConfig) ECHConfigList() []byte {
return c.config.EncryptedClientHelloConfigList
}
func (c *UTLSClientConfig) SetECHConfigList(EncryptedClientHelloConfigList []byte) {
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
}
2022-09-10 10:27:00 +08:00
type utlsConnWrapper struct {
*utls.UConn
}
func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
state := c.Conn.ConnectionState()
2024-12-29 18:39:22 +08:00
//nolint:staticcheck
return tls.ConnectionState{
Version: state.Version,
HandshakeComplete: state.HandshakeComplete,
DidResume: state.DidResume,
CipherSuite: state.CipherSuite,
NegotiatedProtocol: state.NegotiatedProtocol,
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
ServerName: state.ServerName,
PeerCertificates: state.PeerCertificates,
VerifiedChains: state.VerifiedChains,
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
OCSPResponse: state.OCSPResponse,
TLSUnique: state.TLSUnique,
}
}
func (c *utlsConnWrapper) Upstream() any {
return c.UConn
2022-11-09 11:43:03 +08:00
}
2023-05-09 17:39:50 +08:00
type utlsALPNWrapper struct {
utlsConnWrapper
nextProtocols []string
}
func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error {
if len(c.nextProtocols) > 0 {
err := c.BuildHandshakeState()
if err != nil {
return err
}
for _, extension := range c.Extensions {
if alpnExtension, isALPN := extension.(*utls.ALPNExtension); isALPN {
alpnExtension.AlpnProtocols = c.nextProtocols
err = c.BuildHandshakeState()
if err != nil {
return err
}
break
}
}
}
return c.UConn.HandshakeContext(ctx)
}
2025-06-12 09:13:23 +08:00
func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
2022-09-10 10:27:00 +08:00
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
2025-06-12 09:13:23 +08:00
serverName = serverAddress
2022-09-10 10:27:00 +08:00
}
if serverName == "" && !options.Insecure {
return nil, E.New("missing server_name or insecure=true")
}
var tlsConfig utls.Config
2023-08-29 13:43:42 +08:00
tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
2025-01-08 10:34:45 +08:00
tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx)
if !options.DisableSNI {
tlsConfig.ServerName = serverName
}
2022-09-10 10:27:00 +08:00
if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI {
if options.Reality != nil && options.Reality.Enabled {
return nil, E.New("disable_sni is unsupported in reality")
}
tlsConfig.InsecureServerNameToVerify = serverName
2022-09-10 10:27:00 +08:00
}
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
minVersion, err := ParseTLSVersion(options.MinVersion)
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
maxVersion, err := ParseTLSVersion(options.MaxVersion)
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
tlsConfig.MaxVersion = maxVersion
}
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range tls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
var certificate []byte
2023-09-19 19:59:07 +08:00
if len(options.Certificate) > 0 {
certificate = []byte(strings.Join(options.Certificate, "\n"))
2022-09-10 10:27:00 +08:00
} else if options.CertificatePath != "" {
content, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate")
}
certificate = content
}
if len(certificate) > 0 {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certificate) {
return nil, E.New("failed to parse certificate:\n\n", certificate)
}
tlsConfig.RootCAs = certPool
}
id, err := uTLSClientHelloID(options.UTLS.Fingerprint)
if err != nil {
return nil, err
}
2025-06-12 09:13:23 +08:00
uConfig := &UTLSClientConfig{ctx, &tlsConfig, id, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
if options.ECH != nil && options.ECH.Enabled {
if options.Reality != nil && options.Reality.Enabled {
return nil, E.New("Reality is conflict with ECH")
}
return parseECHClientConfig(ctx, uConfig, options)
} else {
return uConfig, nil
}
}
2023-03-05 13:07:26 +08:00
var (
randomFingerprint utls.ClientHelloID
randomizedFingerprint utls.ClientHelloID
)
2023-02-28 21:10:11 +08:00
func init() {
modernFingerprints := []utls.ClientHelloID{
utls.HelloChrome_Auto,
utls.HelloFirefox_Auto,
utls.HelloEdge_Auto,
utls.HelloSafari_Auto,
utls.HelloIOS_Auto,
}
randomFingerprint = modernFingerprints[rand.Intn(len(modernFingerprints))]
2023-03-05 13:07:26 +08:00
weights := utls.DefaultWeights
weights.TLSVersMax_Set_VersionTLS13 = 1
weights.FirstKeyShare_Set_CurveP256 = 0
randomizedFingerprint = utls.HelloRandomized
randomizedFingerprint.Seed, _ = utls.NewPRNGSeed()
randomizedFingerprint.Weights = &weights
2023-02-28 21:10:11 +08:00
}
func uTLSClientHelloID(name string) (utls.ClientHelloID, error) {
switch name {
2025-06-12 09:13:23 +08:00
case "chrome_psk", "chrome_psk_shuffle", "chrome_padding_psk_shuffle", "chrome_pq", "chrome_pq_psk":
2024-09-23 16:51:55 +08:00
fallthrough
2022-09-10 10:27:00 +08:00
case "chrome", "":
return utls.HelloChrome_Auto, nil
2022-09-10 10:27:00 +08:00
case "firefox":
return utls.HelloFirefox_Auto, nil
2022-10-26 19:31:57 +08:00
case "edge":
return utls.HelloEdge_Auto, nil
2022-10-26 19:31:57 +08:00
case "safari":
return utls.HelloSafari_Auto, nil
2022-10-26 19:31:57 +08:00
case "360":
return utls.Hello360_Auto, nil
2022-10-26 19:31:57 +08:00
case "qq":
return utls.HelloQQ_Auto, nil
2022-09-10 10:27:00 +08:00
case "ios":
return utls.HelloIOS_Auto, nil
2022-09-10 10:27:00 +08:00
case "android":
return utls.HelloAndroid_11_OkHttp, nil
2022-09-10 10:27:00 +08:00
case "random":
2023-02-28 21:10:11 +08:00
return randomFingerprint, nil
case "randomized":
2023-03-05 13:07:26 +08:00
return randomizedFingerprint, nil
2022-10-07 20:18:50 +08:00
default:
return utls.ClientHelloID{}, E.New("unknown uTLS fingerprint: ", name)
2022-09-10 10:27:00 +08:00
}
}