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  }