sing-box/common/tls/std_client.go

132 lines
3.4 KiB
Go
Raw Normal View History

2022-09-09 10:45:10 +00:00
package tls
2022-07-18 12:40:14 +00:00
import (
2023-08-29 05:43:42 +00:00
"context"
2022-07-18 12:40:14 +00:00
"crypto/tls"
"crypto/x509"
"net"
"net/netip"
"os"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
2023-08-29 05:43:42 +00:00
"github.com/sagernet/sing/common/ntp"
2022-07-18 12:40:14 +00:00
)
type STDClientConfig struct {
2022-07-18 12:40:14 +00:00
config *tls.Config
}
func (s *STDClientConfig) ServerName() string {
return s.config.ServerName
}
func (s *STDClientConfig) SetServerName(serverName string) {
s.config.ServerName = serverName
}
func (s *STDClientConfig) NextProtos() []string {
return s.config.NextProtos
}
func (s *STDClientConfig) SetNextProtos(nextProto []string) {
s.config.NextProtos = nextProto
}
func (s *STDClientConfig) Config() (*STDConfig, error) {
return s.config, nil
}
func (s *STDClientConfig) Client(conn net.Conn) (Conn, error) {
return tls.Client(conn, s.config), nil
}
func (s *STDClientConfig) Clone() Config {
return &STDClientConfig{s.config.Clone()}
}
2023-08-29 05:43:42 +00:00
func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
2022-07-18 12:40:14 +00:00
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
2022-09-09 10:45:10 +00:00
if _, err := netip.ParseAddr(serverName); err != nil {
2022-07-18 12:40:14 +00:00
serverName = serverAddress
}
}
2022-08-24 08:11:41 +00:00
if serverName == "" && !options.Insecure {
2022-07-18 12:40:14 +00:00
return nil, E.New("missing server_name or insecure=true")
}
var tlsConfig tls.Config
2023-08-29 05:43:42 +00:00
tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
2022-07-18 12:40:14 +00:00
if options.DisableSNI {
tlsConfig.ServerName = "127.0.0.1"
} else {
tlsConfig.ServerName = serverName
}
if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI {
tlsConfig.InsecureSkipVerify = true
tlsConfig.VerifyConnection = func(state tls.ConnectionState) error {
verifyOptions := x509.VerifyOptions{
DNSName: serverName,
Intermediates: x509.NewCertPool(),
}
for _, cert := range state.PeerCertificates[1:] {
verifyOptions.Intermediates.AddCert(cert)
}
_, err := state.PeerCertificates[0].Verify(verifyOptions)
return err
}
}
2022-07-25 00:14:09 +00:00
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
2022-09-09 10:45:10 +00:00
minVersion, err := ParseTLSVersion(options.MinVersion)
2022-07-25 00:14:09 +00:00
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
2022-09-09 10:45:10 +00:00
maxVersion, err := ParseTLSVersion(options.MaxVersion)
2022-07-25 00:14:09 +00:00
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
if options.Certificate != "" {
certificate = []byte(options.Certificate)
} 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 {
2022-07-27 04:03:07 +00:00
certPool := x509.NewCertPool()
2022-08-08 00:56:04 +00:00
if !certPool.AppendCertsFromPEM(certificate) {
return nil, E.New("failed to parse certificate:\n\n", certificate)
2022-07-25 00:14:09 +00:00
}
tlsConfig.RootCAs = certPool
}
return &STDClientConfig{&tlsConfig}, nil
2022-08-22 10:53:47 +00:00
}