2022-09-09 18:45:10 +08:00
package tls
import (
"context"
2025-08-11 21:18:34 +08:00
"crypto/tls"
"errors"
2022-09-09 18:45:10 +08:00
"net"
"os"
2023-12-20 20:00:00 +08:00
"github.com/sagernet/sing-box/common/badtls"
2022-09-09 18:45:10 +08:00
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
2025-09-07 21:03:32 +08:00
"github.com/sagernet/sing/common/logger"
2022-09-09 18:45:10 +08:00
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
2023-02-28 11:30:46 +08:00
aTLS "github.com/sagernet/sing/common/tls"
2022-09-09 18:45:10 +08:00
)
2025-09-07 21:03:32 +08:00
func NewDialerFromOptions ( ctx context . Context , logger logger . ContextLogger , dialer N . Dialer , serverAddress string , options option . OutboundTLSOptions ) ( N . Dialer , error ) {
2022-11-13 11:24:37 +08:00
if ! options . Enabled {
return dialer , nil
}
2025-09-08 19:35:17 +08:00
config , err := NewClientWithOptions ( ClientOptions {
Context : ctx ,
Logger : logger ,
ServerAddress : serverAddress ,
Options : options ,
})
2022-09-09 18:45:10 +08:00
if err != nil {
return nil , err
}
return NewDialer ( dialer , config ), nil
}
2025-09-07 21:03:32 +08:00
func NewClient ( ctx context . Context , logger logger . ContextLogger , serverAddress string , options option . OutboundTLSOptions ) ( Config , error ) {
2025-09-08 19:35:17 +08:00
return NewClientWithOptions ( ClientOptions {
Context : ctx ,
Logger : logger ,
ServerAddress : serverAddress ,
Options : options ,
})
}
type ClientOptions struct {
Context context . Context
Logger logger . ContextLogger
ServerAddress string
Options option . OutboundTLSOptions
KTLSCompatible bool
}
func NewClientWithOptions ( options ClientOptions ) ( Config , error ) {
if ! options . Options . Enabled {
2022-11-13 11:24:37 +08:00
return nil , nil
}
2025-09-08 19:35:17 +08:00
if ! options . KTLSCompatible {
if options . Options . KernelTx {
options . Logger . Warn ( "enabling kTLS TX in current scenarios will definitely reduce performance, please checkout https://sing-box.sagernet.org/configuration/shared/tls/#kernel_tx" )
}
}
if options . Options . KernelRx {
options . Logger . Warn ( "enabling kTLS RX will definitely reduce performance, please checkout https://sing-box.sagernet.org/configuration/shared/tls/#kernel_rx" )
}
if options . Options . Reality != nil && options . Options . Reality . Enabled {
return NewRealityClient ( options . Context , options . Logger , options . ServerAddress , options . Options )
} else if options . Options . UTLS != nil && options . Options . UTLS . Enabled {
return NewUTLSClient ( options . Context , options . Logger , options . ServerAddress , options . Options )
2022-09-09 18:19:50 +08:00
}
2025-09-08 19:35:17 +08:00
return NewSTDClient ( options . Context , options . Logger , options . ServerAddress , options . Options )
2022-09-09 18:45:10 +08:00
}
2022-09-11 10:22:52 +08:00
func ClientHandshake ( ctx context . Context , conn net . Conn , config Config ) ( Conn , error ) {
2022-09-09 18:45:10 +08:00
ctx , cancel := context . WithTimeout ( ctx , C . TCPTimeout )
defer cancel ()
2023-12-20 20:00:00 +08:00
tlsConn , err := aTLS . ClientHandshake ( ctx , conn , config )
if err != nil {
return nil , err
}
readWaitConn , err := badtls . NewReadWaitConn ( tlsConn )
if err == nil {
return readWaitConn , nil
} else if err != os . ErrInvalid {
return nil , err
}
return tlsConn , nil
2022-09-09 18:45:10 +08:00
}
2025-10-07 13:41:25 +08:00
type Dialer interface {
N . Dialer
DialTLSContext ( ctx context . Context , destination M . Socksaddr ) ( Conn , error )
}
type defaultDialer struct {
2022-09-09 18:45:10 +08:00
dialer N . Dialer
config Config
}
2025-10-07 13:41:25 +08:00
func NewDialer ( dialer N . Dialer , config Config ) Dialer {
return & defaultDialer { dialer , config }
2022-09-09 18:45:10 +08:00
}
2025-10-07 13:41:25 +08:00
func ( d * defaultDialer ) DialContext ( ctx context . Context , network string , destination M . Socksaddr ) ( net . Conn , error ) {
if N . NetworkName ( network ) != N . NetworkTCP {
2022-09-09 18:45:10 +08:00
return nil , os . ErrInvalid
}
2025-10-07 13:41:25 +08:00
return d . DialTLSContext ( ctx , destination )
}
func ( d * defaultDialer ) ListenPacket ( ctx context . Context , destination M . Socksaddr ) ( net . PacketConn , error ) {
return nil , os . ErrInvalid
}
func ( d * defaultDialer ) DialTLSContext ( ctx context . Context , destination M . Socksaddr ) ( Conn , error ) {
2025-08-11 21:18:34 +08:00
return d . dialContext ( ctx , destination , true )
2025-10-07 13:41:25 +08:00
}
2025-08-11 21:18:34 +08:00
func ( d * defaultDialer ) dialContext ( ctx context . Context , destination M . Socksaddr , echRetry bool ) ( Conn , error ) {
2025-10-07 13:41:25 +08:00
conn , err := d . dialer . DialContext ( ctx , N . NetworkTCP , destination )
if err != nil {
return nil , err
}
tlsConn , err := aTLS . ClientHandshake ( ctx , conn , d . config )
2025-10-07 13:19:57 +08:00
if err != nil {
conn . Close ()
2025-08-11 21:18:34 +08:00
var echErr * tls . ECHRejectionError
2025-10-07 13:19:57 +08:00
if echRetry && errors . As ( err , & echErr ) && len ( echErr . RetryConfigList ) > 0 {
2025-08-11 21:18:34 +08:00
if echConfig , isECH := d . config .( ECHCapableConfig ); isECH {
echConfig . SetECHConfigList ( echErr . RetryConfigList )
2025-10-07 13:19:57 +08:00
return d . dialContext ( ctx , destination , false )
2025-08-11 21:18:34 +08:00
}
}
2025-10-07 13:19:57 +08:00
return nil , err
2025-08-11 21:18:34 +08:00
}
2025-10-07 13:19:57 +08:00
return tlsConn , nil
2022-09-09 18:45:10 +08:00
}
2025-10-07 13:41:25 +08:00
func ( d * defaultDialer ) Upstream () any {
return d . dialer
2022-09-09 18:45:10 +08:00
}