Compare commits

..

27 Commits

Author SHA1 Message Date
renovate[bot]
c4207d86d0
[dependencies] Update github-actions 2025-09-08 11:42:36 +00:00
世界
47649a7552
ktls: Add warning for inappropriate scenarios 2025-09-08 19:41:52 +08:00
世界
2599ab5117
ktls: Fix parse kernel version 2025-09-08 19:41:10 +08:00
世界
028d345d9d
documentation: Bump version 2025-09-08 19:41:10 +08:00
世界
6a43a8037a
Update Go to 1.25.1 2025-09-08 19:41:10 +08:00
世界
b0e2375fd8
documentation: Add kTLS 2025-09-08 19:41:10 +08:00
世界
46e73d8532
Add support for kTLS
Reference: https://gitlab.com/go-extension/tls
2025-09-08 19:41:09 +08:00
世界
c8d8b7fac9
Add proxy support for ICMP echo request 2025-09-08 19:41:09 +08:00
世界
1bcf36a487
Fix resolve using resolved 2025-09-08 19:41:09 +08:00
世界
6d59c94f33
documentation: Update behavior of local DNS server on darwin 2025-09-08 19:41:09 +08:00
世界
cb50ee4627
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 19:41:09 +08:00
世界
c0d7ad6118
Remove use of ldflags -checklinkname=0 on darwin 2025-09-08 19:41:08 +08:00
世界
2b2ef490d6
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 19:41:08 +08:00
世界
b49ee4eff4
Fix legacy DNS config 2025-09-08 19:41:07 +08:00
世界
a91478d2c7
Fix rule-set format 2025-09-08 19:41:07 +08:00
世界
414fe5f66f
documentation: Remove outdated icons 2025-09-08 19:41:07 +08:00
世界
112f33eeff
documentation: Improve local DNS server 2025-09-08 19:41:07 +08:00
世界
ca3b491c32
Use libresolv in local DNS server on darwin 2025-09-08 19:41:05 +08:00
世界
96e3838dd0
Use resolved in local DNS server if available 2025-09-08 19:41:05 +08:00
xchacha20-poly1305
4be4437ec6
Fix rule set version 2025-09-08 19:41:05 +08:00
世界
69b5a46838
documentation: Add preferred_by route rule item 2025-09-08 19:41:05 +08:00
世界
52ceaf3582
Add preferred_by route rule item 2025-09-08 19:41:05 +08:00
世界
d95f17a655
documentation: Add interface address rule items 2025-09-08 19:41:04 +08:00
世界
e727fdae61
Add interface address rule items 2025-09-08 19:41:03 +08:00
neletor
288a7b9422
Add support for ech retry configs 2025-09-08 19:41:03 +08:00
Zephyruso
65b48422ac
Add /dns/flush-clash meta api 2025-09-08 19:41:03 +08:00
世界
d3fe6e6473
Fix tls options ignored in mixed inbounds 2025-09-08 19:37:58 +08:00
20 changed files with 335 additions and 108 deletions

View File

@ -46,7 +46,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ^1.25.0
go-version: ^1.25.1
- name: Check input version
if: github.event_name == 'workflow_dispatch'
run: |-
@ -110,7 +110,7 @@ jobs:
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
uses: actions/setup-go@v6
with:
go-version: ^1.25.0
go-version: ^1.25.1
- name: Setup Go 1.24
if: matrix.legacy_go124
uses: actions/setup-go@v6
@ -300,7 +300,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ^1.25.0
go-version: ^1.25.1
- name: Setup Android NDK
id: setup-ndk
uses: nttld/setup-ndk@v1
@ -380,7 +380,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ^1.25.0
go-version: ^1.25.1
- name: Setup Android NDK
id: setup-ndk
uses: nttld/setup-ndk@v1
@ -478,7 +478,7 @@ jobs:
if: matrix.if
uses: actions/setup-go@v6
with:
go-version: ^1.25.0
go-version: ^1.25.1
- name: Setup Xcode stable
if: matrix.if && github.ref == 'refs/heads/main-next'
run: |-

View File

@ -30,7 +30,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ^1.25.0
go-version: ^1.25.1
- name: Check input version
if: github.event_name == 'workflow_dispatch'
run: |-
@ -71,7 +71,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ^1.25.0
go-version: ^1.25.1
- name: Setup Android NDK
if: matrix.os == 'android'
uses: nttld/setup-ndk@v1

