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 }