世界 %!s(int64=3) %!d(string=hai) anos
pai
achega
ee7e976084

+ 1 - 1
inbound/tls_acme.go → common/tls/acme.go

@@ -1,6 +1,6 @@
 //go:build with_acme
 
-package inbound
+package tls
 
 import (
 	"context"

+ 1 - 1
inbound/tls_acme_stub.go → common/tls/acme_stub.go

@@ -1,6 +1,6 @@
 //go:build !with_acme
 
-package inbound
+package tls
 
 import (
 	"context"

+ 57 - 0
common/tls/client.go

@@ -0,0 +1,57 @@
+package tls
+
+import (
+	"context"
+	"net"
+	"os"
+
+	"github.com/sagernet/sing-box/adapter"
+	C "github.com/sagernet/sing-box/constant"
+	"github.com/sagernet/sing-box/option"
+	M "github.com/sagernet/sing/common/metadata"
+	N "github.com/sagernet/sing/common/network"
+)
+
+func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
+	config, err := NewClient(router, serverAddress, options)
+	if err != nil {
+		return nil, err
+	}
+	return NewDialer(dialer, config), nil
+}
+
+func NewClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
+	return newStdClient(serverAddress, options)
+}
+
+func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (net.Conn, error) {
+	tlsConn := config.Client(conn)
+	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
+	defer cancel()
+	err := tlsConn.HandshakeContext(ctx)
+	return tlsConn, err
+}
+
+type Dialer struct {
+	dialer N.Dialer
+	config Config
+}
+
+func NewDialer(dialer N.Dialer, config Config) N.Dialer {
+	return &Dialer{dialer, config}
+}
+
+func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
+	if network != N.NetworkTCP {
+		return nil, os.ErrInvalid
+	}
+	conn, err := d.dialer.DialContext(ctx, network, destination)
+	if err != nil {
+		return nil, err
+	}
+	return ClientHandshake(ctx, conn, d.config)
+}
+
+func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
+	return nil, os.ErrInvalid
+}

+ 12 - 0
common/tls/common.go

@@ -0,0 +1,12 @@
+package tls
+
+const (
+	VersionTLS10 = 0x0301
+	VersionTLS11 = 0x0302
+	VersionTLS12 = 0x0303
+	VersionTLS13 = 0x0304
+
+	// Deprecated: SSLv3 is cryptographically broken, and is no longer
+	// supported by this package. See golang.org/issue/32716.
+	VersionSSL30 = 0x0300
+)

+ 46 - 0
common/tls/config.go

@@ -0,0 +1,46 @@
+package tls
+
+import (
+	"context"
+	"crypto/tls"
+	"net"
+
+	"github.com/sagernet/sing-box/adapter"
+	E "github.com/sagernet/sing/common/exceptions"
+)
+
+type (
+	STDConfig = tls.Config
+	STDConn   = tls.Conn
+)
+
+type Config interface {
+	Config() (*STDConfig, error)
+	Client(conn net.Conn) Conn
+}
+
+type ServerConfig interface {
+	Config
+	adapter.Service
+	Server(conn net.Conn) Conn
+}
+
+type Conn interface {
+	net.Conn
+	HandshakeContext(ctx context.Context) error
+}
+
+func ParseTLSVersion(version string) (uint16, error) {
+	switch version {
+	case "1.0":
+		return tls.VersionTLS10, nil
+	case "1.1":
+		return tls.VersionTLS11, nil
+	case "1.2":
+		return tls.VersionTLS12, nil
+	case "1.3":
+		return tls.VersionTLS13, nil
+	default:
+		return 0, E.New("unknown tls version:", version)
+	}
+}

+ 11 - 48
common/dialer/tls.go → common/tls/std_client.go

@@ -1,34 +1,26 @@
-package dialer
+package tls
 
 import (
-	"context"
 	"crypto/tls"
 	"crypto/x509"
 	"net"
 	"net/netip"
 	"os"
 
-	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	E "github.com/sagernet/sing/common/exceptions"
-	M "github.com/sagernet/sing/common/metadata"
-	N "github.com/sagernet/sing/common/network"
 )
 
