Compare commits

..

25 Commits

Author SHA1 Message Date
世界
12e8d43925
Add support for kTLS
Reference: https://gitlab.com/go-extension/tls
2025-09-08 11:48:26 +08:00
世界
f13327f4d2
documentation: Bump version 2025-09-08 09:13:10 +08:00
世界
e1f54d429a
Add proxy support for ICMP echo request 2025-09-08 09:13:10 +08:00
世界
19f9af5945
Fix resolve using resolved 2025-09-08 09:13:10 +08:00
世界
b0fc436a16
documentation: Update behavior of local DNS server on darwin 2025-09-08 09:13:10 +08:00
世界
53bb549809
Stop using DHCP on iOS and tvOS
We do not have the `com.apple.developer.networking.multicast` entitlement and are unable to obtain it for non-technical reasons.
2025-09-08 09:13:10 +08:00
世界
e7de4f5a88
Remove use of ldflags -checklinkname=0 on darwin 2025-09-08 09:13:10 +08:00
世界
8583c6e509
Fix local DNS server on darwin
We mistakenly believed that `libresolv`'s `search` function worked correctly in NetworkExtension, but it seems only `getaddrinfo` does.

This commit changes the behavior of the `local` DNS server in NetworkExtension to prefer DHCP, falling back to `getaddrinfo` if DHCP servers are unavailable.

It's worth noting that `prefer_go` does not disable DHCP since it respects Dial Fields, but `getaddrinfo` does the opposite. The new behavior only applies to NetworkExtension, not to all scenarios (primarily command-line binaries) as it did previously.

In addition, this commit also improves the DHCP DNS server to use the same robust query logic as `local`.
2025-09-08 09:13:09 +08:00
世界
a67f3d8be2
Fix legacy DNS config 2025-09-08 09:13:09 +08:00
世界
87ed5f86d8
Fix rule-set format 2025-09-08 09:13:09 +08:00
世界
e53122d255
documentation: Remove outdated icons 2025-09-08 09:13:08 +08:00
世界
5982358407
documentation: Improve local DNS server 2025-09-08 09:13:08 +08:00
世界
490daad0a7
Use libresolv in local DNS server on darwin 2025-09-08 09:13:08 +08:00
世界
b1ea333bd4
Use resolved in local DNS server if available 2025-09-08 09:13:08 +08:00
xchacha20-poly1305
e69b61747f
Fix rule set version 2025-09-08 09:13:07 +08:00
世界
16d8f108ca
documentation: Add preferred_by route rule item 2025-09-08 09:13:07 +08:00
世界
ffdfb3237c
Add preferred_by route rule item 2025-09-08 09:13:07 +08:00
世界
92b6cc4904
documentation: Add interface address rule items 2025-09-08 09:13:07 +08:00
世界
c630819ee9
Add interface address rule items 2025-09-08 09:13:07 +08:00
neletor
e9f519aafc
Add support for ech retry configs 2025-09-08 09:13:07 +08:00
Zephyruso
84279243ce
Add /dns/flush-clash meta api 2025-09-08 09:13:06 +08:00
世界
f98a3a4f65
Treat requests with OPT extra but no options as simple requests 2025-09-08 09:12:30 +08:00
世界
b14cecaeb2
Fix DNS packet size 2025-09-08 09:12:30 +08:00
世界
2594745ef8
Fix DNS client 2025-09-08 09:12:30 +08:00
世界
cc3041322e
Fix DNS cache 2025-09-08 09:12:30 +08:00
36 changed files with 306 additions and 177 deletions

View File

@ -36,6 +36,7 @@ type RawConn struct {
PacketsSent *int64
ActiveCall *atomic.Int32
Tmp *[16]byte
}
func NewRawConn(rawTLSConn tls.Conn) (*RawConn, error) {
@ -153,6 +154,12 @@ func NewRawConn(rawTLSConn tls.Conn) (*RawConn, error) {
}
conn.ActiveCall = (*atomic.Int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr()))
rawTmp := rawConn.FieldByName("tmp")
if !rawTmp.IsValid() || rawTmp.Kind() != reflect.Array || rawTmp.Len() != 16 || rawTmp.Type().Elem().Kind() != reflect.Uint8 {
return nil, E.New("invalid Conn.tmp")
}
conn.Tmp = (*[16]byte)(unsafe.Pointer(rawTmp.UnsafeAddr()))
return conn, nil
}