View File

@ -5,6 +5,8 @@ import (
"strings"
F "github.com/sagernet/sing/common/format"
"golang.org/x/mod/semver"
)
type Version struct {
@ -16,7 +18,19 @@ type Version struct {
PreReleaseVersion int
}
func (v Version) After(anotherVersion Version) bool {
func (v Version) LessThan(anotherVersion Version) bool {
return !v.GreaterThanOrEqual(anotherVersion)
}
func (v Version) LessThanOrEqual(anotherVersion Version) bool {
return v == anotherVersion || anotherVersion.GreaterThan(v)
}
func (v Version) GreaterThanOrEqual(anotherVersion Version) bool {
return v == anotherVersion || v.GreaterThan(anotherVersion)
}
func (v Version) GreaterThan(anotherVersion Version) bool {
if v.Major > anotherVersion.Major {
return true
} else if v.Major < anotherVersion.Major {
@ -44,21 +58,27 @@ func (v Version) After(anotherVersion Version) bool {
} else if v.PreReleaseVersion < anotherVersion.PreReleaseVersion {
return false
}
} else if v.PreReleaseIdentifier == "rc" && anotherVersion.PreReleaseIdentifier == "beta" {
}
preReleaseIdentifier := parsePreReleaseIdentifier(v.PreReleaseIdentifier)
anotherPreReleaseIdentifier := parsePreReleaseIdentifier(anotherVersion.PreReleaseIdentifier)
if preReleaseIdentifier < anotherPreReleaseIdentifier {
return true
} else if v.PreReleaseIdentifier == "beta" && anotherVersion.PreReleaseIdentifier == "rc" {
return false
} else if v.PreReleaseIdentifier == "beta" && anotherVersion.PreReleaseIdentifier == "alpha" {
return true
} else if v.PreReleaseIdentifier == "alpha" && anotherVersion.PreReleaseIdentifier == "beta" {
} else if preReleaseIdentifier > anotherPreReleaseIdentifier {
return false
}
}
return false
}
func (v Version) VersionString() string {
return F.ToString(v.Major, ".", v.Minor, ".", v.Patch)
func parsePreReleaseIdentifier(identifier string) int {
if strings.HasPrefix(identifier, "rc") {
return 1
} else if strings.HasPrefix(identifier, "beta") {
return 2
} else if strings.HasPrefix(identifier, "alpha") {
return 3
}
return 0
}
func (v Version) String() string {
@ -83,6 +103,10 @@ func (v Version) BadString() string {
return version
}
func IsValid(versionName string) bool {
return semver.IsValid("v" + versionName)
}
func Parse(versionName string) (version Version) {
if strings.HasPrefix(versionName, "v") {
versionName = versionName[1:]

View File

@ -10,9 +10,9 @@ func TestCompareVersion(t *testing.T) {
t.Parallel()
require.Equal(t, "1.3.0-beta.1", Parse("v1.3.0-beta1").String())
require.Equal(t, "1.3-beta1", Parse("v1.3.0-beta.1").BadString())
require.True(t, Parse("1.3.0").After(Parse("1.3-beta1")))
require.True(t, Parse("1.3.0").After(Parse("1.3.0-beta1")))
require.True(t, Parse("1.3.0-beta1").After(Parse("1.3.0-alpha1")))
require.True(t, Parse("1.3.1").After(Parse("1.3.0")))
require.True(t, Parse("1.4").After(Parse("1.3")))
require.True(t, Parse("1.3.0").GreaterThan(Parse("1.3-beta1")))
require.True(t, Parse("1.3.0").GreaterThan(Parse("1.3.0-beta1")))
require.True(t, Parse("1.3.0-beta1").GreaterThan(Parse("1.3.0-alpha1")))
require.True(t, Parse("1.3.1").GreaterThan(Parse("1.3.0")))
require.True(t, Parse("1.4").GreaterThan(Parse("1.3")))
}

View File

@ -11,7 +11,6 @@ import (
"syscall"
"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"
@ -29,8 +28,6 @@ type Conn struct {
readWaitOptions N.ReadWaitOptions
kernelTx bool
kernelRx bool
kernelDidRead bool
kernelDidWrite bool
}
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
@ -64,7 +61,7 @@ func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, t
for rawConn.Hand.Len() > 0 {
err = rawConn.HandlePostHandshakeMessage()
if err != nil {
return nil, E.Cause(err, "ktls: failed to handle post-handshake messages")
return nil, E.Cause(err, "handle post-handshake messages")
}
}
}
@ -85,36 +82,15 @@ func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, t
}
func (c *Conn) Upstream() any {
return c.conn
}
func (c *Conn) ReaderReplaceable() bool {
if !c.kernelRx {
return true
}
c.rawConn.In.Lock()
defer c.rawConn.In.Unlock()
return !c.kernelDidRead
}
func (c *Conn) WriterReplaceable() bool {
if !c.kernelTx {
return true
}
/*c.rawConn.Out.Lock()
defer c.rawConn.Out.Unlock()
return !c.kernelDidWrite*/
return true
return c.Conn
}
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")
if !*c.rawConn.IsClient {
c.logger.WarnContext(c.ctx, "ktls: RX splice is unavailable on the server size, since it will cause an unknown failure")
return nil
}
c.logger.DebugContext(c.ctx, "ktls: RX splice requested")
@ -125,13 +101,6 @@ 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

@ -12,11 +12,11 @@ import (
"syscall"
"unsafe"
"github.com/sagernet/sing-box/common/badversion"
"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"
)
@ -55,46 +55,42 @@ var KernelSupport = sync.OnceValues(func() (*Support, error) {
return nil, err
}
kernelVersion, err := semver.Parse(strings.Trim(string(uname.Release[:]), "\x00"))
kernelVersion := badversion.Parse(strings.Trim(string(uname.Release[:]), "\x00"))
if err != nil {
return nil, err
}
kernelVersion.Pre = nil
kernelVersion.Build = nil
var support Support
switch {
case kernelVersion.GTE(semver.Version{Major: 6, Minor: 14}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 6, Minor: 14}):
support.TLS_Version13_KeyUpdate = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 6, Minor: 1}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 6, Minor: 1}):
support.TLS_ARIA_GCM = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 6}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 6}):
support.TLS_Version13_RX = true
support.TLS_RX_NOPADDING = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 19}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 19}):
support.TLS_TX_ZEROCOPY = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 16}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 16}):
support.TLS_SM4 = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 11}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 11}):
support.TLS_CHACHA20_POLY1305 = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 2}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 2}):
support.TLS_AES_128_CCM = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 1}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 1}):
support.TLS_AES_256_GCM = true
support.TLS_Version13 = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 4, Minor: 17}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 4, Minor: 17}):
support.TLS_RX = true
fallthrough
case kernelVersion.GTE(semver.Version{Major: 4, Minor: 13}):
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 4, Minor: 13}):
support.TLS = true
}
@ -118,7 +114,7 @@ var KernelSupport = sync.OnceValues(func() (*Support, error) {
func Load() error {
support, err := KernelSupport()
if err != nil {
return err
return E.Cause(err, "ktls: check availability")
}
if !support.TLS || !support.TLS_Version13 {
return E.New("ktls: kernel does not support TLS 1.3")
@ -132,10 +128,10 @@ func (c *Conn) setupKernel(txOffload, rxOffload bool) error {
}
support, err := KernelSupport()
if err != nil {
return err
return E.Cause(err, "check availability")
}
if !support.TLS || !support.TLS_Version13 {
return E.New("ktls: kernel does not support TLS 1.3")
return E.New("kernel does not support TLS 1.3")
}
c.rawConn.Out.Lock()
defer c.rawConn.Out.Unlock()
@ -143,13 +139,13 @@ func (c *Conn) setupKernel(txOffload, rxOffload bool) error {
return syscall.SetsockoptString(int(fd), unix.SOL_TCP, unix.TCP_ULP, "tls")
})
if err != nil {
return E.Cause(err, "initialize kernel TLS")
return os.NewSyscallError("setsockopt", err)
}
if txOffload {
txCrypto := kernelCipher(support, c.rawConn.Out, *c.rawConn.CipherSuite, false)
if txCrypto == nil {
return E.New("kTLS: unsupported cipher suite")
return E.New("unsupported cipher suite")
}
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_TX, txCrypto.String())
@ -172,7 +168,7 @@ func (c *Conn) setupKernel(txOffload, rxOffload bool) error {
if rxOffload {
rxCrypto := kernelCipher(support, c.rawConn.In, *c.rawConn.CipherSuite, true)
if rxCrypto == nil {
return E.New("kTLS: unsupported cipher suite")
return E.New("unsupported cipher suite")
}
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_RX, rxCrypto.String())
@ -278,7 +274,6 @@ func (c *Conn) readKernelRecord() (uint8, []byte, error) {
default:
return 0, nil, err
}
c.kernelDidRead = true
if n <= 0 {
return 0, nil, io.EOF
@ -324,7 +319,6 @@ func (c *Conn) writeKernelRecord(typ uint16, data []byte) (int, error) {
if ew != nil {
return 0, ew
}
c.kernelDidWrite = true
return n, err
}

View File

@ -20,7 +20,12 @@ func NewDialerFromOptions(ctx context.Context, logger logger.ContextLogger, dial
if !options.Enabled {
return dialer, nil
}
config, err := NewClient(ctx, logger, serverAddress, options)
config, err := NewClientWithOptions(ClientOptions{
Context: ctx,
Logger: logger,
ServerAddress: serverAddress,
Options: options,
})
if err != nil {
return nil, err
}
@ -28,15 +33,40 @@ func NewDialerFromOptions(ctx context.Context, logger logger.ContextLogger, dial
}
func NewClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
if !options.Enabled {
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 {
return nil, nil
}
if options.Reality != nil && options.Reality.Enabled {
return NewRealityClient(ctx, logger, serverAddress, options)
} else if options.UTLS != nil && options.UTLS.Enabled {
return NewUTLSClient(ctx, logger, serverAddress, options)
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")
}
}
return NewSTDClient(ctx, logger, serverAddress, options)
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)
}
return NewSTDClient(options.Context, options.Logger, options.ServerAddress, options.Options)
}
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {

View File

@ -5,6 +5,7 @@ import (
"net"
"github.com/sagernet/sing-box/common/ktls"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
aTLS "github.com/sagernet/sing/common/tls"
)
@ -20,7 +21,12 @@ func (w *KTLSClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (
if err != nil {
return nil, err
}
return ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
kConn, err := ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
if err != nil {
tlsConn.Close()
return nil, E.Cause(err, "initialize kernel TLS")
}
return kConn, nil
}
func (w *KTLSClientConfig) Clone() Config {
@ -43,7 +49,12 @@ func (w *KTlSServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (
if err != nil {
return nil, err
}
return ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
kConn, err := ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
if err != nil {
tlsConn.Close()
return nil, E.Cause(err, "initialize kernel TLS")
}
return kConn, nil
}
func (w *KTlSServerConfig) Clone() Config {

View File

@ -12,14 +12,37 @@ import (
aTLS "github.com/sagernet/sing/common/tls"
)
type ServerOptions struct {
Context context.Context
Logger log.ContextLogger
Options option.InboundTLSOptions
KTLSCompatible bool
}
func NewServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
if !options.Enabled {
return NewServerWithOptions(ServerOptions{
Context: ctx,
Logger: logger,
Options: options,
})
}
func NewServerWithOptions(options ServerOptions) (ServerConfig, error) {
if !options.Options.Enabled {
return nil, nil
}
if options.Reality != nil && options.Reality.Enabled {
return NewRealityServer(ctx, logger, options)
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")
}
}
return NewSTDServer(ctx, logger, options)
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 NewRealityServer(options.Context, options.Logger, options.Options)
}
return NewSTDServer(options.Context, options.Logger, options.Options)
}
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {

View File

@ -4,8 +4,13 @@ icon: material/alert-decagram
#### 1.13.0-alpha.9
* Add kTLS support **1**
* Fixes and improvements
**1**:
See [TLS](/configuration/shared/tls/).
#### 1.12.4
* Fixes and improvements

View File

@ -1,7 +1,12 @@
---
icon: material/alert-decagram
icon: material/new-box
---
!!! quote "Changes in sing-box 1.13.0"
:material-plus: [kernel_tx](#kernel_tx)
:material-plus: [kernel_rx](#kernel_rx)
!!! quote "Changes in sing-box 1.12.0"
:material-plus: [fragment](#fragment)
@ -28,6 +33,8 @@ icon: material/alert-decagram
"certificate_path": "",
"key": [],
"key_path": "",
"kernel_tx": false,
"kernel_rx": false,
"acme": {
"domain": [],
"data_directory": "",
@ -188,7 +195,8 @@ By default, the maximum version is currently TLS 1.3.
#### cipher_suites
A list of enabled TLS 1.01.2 cipher suites. The order of the list is ignored. Note that TLS 1.3 cipher suites are not configurable.
A list of enabled TLS 1.01.2 cipher suites. The order of the list is ignored.
Note that TLS 1.3 cipher suites are not configurable.
If empty, a safe default list is used. The default cipher suites might change over time.
@ -220,6 +228,50 @@ The server private key line array, in PEM format.
The path to the server private key, in PEM format.
#### kernel_tx
!!! question "Since sing-box 1.13.0"
!!! quote ""
Only supported on Linux 5.1+, use a newer kernel if possible.
!!! quote ""
Only TLS 1.3 is supported.
!!! warning ""
uTLS is compatible, but not other custom TLS.
!!! warning ""
kTLS TX may only improve performance when `splice(2)` is available (both ends must be TCP or TLS without additional protocols after handshake); otherwise, it will definitely degrade performance.
Enable kernel TLS transmit support.
#### kernel_rx
!!! question "Since sing-box 1.13.0"
!!! quote ""
Only supported on Linux 5.1+, use a newer kernel if possible.
!!! quote ""
Only TLS 1.3 is supported.
!!! warning ""
uTLS is compatible, but not other custom TLS.
!!! failure ""
kTLS RX will definitely degrade performance even if `splice(2)` is in use, so enabling it is not recommended.
Enable kernel TLS receive support.
## Custom TLS support
!!! info "QUIC support"

View File

@ -2,6 +2,11 @@
icon: material/alert-decagram
---
!!! quote "sing-box 1.13.0 中的更改"
:material-plus: [kernel_tx](#kernel_tx)
:material-plus: [kernel_rx](#kernel_rx)
!!! quote "sing-box 1.12.0 中的更改"
:material-plus: [tls_fragment](#tls_fragment)
@ -28,6 +33,8 @@ icon: material/alert-decagram
"certificate_path": "",
"key": [],
"key_path": "",
"kernel_tx": false,
"kernel_rx": false,
"acme": {
"domain": [],
"data_directory": "",
@ -216,6 +223,56 @@ TLS 版本值:
服务器 PEM 私钥路径。
#### kernel_tx
!!! question "自 sing-box 1.13.0 起"
!!! quote ""
仅支持 Linux 5.1+,如果可能,使用较新的内核。
!!! quote ""
仅支持 TLS 1.3。
!!! warning ""
兼容 uTLS但不兼容其他自定义 TLS。
!!! warning ""
kTLS TX 仅当 `splice(2)` 可用时(两端经过握手后必须为没有附加协议的 TCP 或 TLS才能提高性能否则肯定会降低性能。
启用内核 TLS 发送支持。
#### kernel_rx
!!! question "自 sing-box 1.13.0 起"
!!! quote ""
仅支持 Linux 5.1+,如果可能,使用较新的内核。
!!! quote ""
仅支持 TLS 1.3。
!!! warning ""
兼容 uTLS但不兼容其他自定义 TLS。
!!! failure ""
即使使用 `splice(2)`kTLS RX 也肯定会降低性能,因此不建议启用。
启用内核 TLS 接收支持。
## 自定义 TLS 支持
!!! info "QUIC 支持"
只有 ECH 在 QUIC 中被支持.
#### utls
==仅客户端==

3
go.mod
View File

@ -4,7 +4,6 @@ go 1.23.1
require (
github.com/anytls/sing-anytls v0.0.8
github.com/blang/semver/v4 v4.0.0
github.com/caddyserver/certmagic v0.23.0
github.com/coder/websocket v1.8.13
github.com/cretz/bine v0.2.0
@ -28,7 +27,7 @@ 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.20250908034727-1ceb6b183d75
github.com/sagernet/sing v0.7.8-0.20250908063931-beb351e61b89
github.com/sagernet/sing-mux v0.3.3
github.com/sagernet/sing-quic v0.5.2-0.20250908021228-186e280a524e
github.com/sagernet/sing-shadowsocks v0.2.8

6
go.sum
View File

@ -12,8 +12,6 @@ github.com/anytls/sing-anytls v0.0.8 h1:1u/fnH1HoeeMV5mX7/eUOjLBvPdkd1UJRmXiRi6V
github.com/anytls/sing-anytls v0.0.8/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
@ -169,8 +167,8 @@ 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.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 v0.7.8-0.20250908063931-beb351e61b89 h1:Of7kq85JItQmR4Y8m3L/DFFxaxyjK9rJvLMpmXPQ7g0=
github.com/sagernet/sing v0.7.8-0.20250908063931-beb351e61b89/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.2-0.20250908021228-186e280a524e h1:l+DICzp1OecGSDoiHE0Ebviaz3zqSp7XM27UcRbGkBM=

View File

@ -43,7 +43,12 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
authenticator: auth.NewAuthenticator(options.Users),
}
if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
Context: ctx,
Logger: logger,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: true,
})
if err != nil {
return nil, err
}

View File

@ -8,10 +8,12 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/inbound"
"github.com/sagernet/sing-box/common/listener"
"github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/common/uot"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
@ -33,6 +35,7 @@ type Inbound struct {
logger log.ContextLogger
listener *listener.Listener
authenticator *auth.Authenticator
tlsConfig tls.ServerConfig
}
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (adapter.Inbound, error) {
@ -42,6 +45,18 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
logger: logger,
authenticator: auth.NewAuthenticator(options.Users),
}
if options.TLS != nil {
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
Context: ctx,
Logger: logger,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: true,
})
if err != nil {
return nil, err
}
inbound.tlsConfig = tlsConfig
}
inbound.listener = listener.New(listener.Options{
Context: ctx,
Logger: logger,
@ -58,13 +73,21 @@ func (h *Inbound) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateStart {
return nil
}
if h.tlsConfig != nil {
err := h.tlsConfig.Start()
if err != nil {
return E.Cause(err, "create TLS config")
}
}
return h.listener.Start()
}
func (h *Inbound) Close() error {
return h.listener.Close()
return common.Close(
h.listener,
h.tlsConfig,
)
}
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
err := h.newConnection(ctx, conn, metadata, onClose)
N.CloseOnHandshakeFailure(conn, onClose, err)
@ -78,6 +101,13 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
}
func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
if h.tlsConfig != nil {
tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
if err != nil {
return E.Cause(err, "TLS handshake")
}
conn = tlsConn
}
reader := std_bufio.NewReader(conn)
headerBytes, err := reader.Peek(1)
if err != nil {

View File

@ -50,7 +50,13 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
users: options.Users,
}
if options.TLS != nil {
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
Context: ctx,
Logger: logger,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
!common.PtrValueOrDefault(options.Multiplex).Enabled,
})
if err != nil {
return nil, err
}

View File

@ -51,7 +51,14 @@ 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, logger, options.Server, common.PtrValueOrDefault(options.TLS))
outbound.tlsConfig, err = tls.NewClientWithOptions(tls.ClientOptions{
Context: ctx,
Logger: logger,
ServerAddress: options.Server,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
!common.PtrValueOrDefault(options.Multiplex).Enabled,
})
if err != nil {
return nil, err
}

View File

@ -68,7 +68,16 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
}))
inbound.service = service
if options.TLS != nil {
inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
inbound.tlsConfig, err = tls.NewServerWithOptions(tls.ServerOptions{
Context: ctx,
Logger: logger,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
!common.PtrValueOrDefault(options.Multiplex).Enabled &&
common.All(options.Users, func(it option.VLESSUser) bool {
return it.Flow == ""
}),
})
if err != nil {
return nil, err
}

View File

@ -53,7 +53,15 @@ 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, logger, options.Server, common.PtrValueOrDefault(options.TLS))
outbound.tlsConfig, err = tls.NewClientWithOptions(tls.ClientOptions{
Context: ctx,
Logger: logger,
ServerAddress: options.Server,
Options: common.PtrValueOrDefault(options.TLS),
KTLSCompatible: common.PtrValueOrDefault(options.Transport).Type == "" &&
!common.PtrValueOrDefault(options.Multiplex).Enabled &&
options.Flow == "",
})
if err != nil {
return nil, err
}