-type TLSDialer struct {
-	dialer N.Dialer
+type stdClientConfig struct {
 	config *tls.Config
 }
 
-func TLSConfig(serverAddress string, options option.OutboundTLSOptions) (*tls.Config, error) {
-	if !options.Enabled {
-		return nil, nil
-	}
+func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) {
 	var serverName string
 	if options.ServerName != "" {
 		serverName = options.ServerName
 	} else if serverAddress != "" {
-		if _, err := netip.ParseAddr(serverName); err == nil {
+		if _, err := netip.ParseAddr(serverName); err != nil {
 			serverName = serverAddress
 		}
 	}
@@ -62,14 +54,14 @@ func TLSConfig(serverAddress string, options option.OutboundTLSOptions) (*tls.Co
 		tlsConfig.NextProtos = options.ALPN
 	}
 	if options.MinVersion != "" {
-		minVersion, err := option.ParseTLSVersion(options.MinVersion)
+		minVersion, err := ParseTLSVersion(options.MinVersion)
 		if err != nil {
 			return nil, E.Cause(err, "parse min_version")
 		}
 		tlsConfig.MinVersion = minVersion
 	}
 	if options.MaxVersion != "" {
-		maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
+		maxVersion, err := ParseTLSVersion(options.MaxVersion)
 		if err != nil {
 			return nil, E.Cause(err, "parse max_version")
 		}
@@ -104,42 +96,13 @@ func TLSConfig(serverAddress string, options option.OutboundTLSOptions) (*tls.Co
 		}
 		tlsConfig.RootCAs = certPool
 	}
-	return &tlsConfig, nil
-}
-
-func NewTLS(dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
-	if !options.Enabled {
-		return dialer, nil
-	}
-	tlsConfig, err := TLSConfig(serverAddress, options)
-	if err != nil {
-		return nil, err
-	}
-	return &TLSDialer{
-		dialer: dialer,
-		config: tlsConfig,
-	}, nil
-}
-
-func (d *TLSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
-	if network != N.NetworkTCP {
-		return nil, os.ErrInvalid
-	}
-	conn, err := d.dialer.DialContext(ctx, network, destination)
-	if err != nil {
-		return nil, err
-	}
-	return TLSClient(ctx, conn, d.config)
+	return &stdClientConfig{&tlsConfig}, nil
 }
 
-func (d *TLSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
-	return nil, os.ErrInvalid
+func (s *stdClientConfig) Config() (*STDConfig, error) {
+	return s.config, nil
 }
 
-func TLSClient(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) (*tls.Conn, error) {
-	tlsConn := tls.Client(conn, tlsConfig)
-	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
-	defer cancel()
-	err := tlsConn.HandshakeContext(ctx)
-	return tlsConn, err
+func (s *stdClientConfig) Client(conn net.Conn) Conn {
+	return tls.Client(conn, s.config)
 }

+ 109 - 102
inbound/tls.go → common/tls/std_server.go

@@ -1,8 +1,9 @@
-package inbound
+package tls
 
 import (
 	"context"
 	"crypto/tls"
+	"net"
 	"os"
 
 	"github.com/sagernet/sing-box/adapter"
@@ -14,9 +15,7 @@ import (
 	"github.com/fsnotify/fsnotify"
 )
 
-var _ adapter.Service = (*TLSConfig)(nil)
-
-type TLSConfig struct {
+type STDServerConfig struct {
 	config          *tls.Config
 	logger          log.Logger
 	acmeService     adapter.Service
@@ -27,11 +26,110 @@ type TLSConfig struct {
 	watcher         *fsnotify.Watcher
 }
 
-func (c *TLSConfig) Config() *tls.Config {
-	return c.config
+func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
+	if !options.Enabled {
+		return nil, nil
+	}
+	var tlsConfig *tls.Config
+	var acmeService adapter.Service
+	var err error
+	if options.ACME != nil && len(options.ACME.Domain) > 0 {
+		tlsConfig, acmeService, err = startACME(ctx, common.PtrValueOrDefault(options.ACME))
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		tlsConfig = &tls.Config{}
+	}
+	if options.ServerName != "" {
+		tlsConfig.ServerName = options.ServerName
+	}
+	if len(options.ALPN) > 0 {
+		tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...)
+	}
+	if options.MinVersion != "" {
+		minVersion, err := ParseTLSVersion(options.MinVersion)
+		if err != nil {
+			return nil, E.Cause(err, "parse min_version")
+		}
+		tlsConfig.MinVersion = minVersion
+	}
+	if options.MaxVersion != "" {
+		maxVersion, err := ParseTLSVersion(options.MaxVersion)
+		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
+	var key []byte
+	if acmeService == nil {
+		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 options.Key != "" {
+			key = []byte(options.Key)
+		} else if options.KeyPath != "" {
+			content, err := os.ReadFile(options.KeyPath)
+			if err != nil {
+				return nil, E.Cause(err, "read key")
+			}
+			key = content
+		}
+		if certificate == nil {
+			return nil, E.New("missing certificate")
+		}
+		if key == nil {
+			return nil, E.New("missing key")
+		}
+		keyPair, err := tls.X509KeyPair(certificate, key)
+		if err != nil {
+			return nil, E.Cause(err, "parse x509 key pair")
+		}
+		tlsConfig.Certificates = []tls.Certificate{keyPair}
+	}
+	return &STDServerConfig{
+		config:          tlsConfig,
+		logger:          logger,
+		acmeService:     acmeService,
+		certificate:     certificate,
+		key:             key,
+		certificatePath: options.CertificatePath,
+		keyPath:         options.KeyPath,
+	}, nil
+}
+
+func (c *STDServerConfig) Config() (*STDConfig, error) {
+	return c.config, nil
+}
+
+func (c *STDServerConfig) Client(conn net.Conn) Conn {
+	return tls.Client(conn, c.config)
 }
 
-func (c *TLSConfig) Start() error {
+func (c *STDServerConfig) Server(conn net.Conn) Conn {
+	return tls.Server(conn, c.config)
+}
+
+func (c *STDServerConfig) Start() error {
 	if c.acmeService != nil {
 		return c.acmeService.Start()
 	} else {
@@ -46,7 +144,7 @@ func (c *TLSConfig) Start() error {
 	}
 }
 
-func (c *TLSConfig) startWatcher() error {
+func (c *STDServerConfig) startWatcher() error {
 	watcher, err := fsnotify.NewWatcher()
 	if err != nil {
 		return err
@@ -68,7 +166,7 @@ func (c *TLSConfig) startWatcher() error {
 	return nil
 }
 
-func (c *TLSConfig) loopUpdate() {
+func (c *STDServerConfig) loopUpdate() {
 	for {
 		select {
 		case event, ok := <-c.watcher.Events:
@@ -91,7 +189,7 @@ func (c *TLSConfig) loopUpdate() {
 	}
 }
 
-func (c *TLSConfig) reloadKeyPair() error {
+func (c *STDServerConfig) reloadKeyPair() error {
 	if c.certificatePath != "" {
 		certificate, err := os.ReadFile(c.certificatePath)
 		if err != nil {
@@ -115,7 +213,7 @@ func (c *TLSConfig) reloadKeyPair() error {
 	return nil
 }
 
-func (c *TLSConfig) Close() error {
+func (c *STDServerConfig) Close() error {
 	if c.acmeService != nil {
 		return c.acmeService.Close()
 	}
@@ -124,94 +222,3 @@ func (c *TLSConfig) Close() error {
 	}
 	return nil
 }
-
-func NewTLSConfig(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (*TLSConfig, error) {
-	if !options.Enabled {
-		return nil, nil
-	}
-	var tlsConfig *tls.Config
-	var acmeService adapter.Service
-	var err error
-	if options.ACME != nil && len(options.ACME.Domain) > 0 {
-		tlsConfig, acmeService, err = startACME(ctx, common.PtrValueOrDefault(options.ACME))
-		if err != nil {
-			return nil, err
-		}
-	} else {
-		tlsConfig = &tls.Config{}
-	}
-	if options.ServerName != "" {
-		tlsConfig.ServerName = options.ServerName
-	}
-	if len(options.ALPN) > 0 {
-		tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...)
-	}
-	if options.MinVersion != "" {
-		minVersion, err := option.ParseTLSVersion(options.MinVersion)
-		if err != nil {
-			return nil, E.Cause(err, "parse min_version")
-		}
-		tlsConfig.MinVersion = minVersion
-	}
-	if options.MaxVersion != "" {
-		maxVersion, err := option.ParseTLSVersion(options.MaxVersion)
-		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
-	var key []byte
-	if acmeService == nil {
-		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 options.Key != "" {
-			key = []byte(options.Key)
-		} else if options.KeyPath != "" {
-			content, err := os.ReadFile(options.KeyPath)
-			if err != nil {
-				return nil, E.Cause(err, "read key")
-			}
-			key = content
-		}
-		if certificate == nil {
-			return nil, E.New("missing certificate")
-		}
-		if key == nil {
-			return nil, E.New("missing key")
-		}
-		keyPair, err := tls.X509KeyPair(certificate, key)
-		if err != nil {
-			return nil, E.Cause(err, "parse x509 key pair")
-		}
-		tlsConfig.Certificates = []tls.Certificate{keyPair}
-	}
-	return &TLSConfig{
-		config:          tlsConfig,
-		logger:          logger,
-		acmeService:     acmeService,
-		certificate:     certificate,
-		key:             key,
-		certificatePath: options.CertificatePath,
-		keyPath:         options.KeyPath,
-	}, nil
-}

+ 5 - 5
inbound/http.go

@@ -3,11 +3,11 @@ package inbound
 import (
 	std_bufio "bufio"
 	"context"
-	"crypto/tls"
 	"net"
 	"os"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -26,7 +26,7 @@ var (
 type HTTP struct {
 	myInboundAdapter
 	authenticator auth.Authenticator
-	tlsConfig     *TLSConfig
+	tlsConfig     tls.ServerConfig
 }
 
 func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (*HTTP, error) {
@@ -44,7 +44,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
 		authenticator: auth.NewAuthenticator(options.Users),
 	}
 	if options.TLS != nil {
-		tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
+		tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
 		if err != nil {
 			return nil, err
 		}
@@ -67,13 +67,13 @@ func (h *HTTP) Start() error {
 func (h *HTTP) Close() error {
 	return common.Close(
 		&h.myInboundAdapter,
-		common.PtrOrNil(h.tlsConfig),
+		h.tlsConfig,
 	)
 }
 
 func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 	if h.tlsConfig != nil {
-		conn = tls.Server(conn, h.tlsConfig.Config())
+		conn = h.tlsConfig.Server(conn)
 	}
 	return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
 }

+ 9 - 4
inbound/hysteria.go

@@ -10,6 +10,7 @@ import (
 	"github.com/sagernet/quic-go"
 	"github.com/sagernet/quic-go/congestion"
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -26,7 +27,7 @@ var _ adapter.Inbound = (*Hysteria)(nil)
 type Hysteria struct {
 	myInboundAdapter
 	quicConfig   *quic.Config
-	tlsConfig    *TLSConfig
+	tlsConfig    tls.ServerConfig
 	authKey      []byte
 	xplusKey     []byte
 	sendBPS      uint64
@@ -116,7 +117,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
 	if len(options.TLS.ALPN) == 0 {
 		options.TLS.ALPN = []string{hysteria.DefaultALPN}
 	}
-	tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
+	tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
 	if err != nil {
 		return nil, err
 	}
@@ -137,7 +138,11 @@ func (h *Hysteria) Start() error {
 	if err != nil {
 		return err
 	}
-	listener, err := quic.Listen(packetConn, h.tlsConfig.Config(), h.quicConfig)
+	rawConfig, err := h.tlsConfig.Config()
+	if err != nil {
+		return err
+	}
+	listener, err := quic.Listen(packetConn, rawConfig, h.quicConfig)
 	if err != nil {
 		return err
 	}
@@ -301,6 +306,6 @@ func (h *Hysteria) Close() error {
 	return common.Close(
 		&h.myInboundAdapter,
 		h.listener,
-		common.PtrOrNil(h.tlsConfig),
+		h.tlsConfig,
 	)
 }

+ 9 - 6
inbound/naive.go

@@ -2,7 +2,6 @@ package inbound
 
 import (
 	"context"
-	"crypto/tls"
 	"encoding/base64"
 	"encoding/binary"
 	"io"
@@ -14,6 +13,7 @@ import (
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -32,7 +32,7 @@ var _ adapter.Inbound = (*Naive)(nil)
 type Naive struct {
 	myInboundAdapter
 	authenticator auth.Authenticator
-	tlsConfig     *TLSConfig
+	tlsConfig     tls.ServerConfig
 	httpServer    *http.Server
 	h3Server      any
 }
@@ -59,7 +59,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
 		return nil, E.New("missing users")
 	}
 	if options.TLS != nil {
-		tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
+		tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
 		if err != nil {
 			return nil, err
 		}
@@ -69,13 +69,16 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
 }
 
 func (n *Naive) Start() error {
-	var tlsConfig *tls.Config
+	var tlsConfig *tls.STDConfig
 	if n.tlsConfig != nil {
 		err := n.tlsConfig.Start()
 		if err != nil {
 			return E.Cause(err, "create TLS config")
 		}
-		tlsConfig = n.tlsConfig.Config()
+		tlsConfig, err = n.tlsConfig.Config()
+		if err != nil {
+			return err
+		}
 	}
 
 	if common.Contains(n.network, N.NetworkTCP) {
@@ -117,7 +120,7 @@ func (n *Naive) Close() error {
 		&n.myInboundAdapter,
 		common.PtrOrNil(n.httpServer),
 		n.h3Server,
-		common.PtrOrNil(n.tlsConfig),
+		n.tlsConfig,
 	)
 }
 

+ 5 - 1
inbound/naive_quic.go

@@ -8,9 +8,13 @@ import (
 )
 
 func (n *Naive) configureHTTP3Listener() error {
+	tlsConfig, err := n.tlsConfig.Config()
+	if err != nil {
+		return err
+	}
 	h3Server := &http3.Server{
 		Port:      int(n.listenOptions.ListenPort),
-		TLSConfig: n.tlsConfig.Config(),
+		TLSConfig: tlsConfig,
 		Handler:   n,
 	}
 

+ 7 - 11
inbound/trojan.go

@@ -2,11 +2,11 @@ package inbound
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"os"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -29,7 +29,7 @@ type Trojan struct {
 	myInboundAdapter
 	service                  *trojan.Service[int]
 	users                    []option.TrojanUser
-	tlsConfig                *TLSConfig
+	tlsConfig                tls.ServerConfig
 	fallbackAddr             M.Socksaddr
 	fallbackAddrTLSNextProto map[string]M.Socksaddr
 	transport                adapter.V2RayServerTransport
@@ -49,7 +49,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
 		users: options.Users,
 	}
 	if options.TLS != nil {
-		tlsConfig, err := NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
+		tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
 		if err != nil {
 			return nil, err
 		}
@@ -89,11 +89,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
 		return nil, err
 	}
 	if options.Transport != nil {
-		var tlsConfig *tls.Config
-		if inbound.tlsConfig != nil {
-			tlsConfig = inbound.tlsConfig.Config()
-		}
-		inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
+		inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
 		if err != nil {
 			return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
 		}
@@ -143,7 +139,7 @@ func (h *Trojan) Start() error {
 func (h *Trojan) Close() error {
 	return common.Close(
 		&h.myInboundAdapter,
-		common.PtrOrNil(h.tlsConfig),
+		h.tlsConfig,
 		h.transport,
 	)
 }
@@ -155,7 +151,7 @@ func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, meta
 
 func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 	if h.tlsConfig != nil && h.transport == nil {
-		conn = tls.Server(conn, h.tlsConfig.Config())
+		conn = h.tlsConfig.Server(conn)
 	}
 	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
 }
@@ -182,7 +178,7 @@ func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adap
 func (h *Trojan) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 	var fallbackAddr M.Socksaddr
 	if len(h.fallbackAddrTLSNextProto) > 0 {
-		if tlsConn, loaded := common.Cast[*tls.Conn](conn); loaded {
+		if tlsConn, loaded := common.Cast[*tls.STDConn](conn); loaded {
 			connectionState := tlsConn.ConnectionState()
 			if connectionState.NegotiatedProtocol != "" {
 				if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {

+ 7 - 11
inbound/vmess.go

@@ -2,11 +2,11 @@ package inbound
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"os"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -31,7 +31,7 @@ type VMess struct {
 	ctx       context.Context
 	service   *vmess.Service[int]
 	users     []option.VMessUser
-	tlsConfig *TLSConfig
+	tlsConfig tls.ServerConfig
 	transport adapter.V2RayServerTransport
 }
 
@@ -62,17 +62,13 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
 		return nil, err
 	}
 	if options.TLS != nil {
-		inbound.tlsConfig, err = NewTLSConfig(ctx, logger, common.PtrValueOrDefault(options.TLS))
+		inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
 		if err != nil {
 			return nil, err
 		}
 	}
 	if options.Transport != nil {
-		var tlsConfig *tls.Config
-		if inbound.tlsConfig != nil {
-			tlsConfig = inbound.tlsConfig.Config()
-		}
-		inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
+		inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newTransportConnection, nil, nil), inbound)
 		if err != nil {
 			return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
 		}
@@ -84,7 +80,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
 func (h *VMess) Start() error {
 	err := common.Start(
 		h.service,
-		common.PtrOrNil(h.tlsConfig),
+		h.tlsConfig,
 	)
 	if err != nil {
 		return err
@@ -123,7 +119,7 @@ func (h *VMess) Close() error {
 	return common.Close(
 		h.service,
 		&h.myInboundAdapter,
-		common.PtrOrNil(h.tlsConfig),
+		h.tlsConfig,
 		h.transport,
 	)
 }
@@ -135,7 +131,7 @@ func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metad
 
 func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 	if h.tlsConfig != nil && h.transport == nil {
-		conn = tls.Server(conn, h.tlsConfig.Config())
+		conn = h.tlsConfig.Server(conn)
 	}
 	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
 }

+ 0 - 21
option/tls.go

@@ -1,11 +1,5 @@
 package option
 
-import (
-	"crypto/tls"
-
-	E "github.com/sagernet/sing/common/exceptions"
-)
-
 type InboundTLSOptions struct {
 	Enabled         bool                `json:"enabled,omitempty"`
 	ServerName      string              `json:"server_name,omitempty"`
@@ -32,18 +26,3 @@ type OutboundTLSOptions struct {
 	Certificate     string           `json:"certificate,omitempty"`
 	CertificatePath string           `json:"certificate_path,omitempty"`
 }
-
-func ParseTLSVersion(version string) (uint16, error) {
-	switch version {
-	case "1.0":
-		return tls.VersionTLS10, nil
-	case "1.1":
-		return tls.VersionTLS11, nil
-	case "1.2":
-		return tls.VersionTLS12, nil
-	case "1.3":
-		return tls.VersionTLS13, nil
-	default:
-		return 0, E.New("unknown tls version:", version)
-	}
-}

+ 2 - 1
outbound/http.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/dialer"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -24,7 +25,7 @@ type HTTP struct {
 }
 
 func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
-	detour, err := dialer.NewTLS(dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS))
+	detour, err := tls.NewDialerFromOptions(router, dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS))
 	if err != nil {
 		return nil, err
 	}

+ 7 - 3
outbound/hysteria.go

@@ -4,7 +4,6 @@ package outbound
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"sync"
 
@@ -12,6 +11,7 @@ import (
 	"github.com/sagernet/quic-go/congestion"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/dialer"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -30,7 +30,7 @@ type Hysteria struct {
 	ctx          context.Context
 	dialer       N.Dialer
 	serverAddr   M.Socksaddr
-	tlsConfig    *tls.Config
+	tlsConfig    *tls.STDConfig
 	quicConfig   *quic.Config
 	authKey      []byte
 	xplusKey     []byte
@@ -47,7 +47,11 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
 	if options.TLS == nil || !options.TLS.Enabled {
 		return nil, C.ErrTLSRequired
 	}
-	tlsConfig, err := dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
+	abstractTLSConfig, err := tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
+	if err != nil {
+		return nil, err
+	}
+	tlsConfig, err := abstractTLSConfig.Config()
 	if err != nil {
 		return nil, err
 	}

+ 4 - 12
outbound/shadowtls.go

@@ -2,12 +2,12 @@ package outbound
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"os"
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/dialer"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -22,7 +22,7 @@ type ShadowTLS struct {
 	myOutboundAdapter
 	dialer     N.Dialer
 	serverAddr M.Socksaddr
-	tlsConfig  *tls.Config
+	tlsConfig  tls.Config
 }
 
 func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) {
@@ -43,7 +43,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
 	options.TLS.MinVersion = "1.2"
 	options.TLS.MaxVersion = "1.2"
 	var err error
-	outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
+	outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
 	if err != nil {
 		return nil, err
 	}
@@ -60,15 +60,7 @@ func (s *ShadowTLS) DialContext(ctx context.Context, network string, destination
 	if err != nil {
 		return nil, err
 	}
-	tlsConn, err := dialer.TLSClient(ctx, conn, s.tlsConfig)
-	if err != nil {
-		return nil, err
-	}
-	err = tlsConn.HandshakeContext(ctx)
-	if err != nil {
-		return nil, err
-	}
-	return conn, nil
+	return tls.ClientHandshake(ctx, conn, s.tlsConfig)
 }
 
 func (s *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {

+ 4 - 4
outbound/trojan.go

@@ -2,12 +2,12 @@ package outbound
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/dialer"
 	"github.com/sagernet/sing-box/common/mux"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -28,7 +28,7 @@ type Trojan struct {
 	serverAddr      M.Socksaddr
 	key             [56]byte
 	multiplexDialer N.Dialer
-	tlsConfig       *tls.Config
+	tlsConfig       tls.Config
 	transport       adapter.V2RayClientTransport
 }
 
@@ -47,7 +47,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
 	}
 	var err error
 	if options.TLS != nil {
-		outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
+		outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
 		if err != nil {
 			return nil, err
 		}
@@ -116,7 +116,7 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
 	} else {
 		conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
 		if err == nil && h.tlsConfig != nil {
-			conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
+			conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
 		}
 	}
 	if err != nil {

+ 5 - 5
outbound/vmess.go

@@ -2,12 +2,12 @@ package outbound
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/dialer"
 	"github.com/sagernet/sing-box/common/mux"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
@@ -28,7 +28,7 @@ type VMess struct {
 	client          *vmess.Client
 	serverAddr      M.Socksaddr
 	multiplexDialer N.Dialer
-	tlsConfig       *tls.Config
+	tlsConfig       tls.Config
 	transport       adapter.V2RayClientTransport
 	packetAddr      bool
 }
@@ -47,7 +47,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
 	}
 	var err error
 	if options.TLS != nil {
-		outbound.tlsConfig, err = dialer.TLSConfig(options.Server, common.PtrValueOrDefault(options.TLS))
+		outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
 		if err != nil {
 			return nil, err
 		}
@@ -142,7 +142,7 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
 	} else {
 		conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
 		if err == nil && h.tlsConfig != nil {
-			conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
+			conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
 		}
 	}
 	if err != nil {
@@ -169,7 +169,7 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
 	} else {
 		conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
 		if err == nil && h.tlsConfig != nil {
-			conn, err = dialer.TLSClient(ctx, conn, h.tlsConfig)
+			conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
 		}
 	}
 	if err != nil {

+ 6 - 6
transport/v2ray/grpc.go

@@ -4,9 +4,9 @@ package v2ray
 
 import (
 	"context"
-	"crypto/tls"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/transport/v2raygrpc"
 	"github.com/sagernet/sing-box/transport/v2raygrpclite"
@@ -15,16 +15,16 @@ import (
 	N "github.com/sagernet/sing/common/network"
 )
 
-func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
+func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
 	if options.ForceLite {
-		return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler), nil
+		return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler, errorHandler)
 	}
-	return v2raygrpc.NewServer(ctx, options, tlsConfig, handler), nil
+	return v2raygrpc.NewServer(ctx, options, tlsConfig, handler)
 }
 
-func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
+func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
 	if options.ForceLite {
 		return v2raygrpclite.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
 	}
-	return v2raygrpc.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
+	return v2raygrpc.NewClient(ctx, dialer, serverAddr, options, tlsConfig)
 }

+ 5 - 5
transport/v2ray/quic.go

@@ -4,9 +4,9 @@ package v2ray
 
 import (
 	"context"
-	"crypto/tls"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/transport/v2rayquic"
 	E "github.com/sagernet/sing/common/exceptions"
@@ -14,10 +14,10 @@ import (
 	N "github.com/sagernet/sing/common/network"
 )
 
-func NewQUICServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
-	return v2rayquic.NewServer(ctx, options, tlsConfig, handler, errorHandler), nil
+func NewQUICServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
+	return v2rayquic.NewServer(ctx, options, tlsConfig, handler, errorHandler)
 }
 
-func NewQUICClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
-	return v2rayquic.NewClient(ctx, dialer, serverAddr, options, tlsConfig), nil
+func NewQUICClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
+	return v2rayquic.NewClient(ctx, dialer, serverAddr, options, tlsConfig)
 }

+ 5 - 5
transport/v2ray/transport.go

@@ -2,9 +2,9 @@ package v2ray
 
 import (
 	"context"
-	"crypto/tls"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/transport/v2rayhttp"
@@ -14,15 +14,15 @@ import (
 	N "github.com/sagernet/sing/common/network"
 )
 
-func NewServerTransport(ctx context.Context, options option.V2RayTransportOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
+func NewServerTransport(ctx context.Context, options option.V2RayTransportOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (adapter.V2RayServerTransport, error) {
 	if options.Type == "" {
 		return nil, nil
 	}
 	switch options.Type {
 	case C.V2RayTransportTypeHTTP:
-		return v2rayhttp.NewServer(ctx, options.HTTPOptions, tlsConfig, handler, errorHandler), nil
+		return v2rayhttp.NewServer(ctx, options.HTTPOptions, tlsConfig, handler, errorHandler)
 	case C.V2RayTransportTypeWebsocket:
-		return v2raywebsocket.NewServer(ctx, options.WebsocketOptions, tlsConfig, handler, errorHandler), nil
+		return v2raywebsocket.NewServer(ctx, options.WebsocketOptions, tlsConfig, handler, errorHandler)
 	case C.V2RayTransportTypeQUIC:
 		if tlsConfig == nil {
 			return nil, C.ErrTLSRequired
@@ -35,7 +35,7 @@ func NewServerTransport(ctx context.Context, options option.V2RayTransportOption
 	}
 }
 
-func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayTransportOptions, tlsConfig *tls.Config) (adapter.V2RayClientTransport, error) {
+func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayTransportOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
 	if options.Type == "" {
 		return nil, nil
 	}

+ 8 - 4
transport/v2raygrpc/client.go

@@ -2,12 +2,12 @@ package v2raygrpc
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"sync"
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing/common"
 	M "github.com/sagernet/sing/common/metadata"
@@ -32,10 +32,14 @@ type Client struct {
 	connAccess  sync.Mutex
 }
 
-func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
+func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
 	var dialOptions []grpc.DialOption
 	if tlsConfig != nil {
-		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
+		stdConfig, err := tlsConfig.Config()
+		if err != nil {
+			return nil, err
+		}
+		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(stdConfig)))
 	} else {
 		dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
 	}
@@ -58,7 +62,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 		serverAddr:  serverAddr.String(),
 		serviceName: options.ServiceName,
 		dialOptions: dialOptions,
-	}
+	}, nil
 }
 
 func (c *Client) Close() error {

+ 9 - 5
transport/v2raygrpc/server.go

@@ -2,11 +2,11 @@ package v2raygrpc
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"os"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
@@ -23,15 +23,19 @@ type Server struct {
 	server  *grpc.Server
 }
 
-func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler) *Server {
+func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler) (*Server, error) {
 	var serverOptions []grpc.ServerOption
 	if tlsConfig != nil {
-		tlsConfig.NextProtos = []string{"h2"}
-		serverOptions = append(serverOptions, grpc.Creds(credentials.NewTLS(tlsConfig)))
+		stdConfig, err := tlsConfig.Config()
+		if err != nil {
+			return nil, err
+		}
+		stdConfig.NextProtos = []string{"h2"}
+		serverOptions = append(serverOptions, grpc.Creds(credentials.NewTLS(stdConfig)))
 	}
 	server := &Server{ctx, handler, grpc.NewServer(serverOptions...)}
 	RegisterGunServiceCustomNameServer(server.server, server, options.ServiceName)
-	return server
+	return server, nil
 }
 
 func (s *Server) Tun(server GunService_TunServer) error {

+ 8 - 5
transport/v2raygrpclite/client.go

@@ -2,7 +2,6 @@ package v2raygrpclite
 
 import (
 	"context"
-	"crypto/tls"
 	"fmt"
 	"io"
 	"net"
@@ -10,6 +9,7 @@ import (
 	"net/url"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
@@ -32,18 +32,21 @@ type Client struct {
 	url        *url.URL
 }
 
-func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
+func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
 	return &Client{
 		ctx:        ctx,
 		dialer:     dialer,
 		serverAddr: serverAddr,
 		options:    options,
 		transport: &http.Transport{
-			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-				return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+			DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+				if err != nil {
+					return nil, err
+				}
+				return tls.ClientHandshake(ctx, conn, tlsConfig)
 			},
 			ForceAttemptHTTP2: true,
-			TLSClientConfig:   tlsConfig,
 		},
 		url: &url.URL{
 			Scheme: "https",

+ 13 - 9
transport/v2raygrpclite/server.go

@@ -2,7 +2,6 @@ package v2raygrpclite
 
 import (
 	"context"
-	"crypto/tls"
 	"fmt"
 	"net"
 	"net/http"
@@ -11,6 +10,7 @@ import (
 	"strings"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
@@ -32,22 +32,26 @@ func (s *Server) Network() []string {
 	return []string{N.NetworkTCP}
 }
 
-func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) *Server {
+func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (*Server, error) {
 	server := &Server{
 		handler:      handler,
 		errorHandler: errorHandler,
 		path:         fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
 	}
+	server.httpServer = &http.Server{
+		Handler: server,
+	}
 	if tlsConfig != nil {
-		if !common.Contains(tlsConfig.NextProtos, "h2") {
-			tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
+		stdConfig, err := tlsConfig.Config()
+		if err != nil {
+			return nil, err
 		}
+		if !common.Contains(stdConfig.NextProtos, "h2") {
+			stdConfig.NextProtos = append(stdConfig.NextProtos, "h2")
+		}
+		server.httpServer.TLSConfig = stdConfig
 	}
-	server.httpServer = &http.Server{
-		Handler:   server,
-		TLSConfig: tlsConfig,
-	}
-	return server
+	return server, nil
 }
 
 func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {

+ 21 - 11
transport/v2rayhttp/client.go

@@ -3,7 +3,6 @@ package v2rayhttp
 import (
 	"bufio"
 	"context"
-	"crypto/tls"
 	"io"
 	"math/rand"
 	"net"
@@ -12,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
@@ -32,7 +32,7 @@ type Client struct {
 	headers    http.Header
 }
 
-func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
+func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
 	client := &Client{
 		ctx:        ctx,
 		dialer:     dialer,
@@ -40,16 +40,26 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 		host:       options.Host,
 		method:     options.Method,
 		headers:    make(http.Header),
-		client: &http.Client{
-			Transport: &http.Transport{
-				DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-					return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
-				},
-				ForceAttemptHTTP2: true,
-				TLSClientConfig:   tlsConfig,
+		client:     &http.Client{},
+		http2:      tlsConfig != nil,
+	}
+	if client.http2 {
+		client.client.Transport = &http.Transport{
+			DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+				if err != nil {
+					return nil, err
+				}
+				return tls.ClientHandshake(ctx, conn, tlsConfig)
 			},
-		},
-		http2: tlsConfig != nil,
+			ForceAttemptHTTP2: true,
+		}
+	} else {
+		client.client.Transport = &http.Transport{
+			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+				return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+			},
+		}
 	}
 	if client.method == "" {
 		client.method = "PUT"

+ 10 - 4
transport/v2rayhttp/server.go

@@ -2,13 +2,13 @@ package v2rayhttp
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"net/http"
 	"os"
 	"strings"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing/common"
@@ -35,7 +35,7 @@ func (s *Server) Network() []string {
 	return []string{N.NetworkTCP}
 }
 
-func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) *Server {
+func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (*Server, error) {
 	server := &Server{
 		ctx:          ctx,
 		handler:      handler,
@@ -58,9 +58,15 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig *
 		Handler:           server,
 		ReadHeaderTimeout: C.TCPTimeout,
 		MaxHeaderBytes:    http.DefaultMaxHeaderBytes,
-		TLSConfig:         tlsConfig,
 	}
-	return server
+	if tlsConfig != nil {
+		stdConfig, err := tlsConfig.Config()
+		if err != nil {
+			return nil, err
+		}
+		server.httpServer.TLSConfig = stdConfig
+	}
+	return server, nil
 }
 
 func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {

+ 11 - 7
transport/v2rayquic/client.go

@@ -2,12 +2,12 @@ package v2rayquic
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"sync"
 
 	"github.com/sagernet/quic-go"
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/transport/hysteria"
@@ -23,26 +23,30 @@ type Client struct {
 	ctx        context.Context
 	dialer     N.Dialer
 	serverAddr M.Socksaddr
-	tlsConfig  *tls.Config
+	tlsConfig  *tls.STDConfig
 	quicConfig *quic.Config
 	conn       quic.Connection
 	connAccess sync.Mutex
 }
 
-func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
+func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) {
 	quicConfig := &quic.Config{
 		DisablePathMTUDiscovery: !C.IsLinux && !C.IsWindows,
 	}
-	if len(tlsConfig.NextProtos) == 0 {
-		tlsConfig.NextProtos = []string{"h2", "http/1.1"}
+	stdConfig, err := tlsConfig.Config()
+	if err != nil {
+		return nil, err
+	}
+	if len(stdConfig.NextProtos) == 0 {
+		stdConfig.NextProtos = []string{"h2", "http/1.1"}
 	}
 	return &Client{
 		ctx:        ctx,
 		dialer:     dialer,
 		serverAddr: serverAddr,
-		tlsConfig:  tlsConfig,
+		tlsConfig:  stdConfig,
 		quicConfig: quicConfig,
-	}
+	}, nil
 }
 
 func (c *Client) offer() (quic.Connection, error) {

+ 11 - 7
transport/v2rayquic/server.go

@@ -2,12 +2,12 @@ package v2rayquic
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"os"
 
 	"github.com/sagernet/quic-go"
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/transport/hysteria"
@@ -21,7 +21,7 @@ var _ adapter.V2RayServerTransport = (*Server)(nil)
 
 type Server struct {
 	ctx          context.Context
-	tlsConfig    *tls.Config
+	tlsConfig    *tls.STDConfig
 	quicConfig   *quic.Config
 	handler      N.TCPConnectionHandler
 	errorHandler E.Handler
@@ -29,21 +29,25 @@ type Server struct {
 	quicListener quic.Listener
 }
 
-func NewServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) *Server {
+func NewServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (*Server, error) {
 	quicConfig := &quic.Config{
 		DisablePathMTUDiscovery: !C.IsLinux && !C.IsWindows,
 	}
-	if len(tlsConfig.NextProtos) == 0 {
-		tlsConfig.NextProtos = []string{"h2", "http/1.1"}
+	stdConfig, err := tlsConfig.Config()
+	if err != nil {
+		return nil, err
+	}
+	if len(stdConfig.NextProtos) == 0 {
+		stdConfig.NextProtos = []string{"h2", "http/1.1"}
 	}
 	server := &Server{
 		ctx:          ctx,
-		tlsConfig:    tlsConfig,
+		tlsConfig:    stdConfig,
 		quicConfig:   quicConfig,
 		handler:      handler,
 		errorHandler: errorHandler,
 	}
-	return server
+	return server, nil
 }
 
 func (s *Server) Network() []string {

+ 15 - 6
transport/v2raywebsocket/client.go

@@ -2,7 +2,6 @@ package v2raywebsocket
 
 import (
 	"context"
-	"crypto/tls"
 	"net"
 	"net/http"
 	"net/url"
@@ -10,6 +9,7 @@ import (
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/option"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
@@ -28,16 +28,25 @@ type Client struct {
 	earlyDataHeaderName string
 }
 
-func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayWebsocketOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
+func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayWebsocketOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
 	wsDialer := &websocket.Dialer{
-		NetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-			return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
-		},
-		TLSClientConfig:  tlsConfig,
 		ReadBufferSize:   4 * 1024,
 		WriteBufferSize:  4 * 1024,
 		HandshakeTimeout: time.Second * 8,
 	}
+	if tlsConfig != nil {
+		wsDialer.NetDialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
+			conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+			if err != nil {
+				return nil, err
+			}
+			return tls.ClientHandshake(ctx, conn, tlsConfig)
+		}
+	} else {
+		wsDialer.NetDialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
+			return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+		}
+	}
 	var uri url.URL
 	if tlsConfig == nil {
 		uri.Scheme = "ws"

+ 10 - 4
transport/v2raywebsocket/server.go

@@ -2,7 +2,6 @@ package v2raywebsocket
 
 import (
 	"context"
-	"crypto/tls"
 	"encoding/base64"
 	"net"
 	"net/http"
@@ -10,6 +9,7 @@ import (
 	"strings"
 
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tls"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing/common"
@@ -35,7 +35,7 @@ type Server struct {
 	earlyDataHeaderName string
 }
 
-func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsConfig *tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) *Server {
+func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsConfig tls.Config, handler N.TCPConnectionHandler, errorHandler E.Handler) (*Server, error) {
 	server := &Server{
 		ctx:                 ctx,
 		handler:             handler,
@@ -51,9 +51,15 @@ func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsCon
 		Handler:           server,
 		ReadHeaderTimeout: C.TCPTimeout,
 		MaxHeaderBytes:    http.DefaultMaxHeaderBytes,
-		TLSConfig:         tlsConfig,
 	}
-	return server
+	if tlsConfig != nil {
+		stdConfig, err := tlsConfig.Config()
+		if err != nil {
+			return nil, err
+		}
+		server.httpServer.TLSConfig = stdConfig
+	}
+	return server, nil
 }
 
 var upgrader = websocket.Upgrader{