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