github.com/sagernet/sing-box@v1.9.0-rc.20/common/tls/std_client.go (about)

     1  package tls
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"net"
     8  	"net/netip"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/sagernet/sing-box/option"
    13  	E "github.com/sagernet/sing/common/exceptions"
    14  	"github.com/sagernet/sing/common/ntp"
    15  )
    16  
    17  type STDClientConfig struct {
    18  	config *tls.Config
    19  }
    20  
    21  func (s *STDClientConfig) ServerName() string {
    22  	return s.config.ServerName
    23  }
    24  
    25  func (s *STDClientConfig) SetServerName(serverName string) {
    26  	s.config.ServerName = serverName
    27  }
    28  
    29  func (s *STDClientConfig) NextProtos() []string {
    30  	return s.config.NextProtos
    31  }
    32  
    33  func (s *STDClientConfig) SetNextProtos(nextProto []string) {
    34  	s.config.NextProtos = nextProto
    35  }
    36  
    37  func (s *STDClientConfig) Config() (*STDConfig, error) {
    38  	return s.config, nil
    39  }
    40  
    41  func (s *STDClientConfig) Client(conn net.Conn) (Conn, error) {
    42  	return tls.Client(conn, s.config), nil
    43  }
    44  
    45  func (s *STDClientConfig) Clone() Config {
    46  	return &STDClientConfig{s.config.Clone()}
    47  }
    48  
    49  func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
    50  	var serverName string
    51  	if options.ServerName != "" {
    52  		serverName = options.ServerName
    53  	} else if serverAddress != "" {
    54  		if _, err := netip.ParseAddr(serverName); err != nil {
    55  			serverName = serverAddress
    56  		}
    57  	}
    58  	if serverName == "" && !options.Insecure {
    59  		return nil, E.New("missing server_name or insecure=true")
    60  	}
    61  
    62  	var tlsConfig tls.Config
    63  	tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
    64  	if options.DisableSNI {
    65  		tlsConfig.ServerName = "127.0.0.1"
    66  	} else {
    67  		tlsConfig.ServerName = serverName
    68  	}
    69  	if options.Insecure {
    70  		tlsConfig.InsecureSkipVerify = options.Insecure
    71  	} else if options.DisableSNI {
    72  		tlsConfig.InsecureSkipVerify = true
    73  		tlsConfig.VerifyConnection = func(state tls.ConnectionState) error {
    74  			verifyOptions := x509.VerifyOptions{
    75  				DNSName:       serverName,
    76  				Intermediates: x509.NewCertPool(),
    77  			}
    78  			for _, cert := range state.PeerCertificates[1:] {
    79  				verifyOptions.Intermediates.AddCert(cert)
    80  			}
    81  			_, err := state.PeerCertificates[0].Verify(verifyOptions)
    82  			return err
    83  		}
    84  	}
    85  	if len(options.ALPN) > 0 {
    86  		tlsConfig.NextProtos = options.ALPN
    87  	}
    88  	if options.MinVersion != "" {
    89  		minVersion, err := ParseTLSVersion(options.MinVersion)
    90  		if err != nil {
    91  			return nil, E.Cause(err, "parse min_version")
    92  		}
    93  		tlsConfig.MinVersion = minVersion
    94  	}
    95  	if options.MaxVersion != "" {
    96  		maxVersion, err := ParseTLSVersion(options.MaxVersion)
    97  		if err != nil {
    98  			return nil, E.Cause(err, "parse max_version")
    99  		}
   100  		tlsConfig.MaxVersion = maxVersion
   101  	}
   102  	if options.CipherSuites != nil {
   103  	find:
   104  		for _, cipherSuite := range options.CipherSuites {
   105  			for _, tlsCipherSuite := range tls.CipherSuites() {
   106  				if cipherSuite == tlsCipherSuite.Name {
   107  					tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
   108  					continue find
   109  				}
   110  			}
   111  			return nil, E.New("unknown cipher_suite: ", cipherSuite)
   112  		}
   113  	}
   114  	var certificate []byte
   115  	if len(options.Certificate) > 0 {
   116  		certificate = []byte(strings.Join(options.Certificate, "\n"))
   117  	} else if options.CertificatePath != "" {
   118  		content, err := os.ReadFile(options.CertificatePath)
   119  		if err != nil {
   120  			return nil, E.Cause(err, "read certificate")
   121  		}
   122  		certificate = content
   123  	}
   124  	if len(certificate) > 0 {
   125  		certPool := x509.NewCertPool()
   126  		if !certPool.AppendCertsFromPEM(certificate) {
   127  			return nil, E.New("failed to parse certificate:\n\n", certificate)
   128  		}
   129  		tlsConfig.RootCAs = certPool
   130  	}
   131  	return &STDClientConfig{&tlsConfig}, nil
   132  }