View File

@ -17,6 +17,9 @@ type ReadWaitConn struct {
}
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
if _, isReadWaitConn := conn.(N.ReadWaiter); isReadWaitConn {
return conn, nil
}
rawConn, err := NewRawConn(conn)
if err != nil {
return nil, err

View File

@ -3,6 +3,7 @@
package ktls
import (
"context"
"crypto/tls"
"io"
"net"
@ -12,22 +13,31 @@ import (
"github.com/sagernet/sing-box/common/badtls"
// C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network"
aTLS "github.com/sagernet/sing/common/tls"
)
type Conn struct {
aTLS.Conn
ctx context.Context
logger logger.ContextLogger
conn net.Conn
rawConn *badtls.RawConn
syscallConn syscall.Conn
rawSyscallConn syscall.RawConn
readWaitOptions N.ReadWaitOptions
kernelTx bool
kernelRx bool
tmp [16]byte
kernelDidRead bool
kernelDidWrite bool
}
func NewConn(conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
err := Load()
if err != nil {
return nil, err
}
syscallConn, isSyscallConn := N.CastReader[interface {
io.Reader
syscall.Conn
@ -60,8 +70,11 @@ func NewConn(conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
}
kConn := &Conn{
Conn: conn,
ctx: ctx,
logger: logger,
conn: conn.NetConn(),
rawConn: rawConn,
syscallConn: syscallConn,
rawSyscallConn: rawSyscallConn,
}
err = kConn.setupKernel(txOffload, rxOffload)
@ -76,9 +89,49 @@ func (c *Conn) Upstream() any {
}
func (c *Conn) ReaderReplaceable() bool {
return c.kernelRx
if !c.kernelRx {
return true
}
c.rawConn.In.Lock()
defer c.rawConn.In.Unlock()
return !c.kernelDidRead
}
func (c *Conn) WriterReplaceable() bool {
return c.kernelTx
if !c.kernelTx {
return true
}
/*c.rawConn.Out.Lock()
defer c.rawConn.Out.Unlock()
return !c.kernelDidWrite*/
return true
}
func (c *Conn) SyscallConnForRead() syscall.Conn {
if !c.kernelRx {
return nil
}
c.rawConn.In.Lock()
defer c.rawConn.In.Unlock()
if c.kernelDidRead {
c.logger.DebugContext(c.ctx, "ktls: RX splice not possible, since did read from user space")
return nil
}
c.logger.DebugContext(c.ctx, "ktls: RX splice requested")
return c.syscallConn
}
func (c *Conn) SyscallConnForWrite() syscall.Conn {
if !c.kernelTx {
return nil
}
/*c.rawConn.Out.Lock()
defer c.rawConn.Out.Unlock()
if c.kernelDidWrite {
c.logger.DebugContext(c.ctx, "ktls: TX splice not possible, since did write from user space")
return nil
}
*/
c.logger.DebugContext(c.ctx, "ktls: TX splice requested")
return c.syscallConn
}

View File

@ -57,13 +57,13 @@ const (
func (c *Conn) sendAlertLocked(err uint8) error {
switch err {
case alertNoRenegotiation, alertCloseNotify:
c.tmp[0] = alertLevelWarning
c.rawConn.Tmp[0] = alertLevelWarning
default:
c.tmp[0] = alertLevelError
c.rawConn.Tmp[0] = alertLevelError
}
c.tmp[1] = byte(err)
c.rawConn.Tmp[1] = byte(err)
_, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2])
_, writeErr := c.writeRecordLocked(recordTypeAlert, c.rawConn.Tmp[0:2])
if err == alertCloseNotify {
// closeNotify is a special case in that it isn't an error.
return writeErr

View File

@ -14,6 +14,7 @@ import (
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/shell"
"github.com/blang/semver/v4"
"golang.org/x/sys/unix"
@ -48,13 +49,8 @@ type Support struct {
}
var KernelSupport = sync.OnceValues(func() (*Support, error) {
_, err := os.Stat("/sys/module/tls")
if err != nil {
return nil, E.New("ktls: kernel module tls not found")
}
var uname unix.Utsname
err = unix.Uname(&uname)
err := unix.Uname(&uname)
if err != nil {
return nil, err
}
@ -102,19 +98,44 @@ var KernelSupport = sync.OnceValues(func() (*Support, error) {
support.TLS = true
}
if support.TLS && support.TLS_Version13 {
_, err := os.Stat("/sys/module/tls")
if err != nil {
if os.Getuid() == 0 {
output, err := shell.Exec("modprobe", "tls").Read()
if err != nil {
return nil, E.Extend(E.Cause(err, "modprobe tls"), output)
}
} else {
return nil, E.New("ktls: kernel TLS module not loaded")
}
}
}
return &support, nil
})
func Load() error {
support, err := KernelSupport()
if err != nil {
return err
}
if !support.TLS || !support.TLS_Version13 {
return E.New("ktls: kernel does not support TLS 1.3")
}
return nil
}
func (c *Conn) setupKernel(txOffload, rxOffload bool) error {
if !txOffload && !rxOffload {
return nil
return os.ErrInvalid
}
support, err := KernelSupport()
if err != nil {
return err
}
if !support.TLS {
return nil
if !support.TLS || !support.TLS_Version13 {
return E.New("ktls: kernel does not support TLS 1.3")
}
c.rawConn.Out.Lock()
defer c.rawConn.Out.Unlock()
@ -125,28 +146,6 @@ func (c *Conn) setupKernel(txOffload, rxOffload bool) error {
return E.Cause(err, "initialize kernel TLS")
}
if rxOffload {
rxCrypto := kernelCipher(support, c.rawConn.In, *c.rawConn.CipherSuite, true)
if rxCrypto == nil {
return E.New("kTLS: unsupported cipher suite")
}
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_RX, rxCrypto.String())
})
if err != nil {
return err
}
if /*config.KernelRXExpectNoPad &&*/ *c.rawConn.Vers >= tls.VersionTLS13 && support.TLS_RX_NOPADDING {
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
return syscall.SetsockoptInt(int(fd), unix.SOL_TLS, TLS_RX_EXPECT_NO_PAD, 1)
})
if err != nil {
return err
}
}
c.kernelRx = true
}
if txOffload {
txCrypto := kernelCipher(support, c.rawConn.Out, *c.rawConn.CipherSuite, false)
if txCrypto == nil {
@ -167,8 +166,31 @@ func (c *Conn) setupKernel(txOffload, rxOffload bool) error {
}
}
c.kernelTx = true
c.logger.DebugContext(c.ctx, "ktls: kernel TLS TX enabled")
}
if rxOffload {
rxCrypto := kernelCipher(support, c.rawConn.In, *c.rawConn.CipherSuite, true)
if rxCrypto == nil {
return E.New("kTLS: unsupported cipher suite")
}
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_RX, rxCrypto.String())
})
if err != nil {
return err
}
if *c.rawConn.Vers >= tls.VersionTLS13 && support.TLS_RX_NOPADDING {
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
return syscall.SetsockoptInt(int(fd), unix.SOL_TLS, TLS_RX_EXPECT_NO_PAD, 1)
})
if err != nil {
return err
}
}
c.kernelRx = true
c.logger.DebugContext(c.ctx, "ktls: kernel TLS RX enabled")
}
return nil
}
@ -256,6 +278,7 @@ func (c *Conn) readKernelRecord() (uint8, []byte, error) {
default:
return 0, nil, err
}
c.kernelDidRead = true
if n <= 0 {
return 0, nil, io.EOF
@ -301,6 +324,7 @@ func (c *Conn) writeKernelRecord(typ uint16, data []byte) (int, error) {
if ew != nil {
return 0, ew
}
c.kernelDidWrite = true
return n, err
}

View File

@ -3,11 +3,13 @@
package ktls
import (
"context"
"os"
"github.com/sagernet/sing/common/logger"
aTLS "github.com/sagernet/sing/common/tls"
)
func NewConn(conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
return nil, os.ErrInvalid
}

View File

@ -8,36 +8,35 @@ import (
"os"
"github.com/sagernet/sing-box/common/badtls"
"github.com/sagernet/sing-box/common/ktls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
aTLS "github.com/sagernet/sing/common/tls"
)
func NewDialerFromOptions(ctx context.Context, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
func NewDialerFromOptions(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
if !options.Enabled {
return dialer, nil
}
config, err := NewClient(ctx, serverAddress, options)
config, err := NewClient(ctx, logger, serverAddress, options)
if err != nil {
return nil, err
}
return NewDialer(dialer, config), nil
}
func NewClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
func NewClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
if !options.Enabled {
return nil, nil
}
if options.Reality != nil && options.Reality.Enabled {
return NewRealityClient(ctx, serverAddress, options)
return NewRealityClient(ctx, logger, serverAddress, options)
} else if options.UTLS != nil && options.UTLS.Enabled {
return NewUTLSClient(ctx, serverAddress, options)
return NewUTLSClient(ctx, logger, serverAddress, options)
}
return NewSTDClient(ctx, serverAddress, options)
return NewSTDClient(ctx, logger, serverAddress, options)
}
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
@ -47,12 +46,6 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e
if err != nil {
return nil, err
}
if kConfig, isKConfig := config.(KTLSCapableConfig); isKConfig && (kConfig.KernelTx() || kConfig.KernelRx()) {
if !C.IsLinux {
return nil, E.New("kTLS is only supported on Linux")
}
return ktls.NewConn(tlsConn, kConfig.KernelTx(), kConfig.KernelRx())
}
readWaitConn, err := badtls.NewReadWaitConn(tlsConn)
if err == nil {
return readWaitConn, nil

View File

@ -21,12 +21,6 @@ type (
CurveID = tls.CurveID
)
type KTLSCapableConfig interface {
Config
KernelTx() bool
KernelRx() bool
}
func ParseTLSVersion(version string) (uint16, error) {
switch version {
case "1.0":

56
common/tls/ktls.go Normal file
View File

@ -0,0 +1,56 @@
package tls
import (
"context"
"net"
"github.com/sagernet/sing-box/common/ktls"
"github.com/sagernet/sing/common/logger"
aTLS "github.com/sagernet/sing/common/tls"
)
type KTLSClientConfig struct {
Config
logger logger.ContextLogger
kernelTx, kernelRx bool
}
func (w *KTLSClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
tlsConn, err := aTLS.ClientHandshake(ctx, conn, w.Config)
if err != nil {
return nil, err
}
return ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
}
func (w *KTLSClientConfig) Clone() Config {
return &KTLSClientConfig{
w.Config.Clone(),
w.logger,
w.kernelTx,
w.kernelRx,
}
}
type KTlSServerConfig struct {
ServerConfig
logger logger.ContextLogger
kernelTx, kernelRx bool
}
func (w *KTlSServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
tlsConn, err := aTLS.ServerHandshake(ctx, conn, w.ServerConfig)
if err != nil {
return nil, err
}
return ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
}
func (w *KTlSServerConfig) Clone() Config {
return &KTlSServerConfig{
w.ServerConfig.Clone().(ServerConfig),
w.logger,
w.kernelTx,
w.kernelRx,
}
}

View File

@ -32,6 +32,7 @@ import (
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/common/ntp"
aTLS "github.com/sagernet/sing/common/tls"
@ -49,12 +50,12 @@ type RealityClientConfig struct {
shortID [8]byte
}
func NewRealityClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {
func NewRealityClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {
if options.UTLS == nil || !options.UTLS.Enabled {
return nil, E.New("uTLS is required by reality client")
}
uClient, err := NewUTLSClient(ctx, serverAddress, options)
uClient, err := NewUTLSClient(ctx, logger, serverAddress, options)
if err != nil {
return nil, err
}
@ -93,7 +94,7 @@ func (e *RealityClientConfig) SetNextProtos(nextProto []string) {
e.uClient.SetNextProtos(nextProto)
}
func (e *RealityClientConfig) Config() (*STDConfig, error) {
func (e *RealityClientConfig) STDConfig() (*STDConfig, error) {
return nil, E.New("unsupported usage for reality")
}

View File

@ -119,6 +119,13 @@ func NewRealityServer(ctx context.Context, logger log.Logger, options option.Inb
return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
}
if options.ECH != nil && options.ECH.Enabled {
return nil, E.New("Reality is conflict with ECH")
}
if options.KernelRx || options.KernelTx {
return nil, E.New("Reality is conflict with kTLS")
}
return &RealityServerConfig{&tlsConfig}, nil
}
@ -138,7 +145,7 @@ func (c *RealityServerConfig) SetNextProtos(nextProto []string) {
c.config.NextProtos = nextProto
}
func (c *RealityServerConfig) Config() (*tls.Config, error) {
func (c *RealityServerConfig) STDConfig() (*tls.Config, error) {
return nil, E.New("unsupported usage for reality")
}

View File

@ -6,15 +6,13 @@ import (
"os"
"github.com/sagernet/sing-box/common/badtls"
"github.com/sagernet/sing-box/common/ktls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
aTLS "github.com/sagernet/sing/common/tls"
)
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
func NewServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
if !options.Enabled {
return nil, nil
}
@ -31,12 +29,6 @@ func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (C
if err != nil {
return nil, err
}
if kConfig, isKConfig := config.(KTLSCapableConfig); isKConfig && (kConfig.KernelTx() || kConfig.KernelRx()) {
if !C.IsLinux {
return nil, E.New("kTLS is only supported on Linux")
}
return ktls.NewConn(tlsConn, kConfig.KernelTx(), kConfig.KernelRx())
}
readWaitConn, err := badtls.NewReadWaitConn(tlsConn)
if err == nil {
return readWaitConn, nil

View File

@ -11,8 +11,10 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tlsfragment"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/common/ntp"
)
@ -22,7 +24,6 @@ type STDClientConfig struct {
fragment bool
fragmentFallbackDelay time.Duration
recordFragment bool
kernelTx, kernelRx bool
}
func (c *STDClientConfig) ServerName() string {
@ -41,7 +42,7 @@ func (c *STDClientConfig) SetNextProtos(nextProto []string) {
c.config.NextProtos = nextProto
}
func (c *STDClientConfig) Config() (*STDConfig, error) {
func (c *STDClientConfig) STDConfig() (*STDConfig, error) {
return c.config, nil
}
@ -59,8 +60,6 @@ func (c *STDClientConfig) Clone() Config {
fragment: c.fragment,
fragmentFallbackDelay: c.fragmentFallbackDelay,
recordFragment: c.recordFragment,
kernelTx: c.kernelTx,
kernelRx: c.kernelRx,
}
}
@ -72,15 +71,7 @@ func (c *STDClientConfig) SetECHConfigList(EncryptedClientHelloConfigList []byte
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
}
func (c *STDClientConfig) KernelTx() bool {
return c.kernelTx
}
func (c *STDClientConfig) KernelRx() bool {
return c.kernelRx
}
func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
@ -163,18 +154,24 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb
}
tlsConfig.RootCAs = certPool
}
stdConfig := &STDClientConfig{
ctx: ctx,
config: &tlsConfig,
fragment: options.Fragment,
fragmentFallbackDelay: time.Duration(options.FragmentFallbackDelay),
recordFragment: options.RecordFragment,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
}
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
if options.ECH != nil && options.ECH.Enabled {
return parseECHClientConfig(ctx, stdConfig, options)
} else {
return stdConfig, nil
var err error
config, err = parseECHClientConfig(ctx, config.(ECHCapableConfig), options)
if err != nil {
return nil, err
}
}
if options.KernelRx || options.KernelTx {
if !C.IsLinux {
return nil, E.New("kTLS is only supported on Linux")
}
config = &KTLSClientConfig{
Config: config,
logger: logger,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
}
}
return config, nil
}

View File

@ -20,16 +20,15 @@ import (
var errInsecureUnused = E.New("tls: insecure unused")
type STDServerConfig struct {
config *tls.Config
logger log.Logger
kernelTx, kernelRx bool
acmeService adapter.SimpleLifecycle
certificate []byte
key []byte
certificatePath string
keyPath string
echKeyPath string
watcher *fswatch.Watcher
config *tls.Config
logger log.Logger
acmeService adapter.SimpleLifecycle
certificate []byte
key []byte
certificatePath string
keyPath string
echKeyPath string
watcher *fswatch.Watcher
}
func (c *STDServerConfig) ServerName() string {
@ -56,7 +55,7 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) {
}
}
func (c *STDServerConfig) Config() (*STDConfig, error) {
func (c *STDServerConfig) STDConfig() (*STDConfig, error) {
return c.config, nil
}
@ -70,20 +69,10 @@ func (c *STDServerConfig) Server(conn net.Conn) (Conn, error) {
func (c *STDServerConfig) Clone() Config {
return &STDServerConfig{
config: c.config.Clone(),
kernelTx: c.kernelTx,
kernelRx: c.kernelRx,
config: c.config.Clone(),
}
}
func (c *STDServerConfig) KernelTx() bool {
return c.kernelTx
}
func (c *STDServerConfig) KernelRx() bool {
return c.kernelRx
}
func (c *STDServerConfig) Start() error {
if c.acmeService != nil {
return c.acmeService.Start()
@ -171,7 +160,7 @@ func (c *STDServerConfig) Close() error {
return nil
}
func NewSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
if !options.Enabled {
return nil, nil
}
@ -273,16 +262,23 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
return nil, err
}
}
return &STDServerConfig{
var config ServerConfig = &STDServerConfig{
config: tlsConfig,
logger: logger,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
acmeService: acmeService,
certificate: certificate,
key: key,
certificatePath: options.CertificatePath,
keyPath: options.KeyPath,
echKeyPath: echKeyPath,
}, nil
}
if options.KernelTx || options.KernelRx {
config = &KTlSServerConfig{
ServerConfig: config,
logger: logger,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
}
}
return config, nil
}

View File

@ -14,8 +14,10 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tlsfragment"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/common/ntp"
utls "github.com/metacubex/utls"
@ -29,8 +31,6 @@ type UTLSClientConfig struct {
fragment bool
fragmentFallbackDelay time.Duration
recordFragment bool
kernelTx bool
kernelRx bool
}
func (c *UTLSClientConfig) ServerName() string {
@ -52,7 +52,7 @@ func (c *UTLSClientConfig) SetNextProtos(nextProto []string) {
c.config.NextProtos = nextProto
}
func (c *UTLSClientConfig) Config() (*STDConfig, error) {
func (c *UTLSClientConfig) STDConfig() (*STDConfig, error) {
return nil, E.New("unsupported usage for uTLS")
}
@ -69,7 +69,7 @@ func (c *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []by
func (c *UTLSClientConfig) Clone() Config {
return &UTLSClientConfig{
c.ctx, c.config.Clone(), c.id, c.fragment, c.fragmentFallbackDelay, c.recordFragment, c.kernelTx, c.kernelRx,
c.ctx, c.config.Clone(), c.id, c.fragment, c.fragmentFallbackDelay, c.recordFragment,
}
}
@ -81,14 +81,6 @@ func (c *UTLSClientConfig) SetECHConfigList(EncryptedClientHelloConfigList []byt
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
}
func (c *UTLSClientConfig) KernelTx() bool {
return c.kernelTx
}
func (c *UTLSClientConfig) KernelRx() bool {
return c.kernelRx
}
type utlsConnWrapper struct {
*utls.UConn
}
@ -149,7 +141,7 @@ func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error {
return c.UConn.HandshakeContext(ctx)
}
func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
@ -224,20 +216,31 @@ func NewUTLSClient(ctx context.Context, serverAddress string, options option.Out
if err != nil {
return nil, err
}
uConfig := &UTLSClientConfig{ctx, &tlsConfig, id, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment, options.KernelTx, options.KernelRx}
if uConfig.kernelTx || uConfig.kernelRx {
if options.Reality != nil && options.Reality.Enabled {
return nil, E.New("Reality is conflict with kTLS")
}
}
var config Config = &UTLSClientConfig{ctx, &tlsConfig, id, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
if options.ECH != nil && options.ECH.Enabled {
if options.Reality != nil && options.Reality.Enabled {
return nil, E.New("Reality is conflict with ECH")
}
return parseECHClientConfig(ctx, uConfig, options)
} else {
return uConfig, nil
config, err = parseECHClientConfig(ctx, config.(ECHCapableConfig), options)
if err != nil {
return nil, err
}
}
if options.KernelRx || options.KernelTx {
if options.Reality != nil && options.Reality.Enabled {
return nil, E.New("Reality is conflict with kTLS")
}
if !C.IsLinux {
return nil, E.New("kTLS is only supported on Linux")
}
config = &KTLSClientConfig{
Config: config,
logger: logger,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
}
}
return config, nil
}
var (

View File

@ -537,7 +537,7 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
}
func MessageToAddresses(response *dns.Msg) []netip.Addr {
if response.Rcode != dns.RcodeSuccess {
if response == nil || response.Rcode != dns.RcodeSuccess {
return nil
}
addresses := make([]netip.Addr, 0, len(response.Answer))

View File

@ -59,11 +59,11 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
}
tlsOptions := common.PtrValueOrDefault(options.TLS)
tlsOptions.Enabled = true
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
if err != nil {
return nil, err
}
if common.Error(tlsConfig.Config()) == nil && !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) {
if common.Error(tlsConfig.STDConfig()) == nil && !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) {
tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), http2.NextProtoTLS))
}
if !common.Contains(tlsConfig.NextProtos(), "http/1.1") {

View File

@ -51,11 +51,11 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
}
tlsOptions := common.PtrValueOrDefault(options.TLS)
tlsOptions.Enabled = true
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
if err != nil {
return nil, err
}
stdConfig, err := tlsConfig.Config()
stdConfig, err := tlsConfig.STDConfig()
if err != nil {
return nil, err
}

View File

@ -48,7 +48,7 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options
}
tlsOptions := common.PtrValueOrDefault(options.TLS)
tlsOptions.Enabled = true
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
if err != nil {
return nil, err
}

View File

@ -49,7 +49,7 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o
}
tlsOptions := common.PtrValueOrDefault(options.TLS)
tlsOptions.Enabled = true
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
if err != nil {
return nil, err
}

4
go.mod
View File

@ -28,9 +28,9 @@ require (
github.com/sagernet/gomobile v0.1.8
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237
github.com/sagernet/quic-go v0.52.0-beta.1
github.com/sagernet/sing v0.7.8-0.20250907125815-3d24f9b5ff7c
github.com/sagernet/sing v0.7.8-0.20250908034727-1ceb6b183d75
github.com/sagernet/sing-mux v0.3.3
github.com/sagernet/sing-quic v0.5.1
github.com/sagernet/sing-quic v0.5.2-0.20250908021228-186e280a524e
github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11

8
go.sum
View File

@ -169,12 +169,12 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.7.8-0.20250907125815-3d24f9b5ff7c h1:7jbIoVt3p1vlgQoyWIlzRvwInNwDxe6+tSJME07LTmI=
github.com/sagernet/sing v0.7.8-0.20250907125815-3d24f9b5ff7c/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.7.8-0.20250908034727-1ceb6b183d75 h1:jc7lqbvND3htLpTeu6xPlhcVz01z7dEmikCZ46LmvxU=
github.com/sagernet/sing v0.7.8-0.20250908034727-1ceb6b183d75/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.5.1 h1:o+mX/schfy6fbbU2rnb6ouUYOL+iUBjA4jOZqyIvDsU=
github.com/sagernet/sing-quic v0.5.1/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI=
github.com/sagernet/sing-quic v0.5.2-0.20250908021228-186e280a524e h1:l+DICzp1OecGSDoiHE0Ebviaz3zqSp7XM27UcRbGkBM=
github.com/sagernet/sing-quic v0.5.2-0.20250908021228-186e280a524e/go.mod h1:gi/sGED8gTWgTAp3GlzXo2D7mXYY+ERoxtGvSkNx3sI=
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=

View File

@ -44,7 +44,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
return nil, C.ErrTLSRequired
}
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -34,7 +34,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil {
return nil, err
}
detour, err := tls.NewDialerFromOptions(ctx, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
detour, err := tls.NewDialerFromOptions(ctx, logger, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired
}
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -44,7 +44,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired
}
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -88,7 +88,7 @@ func (n *Inbound) Start(stage adapter.StartStage) error {
if err != nil {
return E.Cause(err, "create TLS config")
}
tlsConfig, err = n.tlsConfig.Config()
tlsConfig, err = n.tlsConfig.STDConfig()
if err != nil {
return err
}

View File

@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
options.TLS.MinVersion = "1.2"
options.TLS.MaxVersion = "1.2"
}
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
@ -61,7 +61,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
}
} else {
stdTLSConfig, err := tlsConfig.Config()
stdTLSConfig, err := tlsConfig.STDConfig()
if err != nil {
return nil, err
}

View File

@ -166,7 +166,7 @@ func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dn
if serverAddr.Port == 0 {
serverAddr.Port = 443
}
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.AddrString(), option.OutboundTLSOptions{
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.AddrString(), option.OutboundTLSOptions{
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
}))
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil

View File

@ -51,7 +51,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
key: trojan.Key(options.Password),
}
if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
outbound.tlsConfig, err = tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if options.TLS == nil || !options.TLS.Enabled {
return nil, C.ErrTLSRequired
}
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -53,7 +53,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
serverAddr: options.ServerOptions.Build(),
}
if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
outbound.tlsConfig, err = tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -53,7 +53,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
serverAddr: options.ServerOptions.Build(),
}
if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
outbound.tlsConfig, err = tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}

View File

@ -301,11 +301,11 @@ func (d *Service) startMeshWithHost(derpServer *derp.Server, server *option.DERP
}
var stdConfig *tls.STDConfig
if server.TLS != nil && server.TLS.Enabled {
tlsConfig, err := tls.NewClient(d.ctx, hostname, common.PtrValueOrDefault(server.TLS))
tlsConfig, err := tls.NewClient(d.ctx, d.logger, hostname, common.PtrValueOrDefault(server.TLS))
if err != nil {
return err
}
stdConfig, err = tlsConfig.Config()
stdConfig, err = tlsConfig.STDConfig()
if err != nil {
return err
}

View File

@ -129,7 +129,7 @@ func (t *Transport) updateTransports(link *TransportLink) error {
return os.ErrInvalid
}
if link.dnsOverTLS {
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.String(), option.OutboundTLSOptions{
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.String(), option.OutboundTLSOptions{
Enabled: true,
ServerName: serverAddr.String(),
}))
@ -151,7 +151,7 @@ func (t *Transport) updateTransports(link *TransportLink) error {
} else {
serverName = serverAddr.String()
}
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.String(), option.OutboundTLSOptions{
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.String(), option.OutboundTLSOptions{
Enabled: true,
ServerName: serverName,
}))

View File

@ -13,6 +13,7 @@ import (
"github.com/sagernet/sing-vmess"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json/badoption"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
@ -55,7 +56,7 @@ func newV2RayPlugin(ctx context.Context, pluginOpts Args, router adapter.Router,
var tlsClient tls.Config
var err error
if tlsOptions.Enabled {
tlsClient, err = tls.NewClient(ctx, serverAddr.AddrString(), tlsOptions)
tlsClient, err = tls.NewClient(ctx, logger.NOP(), serverAddr.AddrString(), tlsOptions)
if err != nil {
return nil, err
}