mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-09-10 13:24:08 +08:00
Compare commits
21 Commits
64384c75fa
...
d03b4eaf0a
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d03b4eaf0a | ||
![]() |
cc3e2fb8fb | ||
![]() |
7cb1cacd89 | ||
![]() |
aa47149382 | ||
![]() |
57c6c2b40e | ||
![]() |
ea822eedfc | ||
![]() |
2284fc465c | ||
![]() |
b4d4a38cd4 | ||
![]() |
feeace6ee2 | ||
![]() |
af2446c150 | ||
![]() |
6010e6cb67 | ||
![]() |
f3cc2fdb73 | ||
![]() |
71f7c7ec43 | ||
![]() |
9e33df85ab | ||
![]() |
e2065120d4 | ||
![]() |
fecafd3cb1 | ||
![]() |
c5f6e22ddb | ||
![]() |
cd7cabcbae | ||
![]() |
7b9b6642d6 | ||
![]() |
331ba48a6a | ||
![]() |
1612e98bc6 |
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@ -110,7 +110,7 @@ jobs:
|
|||||||
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Go 1.24
|
- name: Setup Go 1.24
|
||||||
if: matrix.legacy_go124
|
if: matrix.legacy_go124
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
@ -154,7 +154,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
@ -174,7 +174,7 @@ jobs:
|
|||||||
export CXX="${CC}++"
|
export CXX="${CC}++"
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@ -300,7 +300,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@ -380,7 +380,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@ -478,7 +478,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Xcode stable
|
- name: Setup Xcode stable
|
||||||
if: matrix.if && github.ref == 'refs/heads/main-next'
|
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||||
run: |-
|
run: |-
|
||||||
|
4
.github/workflows/linux.yml
vendored
4
.github/workflows/linux.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@ -71,7 +71,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
if: matrix.os == 'android'
|
if: matrix.os == 'android'
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
|
@ -15,7 +15,7 @@ RUN set -ex \
|
|||||||
&& go build -v -trimpath -tags \
|
&& go build -v -trimpath -tags \
|
||||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale" \
|
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale" \
|
||||||
-o /go/bin/sing-box \
|
-o /go/bin/sing-box \
|
||||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid=" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
FROM --platform=$TARGETPLATFORM alpine AS dist
|
FROM --platform=$TARGETPLATFORM alpine AS dist
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
|
@ -57,7 +57,6 @@ type InboundContext struct {
|
|||||||
Domain string
|
Domain string
|
||||||
Client string
|
Client string
|
||||||
SniffContext any
|
SniffContext any
|
||||||
SnifferNames []string
|
|
||||||
SniffError error
|
SniffError error
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
|
@ -78,8 +78,8 @@ func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
|||||||
// Deprecated: removed
|
// Deprecated: removed
|
||||||
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
||||||
return M.Metadata{
|
return M.Metadata{
|
||||||
Source: metadata.Source.Unwrap(),
|
Source: metadata.Source,
|
||||||
Destination: metadata.Destination.Unwrap(),
|
Destination: metadata.Destination,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +59,8 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
currentTag = "unknown"
|
currentTag = "unknown"
|
||||||
}
|
}
|
||||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
||||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
||||||
|
|
||||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack")
|
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack")
|
||||||
macOSTags = append(macOSTags, "with_dhcp")
|
macOSTags = append(macOSTags, "with_dhcp")
|
||||||
@ -107,10 +107,10 @@ func buildAndroid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
// sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
|
sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
|
||||||
args = append(args, sharedFlags...)
|
args = append(args, sharedFlags...)
|
||||||
} else {
|
} else {
|
||||||
// debugFlags[1] = debugFlags[1] + " -checklinkname=0"
|
debugFlags[1] = debugFlags[1] + " -checklinkname=0"
|
||||||
args = append(args, debugFlags...)
|
args = append(args, debugFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,176 +0,0 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"sync/atomic"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RawConn struct {
|
|
||||||
pointer unsafe.Pointer
|
|
||||||
methods *Methods
|
|
||||||
|
|
||||||
IsClient *bool
|
|
||||||
IsHandshakeComplete *atomic.Bool
|
|
||||||
Vers *uint16
|
|
||||||
CipherSuite *uint16
|
|
||||||
|
|
||||||
RawInput *bytes.Buffer
|
|
||||||
Input *bytes.Reader
|
|
||||||
Hand *bytes.Buffer
|
|
||||||
|
|
||||||
CloseNotifySent *bool
|
|
||||||
CloseNotifyErr *error
|
|
||||||
|
|
||||||
In *RawHalfConn
|
|
||||||
Out *RawHalfConn
|
|
||||||
|
|
||||||
BytesSent *int64
|
|
||||||
PacketsSent *int64
|
|
||||||
|
|
||||||
ActiveCall *atomic.Int32
|
|
||||||
Tmp *[16]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRawConn(rawTLSConn tls.Conn) (*RawConn, error) {
|
|
||||||
var (
|
|
||||||
pointer unsafe.Pointer
|
|
||||||
methods *Methods
|
|
||||||
loaded bool
|
|
||||||
)
|
|
||||||
for _, tlsCreator := range methodRegistry {
|
|
||||||
pointer, methods, loaded = tlsCreator(rawTLSConn)
|
|
||||||
if loaded {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !loaded {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := &RawConn{
|
|
||||||
pointer: pointer,
|
|
||||||
methods: methods,
|
|
||||||
}
|
|
||||||
|
|
||||||
rawConn := reflect.Indirect(reflect.ValueOf(rawTLSConn))
|
|
||||||
|
|
||||||
rawIsClient := rawConn.FieldByName("isClient")
|
|
||||||
if !rawIsClient.IsValid() || rawIsClient.Kind() != reflect.Bool {
|
|
||||||
return nil, E.New("invalid Conn.isClient")
|
|
||||||
}
|
|
||||||
conn.IsClient = (*bool)(unsafe.Pointer(rawIsClient.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawIsHandshakeComplete := rawConn.FieldByName("isHandshakeComplete")
|
|
||||||
if !rawIsHandshakeComplete.IsValid() || rawIsHandshakeComplete.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.isHandshakeComplete")
|
|
||||||
}
|
|
||||||
conn.IsHandshakeComplete = (*atomic.Bool)(unsafe.Pointer(rawIsHandshakeComplete.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawVers := rawConn.FieldByName("vers")
|
|
||||||
if !rawVers.IsValid() || rawVers.Kind() != reflect.Uint16 {
|
|
||||||
return nil, E.New("invalid Conn.vers")
|
|
||||||
}
|
|
||||||
conn.Vers = (*uint16)(unsafe.Pointer(rawVers.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCipherSuite := rawConn.FieldByName("cipherSuite")
|
|
||||||
if !rawCipherSuite.IsValid() || rawCipherSuite.Kind() != reflect.Uint16 {
|
|
||||||
return nil, E.New("invalid Conn.cipherSuite")
|
|
||||||
}
|
|
||||||
conn.CipherSuite = (*uint16)(unsafe.Pointer(rawCipherSuite.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawRawInput := rawConn.FieldByName("rawInput")
|
|
||||||
if !rawRawInput.IsValid() || rawRawInput.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.rawInput")
|
|
||||||
}
|
|
||||||
conn.RawInput = (*bytes.Buffer)(unsafe.Pointer(rawRawInput.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawInput := rawConn.FieldByName("input")
|
|
||||||
if !rawInput.IsValid() || rawInput.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.input")
|
|
||||||
}
|
|
||||||
conn.Input = (*bytes.Reader)(unsafe.Pointer(rawInput.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawHand := rawConn.FieldByName("hand")
|
|
||||||
if !rawHand.IsValid() || rawHand.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.hand")
|
|
||||||
}
|
|
||||||
conn.Hand = (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCloseNotifySent := rawConn.FieldByName("closeNotifySent")
|
|
||||||
if !rawCloseNotifySent.IsValid() || rawCloseNotifySent.Kind() != reflect.Bool {
|
|
||||||
return nil, E.New("invalid Conn.closeNotifySent")
|
|
||||||
}
|
|
||||||
conn.CloseNotifySent = (*bool)(unsafe.Pointer(rawCloseNotifySent.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCloseNotifyErr := rawConn.FieldByName("closeNotifyErr")
|
|
||||||
if !rawCloseNotifyErr.IsValid() || rawCloseNotifyErr.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("invalid Conn.closeNotifyErr")
|
|
||||||
}
|
|
||||||
conn.CloseNotifyErr = (*error)(unsafe.Pointer(rawCloseNotifyErr.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawIn := rawConn.FieldByName("in")
|
|
||||||
if !rawIn.IsValid() || rawIn.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.in")
|
|
||||||
}
|
|
||||||
halfIn, err := NewRawHalfConn(rawIn, methods)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid Conn.in")
|
|
||||||
}
|
|
||||||
conn.In = halfIn
|
|
||||||
|
|
||||||
rawOut := rawConn.FieldByName("out")
|
|
||||||
if !rawOut.IsValid() || rawOut.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.out")
|
|
||||||
}
|
|
||||||
halfOut, err := NewRawHalfConn(rawOut, methods)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid Conn.out")
|
|
||||||
}
|
|
||||||
conn.Out = halfOut
|
|
||||||
|
|
||||||
rawBytesSent := rawConn.FieldByName("bytesSent")
|
|
||||||
if !rawBytesSent.IsValid() || rawBytesSent.Kind() != reflect.Int64 {
|
|
||||||
return nil, E.New("invalid Conn.bytesSent")
|
|
||||||
}
|
|
||||||
conn.BytesSent = (*int64)(unsafe.Pointer(rawBytesSent.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawPacketsSent := rawConn.FieldByName("packetsSent")
|
|
||||||
if !rawPacketsSent.IsValid() || rawPacketsSent.Kind() != reflect.Int64 {
|
|
||||||
return nil, E.New("invalid Conn.packetsSent")
|
|
||||||
}
|
|
||||||
conn.PacketsSent = (*int64)(unsafe.Pointer(rawPacketsSent.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawActiveCall := rawConn.FieldByName("activeCall")
|
|
||||||
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.activeCall")
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RawConn) ReadRecord() error {
|
|
||||||
return c.methods.readRecord(c.pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RawConn) HandlePostHandshakeMessage() error {
|
|
||||||
return c.methods.handlePostHandshakeMessage(c.pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RawConn) WriteRecordLocked(typ uint16, data []byte) (int, error) {
|
|
||||||
return c.methods.writeRecordLocked(c.pointer, typ, data)
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"hash"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RawHalfConn struct {
|
|
||||||
pointer unsafe.Pointer
|
|
||||||
methods *Methods
|
|
||||||
*sync.Mutex
|
|
||||||
Err *error
|
|
||||||
Version *uint16
|
|
||||||
Cipher *any
|
|
||||||
Seq *[8]byte
|
|
||||||
ScratchBuf *[13]byte
|
|
||||||
TrafficSecret *[]byte
|
|
||||||
Mac *hash.Hash
|
|
||||||
RawKey *[]byte
|
|
||||||
RawIV *[]byte
|
|
||||||
RawMac *[]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRawHalfConn(rawHalfConn reflect.Value, methods *Methods) (*RawHalfConn, error) {
|
|
||||||
halfConn := &RawHalfConn{
|
|
||||||
pointer: (unsafe.Pointer)(rawHalfConn.UnsafeAddr()),
|
|
||||||
methods: methods,
|
|
||||||
}
|
|
||||||
|
|
||||||
rawMutex := rawHalfConn.FieldByName("Mutex")
|
|
||||||
if !rawMutex.IsValid() || rawMutex.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.Mutex")
|
|
||||||
}
|
|
||||||
halfConn.Mutex = (*sync.Mutex)(unsafe.Pointer(rawMutex.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawErr := rawHalfConn.FieldByName("err")
|
|
||||||
if !rawErr.IsValid() || rawErr.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.err")
|
|
||||||
}
|
|
||||||
halfConn.Err = (*error)(unsafe.Pointer(rawErr.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawVersion := rawHalfConn.FieldByName("version")
|
|
||||||
if !rawVersion.IsValid() || rawVersion.Kind() != reflect.Uint16 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.version")
|
|
||||||
}
|
|
||||||
halfConn.Version = (*uint16)(unsafe.Pointer(rawVersion.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCipher := rawHalfConn.FieldByName("cipher")
|
|
||||||
if !rawCipher.IsValid() || rawCipher.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.cipher")
|
|
||||||
}
|
|
||||||
halfConn.Cipher = (*any)(unsafe.Pointer(rawCipher.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawSeq := rawHalfConn.FieldByName("seq")
|
|
||||||
if !rawSeq.IsValid() || rawSeq.Kind() != reflect.Array || rawSeq.Len() != 8 || rawSeq.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.seq")
|
|
||||||
}
|
|
||||||
halfConn.Seq = (*[8]byte)(unsafe.Pointer(rawSeq.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawScratchBuf := rawHalfConn.FieldByName("scratchBuf")
|
|
||||||
if !rawScratchBuf.IsValid() || rawScratchBuf.Kind() != reflect.Array || rawScratchBuf.Len() != 13 || rawScratchBuf.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.scratchBuf")
|
|
||||||
}
|
|
||||||
halfConn.ScratchBuf = (*[13]byte)(unsafe.Pointer(rawScratchBuf.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawTrafficSecret := rawHalfConn.FieldByName("trafficSecret")
|
|
||||||
if !rawTrafficSecret.IsValid() || rawTrafficSecret.Kind() != reflect.Slice || rawTrafficSecret.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.trafficSecret")
|
|
||||||
}
|
|
||||||
halfConn.TrafficSecret = (*[]byte)(unsafe.Pointer(rawTrafficSecret.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawMac := rawHalfConn.FieldByName("mac")
|
|
||||||
if !rawMac.IsValid() || rawMac.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.mac")
|
|
||||||
}
|
|
||||||
halfConn.Mac = (*hash.Hash)(unsafe.Pointer(rawMac.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawKey := rawHalfConn.FieldByName("rawKey")
|
|
||||||
if rawKey.IsValid() {
|
|
||||||
if /*!rawKey.IsValid() || */ rawKey.Kind() != reflect.Slice || rawKey.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.rawKey")
|
|
||||||
}
|
|
||||||
halfConn.RawKey = (*[]byte)(unsafe.Pointer(rawKey.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawIV := rawHalfConn.FieldByName("rawIV")
|
|
||||||
if !rawIV.IsValid() || rawIV.Kind() != reflect.Slice || rawIV.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.rawIV")
|
|
||||||
}
|
|
||||||
halfConn.RawIV = (*[]byte)(unsafe.Pointer(rawIV.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawMAC := rawHalfConn.FieldByName("rawMac")
|
|
||||||
if !rawMAC.IsValid() || rawMAC.Kind() != reflect.Slice || rawMAC.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.rawMac")
|
|
||||||
}
|
|
||||||
halfConn.RawMac = (*[]byte)(unsafe.Pointer(rawMAC.UnsafeAddr()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return halfConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) Decrypt(record []byte) ([]byte, uint8, error) {
|
|
||||||
return hc.methods.decrypt(hc.pointer, record)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) SetErrorLocked(err error) error {
|
|
||||||
return hc.methods.setErrorLocked(hc.pointer, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) SetTrafficSecret(suite unsafe.Pointer, level int, secret []byte) {
|
|
||||||
hc.methods.setTrafficSecret(hc.pointer, suite, level, secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) ExplicitNonceLen() int {
|
|
||||||
return hc.methods.explicitNonceLen(hc.pointer)
|
|
||||||
}
|
|
@ -1,9 +1,18 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
//go:build go1.21 && !without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/tls"
|
"github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
@ -12,21 +21,63 @@ var _ N.ReadWaiter = (*ReadWaitConn)(nil)
|
|||||||
|
|
||||||
type ReadWaitConn struct {
|
type ReadWaitConn struct {
|
||||||
tls.Conn
|
tls.Conn
|
||||||
rawConn *RawConn
|
halfAccess *sync.Mutex
|
||||||
readWaitOptions N.ReadWaitOptions
|
rawInput *bytes.Buffer
|
||||||
|
input *bytes.Reader
|
||||||
|
hand *bytes.Buffer
|
||||||
|
readWaitOptions N.ReadWaitOptions
|
||||||
|
tlsReadRecord func() error
|
||||||
|
tlsHandlePostHandshakeMessage func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
||||||
if _, isReadWaitConn := conn.(N.ReadWaiter); isReadWaitConn {
|
var (
|
||||||
return conn, nil
|
loaded bool
|
||||||
|
tlsReadRecord func() error
|
||||||
|
tlsHandlePostHandshakeMessage func() error
|
||||||
|
)
|
||||||
|
for _, tlsCreator := range tlsRegistry {
|
||||||
|
loaded, tlsReadRecord, tlsHandlePostHandshakeMessage = tlsCreator(conn)
|
||||||
|
if loaded {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rawConn, err := NewRawConn(conn)
|
if !loaded {
|
||||||
if err != nil {
|
return nil, os.ErrInvalid
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||||
|
rawHalfConn := rawConn.FieldByName("in")
|
||||||
|
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid half conn")
|
||||||
|
}
|
||||||
|
rawHalfMutex := rawHalfConn.FieldByName("Mutex")
|
||||||
|
if !rawHalfMutex.IsValid() || rawHalfMutex.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid half mutex")
|
||||||
|
}
|
||||||
|
halfAccess := (*sync.Mutex)(unsafe.Pointer(rawHalfMutex.UnsafeAddr()))
|
||||||
|
rawRawInput := rawConn.FieldByName("rawInput")
|
||||||
|
if !rawRawInput.IsValid() || rawRawInput.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid raw input")
|
||||||
|
}
|
||||||
|
rawInput := (*bytes.Buffer)(unsafe.Pointer(rawRawInput.UnsafeAddr()))
|
||||||
|
rawInput0 := rawConn.FieldByName("input")
|
||||||
|
if !rawInput0.IsValid() || rawInput0.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid input")
|
||||||
|
}
|
||||||
|
input := (*bytes.Reader)(unsafe.Pointer(rawInput0.UnsafeAddr()))
|
||||||
|
rawHand := rawConn.FieldByName("hand")
|
||||||
|
if !rawHand.IsValid() || rawHand.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid hand")
|
||||||
|
}
|
||||||
|
hand := (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
|
||||||
return &ReadWaitConn{
|
return &ReadWaitConn{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
rawConn: rawConn,
|
halfAccess: halfAccess,
|
||||||
|
rawInput: rawInput,
|
||||||
|
input: input,
|
||||||
|
hand: hand,
|
||||||
|
tlsReadRecord: tlsReadRecord,
|
||||||
|
tlsHandlePostHandshakeMessage: tlsHandlePostHandshakeMessage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,36 +87,36 @@ func (c *ReadWaitConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||||
//err = c.HandshakeContext(context.Background())
|
err = c.HandshakeContext(context.Background())
|
||||||
//if err != nil {
|
if err != nil {
|
||||||
// return
|
return
|
||||||
//}
|
}
|
||||||
c.rawConn.In.Lock()
|
c.halfAccess.Lock()
|
||||||
defer c.rawConn.In.Unlock()
|
defer c.halfAccess.Unlock()
|
||||||
for c.rawConn.Input.Len() == 0 {
|
for c.input.Len() == 0 {
|
||||||
err = c.rawConn.ReadRecord()
|
err = c.tlsReadRecord()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for c.rawConn.Hand.Len() > 0 {
|
for c.hand.Len() > 0 {
|
||||||
err = c.rawConn.HandlePostHandshakeMessage()
|
err = c.tlsHandlePostHandshakeMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer = c.readWaitOptions.NewBuffer()
|
buffer = c.readWaitOptions.NewBuffer()
|
||||||
n, err := c.rawConn.Input.Read(buffer.FreeBytes())
|
n, err := c.input.Read(buffer.FreeBytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buffer.Truncate(n)
|
buffer.Truncate(n)
|
||||||
|
|
||||||
if n != 0 && c.rawConn.Input.Len() == 0 && c.rawConn.Input.Len() > 0 &&
|
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
|
||||||
// recordType(c.RawInput.Bytes()[0]) == recordTypeAlert {
|
// recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
|
||||||
c.rawConn.RawInput.Bytes()[0] == 21 {
|
c.rawInput.Bytes()[0] == 21 {
|
||||||
_ = c.rawConn.ReadRecord()
|
_ = c.tlsReadRecord()
|
||||||
// return n, err // will be io.EOF on closeNotify
|
// return n, err // will be io.EOF on closeNotify
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,3 +131,25 @@ func (c *ReadWaitConn) Upstream() any {
|
|||||||
func (c *ReadWaitConn) ReaderReplaceable() bool {
|
func (c *ReadWaitConn) ReaderReplaceable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||||
|
tlsConn, loaded := conn.(*tls.STDConn)
|
||||||
|
if !loaded {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return true, func() error {
|
||||||
|
return stdTLSReadRecord(tlsConn)
|
||||||
|
}, func() error {
|
||||||
|
return stdTLSHandlePostHandshakeMessage(tlsConn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname stdTLSReadRecord crypto/tls.(*Conn).readRecord
|
||||||
|
func stdTLSReadRecord(c *tls.STDConn) error
|
||||||
|
|
||||||
|
//go:linkname stdTLSHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
||||||
|
func stdTLSHandlePostHandshakeMessage(c *tls.STDConn) error
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//go:build !go1.25 || without_badtls
|
//go:build !go1.21 || without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
36
common/badtls/read_wait_utls.go
Normal file
36
common/badtls/read_wait_utls.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//go:build go1.21 && !without_badtls && with_utls
|
||||||
|
|
||||||
|
package badtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/metacubex/utls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||||
|
switch tlsConn := conn.(type) {
|
||||||
|
case *tls.UConn:
|
||||||
|
return true, func() error {
|
||||||
|
return utlsReadRecord(tlsConn.Conn)
|
||||||
|
}, func() error {
|
||||||
|
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
|
||||||
|
}
|
||||||
|
case *tls.Conn:
|
||||||
|
return true, func() error {
|
||||||
|
return utlsReadRecord(tlsConn)
|
||||||
|
}, func() error {
|
||||||
|
return utlsHandlePostHandshakeMessage(tlsConn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname utlsReadRecord github.com/metacubex/utls.(*Conn).readRecord
|
||||||
|
func utlsReadRecord(c *tls.Conn) error
|
||||||
|
|
||||||
|
//go:linkname utlsHandlePostHandshakeMessage github.com/metacubex/utls.(*Conn).handlePostHandshakeMessage
|
||||||
|
func utlsHandlePostHandshakeMessage(c *tls.Conn) error
|
@ -1,62 +0,0 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Methods struct {
|
|
||||||
readRecord func(c unsafe.Pointer) error
|
|
||||||
handlePostHandshakeMessage func(c unsafe.Pointer) error
|
|
||||||
writeRecordLocked func(c unsafe.Pointer, typ uint16, data []byte) (int, error)
|
|
||||||
|
|
||||||
setErrorLocked func(hc unsafe.Pointer, err error) error
|
|
||||||
decrypt func(hc unsafe.Pointer, record []byte) ([]byte, uint8, error)
|
|
||||||
setTrafficSecret func(hc unsafe.Pointer, suite unsafe.Pointer, level int, secret []byte)
|
|
||||||
explicitNonceLen func(hc unsafe.Pointer) int
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodRegistry []func(conn net.Conn) (unsafe.Pointer, *Methods, bool)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
methodRegistry = append(methodRegistry, func(conn net.Conn) (unsafe.Pointer, *Methods, bool) {
|
|
||||||
tlsConn, loaded := conn.(*tls.Conn)
|
|
||||||
if !loaded {
|
|
||||||
return nil, nil, false
|
|
||||||
}
|
|
||||||
return unsafe.Pointer(tlsConn), &Methods{
|
|
||||||
readRecord: stdTLSReadRecord,
|
|
||||||
handlePostHandshakeMessage: stdTLSHandlePostHandshakeMessage,
|
|
||||||
writeRecordLocked: stdWriteRecordLocked,
|
|
||||||
|
|
||||||
setErrorLocked: stdSetErrorLocked,
|
|
||||||
decrypt: stdDecrypt,
|
|
||||||
setTrafficSecret: stdSetTrafficSecret,
|
|
||||||
explicitNonceLen: stdExplicitNonceLen,
|
|
||||||
}, true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname stdTLSReadRecord crypto/tls.(*Conn).readRecord
|
|
||||||
func stdTLSReadRecord(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname stdTLSHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
|
||||||
func stdTLSHandlePostHandshakeMessage(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname stdWriteRecordLocked crypto/tls.(*Conn).writeRecordLocked
|
|
||||||
func stdWriteRecordLocked(c unsafe.Pointer, typ uint16, data []byte) (int, error)
|
|
||||||
|
|
||||||
//go:linkname stdSetErrorLocked crypto/tls.(*halfConn).setErrorLocked
|
|
||||||
func stdSetErrorLocked(hc unsafe.Pointer, err error) error
|
|
||||||
|
|
||||||
//go:linkname stdDecrypt crypto/tls.(*halfConn).decrypt
|
|
||||||
func stdDecrypt(hc unsafe.Pointer, record []byte) ([]byte, uint8, error)
|
|
||||||
|
|
||||||
//go:linkname stdSetTrafficSecret crypto/tls.(*halfConn).setTrafficSecret
|
|
||||||
func stdSetTrafficSecret(hc unsafe.Pointer, suite unsafe.Pointer, level int, secret []byte)
|
|
||||||
|
|
||||||
//go:linkname stdExplicitNonceLen crypto/tls.(*halfConn).explicitNonceLen
|
|
||||||
func stdExplicitNonceLen(hc unsafe.Pointer) int
|
|
@ -1,56 +0,0 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
|
|
||||||
"github.com/metacubex/utls"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
methodRegistry = append(methodRegistry, func(conn net.Conn) (unsafe.Pointer, *Methods, bool) {
|
|
||||||
var pointer unsafe.Pointer
|
|
||||||
if uConn, loaded := N.CastReader[*tls.Conn](conn); loaded {
|
|
||||||
pointer = unsafe.Pointer(uConn)
|
|
||||||
} else if uConn, loaded := N.CastReader[*tls.UConn](conn); loaded {
|
|
||||||
pointer = unsafe.Pointer(uConn.Conn)
|
|
||||||
} else {
|
|
||||||
return nil, nil, false
|
|
||||||
}
|
|
||||||
return pointer, &Methods{
|
|
||||||
readRecord: utlsReadRecord,
|
|
||||||
handlePostHandshakeMessage: utlsHandlePostHandshakeMessage,
|
|
||||||
writeRecordLocked: utlsWriteRecordLocked,
|
|
||||||
|
|
||||||
setErrorLocked: utlsSetErrorLocked,
|
|
||||||
decrypt: utlsDecrypt,
|
|
||||||
setTrafficSecret: utlsSetTrafficSecret,
|
|
||||||
explicitNonceLen: utlsExplicitNonceLen,
|
|
||||||
}, true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname utlsReadRecord github.com/metacubex/utls.(*Conn).readRecord
|
|
||||||
func utlsReadRecord(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname utlsHandlePostHandshakeMessage github.com/metacubex/utls.(*Conn).handlePostHandshakeMessage
|
|
||||||
func utlsHandlePostHandshakeMessage(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname utlsWriteRecordLocked github.com/metacubex/utls.(*Conn).writeRecordLocked
|
|
||||||
func utlsWriteRecordLocked(hc unsafe.Pointer, typ uint16, data []byte) (int, error)
|
|
||||||
|
|
||||||
//go:linkname utlsSetErrorLocked github.com/metacubex/utls.(*halfConn).setErrorLocked
|
|
||||||
func utlsSetErrorLocked(hc unsafe.Pointer, err error) error
|
|
||||||
|
|
||||||
//go:linkname utlsDecrypt github.com/metacubex/utls.(*halfConn).decrypt
|
|
||||||
func utlsDecrypt(hc unsafe.Pointer, record []byte) ([]byte, uint8, error)
|
|
||||||
|
|
||||||
//go:linkname utlsSetTrafficSecret github.com/metacubex/utls.(*halfConn).setTrafficSecret
|
|
||||||
func utlsSetTrafficSecret(hc unsafe.Pointer, suite unsafe.Pointer, level int, secret []byte)
|
|
||||||
|
|
||||||
//go:linkname utlsExplicitNonceLen github.com/metacubex/utls.(*halfConn).explicitNonceLen
|
|
||||||
func utlsExplicitNonceLen(hc unsafe.Pointer) int
|
|
@ -88,40 +88,42 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
|
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
defaultOptions := networkManager.DefaultOptions()
|
defaultOptions := networkManager.DefaultOptions()
|
||||||
if defaultOptions.BindInterface != "" {
|
if !disableDefaultBind {
|
||||||
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
if defaultOptions.BindInterface != "" {
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
|
||||||
} else if networkManager.AutoDetectInterface() && !disableDefaultBind {
|
|
||||||
if platformInterface != nil {
|
|
||||||
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
|
|
||||||
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
|
|
||||||
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
|
|
||||||
if networkStrategy == nil && len(networkType) == 0 && len(fallbackNetworkType) == 0 {
|
|
||||||
networkStrategy = defaultOptions.NetworkStrategy
|
|
||||||
networkType = defaultOptions.NetworkType
|
|
||||||
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
|
||||||
}
|
|
||||||
networkFallbackDelay = time.Duration(options.FallbackDelay)
|
|
||||||
if networkFallbackDelay == 0 && defaultOptions.FallbackDelay != 0 {
|
|
||||||
networkFallbackDelay = defaultOptions.FallbackDelay
|
|
||||||
}
|
|
||||||
if networkStrategy == nil {
|
|
||||||
networkStrategy = common.Ptr(C.NetworkStrategyDefault)
|
|
||||||
defaultNetworkStrategy = true
|
|
||||||
}
|
|
||||||
bindFunc := networkManager.ProtectFunc()
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
|
||||||
} else {
|
|
||||||
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
} else if networkManager.AutoDetectInterface() {
|
||||||
|
if platformInterface != nil {
|
||||||
|
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
|
||||||
|
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
|
||||||
|
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
|
||||||
|
if networkStrategy == nil && len(networkType) == 0 && len(fallbackNetworkType) == 0 {
|
||||||
|
networkStrategy = defaultOptions.NetworkStrategy
|
||||||
|
networkType = defaultOptions.NetworkType
|
||||||
|
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
||||||
|
}
|
||||||
|
networkFallbackDelay = time.Duration(options.FallbackDelay)
|
||||||
|
if networkFallbackDelay == 0 && defaultOptions.FallbackDelay != 0 {
|
||||||
|
networkFallbackDelay = defaultOptions.FallbackDelay
|
||||||
|
}
|
||||||
|
if networkStrategy == nil {
|
||||||
|
networkStrategy = common.Ptr(C.NetworkStrategyDefault)
|
||||||
|
defaultNetworkStrategy = true
|
||||||
|
}
|
||||||
|
bindFunc := networkManager.ProtectFunc()
|
||||||
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
} else {
|
||||||
|
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
||||||
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||||
|
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||||
|
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
|
||||||
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
|
||||||
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}](conn.NetConn())
|
|
||||||
if !isSyscallConn {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
rawSyscallConn, err := syscallConn.SyscallConn()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawConn, err := badtls.NewRawConn(conn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if *rawConn.Vers != tls.VersionTLS13 {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
for rawConn.RawInput.Len() > 0 {
|
|
||||||
err = rawConn.ReadRecord()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rawConn.Hand.Len() > 0 {
|
|
||||||
err = rawConn.HandlePostHandshakeMessage()
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "ktls: failed to handle post-handshake messages")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kConn := &Conn{
|
|
||||||
Conn: conn,
|
|
||||||
ctx: ctx,
|
|
||||||
logger: logger,
|
|
||||||
conn: conn.NetConn(),
|
|
||||||
rawConn: rawConn,
|
|
||||||
syscallConn: syscallConn,
|
|
||||||
rawSyscallConn: rawSyscallConn,
|
|
||||||
}
|
|
||||||
err = kConn.setupKernel(txOffload, rxOffload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return kConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Upstream() any {
|
|
||||||
return c.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) SyscallConnForRead() syscall.Conn {
|
|
||||||
if !c.kernelRx {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
return c.syscallConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) SyscallConnForWrite() syscall.Conn {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
c.logger.DebugContext(c.ctx, "ktls: TX splice requested")
|
|
||||||
return c.syscallConn
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// alert level
|
|
||||||
alertLevelWarning = 1
|
|
||||||
alertLevelError = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
alertCloseNotify = 0
|
|
||||||
alertUnexpectedMessage = 10
|
|
||||||
alertBadRecordMAC = 20
|
|
||||||
alertDecryptionFailed = 21
|
|
||||||
alertRecordOverflow = 22
|
|
||||||
alertDecompressionFailure = 30
|
|
||||||
alertHandshakeFailure = 40
|
|
||||||
alertBadCertificate = 42
|
|
||||||
alertUnsupportedCertificate = 43
|
|
||||||
alertCertificateRevoked = 44
|
|
||||||
alertCertificateExpired = 45
|
|
||||||
alertCertificateUnknown = 46
|
|
||||||
alertIllegalParameter = 47
|
|
||||||
alertUnknownCA = 48
|
|
||||||
alertAccessDenied = 49
|
|
||||||
alertDecodeError = 50
|
|
||||||
alertDecryptError = 51
|
|
||||||
alertExportRestriction = 60
|
|
||||||
alertProtocolVersion = 70
|
|
||||||
alertInsufficientSecurity = 71
|
|
||||||
alertInternalError = 80
|
|
||||||
alertInappropriateFallback = 86
|
|
||||||
alertUserCanceled = 90
|
|
||||||
alertNoRenegotiation = 100
|
|
||||||
alertMissingExtension = 109
|
|
||||||
alertUnsupportedExtension = 110
|
|
||||||
alertCertificateUnobtainable = 111
|
|
||||||
alertUnrecognizedName = 112
|
|
||||||
alertBadCertificateStatusResponse = 113
|
|
||||||
alertBadCertificateHashValue = 114
|
|
||||||
alertUnknownPSKIdentity = 115
|
|
||||||
alertCertificateRequired = 116
|
|
||||||
alertNoApplicationProtocol = 120
|
|
||||||
alertECHRequired = 121
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) sendAlertLocked(err uint8) error {
|
|
||||||
switch err {
|
|
||||||
case alertNoRenegotiation, alertCloseNotify:
|
|
||||||
c.rawConn.Tmp[0] = alertLevelWarning
|
|
||||||
default:
|
|
||||||
c.rawConn.Tmp[0] = alertLevelError
|
|
||||||
}
|
|
||||||
c.rawConn.Tmp[1] = byte(err)
|
|
||||||
|
|
||||||
_, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.rawConn.Out.SetErrorLocked(&net.OpError{Op: "local error", Err: tls.AlertError(err)})
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendAlert sends a TLS alert message.
|
|
||||||
func (c *Conn) sendAlert(err uint8) error {
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
return c.sendAlertLocked(err)
|
|
||||||
}
|
|
@ -1,326 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
|
||||||
)
|
|
||||||
|
|
||||||
type kernelCryptoCipherType uint16
|
|
||||||
|
|
||||||
const (
|
|
||||||
TLS_CIPHER_AES_GCM_128 kernelCryptoCipherType = 51
|
|
||||||
TLS_CIPHER_AES_GCM_128_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_AES_GCM_128_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_GCM_128_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_AES_GCM_128_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_AES_GCM_256 kernelCryptoCipherType = 52
|
|
||||||
TLS_CIPHER_AES_GCM_256_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_AES_GCM_256_KEY_SIZE kernelCryptoCipherType = 32
|
|
||||||
TLS_CIPHER_AES_GCM_256_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_AES_GCM_256_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_AES_CCM_128 kernelCryptoCipherType = 53
|
|
||||||
TLS_CIPHER_AES_CCM_128_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_AES_CCM_128_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_CCM_128_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_AES_CCM_128_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305 kernelCryptoCipherType = 54
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE kernelCryptoCipherType = 12
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE kernelCryptoCipherType = 32
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE kernelCryptoCipherType = 0
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
// TLS_CIPHER_SM4_GCM kernelCryptoCipherType = 55
|
|
||||||
// TLS_CIPHER_SM4_GCM_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
// TLS_CIPHER_SM4_GCM_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_GCM_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
// TLS_CIPHER_SM4_GCM_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
// TLS_CIPHER_SM4_CCM kernelCryptoCipherType = 56
|
|
||||||
// TLS_CIPHER_SM4_CCM_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
// TLS_CIPHER_SM4_CCM_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_CCM_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
// TLS_CIPHER_SM4_CCM_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_ARIA_GCM_128 kernelCryptoCipherType = 57
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_ARIA_GCM_256 kernelCryptoCipherType = 58
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_KEY_SIZE kernelCryptoCipherType = 32
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
type kernelCrypto interface {
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type kernelCryptoInfo struct {
|
|
||||||
version uint16
|
|
||||||
cipher_type kernelCryptoCipherType
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoAES128GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoAES128GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_AES_GCM_128_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_AES_GCM_128_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_AES_GCM_128_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoAES128GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_AES_GCM_128
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoAES256GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoAES256GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_AES_GCM_256_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_AES_GCM_256_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_AES_GCM_256_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoAES256GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_AES_GCM_256
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoAES128CCM{}
|
|
||||||
|
|
||||||
type kernelCryptoAES128CCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_AES_CCM_128_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_AES_CCM_128_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_AES_CCM_128_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoAES128CCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_AES_CCM_128
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoChacha20Poly1035{}
|
|
||||||
|
|
||||||
type kernelCryptoChacha20Poly1035 struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoChacha20Poly1035) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_CHACHA20_POLY1305
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// var _ kernelCrypto = &kernelCryptoSM4GCM{}
|
|
||||||
|
|
||||||
// type kernelCryptoSM4GCM struct {
|
|
||||||
// kernelCryptoInfo
|
|
||||||
// iv [TLS_CIPHER_SM4_GCM_IV_SIZE]byte
|
|
||||||
// key [TLS_CIPHER_SM4_GCM_KEY_SIZE]byte
|
|
||||||
// salt [TLS_CIPHER_SM4_GCM_SALT_SIZE]byte
|
|
||||||
// rec_seq [TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE]byte
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (crypto *kernelCryptoSM4GCM) String() string {
|
|
||||||
// crypto.cipher_type = TLS_CIPHER_SM4_GCM
|
|
||||||
// return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var _ kernelCrypto = &kernelCryptoSM4CCM{}
|
|
||||||
|
|
||||||
// type kernelCryptoSM4CCM struct {
|
|
||||||
// kernelCryptoInfo
|
|
||||||
// iv [TLS_CIPHER_SM4_CCM_IV_SIZE]byte
|
|
||||||
// key [TLS_CIPHER_SM4_CCM_KEY_SIZE]byte
|
|
||||||
// salt [TLS_CIPHER_SM4_CCM_SALT_SIZE]byte
|
|
||||||
// rec_seq [TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE]byte
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (crypto *kernelCryptoSM4CCM) String() string {
|
|
||||||
// crypto.cipher_type = TLS_CIPHER_SM4_CCM
|
|
||||||
// return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
// }
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoARIA128GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoARIA128GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_ARIA_GCM_128_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_ARIA_GCM_128_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_ARIA_GCM_128_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoARIA128GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_ARIA_GCM_128
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoARIA256GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoARIA256GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_ARIA_GCM_256_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_ARIA_GCM_256_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_ARIA_GCM_256_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoARIA256GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_ARIA_GCM_256
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func kernelCipher(kernel *Support, hc *badtls.RawHalfConn, cipherSuite uint16, isRX bool) kernelCrypto {
|
|
||||||
if !kernel.TLS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *hc.Version {
|
|
||||||
case tls.VersionTLS12:
|
|
||||||
if isRX && !kernel.TLS_Version13_RX {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case tls.VersionTLS13:
|
|
||||||
if !kernel.TLS_Version13 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if isRX && !kernel.TLS_Version13_RX {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var key, iv []byte
|
|
||||||
if *hc.Version == tls.VersionTLS13 {
|
|
||||||
key, iv = trafficKey(cipherSuiteTLS13ByID(cipherSuite), *hc.TrafficSecret)
|
|
||||||
/*if isRX {
|
|
||||||
key, iv = trafficKey(cipherSuiteTLS13ByID(cipherSuite), keyLog.RemoteTrafficSecret)
|
|
||||||
} else {
|
|
||||||
key, iv = trafficKey(cipherSuiteTLS13ByID(cipherSuite), keyLog.TrafficSecret)
|
|
||||||
}*/
|
|
||||||
} else {
|
|
||||||
// csPtr := cipherSuiteByID(cipherSuite)
|
|
||||||
// keysFromMasterSecret(*hc.Version, csPtr, keyLog.Secret, keyLog.Random)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch cipherSuite {
|
|
||||||
case tls.TLS_AES_128_GCM_SHA256, tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
|
||||||
crypto := new(kernelCryptoAES128GCM)
|
|
||||||
|
|
||||||
crypto.version = *hc.Version
|
|
||||||
copy(crypto.key[:], key)
|
|
||||||
copy(crypto.iv[:], iv[4:])
|
|
||||||
copy(crypto.salt[:], iv[:4])
|
|
||||||
crypto.rec_seq = *hc.Seq
|
|
||||||
|
|
||||||
return crypto
|
|
||||||
case tls.TLS_AES_256_GCM_SHA384, tls.TLS_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
|
|
||||||
if !kernel.TLS_AES_256_GCM {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
crypto := new(kernelCryptoAES256GCM)
|
|
||||||
|
|
||||||
crypto.version = *hc.Version
|
|
||||||
copy(crypto.key[:], key)
|
|
||||||
copy(crypto.iv[:], iv[4:])
|
|
||||||
copy(crypto.salt[:], iv[:4])
|
|
||||||
crypto.rec_seq = *hc.Seq
|
|
||||||
|
|
||||||
return crypto
|
|
||||||
//case tls.TLS_AES_128_CCM_SHA256, tls.TLS_RSA_WITH_AES_128_CCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_SHA256:
|
|
||||||
// if !kernel.TLS_AES_128_CCM {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// crypto := new(kernelCryptoAES128CCM)
|
|
||||||
//
|
|
||||||
// crypto.version = *hc.Version
|
|
||||||
// copy(crypto.key[:], key)
|
|
||||||
// copy(crypto.iv[:], iv[4:])
|
|
||||||
// copy(crypto.salt[:], iv[:4])
|
|
||||||
// crypto.rec_seq = *hc.Seq
|
|
||||||
//
|
|
||||||
// return crypto
|
|
||||||
case tls.TLS_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
|
|
||||||
if !kernel.TLS_CHACHA20_POLY1305 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
crypto := new(kernelCryptoChacha20Poly1035)
|
|
||||||
|
|
||||||
crypto.version = *hc.Version
|
|
||||||
copy(crypto.key[:], key)
|
|
||||||
copy(crypto.iv[:], iv)
|
|
||||||
crypto.rec_seq = *hc.Seq
|
|
||||||
|
|
||||||
return crypto
|
|
||||||
//case tls.TLS_RSA_WITH_ARIA_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256:
|
|
||||||
// if !kernel.TLS_ARIA_GCM {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// crypto := new(kernelCryptoARIA128GCM)
|
|
||||||
//
|
|
||||||
// crypto.version = *hc.Version
|
|
||||||
// copy(crypto.key[:], key)
|
|
||||||
// copy(crypto.iv[:], iv[4:])
|
|
||||||
// copy(crypto.salt[:], iv[:4])
|
|
||||||
// crypto.rec_seq = *hc.Seq
|
|
||||||
//
|
|
||||||
// return crypto
|
|
||||||
//case tls.TLS_RSA_WITH_ARIA_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384:
|
|
||||||
// if !kernel.TLS_ARIA_GCM {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// crypto := new(kernelCryptoARIA256GCM)
|
|
||||||
//
|
|
||||||
// crypto.version = *hc.Version
|
|
||||||
// copy(crypto.key[:], key)
|
|
||||||
// copy(crypto.iv[:], iv[4:])
|
|
||||||
// copy(crypto.salt[:], iv[:4])
|
|
||||||
// crypto.rec_seq = *hc.Seq
|
|
||||||
//
|
|
||||||
// return crypto
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) Close() error {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interlock with Conn.Write above.
|
|
||||||
var x int32
|
|
||||||
for {
|
|
||||||
x = c.rawConn.ActiveCall.Load()
|
|
||||||
if x&1 != 0 {
|
|
||||||
return net.ErrClosed
|
|
||||||
}
|
|
||||||
if c.rawConn.ActiveCall.CompareAndSwap(x, x|1) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if x != 0 {
|
|
||||||
// io.Writer and io.Closer should not be used concurrently.
|
|
||||||
// If Close is called while a Write is currently in-flight,
|
|
||||||
// interpret that as a sign that this Close is really just
|
|
||||||
// being used to break the Write and/or clean up resources and
|
|
||||||
// avoid sending the alertCloseNotify, which may block
|
|
||||||
// waiting on handshakeMutex or the c.out mutex.
|
|
||||||
return c.conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
var alertErr error
|
|
||||||
if c.rawConn.IsHandshakeComplete.Load() {
|
|
||||||
if err := c.closeNotify(); err != nil {
|
|
||||||
alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.conn.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return alertErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) closeNotify() error {
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
|
|
||||||
if !*c.rawConn.CloseNotifySent {
|
|
||||||
// Set a Write Deadline to prevent possibly blocking forever.
|
|
||||||
c.SetWriteDeadline(time.Now().Add(time.Second * 5))
|
|
||||||
*c.rawConn.CloseNotifyErr = c.sendAlertLocked(alertCloseNotify)
|
|
||||||
*c.rawConn.CloseNotifySent = true
|
|
||||||
// Any subsequent writes will fail.
|
|
||||||
c.SetWriteDeadline(time.Now())
|
|
||||||
}
|
|
||||||
return *c.rawConn.CloseNotifyErr
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxPlaintext = 16384 // maximum plaintext payload length
|
|
||||||
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
|
|
||||||
maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3
|
|
||||||
recordHeaderLen = 5 // record header length
|
|
||||||
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
|
|
||||||
maxHandshakeCertificateMsg = 262144 // maximum certificate message size (256 KiB)
|
|
||||||
maxUselessRecords = 16 // maximum number of consecutive non-advancing records
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
recordTypeChangeCipherSpec = 20
|
|
||||||
recordTypeAlert = 21
|
|
||||||
recordTypeHandshake = 22
|
|
||||||
recordTypeApplicationData = 23
|
|
||||||
)
|
|
@ -1,238 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The marshalingFunction type is an adapter to allow the use of ordinary
|
|
||||||
// functions as cryptobyte.MarshalingValue.
|
|
||||||
type marshalingFunction func(b *cryptobyte.Builder) error
|
|
||||||
|
|
||||||
func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error {
|
|
||||||
return f(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If
|
|
||||||
// the length of the sequence is not the value specified, it produces an error.
|
|
||||||
func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) {
|
|
||||||
b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error {
|
|
||||||
if len(v) != n {
|
|
||||||
return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v))
|
|
||||||
}
|
|
||||||
b.AddBytes(v)
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder.
|
|
||||||
func addUint64(b *cryptobyte.Builder, v uint64) {
|
|
||||||
b.AddUint32(uint32(v >> 32))
|
|
||||||
b.AddUint32(uint32(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint64 decodes a big-endian, 64-bit value into out and advances over it.
|
|
||||||
// It reports whether the read was successful.
|
|
||||||
func readUint64(s *cryptobyte.String, out *uint64) bool {
|
|
||||||
var hi, lo uint32
|
|
||||||
if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
*out = uint64(hi)<<32 | uint64(lo)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a
|
|
||||||
// []byte instead of a cryptobyte.String.
|
|
||||||
func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
|
|
||||||
return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a
|
|
||||||
// []byte instead of a cryptobyte.String.
|
|
||||||
func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
|
|
||||||
return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a
|
|
||||||
// []byte instead of a cryptobyte.String.
|
|
||||||
func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
|
|
||||||
return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
type keyUpdateMsg struct {
|
|
||||||
updateRequested bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *keyUpdateMsg) marshal() ([]byte, error) {
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddUint8(typeKeyUpdate)
|
|
||||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
if m.updateRequested {
|
|
||||||
b.AddUint8(1)
|
|
||||||
} else {
|
|
||||||
b.AddUint8(0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *keyUpdateMsg) unmarshal(data []byte) bool {
|
|
||||||
s := cryptobyte.String(data)
|
|
||||||
|
|
||||||
var updateRequested uint8
|
|
||||||
if !s.Skip(4) || // message type and uint24 length field
|
|
||||||
!s.ReadUint8(&updateRequested) || !s.Empty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch updateRequested {
|
|
||||||
case 0:
|
|
||||||
m.updateRequested = false
|
|
||||||
case 1:
|
|
||||||
m.updateRequested = true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS handshake message types.
|
|
||||||
const (
|
|
||||||
typeHelloRequest uint8 = 0
|
|
||||||
typeClientHello uint8 = 1
|
|
||||||
typeServerHello uint8 = 2
|
|
||||||
typeNewSessionTicket uint8 = 4
|
|
||||||
typeEndOfEarlyData uint8 = 5
|
|
||||||
typeEncryptedExtensions uint8 = 8
|
|
||||||
typeCertificate uint8 = 11
|
|
||||||
typeServerKeyExchange uint8 = 12
|
|
||||||
typeCertificateRequest uint8 = 13
|
|
||||||
typeServerHelloDone uint8 = 14
|
|
||||||
typeCertificateVerify uint8 = 15
|
|
||||||
typeClientKeyExchange uint8 = 16
|
|
||||||
typeFinished uint8 = 20
|
|
||||||
typeCertificateStatus uint8 = 22
|
|
||||||
typeKeyUpdate uint8 = 24
|
|
||||||
typeCompressedCertificate uint8 = 25
|
|
||||||
typeMessageHash uint8 = 254 // synthetic message
|
|
||||||
)
|
|
||||||
|
|
||||||
// TLS compression types.
|
|
||||||
const (
|
|
||||||
compressionNone uint8 = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// TLS extension numbers
|
|
||||||
const (
|
|
||||||
extensionServerName uint16 = 0
|
|
||||||
extensionStatusRequest uint16 = 5
|
|
||||||
extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7
|
|
||||||
extensionSupportedPoints uint16 = 11
|
|
||||||
extensionSignatureAlgorithms uint16 = 13
|
|
||||||
extensionALPN uint16 = 16
|
|
||||||
extensionSCT uint16 = 18
|
|
||||||
extensionPadding uint16 = 21
|
|
||||||
extensionExtendedMasterSecret uint16 = 23
|
|
||||||
extensionCompressCertificate uint16 = 27 // compress_certificate in TLS 1.3
|
|
||||||
extensionSessionTicket uint16 = 35
|
|
||||||
extensionPreSharedKey uint16 = 41
|
|
||||||
extensionEarlyData uint16 = 42
|
|
||||||
extensionSupportedVersions uint16 = 43
|
|
||||||
extensionCookie uint16 = 44
|
|
||||||
extensionPSKModes uint16 = 45
|
|
||||||
extensionCertificateAuthorities uint16 = 47
|
|
||||||
extensionSignatureAlgorithmsCert uint16 = 50
|
|
||||||
extensionKeyShare uint16 = 51
|
|
||||||
extensionQUICTransportParameters uint16 = 57
|
|
||||||
extensionALPS uint16 = 17513
|
|
||||||
extensionRenegotiationInfo uint16 = 0xff01
|
|
||||||
extensionECHOuterExtensions uint16 = 0xfd00
|
|
||||||
extensionEncryptedClientHello uint16 = 0xfe0d
|
|
||||||
)
|
|
||||||
|
|
||||||
type handshakeMessage interface {
|
|
||||||
marshal() ([]byte, error)
|
|
||||||
unmarshal([]byte) bool
|
|
||||||
}
|
|
||||||
type newSessionTicketMsgTLS13 struct {
|
|
||||||
lifetime uint32
|
|
||||||
ageAdd uint32
|
|
||||||
nonce []byte
|
|
||||||
label []byte
|
|
||||||
maxEarlyData uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *newSessionTicketMsgTLS13) marshal() ([]byte, error) {
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddUint8(typeNewSessionTicket)
|
|
||||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddUint32(m.lifetime)
|
|
||||||
b.AddUint32(m.ageAdd)
|
|
||||||
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(m.nonce)
|
|
||||||
})
|
|
||||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(m.label)
|
|
||||||
})
|
|
||||||
|
|
||||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
if m.maxEarlyData > 0 {
|
|
||||||
b.AddUint16(extensionEarlyData)
|
|
||||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddUint32(m.maxEarlyData)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool {
|
|
||||||
*m = newSessionTicketMsgTLS13{}
|
|
||||||
s := cryptobyte.String(data)
|
|
||||||
|
|
||||||
var extensions cryptobyte.String
|
|
||||||
if !s.Skip(4) || // message type and uint24 length field
|
|
||||||
!s.ReadUint32(&m.lifetime) ||
|
|
||||||
!s.ReadUint32(&m.ageAdd) ||
|
|
||||||
!readUint8LengthPrefixed(&s, &m.nonce) ||
|
|
||||||
!readUint16LengthPrefixed(&s, &m.label) ||
|
|
||||||
!s.ReadUint16LengthPrefixed(&extensions) ||
|
|
||||||
!s.Empty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for !extensions.Empty() {
|
|
||||||
var extension uint16
|
|
||||||
var extData cryptobyte.String
|
|
||||||
if !extensions.ReadUint16(&extension) ||
|
|
||||||
!extensions.ReadUint16LengthPrefixed(&extData) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch extension {
|
|
||||||
case extensionEarlyData:
|
|
||||||
if !extData.ReadUint32(&m.maxEarlyData) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Ignore unknown extensions.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !extData.Empty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// handlePostHandshakeMessage processes a handshake message arrived after the
|
|
||||||
// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
|
|
||||||
func (c *Conn) handlePostHandshakeMessage() error {
|
|
||||||
if *c.rawConn.Vers != tls.VersionTLS13 {
|
|
||||||
return errors.New("ktls: kernel does not support TLS 1.2 renegotiation")
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := c.readHandshake(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
//c.retryCount++
|
|
||||||
//if c.retryCount > maxUselessRecords {
|
|
||||||
// c.sendAlert(alertUnexpectedMessage)
|
|
||||||
// return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
|
|
||||||
//}
|
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case *newSessionTicketMsgTLS13:
|
|
||||||
// return errors.New("ktls: received new session ticket")
|
|
||||||
return nil
|
|
||||||
case *keyUpdateMsg:
|
|
||||||
return c.handleKeyUpdate(msg)
|
|
||||||
}
|
|
||||||
// The QUIC layer is supposed to treat an unexpected post-handshake CertificateRequest
|
|
||||||
// as a QUIC-level PROTOCOL_VIOLATION error (RFC 9001, Section 4.4). Returning an
|
|
||||||
// unexpected_message alert here doesn't provide it with enough information to distinguish
|
|
||||||
// this condition from other unexpected messages. This is probably fine.
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
|
|
||||||
//if c.quic != nil {
|
|
||||||
// c.sendAlert(alertUnexpectedMessage)
|
|
||||||
// return c.in.setErrorLocked(errors.New("tls: received unexpected key update message"))
|
|
||||||
//}
|
|
||||||
|
|
||||||
cipherSuite := cipherSuiteTLS13ByID(*c.rawConn.CipherSuite)
|
|
||||||
if cipherSuite == nil {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertInternalError))
|
|
||||||
}
|
|
||||||
|
|
||||||
newSecret := nextTrafficSecret(cipherSuite, *c.rawConn.In.TrafficSecret)
|
|
||||||
c.rawConn.In.SetTrafficSecret(cipherSuite, 0 /*tls.QUICEncryptionLevelInitial*/, newSecret)
|
|
||||||
|
|
||||||
err := c.resetupRX()
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return c.rawConn.In.SetErrorLocked(fmt.Errorf("ktls: resetupRX failed: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyUpdate.updateRequested {
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
|
|
||||||
resetup, err := c.resetupTX()
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlertLocked(alertInternalError)
|
|
||||||
return c.rawConn.Out.SetErrorLocked(fmt.Errorf("ktls: resetupTX failed: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &keyUpdateMsg{}
|
|
||||||
msgBytes, err := msg.marshal()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = c.writeRecordLocked(recordTypeHandshake, msgBytes)
|
|
||||||
if err != nil {
|
|
||||||
// Surface the error at the next write.
|
|
||||||
c.rawConn.Out.SetErrorLocked(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newSecret := nextTrafficSecret(cipherSuite, *c.rawConn.Out.TrafficSecret)
|
|
||||||
c.rawConn.Out.SetTrafficSecret(cipherSuite, 0 /*QUICEncryptionLevelInitial*/, newSecret)
|
|
||||||
|
|
||||||
err = resetup()
|
|
||||||
if err != nil {
|
|
||||||
return c.rawConn.Out.SetErrorLocked(fmt.Errorf("ktls: resetupTX failed: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readHandshakeBytes(n int) error {
|
|
||||||
//if c.quic != nil {
|
|
||||||
// return c.quicReadHandshakeBytes(n)
|
|
||||||
//}
|
|
||||||
for c.rawConn.Hand.Len() < n {
|
|
||||||
if err := c.readRecord(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readHandshake(transcript io.Writer) (any, error) {
|
|
||||||
if err := c.readHandshakeBytes(4); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data := c.rawConn.Hand.Bytes()
|
|
||||||
|
|
||||||
maxHandshakeSize := maxHandshake
|
|
||||||
// hasVers indicates we're past the first message, forcing someone trying to
|
|
||||||
// make us just allocate a large buffer to at least do the initial part of
|
|
||||||
// the handshake first.
|
|
||||||
//if c.haveVers && data[0] == typeCertificate {
|
|
||||||
// Since certificate messages are likely to be the only messages that
|
|
||||||
// can be larger than maxHandshake, we use a special limit for just
|
|
||||||
// those messages.
|
|
||||||
//maxHandshakeSize = maxHandshakeCertificateMsg
|
|
||||||
//}
|
|
||||||
|
|
||||||
n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
|
||||||
if n > maxHandshakeSize {
|
|
||||||
c.sendAlertLocked(alertInternalError)
|
|
||||||
return nil, c.rawConn.In.SetErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshakeSize))
|
|
||||||
}
|
|
||||||
if err := c.readHandshakeBytes(4 + n); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data = c.rawConn.Hand.Next(4 + n)
|
|
||||||
return c.unmarshalHandshakeMessage(data, transcript)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) unmarshalHandshakeMessage(data []byte, transcript io.Writer) (any, error) {
|
|
||||||
var m handshakeMessage
|
|
||||||
switch data[0] {
|
|
||||||
case typeNewSessionTicket:
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
m = new(newSessionTicketMsgTLS13)
|
|
||||||
} else {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
case typeKeyUpdate:
|
|
||||||
m = new(keyUpdateMsg)
|
|
||||||
default:
|
|
||||||
return nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The handshake message unmarshalers
|
|
||||||
// expect to be able to keep references to data,
|
|
||||||
// so pass in a fresh copy that won't be overwritten.
|
|
||||||
data = append([]byte(nil), data...)
|
|
||||||
|
|
||||||
if !m.unmarshal(data) {
|
|
||||||
return nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertDecodeError))
|
|
||||||
}
|
|
||||||
|
|
||||||
if transcript != nil {
|
|
||||||
transcript.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, nil
|
|
||||||
}
|
|
@ -1,333 +0,0 @@
|
|||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// mod from https://gitlab.com/go-extension/tls
|
|
||||||
|
|
||||||
const (
|
|
||||||
TLS_TX = 1
|
|
||||||
TLS_RX = 2
|
|
||||||
TLS_TX_ZEROCOPY_RO = 3 // TX zerocopy (only sendfile now)
|
|
||||||
TLS_RX_EXPECT_NO_PAD = 4 // Attempt opportunistic zero-copy, TLS 1.3 only
|
|
||||||
|
|
||||||
TLS_SET_RECORD_TYPE = 1
|
|
||||||
TLS_GET_RECORD_TYPE = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type Support struct {
|
|
||||||
TLS, TLS_RX bool
|
|
||||||
TLS_Version13, TLS_Version13_RX bool
|
|
||||||
|
|
||||||
TLS_TX_ZEROCOPY bool
|
|
||||||
TLS_RX_NOPADDING bool
|
|
||||||
|
|
||||||
TLS_AES_256_GCM bool
|
|
||||||
TLS_AES_128_CCM bool
|
|
||||||
TLS_CHACHA20_POLY1305 bool
|
|
||||||
TLS_SM4 bool
|
|
||||||
TLS_ARIA_GCM bool
|
|
||||||
|
|
||||||
TLS_Version13_KeyUpdate bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var KernelSupport = sync.OnceValues(func() (*Support, error) {
|
|
||||||
var uname unix.Utsname
|
|
||||||
err := unix.Uname(&uname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
kernelVersion, err := semver.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}):
|
|
||||||
support.TLS_Version13_KeyUpdate = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.Version{Major: 6, Minor: 1}):
|
|
||||||
support.TLS_ARIA_GCM = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.Version{Major: 6}):
|
|
||||||
support.TLS_Version13_RX = true
|
|
||||||
support.TLS_RX_NOPADDING = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 19}):
|
|
||||||
support.TLS_TX_ZEROCOPY = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 16}):
|
|
||||||
support.TLS_SM4 = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 11}):
|
|
||||||
support.TLS_CHACHA20_POLY1305 = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.Version{Major: 5, Minor: 2}):
|
|
||||||
support.TLS_AES_128_CCM = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.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}):
|
|
||||||
support.TLS_RX = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GTE(semver.Version{Major: 4, Minor: 13}):
|
|
||||||
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 os.ErrInvalid
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TCP, unix.TCP_ULP, "tls")
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "initialize kernel TLS")
|
|
||||||
}
|
|
||||||
|
|
||||||
if txOffload {
|
|
||||||
txCrypto := kernelCipher(support, c.rawConn.Out, *c.rawConn.CipherSuite, false)
|
|
||||||
if txCrypto == 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_TX, txCrypto.String())
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if support.TLS_TX_ZEROCOPY {
|
|
||||||
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptInt(int(fd), unix.SOL_TLS, TLS_TX_ZEROCOPY_RO, 1)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) resetupTX() (func() error, error) {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
support, err := KernelSupport()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !support.TLS_Version13_KeyUpdate {
|
|
||||||
return nil, errors.New("ktls: kernel does not support rekey")
|
|
||||||
}
|
|
||||||
txCrypto := kernelCipher(support, c.rawConn.Out, *c.rawConn.CipherSuite, false)
|
|
||||||
if txCrypto == nil {
|
|
||||||
return nil, errors.New("ktls: set kernelCipher on unsupported tls session")
|
|
||||||
}
|
|
||||||
return func() error {
|
|
||||||
return control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_TX, txCrypto.String())
|
|
||||||
})
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) resetupRX() error {
|
|
||||||
if !c.kernelRx {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
support, err := KernelSupport()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !support.TLS_Version13_KeyUpdate {
|
|
||||||
return errors.New("ktls: kernel does not support rekey")
|
|
||||||
}
|
|
||||||
rxCrypto := kernelCipher(support, c.rawConn.In, *c.rawConn.CipherSuite, true)
|
|
||||||
if rxCrypto == nil {
|
|
||||||
return errors.New("ktls: set kernelCipher on unsupported tls session")
|
|
||||||
}
|
|
||||||
return control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_RX, rxCrypto.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readKernelRecord() (uint8, []byte, error) {
|
|
||||||
if c.rawConn.RawInput.Len() < maxPlaintext {
|
|
||||||
c.rawConn.RawInput.Grow(maxPlaintext - c.rawConn.RawInput.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
data := c.rawConn.RawInput.Bytes()[:maxPlaintext]
|
|
||||||
|
|
||||||
// cmsg for record type
|
|
||||||
buffer := make([]byte, unix.CmsgSpace(1))
|
|
||||||
cmsg := (*unix.Cmsghdr)(unsafe.Pointer(&buffer[0]))
|
|
||||||
cmsg.SetLen(unix.CmsgLen(1))
|
|
||||||
|
|
||||||
var iov unix.Iovec
|
|
||||||
iov.Base = &data[0]
|
|
||||||
iov.SetLen(len(data))
|
|
||||||
|
|
||||||
var msg unix.Msghdr
|
|
||||||
msg.Control = &buffer[0]
|
|
||||||
msg.Controllen = cmsg.Len
|
|
||||||
msg.Iov = &iov
|
|
||||||
msg.Iovlen = 1
|
|
||||||
|
|
||||||
var n int
|
|
||||||
var err error
|
|
||||||
er := c.rawSyscallConn.Read(func(fd uintptr) bool {
|
|
||||||
n, err = recvmsg(int(fd), &msg, 0)
|
|
||||||
return err != unix.EAGAIN
|
|
||||||
})
|
|
||||||
if er != nil {
|
|
||||||
return 0, nil, er
|
|
||||||
}
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
case syscall.EINVAL:
|
|
||||||
return 0, nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertProtocolVersion))
|
|
||||||
case syscall.EMSGSIZE:
|
|
||||||
return 0, nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertRecordOverflow))
|
|
||||||
case syscall.EBADMSG:
|
|
||||||
return 0, nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertDecryptError))
|
|
||||||
default:
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if n <= 0 {
|
|
||||||
return 0, nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmsg.Level == unix.SOL_TLS && cmsg.Type == TLS_GET_RECORD_TYPE {
|
|
||||||
typ := buffer[unix.CmsgLen(0)]
|
|
||||||
return typ, data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return recordTypeApplicationData, data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) writeKernelRecord(typ uint16, data []byte) (int, error) {
|
|
||||||
if typ == recordTypeApplicationData {
|
|
||||||
return c.conn.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmsg for record type
|
|
||||||
buffer := make([]byte, unix.CmsgSpace(1))
|
|
||||||
cmsg := (*unix.Cmsghdr)(unsafe.Pointer(&buffer[0]))
|
|
||||||
cmsg.SetLen(unix.CmsgLen(1))
|
|
||||||
buffer[unix.CmsgLen(0)] = byte(typ)
|
|
||||||
cmsg.Level = unix.SOL_TLS
|
|
||||||
cmsg.Type = TLS_SET_RECORD_TYPE
|
|
||||||
|
|
||||||
var iov unix.Iovec
|
|
||||||
iov.Base = &data[0]
|
|
||||||
iov.SetLen(len(data))
|
|
||||||
|
|
||||||
var msg unix.Msghdr
|
|
||||||
msg.Control = &buffer[0]
|
|
||||||
msg.Controllen = cmsg.Len
|
|
||||||
msg.Iov = &iov
|
|
||||||
msg.Iovlen = 1
|
|
||||||
|
|
||||||
var n int
|
|
||||||
var err error
|
|
||||||
ew := c.rawSyscallConn.Write(func(fd uintptr) bool {
|
|
||||||
n, err = sendmsg(int(fd), &msg, 0)
|
|
||||||
return err != unix.EAGAIN
|
|
||||||
})
|
|
||||||
if ew != nil {
|
|
||||||
return 0, ew
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname recvmsg golang.org/x/sys/unix.recvmsg
|
|
||||||
func recvmsg(fd int, msg *unix.Msghdr, flags int) (n int, err error)
|
|
||||||
|
|
||||||
//go:linkname sendmsg golang.org/x/sys/unix.sendmsg
|
|
||||||
func sendmsg(fd int, msg *unix.Msghdr, flags int) (n int, err error)
|
|
@ -1,24 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
//go:linkname cipherSuiteByID github.com/metacubex/utls.cipherSuiteByID
|
|
||||||
func cipherSuiteByID(id uint16) unsafe.Pointer
|
|
||||||
|
|
||||||
//go:linkname keysFromMasterSecret github.com/metacubex/utls.keysFromMasterSecret
|
|
||||||
func keysFromMasterSecret(version uint16, suite unsafe.Pointer, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte)
|
|
||||||
|
|
||||||
//go:linkname cipherSuiteTLS13ByID github.com/metacubex/utls.cipherSuiteTLS13ByID
|
|
||||||
func cipherSuiteTLS13ByID(id uint16) unsafe.Pointer
|
|
||||||
|
|
||||||
//go:linkname nextTrafficSecret github.com/metacubex/utls.(*cipherSuiteTLS13).nextTrafficSecret
|
|
||||||
func nextTrafficSecret(cs unsafe.Pointer, trafficSecret []byte) []byte
|
|
||||||
|
|
||||||
//go:linkname trafficKey github.com/metacubex/utls.(*cipherSuiteTLS13).trafficKey
|
|
||||||
func trafficKey(cs unsafe.Pointer, trafficSecret []byte) (key, iv []byte)
|
|
@ -1,292 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) Read(b []byte) (int, error) {
|
|
||||||
if !c.kernelRx {
|
|
||||||
return c.Conn.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b) == 0 {
|
|
||||||
// Put this after Handshake, in case people were calling
|
|
||||||
// Read(nil) for the side effect of the Handshake.
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c.rawConn.In.Lock()
|
|
||||||
defer c.rawConn.In.Unlock()
|
|
||||||
|
|
||||||
for c.rawConn.Input.Len() == 0 {
|
|
||||||
if err := c.readRecord(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
for c.rawConn.Hand.Len() > 0 {
|
|
||||||
if err := c.handlePostHandshakeMessage(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, _ := c.rawConn.Input.Read(b)
|
|
||||||
|
|
||||||
// If a close-notify alert is waiting, read it so that we can return (n,
|
|
||||||
// EOF) instead of (n, nil), to signal to the HTTP response reading
|
|
||||||
// goroutine that the connection is now closed. This eliminates a race
|
|
||||||
// where the HTTP response reading goroutine would otherwise not observe
|
|
||||||
// the EOF until its next read, by which time a client goroutine might
|
|
||||||
// have already tried to reuse the HTTP connection for a new request.
|
|
||||||
// See https://golang.org/cl/76400046 and https://golang.org/issue/3514
|
|
||||||
if n != 0 && c.rawConn.Input.Len() == 0 && c.rawConn.RawInput.Len() > 0 &&
|
|
||||||
c.rawConn.RawInput.Bytes()[0] == recordTypeAlert {
|
|
||||||
if err := c.readRecord(); err != nil {
|
|
||||||
return n, err // will be io.EOF on closeNotify
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readRecord() error {
|
|
||||||
if *c.rawConn.In.Err != nil {
|
|
||||||
return *c.rawConn.In.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
typ, data, err := c.readRawRecord()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) > maxPlaintext {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertRecordOverflow))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application Data messages are always protected.
|
|
||||||
if c.rawConn.In.Cipher == nil && typ == recordTypeApplicationData {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
//if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
|
|
||||||
// This is a state-advancing message: reset the retry count.
|
|
||||||
// c.retryCount = 0
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Handshake messages MUST NOT be interleaved with other record types in TLS 1.3.
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 && typ != recordTypeHandshake && c.rawConn.Hand.Len() > 0 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typ {
|
|
||||||
default:
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
case recordTypeAlert:
|
|
||||||
//if c.quic != nil {
|
|
||||||
// return c.rawConn.In.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
//}
|
|
||||||
if len(data) != 2 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
if data[1] == alertCloseNotify {
|
|
||||||
return c.rawConn.In.SetErrorLocked(io.EOF)
|
|
||||||
}
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
// TLS 1.3 removed warning-level alerts except for alertUserCanceled
|
|
||||||
// (RFC 8446, § 6.1). Since at least one major implementation
|
|
||||||
// (https://bugs.openjdk.org/browse/JDK-8323517) misuses this alert,
|
|
||||||
// many TLS stacks now ignore it outright when seen in a TLS 1.3
|
|
||||||
// handshake (e.g. BoringSSL, NSS, Rustls).
|
|
||||||
if data[1] == alertUserCanceled {
|
|
||||||
// Like TLS 1.2 alertLevelWarning alerts, we drop the record and retry.
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
return c.rawConn.In.SetErrorLocked(&net.OpError{Op: "remote error", Err: tls.AlertError(data[1])})
|
|
||||||
}
|
|
||||||
switch data[0] {
|
|
||||||
case alertLevelWarning:
|
|
||||||
// Drop the record on the floor and retry.
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
case alertLevelError:
|
|
||||||
return c.rawConn.In.SetErrorLocked(&net.OpError{Op: "remote error", Err: tls.AlertError(data[1])})
|
|
||||||
default:
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
case recordTypeChangeCipherSpec:
|
|
||||||
if len(data) != 1 || data[0] != 1 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertDecodeError))
|
|
||||||
}
|
|
||||||
// Handshake messages are not allowed to fragment across the CCS.
|
|
||||||
if c.rawConn.Hand.Len() > 0 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
// In TLS 1.3, change_cipher_spec records are ignored until the
|
|
||||||
// Finished. See RFC 8446, Appendix D.4. Note that according to Section
|
|
||||||
// 5, a server can send a ChangeCipherSpec before its ServerHello, when
|
|
||||||
// c.vers is still unset. That's not useful though and suspicious if the
|
|
||||||
// server then selects a lower protocol version, so don't allow that.
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
// if !expectChangeCipherSpec {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
//}
|
|
||||||
//if err := c.rawConn.In.changeCipherSpec(); err != nil {
|
|
||||||
// return c.rawConn.In.setErrorLocked(c.sendAlert(err.(alert)))
|
|
||||||
//}
|
|
||||||
|
|
||||||
case recordTypeApplicationData:
|
|
||||||
// Some OpenSSL servers send empty records in order to randomize the
|
|
||||||
// CBC RawIV. Ignore a limited number of empty records.
|
|
||||||
if len(data) == 0 {
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
// Note that data is owned by c.rawInput, following the Next call above,
|
|
||||||
// to avoid copying the plaintext. This is safe because c.rawInput is
|
|
||||||
// not read from or written to until c.input is drained.
|
|
||||||
c.rawConn.Input.Reset(data)
|
|
||||||
case recordTypeHandshake:
|
|
||||||
if len(data) == 0 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
c.rawConn.Hand.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:staticcheck
|
|
||||||
func (c *Conn) readRawRecord() (typ uint8, data []byte, err error) {
|
|
||||||
// Read from kernel.
|
|
||||||
if c.kernelRx {
|
|
||||||
return c.readKernelRecord()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read header, payload.
|
|
||||||
if err = c.readFromUntil(c.conn, recordHeaderLen); err != nil {
|
|
||||||
// RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
|
|
||||||
// is an error, but popular web sites seem to do this, so we accept it
|
|
||||||
// if and only if at the record boundary.
|
|
||||||
if err == io.ErrUnexpectedEOF && c.rawConn.RawInput.Len() == 0 {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
if e, ok := err.(net.Error); !ok || !e.Temporary() {
|
|
||||||
c.rawConn.In.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hdr := c.rawConn.RawInput.Bytes()[:recordHeaderLen]
|
|
||||||
typ = hdr[0]
|
|
||||||
|
|
||||||
vers := uint16(hdr[1])<<8 | uint16(hdr[2])
|
|
||||||
expectedVers := *c.rawConn.Vers
|
|
||||||
if expectedVers == tls.VersionTLS13 {
|
|
||||||
// All TLS 1.3 records are expected to have 0x0303 (1.2) after
|
|
||||||
// the initial hello (RFC 8446 Section 5.1).
|
|
||||||
expectedVers = tls.VersionTLS12
|
|
||||||
}
|
|
||||||
n := int(hdr[3])<<8 | int(hdr[4])
|
|
||||||
if /*c.haveVers && */ vers != expectedVers {
|
|
||||||
c.sendAlert(alertProtocolVersion)
|
|
||||||
msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, expectedVers)
|
|
||||||
err = c.rawConn.In.SetErrorLocked(c.newRecordHeaderError(nil, msg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//if !c.haveVers {
|
|
||||||
// // First message, be extra suspicious: this might not be a TLS
|
|
||||||
// // client. Bail out before reading a full 'body', if possible.
|
|
||||||
// // The current max version is 3.3 so if the version is >= 16.0,
|
|
||||||
// // it's probably not real.
|
|
||||||
// if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 {
|
|
||||||
// err = c.rawConn.In.SetErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake"))
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext {
|
|
||||||
c.sendAlert(alertRecordOverflow)
|
|
||||||
msg := fmt.Sprintf("oversized record received with length %d", n)
|
|
||||||
err = c.rawConn.In.SetErrorLocked(c.newRecordHeaderError(nil, msg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = c.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
|
|
||||||
if e, ok := err.(net.Error); !ok || !e.Temporary() {
|
|
||||||
c.rawConn.In.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process message.
|
|
||||||
record := c.rawConn.RawInput.Next(recordHeaderLen + n)
|
|
||||||
data, typ, err = c.rawConn.In.Decrypt(record)
|
|
||||||
if err != nil {
|
|
||||||
err = c.rawConn.In.SetErrorLocked(c.sendAlert(uint8(err.(tls.AlertError))))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// retryReadRecord recurs into readRecordOrCCS to drop a non-advancing record, like
|
|
||||||
// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
|
|
||||||
func (c *Conn) retryReadRecord( /*expectChangeCipherSpec bool*/ ) error {
|
|
||||||
//c.retryCount++
|
|
||||||
//if c.retryCount > maxUselessRecords {
|
|
||||||
// c.sendAlert(alertUnexpectedMessage)
|
|
||||||
// return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
|
|
||||||
//}
|
|
||||||
return c.readRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
|
|
||||||
// atLeastReader reads from R, stopping with EOF once at least N bytes have been
|
|
||||||
// read. It is different from an io.LimitedReader in that it doesn't cut short
|
|
||||||
// the last Read call, and in that it considers an early EOF an error.
|
|
||||||
type atLeastReader struct {
|
|
||||||
R io.Reader
|
|
||||||
N int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *atLeastReader) Read(p []byte) (int, error) {
|
|
||||||
if r.N <= 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
n, err := r.R.Read(p)
|
|
||||||
r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809
|
|
||||||
if r.N > 0 && err == io.EOF {
|
|
||||||
return n, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
if r.N <= 0 && err == nil {
|
|
||||||
return n, io.EOF
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// readFromUntil reads from r into c.rawConn.RawInput until c.rawConn.RawInput contains
|
|
||||||
// at least n bytes or else returns an error.
|
|
||||||
func (c *Conn) readFromUntil(r io.Reader, n int) error {
|
|
||||||
if c.rawConn.RawInput.Len() >= n {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
needs := n - c.rawConn.RawInput.Len()
|
|
||||||
// There might be extra input waiting on the wire. Make a best effort
|
|
||||||
// attempt to fetch it so that it can be used in (*Conn).Read to
|
|
||||||
// "predict" closeNotify alerts.
|
|
||||||
c.rawConn.RawInput.Grow(needs + bytes.MinRead)
|
|
||||||
_, err := c.rawConn.RawInput.ReadFrom(&atLeastReader{r, int64(needs)})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err tls.RecordHeaderError) {
|
|
||||||
err.Msg = msg
|
|
||||||
err.Conn = conn
|
|
||||||
copy(err.RecordHeader[:], c.rawConn.RawInput.Bytes())
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {
|
|
||||||
c.readWaitOptions = options
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
|
||||||
c.rawConn.In.Lock()
|
|
||||||
defer c.rawConn.In.Unlock()
|
|
||||||
for c.rawConn.Input.Len() == 0 {
|
|
||||||
err = c.readRecord()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer = c.readWaitOptions.NewBuffer()
|
|
||||||
n, err := c.rawConn.Input.Read(buffer.FreeBytes())
|
|
||||||
if err != nil {
|
|
||||||
buffer.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer.Truncate(n)
|
|
||||||
if n != 0 && c.rawConn.Input.Len() == 0 && c.rawConn.Input.Len() > 0 &&
|
|
||||||
c.rawConn.RawInput.Bytes()[0] == recordTypeAlert {
|
|
||||||
_ = c.rawConn.ReadRecord()
|
|
||||||
}
|
|
||||||
c.readWaitOptions.PostReturn(buffer)
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
//go:build !linux || !go1.25 || without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) Write(b []byte) (int, error) {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return c.Conn.Write(b)
|
|
||||||
}
|
|
||||||
// interlock with Close below
|
|
||||||
for {
|
|
||||||
x := c.rawConn.ActiveCall.Load()
|
|
||||||
if x&1 != 0 {
|
|
||||||
return 0, net.ErrClosed
|
|
||||||
}
|
|
||||||
if c.rawConn.ActiveCall.CompareAndSwap(x, x+2) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer c.rawConn.ActiveCall.Add(-2)
|
|
||||||
|
|
||||||
//if err := c.Conn.HandshakeContext(context.Background()); err != nil {
|
|
||||||
// return 0, err
|
|
||||||
//}
|
|
||||||
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
|
|
||||||
if err := *c.rawConn.Out.Err; err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.rawConn.IsHandshakeComplete.Load() {
|
|
||||||
return 0, tls.AlertError(alertInternalError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *c.rawConn.CloseNotifySent {
|
|
||||||
// return 0, errShutdown
|
|
||||||
return 0, errors.New("tls: protocol is shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS 1.0 is susceptible to a chosen-plaintext
|
|
||||||
// attack when using block mode ciphers due to predictable IVs.
|
|
||||||
// This can be prevented by splitting each Application Data
|
|
||||||
// record into two records, effectively randomizing the RawIV.
|
|
||||||
//
|
|
||||||
// https://www.openssl.org/~bodo/tls-cbc.txt
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
|
|
||||||
// https://www.imperialviolet.org/2012/01/15/beastfollowup.html
|
|
||||||
|
|
||||||
var m int
|
|
||||||
if len(b) > 1 && *c.rawConn.Vers == tls.VersionTLS10 {
|
|
||||||
if _, ok := (*c.rawConn.Out.Cipher).(cipher.BlockMode); ok {
|
|
||||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
|
|
||||||
if err != nil {
|
|
||||||
return n, c.rawConn.Out.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
m, b = 1, b[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b)
|
|
||||||
return n + m, c.rawConn.Out.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) writeRecordLocked(typ uint16, data []byte) (n int, err error) {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return c.rawConn.WriteRecordLocked(typ, data)
|
|
||||||
}
|
|
||||||
/*for len(data) > 0 {
|
|
||||||
m := len(data)
|
|
||||||
if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
|
|
||||||
m = maxPayload
|
|
||||||
}
|
|
||||||
_, err = c.writeKernelRecord(typ, data[:m])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n += m
|
|
||||||
data = data[m:]
|
|
||||||
}*/
|
|
||||||
return c.writeKernelRecord(typ, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// tcpMSSEstimate is a conservative estimate of the TCP maximum segment
|
|
||||||
// size (MSS). A constant is used, rather than querying the kernel for
|
|
||||||
// the actual MSS, to avoid complexity. The value here is the IPv6
|
|
||||||
// minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
|
|
||||||
// bytes) and a TCP header with timestamps (32 bytes).
|
|
||||||
tcpMSSEstimate = 1208
|
|
||||||
|
|
||||||
// recordSizeBoostThreshold is the number of bytes of application data
|
|
||||||
// sent after which the TLS record size will be increased to the
|
|
||||||
// maximum.
|
|
||||||
recordSizeBoostThreshold = 128 * 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) maxPayloadSizeForWrite(typ uint16) int {
|
|
||||||
if /*c.config.DynamicRecordSizingDisabled ||*/ typ != recordTypeApplicationData {
|
|
||||||
return maxPlaintext
|
|
||||||
}
|
|
||||||
|
|
||||||
if *c.rawConn.PacketsSent >= recordSizeBoostThreshold {
|
|
||||||
return maxPlaintext
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract TLS overheads to get the maximum payload size.
|
|
||||||
payloadBytes := tcpMSSEstimate - recordHeaderLen - c.rawConn.Out.ExplicitNonceLen()
|
|
||||||
if rawCipher := *c.rawConn.Out.Cipher; rawCipher != nil {
|
|
||||||
switch ciph := rawCipher.(type) {
|
|
||||||
case cipher.Stream:
|
|
||||||
payloadBytes -= (*c.rawConn.Out.Mac).Size()
|
|
||||||
case cipher.AEAD:
|
|
||||||
payloadBytes -= ciph.Overhead()
|
|
||||||
/*case cbcMode:
|
|
||||||
blockSize := ciph.BlockSize()
|
|
||||||
// The payload must fit in a multiple of blockSize, with
|
|
||||||
// room for at least one padding byte.
|
|
||||||
payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
|
|
||||||
// The RawMac is appended before padding so affects the
|
|
||||||
// payload size directly.
|
|
||||||
payloadBytes -= c.out.mac.Size()*/
|
|
||||||
default:
|
|
||||||
panic("unknown cipher type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
payloadBytes-- // encrypted ContentType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow packet growth in arithmetic progression up to max.
|
|
||||||
pkt := *c.rawConn.PacketsSent
|
|
||||||
*c.rawConn.PacketsSent++
|
|
||||||
if pkt > 1000 {
|
|
||||||
return maxPlaintext // avoid overflow in multiply below
|
|
||||||
}
|
|
||||||
|
|
||||||
n := payloadBytes * int(pkt+1)
|
|
||||||
if n > maxPlaintext {
|
|
||||||
n = maxPlaintext
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
@ -56,7 +56,7 @@ func TestSniffUQUICChrome115(t *testing.T) {
|
|||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
require.Equal(t, metadata.Client, C.ClientQUICGo)
|
||||||
require.Equal(t, metadata.Domain, "www.google.com")
|
require.Equal(t, metadata.Domain, "www.google.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,33 +10,32 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/badtls"
|
"github.com/sagernet/sing-box/common/badtls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDialerFromOptions(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
func NewDialerFromOptions(ctx context.Context, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return dialer, nil
|
return dialer, nil
|
||||||
}
|
}
|
||||||
config, err := NewClient(ctx, logger, serverAddress, options)
|
config, err := NewClient(ctx, serverAddress, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewDialer(dialer, config), nil
|
return NewDialer(dialer, config), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if options.Reality != nil && options.Reality.Enabled {
|
if options.Reality != nil && options.Reality.Enabled {
|
||||||
return NewRealityClient(ctx, logger, serverAddress, options)
|
return NewRealityClient(ctx, serverAddress, options)
|
||||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||||
return NewUTLSClient(ctx, logger, serverAddress, options)
|
return NewUTLSClient(ctx, serverAddress, options)
|
||||||
}
|
}
|
||||||
return NewSTDClient(ctx, logger, serverAddress, options)
|
return NewSTDClient(ctx, serverAddress, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,7 +32,6 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/debug"
|
"github.com/sagernet/sing/common/debug"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
|
|
||||||
@ -50,12 +49,12 @@ type RealityClientConfig struct {
|
|||||||
shortID [8]byte
|
shortID [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRealityClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {
|
func NewRealityClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {
|
||||||
if options.UTLS == nil || !options.UTLS.Enabled {
|
if options.UTLS == nil || !options.UTLS.Enabled {
|
||||||
return nil, E.New("uTLS is required by reality client")
|
return nil, E.New("uTLS is required by reality client")
|
||||||
}
|
}
|
||||||
|
|
||||||
uClient, err := NewUTLSClient(ctx, logger, serverAddress, options)
|
uClient, err := NewUTLSClient(ctx, serverAddress, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -94,7 +93,7 @@ func (e *RealityClientConfig) SetNextProtos(nextProto []string) {
|
|||||||
e.uClient.SetNextProtos(nextProto)
|
e.uClient.SetNextProtos(nextProto)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *RealityClientConfig) STDConfig() (*STDConfig, error) {
|
func (e *RealityClientConfig) Config() (*STDConfig, error) {
|
||||||
return nil, E.New("unsupported usage for reality")
|
return nil, E.New("unsupported usage for reality")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,13 +119,6 @@ func NewRealityServer(ctx context.Context, logger log.Logger, options option.Inb
|
|||||||
return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
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
|
return &RealityServerConfig{&tlsConfig}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +138,7 @@ func (c *RealityServerConfig) SetNextProtos(nextProto []string) {
|
|||||||
c.config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) STDConfig() (*tls.Config, error) {
|
func (c *RealityServerConfig) Config() (*tls.Config, error) {
|
||||||
return nil, E.New("unsupported usage for reality")
|
return nil, E.New("unsupported usage for reality")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/tlsfragment"
|
"github.com/sagernet/sing-box/common/tlsfragment"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,7 +40,7 @@ func (c *STDClientConfig) SetNextProtos(nextProto []string) {
|
|||||||
c.config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDClientConfig) STDConfig() (*STDConfig, error) {
|
func (c *STDClientConfig) Config() (*STDConfig, error) {
|
||||||
return c.config, nil
|
return c.config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,13 +52,7 @@ func (c *STDClientConfig) Client(conn net.Conn) (Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDClientConfig) Clone() Config {
|
func (c *STDClientConfig) Clone() Config {
|
||||||
return &STDClientConfig{
|
return &STDClientConfig{c.ctx, c.config.Clone(), c.fragment, c.fragmentFallbackDelay, c.recordFragment}
|
||||||
ctx: c.ctx,
|
|
||||||
config: c.config.Clone(),
|
|
||||||
fragment: c.fragment,
|
|
||||||
fragmentFallbackDelay: c.fragmentFallbackDelay,
|
|
||||||
recordFragment: c.recordFragment,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDClientConfig) ECHConfigList() []byte {
|
func (c *STDClientConfig) ECHConfigList() []byte {
|
||||||
@ -71,7 +63,7 @@ func (c *STDClientConfig) SetECHConfigList(EncryptedClientHelloConfigList []byte
|
|||||||
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
|
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
var serverName string
|
var serverName string
|
||||||
if options.ServerName != "" {
|
if options.ServerName != "" {
|
||||||
serverName = options.ServerName
|
serverName = options.ServerName
|
||||||
@ -154,24 +146,10 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
stdConfig := &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
var err error
|
return parseECHClientConfig(ctx, stdConfig, options)
|
||||||
config, err = parseECHClientConfig(ctx, config.(ECHCapableConfig), options)
|
} else {
|
||||||
if err != nil {
|
return stdConfig, 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
|
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) STDConfig() (*STDConfig, error) {
|
func (c *STDServerConfig) Config() (*STDConfig, error) {
|
||||||
return c.config, nil
|
return c.config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ func (c *STDServerConfig) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -262,7 +262,7 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var config ServerConfig = &STDServerConfig{
|
return &STDServerConfig{
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
acmeService: acmeService,
|
acmeService: acmeService,
|
||||||
@ -271,14 +271,5 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
certificatePath: options.CertificatePath,
|
certificatePath: options.CertificatePath,
|
||||||
keyPath: options.KeyPath,
|
keyPath: options.KeyPath,
|
||||||
echKeyPath: echKeyPath,
|
echKeyPath: echKeyPath,
|
||||||
}
|
}, nil
|
||||||
if options.KernelTx || options.KernelRx {
|
|
||||||
config = &KTlSServerConfig{
|
|
||||||
ServerConfig: config,
|
|
||||||
logger: logger,
|
|
||||||
kernelTx: options.KernelTx,
|
|
||||||
kernelRx: options.KernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,8 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/tlsfragment"
|
"github.com/sagernet/sing-box/common/tlsfragment"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
|
|
||||||
utls "github.com/metacubex/utls"
|
utls "github.com/metacubex/utls"
|
||||||
@ -52,7 +50,7 @@ func (c *UTLSClientConfig) SetNextProtos(nextProto []string) {
|
|||||||
c.config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UTLSClientConfig) STDConfig() (*STDConfig, error) {
|
func (c *UTLSClientConfig) Config() (*STDConfig, error) {
|
||||||
return nil, E.New("unsupported usage for uTLS")
|
return nil, E.New("unsupported usage for uTLS")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +139,7 @@ func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error {
|
|||||||
return c.UConn.HandshakeContext(ctx)
|
return c.UConn.HandshakeContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
var serverName string
|
var serverName string
|
||||||
if options.ServerName != "" {
|
if options.ServerName != "" {
|
||||||
serverName = options.ServerName
|
serverName = options.ServerName
|
||||||
@ -216,31 +214,15 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var config Config = &UTLSClientConfig{ctx, &tlsConfig, id, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
uConfig := &UTLSClientConfig{ctx, &tlsConfig, id, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
if options.Reality != nil && options.Reality.Enabled {
|
if options.Reality != nil && options.Reality.Enabled {
|
||||||
return nil, E.New("Reality is conflict with ECH")
|
return nil, E.New("Reality is conflict with ECH")
|
||||||
}
|
}
|
||||||
config, err = parseECHClientConfig(ctx, config.(ECHCapableConfig), options)
|
return parseECHClientConfig(ctx, uConfig, options)
|
||||||
if err != nil {
|
} else {
|
||||||
return nil, err
|
return uConfig, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 (
|
var (
|
||||||
|
146
dns/client.go
146
dns/client.go
@ -2,14 +2,12 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/compatible"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@ -19,7 +17,7 @@ import (
|
|||||||
"github.com/sagernet/sing/contrab/freelru"
|
"github.com/sagernet/sing/contrab/freelru"
|
||||||
"github.com/sagernet/sing/contrab/maphash"
|
"github.com/sagernet/sing/contrab/maphash"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
dns "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -32,18 +30,16 @@ var (
|
|||||||
var _ adapter.DNSClient = (*Client)(nil)
|
var _ adapter.DNSClient = (*Client)(nil)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
disableCache bool
|
disableCache bool
|
||||||
disableExpire bool
|
disableExpire bool
|
||||||
independentCache bool
|
independentCache bool
|
||||||
clientSubnet netip.Prefix
|
clientSubnet netip.Prefix
|
||||||
rdrc adapter.RDRCStore
|
rdrc adapter.RDRCStore
|
||||||
initRDRCFunc func() adapter.RDRCStore
|
initRDRCFunc func() adapter.RDRCStore
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
cache freelru.Cache[dns.Question, *dns.Msg]
|
cache freelru.Cache[dns.Question, *dns.Msg]
|
||||||
cacheLock compatible.Map[dns.Question, chan struct{}]
|
transportCache freelru.Cache[transportCacheKey, *dns.Msg]
|
||||||
transportCache freelru.Cache[transportCacheKey, *dns.Msg]
|
|
||||||
transportCacheLock compatible.Map[dns.Question, chan struct{}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientOptions struct {
|
type ClientOptions struct {
|
||||||
@ -100,15 +96,17 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
if c.logger != nil {
|
if c.logger != nil {
|
||||||
c.logger.WarnContext(ctx, "bad question size: ", len(message.Question))
|
c.logger.WarnContext(ctx, "bad question size: ", len(message.Question))
|
||||||
}
|
}
|
||||||
return FixedResponseStatus(message, dns.RcodeFormatError), nil
|
responseMessage := dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Id: message.Id,
|
||||||
|
Response: true,
|
||||||
|
Rcode: dns.RcodeFormatError,
|
||||||
|
},
|
||||||
|
Question: message.Question,
|
||||||
|
}
|
||||||
|
return &responseMessage, nil
|
||||||
}
|
}
|
||||||
question := message.Question[0]
|
question := message.Question[0]
|
||||||
if question.Qtype == dns.TypeA && options.Strategy == C.DomainStrategyIPv6Only || question.Qtype == dns.TypeAAAA && options.Strategy == C.DomainStrategyIPv4Only {
|
|
||||||
if c.logger != nil {
|
|
||||||
c.logger.DebugContext(ctx, "strategy rejected")
|
|
||||||
}
|
|
||||||
return FixedResponseStatus(message, dns.RcodeSuccess), nil
|
|
||||||
}
|
|
||||||
clientSubnet := options.ClientSubnet
|
clientSubnet := options.ClientSubnet
|
||||||
if !clientSubnet.IsValid() {
|
if !clientSubnet.IsValid() {
|
||||||
clientSubnet = c.clientSubnet
|
clientSubnet = c.clientSubnet
|
||||||
@ -116,38 +114,12 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
if clientSubnet.IsValid() {
|
if clientSubnet.IsValid() {
|
||||||
message = SetClientSubnet(message, clientSubnet)
|
message = SetClientSubnet(message, clientSubnet)
|
||||||
}
|
}
|
||||||
|
|
||||||
isSimpleRequest := len(message.Question) == 1 &&
|
isSimpleRequest := len(message.Question) == 1 &&
|
||||||
len(message.Ns) == 0 &&
|
len(message.Ns) == 0 &&
|
||||||
(len(message.Extra) == 0 || len(message.Extra) == 1 &&
|
len(message.Extra) == 0 &&
|
||||||
message.Extra[0].Header().Rrtype == dns.TypeOPT &&
|
|
||||||
message.Extra[0].Header().Class > 0 &&
|
|
||||||
message.Extra[0].Header().Ttl == 0 &&
|
|
||||||
len(message.Extra[0].(*dns.OPT).Option) == 0) &&
|
|
||||||
!options.ClientSubnet.IsValid()
|
!options.ClientSubnet.IsValid()
|
||||||
disableCache := !isSimpleRequest || c.disableCache || options.DisableCache
|
disableCache := !isSimpleRequest || c.disableCache || options.DisableCache
|
||||||
if !disableCache {
|
if !disableCache {
|
||||||
if c.cache != nil {
|
|
||||||
cond, loaded := c.cacheLock.LoadOrStore(question, make(chan struct{}))
|
|
||||||
if loaded {
|
|
||||||
<-cond
|
|
||||||
} else {
|
|
||||||
defer func() {
|
|
||||||
c.cacheLock.Delete(question)
|
|
||||||
close(cond)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
} else if c.transportCache != nil {
|
|
||||||
cond, loaded := c.transportCacheLock.LoadOrStore(question, make(chan struct{}))
|
|
||||||
if loaded {
|
|
||||||
<-cond
|
|
||||||
} else {
|
|
||||||
defer func() {
|
|
||||||
c.transportCacheLock.Delete(question)
|
|
||||||
close(cond)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response, ttl := c.loadResponse(question, transport)
|
response, ttl := c.loadResponse(question, transport)
|
||||||
if response != nil {
|
if response != nil {
|
||||||
logCachedResponse(c.logger, ctx, response, ttl)
|
logCachedResponse(c.logger, ctx, response, ttl)
|
||||||
@ -155,14 +127,27 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if question.Qtype == dns.TypeA && options.Strategy == C.DomainStrategyIPv6Only || question.Qtype == dns.TypeAAAA && options.Strategy == C.DomainStrategyIPv4Only {
|
||||||
|
responseMessage := dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Id: message.Id,
|
||||||
|
Response: true,
|
||||||
|
Rcode: dns.RcodeSuccess,
|
||||||
|
},
|
||||||
|
Question: []dns.Question{question},
|
||||||
|
}
|
||||||
|
if c.logger != nil {
|
||||||
|
c.logger.DebugContext(ctx, "strategy rejected")
|
||||||
|
}
|
||||||
|
return &responseMessage, nil
|
||||||
|
}
|
||||||
messageId := message.Id
|
messageId := message.Id
|
||||||
contextTransport, clientSubnetLoaded := transportTagFromContext(ctx)
|
contextTransport, clientSubnetLoaded := transportTagFromContext(ctx)
|
||||||
if clientSubnetLoaded && transport.Tag() == contextTransport {
|
if clientSubnetLoaded && transport.Tag() == contextTransport {
|
||||||
return nil, E.New("DNS query loopback in transport[", contextTransport, "]")
|
return nil, E.New("DNS query loopback in transport[", contextTransport, "]")
|
||||||
}
|
}
|
||||||
ctx = contextWithTransportTag(ctx, transport.Tag())
|
ctx = contextWithTransportTag(ctx, transport.Tag())
|
||||||
if !disableCache && responseChecker != nil && c.rdrc != nil {
|
if responseChecker != nil && c.rdrc != nil {
|
||||||
rejected := c.rdrc.LoadRDRC(transport.Tag(), question.Name, question.Qtype)
|
rejected := c.rdrc.LoadRDRC(transport.Tag(), question.Name, question.Qtype)
|
||||||
if rejected {
|
if rejected {
|
||||||
return nil, ErrResponseRejectedCached
|
return nil, ErrResponseRejectedCached
|
||||||
@ -172,12 +157,7 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
response, err := transport.Exchange(ctx, message)
|
response, err := transport.Exchange(ctx, message)
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var rcodeError RcodeError
|
return nil, err
|
||||||
if errors.As(err, &rcodeError) {
|
|
||||||
response = FixedResponseStatus(message, int(rcodeError))
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
|
/*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
|
||||||
validResponse := response
|
validResponse := response
|
||||||
@ -216,14 +196,13 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
}*/
|
}*/
|
||||||
if responseChecker != nil {
|
if responseChecker != nil {
|
||||||
var rejected bool
|
var rejected bool
|
||||||
// TODO: add accept_any rule and support to check response instead of addresses
|
if !(response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError) {
|
||||||
if response.Rcode != dns.RcodeSuccess || len(response.Answer) == 0 {
|
|
||||||
rejected = true
|
rejected = true
|
||||||
} else {
|
} else {
|
||||||
rejected = !responseChecker(MessageToAddresses(response))
|
rejected = !responseChecker(MessageToAddresses(response))
|
||||||
}
|
}
|
||||||
if rejected {
|
if rejected {
|
||||||
if !disableCache && c.rdrc != nil {
|
if c.rdrc != nil {
|
||||||
c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger)
|
c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger)
|
||||||
}
|
}
|
||||||
logRejectedResponse(c.logger, ctx, response)
|
logRejectedResponse(c.logger, ctx, response)
|
||||||
@ -326,7 +305,8 @@ func (c *Client) Lookup(ctx context.Context, transport adapter.DNSTransport, dom
|
|||||||
func (c *Client) ClearCache() {
|
func (c *Client) ClearCache() {
|
||||||
if c.cache != nil {
|
if c.cache != nil {
|
||||||
c.cache.Purge()
|
c.cache.Purge()
|
||||||
} else if c.transportCache != nil {
|
}
|
||||||
|
if c.transportCache != nil {
|
||||||
c.transportCache.Purge()
|
c.transportCache.Purge()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -340,36 +320,36 @@ func (c *Client) LookupCache(domain string, strategy C.DomainStrategy) ([]netip.
|
|||||||
}
|
}
|
||||||
dnsName := dns.Fqdn(domain)
|
dnsName := dns.Fqdn(domain)
|
||||||
if strategy == C.DomainStrategyIPv4Only {
|
if strategy == C.DomainStrategyIPv4Only {
|
||||||
addresses, err := c.questionCache(dns.Question{
|
response, err := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeA,
|
Qtype: dns.TypeA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != ErrNotCached {
|
if err != ErrNotCached {
|
||||||
return addresses, true
|
return response, true
|
||||||
}
|
}
|
||||||
} else if strategy == C.DomainStrategyIPv6Only {
|
} else if strategy == C.DomainStrategyIPv6Only {
|
||||||
addresses, err := c.questionCache(dns.Question{
|
response, err := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeAAAA,
|
Qtype: dns.TypeAAAA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != ErrNotCached {
|
if err != ErrNotCached {
|
||||||
return addresses, true
|
return response, true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response4, _ := c.loadResponse(dns.Question{
|
response4, _ := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeA,
|
Qtype: dns.TypeA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
response6, _ := c.loadResponse(dns.Question{
|
response6, _ := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeAAAA,
|
Qtype: dns.TypeAAAA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if response4 != nil || response6 != nil {
|
if len(response4) > 0 || len(response6) > 0 {
|
||||||
return sortAddresses(MessageToAddresses(response4), MessageToAddresses(response6), strategy), true
|
return sortAddresses(response4, response6, strategy), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -410,15 +390,15 @@ func (c *Client) storeCache(transport adapter.DNSTransport, question dns.Questio
|
|||||||
transportTag: transport.Tag(),
|
transportTag: transport.Tag(),
|
||||||
}, message)
|
}, message)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !c.independentCache {
|
||||||
|
c.cache.AddWithLifetime(question, message, time.Second*time.Duration(timeToLive))
|
||||||
} else {
|
} else {
|
||||||
if !c.independentCache {
|
c.transportCache.AddWithLifetime(transportCacheKey{
|
||||||
c.cache.AddWithLifetime(question, message, time.Second*time.Duration(timeToLive))
|
Question: question,
|
||||||
} else {
|
transportTag: transport.Tag(),
|
||||||
c.transportCache.AddWithLifetime(transportCacheKey{
|
}, message, time.Second*time.Duration(timeToLive))
|
||||||
Question: question,
|
|
||||||
transportTag: transport.Tag(),
|
|
||||||
}, message, time.Second*time.Duration(timeToLive))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,9 +517,6 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MessageToAddresses(response *dns.Msg) []netip.Addr {
|
func MessageToAddresses(response *dns.Msg) []netip.Addr {
|
||||||
if response == nil || response.Rcode != dns.RcodeSuccess {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
addresses := make([]netip.Addr, 0, len(response.Answer))
|
addresses := make([]netip.Addr, 0, len(response.Answer))
|
||||||
for _, rawAnswer := range response.Answer {
|
for _, rawAnswer := range response.Answer {
|
||||||
switch answer := rawAnswer.(type) {
|
switch answer := rawAnswer.(type) {
|
||||||
@ -584,12 +561,9 @@ func transportTagFromContext(ctx context.Context) (string, bool) {
|
|||||||
func FixedResponseStatus(message *dns.Msg, rcode int) *dns.Msg {
|
func FixedResponseStatus(message *dns.Msg, rcode int) *dns.Msg {
|
||||||
return &dns.Msg{
|
return &dns.Msg{
|
||||||
MsgHdr: dns.MsgHdr{
|
MsgHdr: dns.MsgHdr{
|
||||||
Id: message.Id,
|
Id: message.Id,
|
||||||
Response: true,
|
Rcode: rcode,
|
||||||
Authoritative: true,
|
Response: true,
|
||||||
RecursionDesired: true,
|
|
||||||
RecursionAvailable: true,
|
|
||||||
Rcode: rcode,
|
|
||||||
},
|
},
|
||||||
Question: message.Question,
|
Question: message.Question,
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,11 @@ import (
|
|||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// net.maxDNSPacketSize
|
||||||
|
maxDNSPacketSize = 1232
|
||||||
|
)
|
||||||
|
|
||||||
func (t *Transport) exchangeSingleRequest(ctx context.Context, servers []M.Socksaddr, message *mDNS.Msg, domain string) (*mDNS.Msg, error) {
|
func (t *Transport) exchangeSingleRequest(ctx context.Context, servers []M.Socksaddr, message *mDNS.Msg, domain string) (*mDNS.Msg, error) {
|
||||||
var lastErr error
|
var lastErr error
|
||||||
for _, fqdn := range t.nameList(domain) {
|
for _, fqdn := range t.nameList(domain) {
|
||||||
@ -113,7 +118,7 @@ func (t *Transport) exchangeOne(ctx context.Context, server M.Socksaddr, questio
|
|||||||
Question: []mDNS.Question{question},
|
Question: []mDNS.Question{question},
|
||||||
Compress: true,
|
Compress: true,
|
||||||
}
|
}
|
||||||
request.SetEdns0(buf.UDPBufferSize, false)
|
request.SetEdns0(maxDNSPacketSize, false)
|
||||||
buffer := buf.Get(buf.UDPBufferSize)
|
buffer := buf.Get(buf.UDPBufferSize)
|
||||||
defer buf.Put(buffer)
|
defer buf.Put(buffer)
|
||||||
for _, network := range networks {
|
for _, network := range networks {
|
||||||
|
@ -59,11 +59,11 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
|
|||||||
}
|
}
|
||||||
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
||||||
tlsOptions.Enabled = true
|
tlsOptions.Enabled = true
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
|
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if common.Error(tlsConfig.STDConfig()) == nil && !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) {
|
if common.Error(tlsConfig.Config()) == nil && !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) {
|
||||||
tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), http2.NextProtoTLS))
|
tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), http2.NextProtoTLS))
|
||||||
}
|
}
|
||||||
if !common.Contains(tlsConfig.NextProtos(), "http/1.1") {
|
if !common.Contains(tlsConfig.NextProtos(), "http/1.1") {
|
||||||
|
@ -121,7 +121,7 @@ func (t *Transport) exchangeOne(ctx context.Context, server M.Socksaddr, questio
|
|||||||
Question: []mDNS.Question{question},
|
Question: []mDNS.Question{question},
|
||||||
Compress: true,
|
Compress: true,
|
||||||
}
|
}
|
||||||
request.SetEdns0(buf.UDPBufferSize, false)
|
request.SetEdns0(maxDNSPacketSize, false)
|
||||||
buffer := buf.Get(buf.UDPBufferSize)
|
buffer := buf.Get(buf.UDPBufferSize)
|
||||||
defer buf.Put(buffer)
|
defer buf.Put(buffer)
|
||||||
for _, network := range networks {
|
for _, network := range networks {
|
||||||
|
@ -10,6 +10,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// net.maxDNSPacketSize
|
||||||
|
maxDNSPacketSize = 1232
|
||||||
|
)
|
||||||
|
|
||||||
type resolverConfig struct {
|
type resolverConfig struct {
|
||||||
initOnce sync.Once
|
initOnce sync.Once
|
||||||
ch chan struct{}
|
ch chan struct{}
|
||||||
|
@ -51,11 +51,11 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
|||||||
}
|
}
|
||||||
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
||||||
tlsOptions.Enabled = true
|
tlsOptions.Enabled = true
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
|
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
stdConfig, err := tlsConfig.STDConfig()
|
stdConfig, err := tlsConfig.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options
|
|||||||
}
|
}
|
||||||
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
||||||
tlsOptions.Enabled = true
|
tlsOptions.Enabled = true
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
|
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o
|
|||||||
}
|
}
|
||||||
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
tlsOptions := common.PtrValueOrDefault(options.TLS)
|
||||||
tlsOptions.Enabled = true
|
tlsOptions.Enabled = true
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, tlsOptions)
|
tlsConfig, err := tls.NewClient(ctx, options.Server, tlsOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,10 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 1.13.0-alpha.9
|
#### 1.13.0-alpha.8
|
||||||
|
|
||||||
* Add kTLS support **1**
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
See [TLS](/configuration/shared/tls/).
|
|
||||||
|
|
||||||
#### 1.12.4
|
#### 1.12.4
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
---
|
---
|
||||||
icon: material/new-box
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! 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"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
:material-plus: [fragment](#fragment)
|
:material-plus: [fragment](#fragment)
|
||||||
@ -33,8 +28,6 @@ icon: material/new-box
|
|||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
"key": [],
|
"key": [],
|
||||||
"key_path": "",
|
"key_path": "",
|
||||||
"kernel_tx": false,
|
|
||||||
"kernel_rx": false,
|
|
||||||
"acme": {
|
"acme": {
|
||||||
"domain": [],
|
"domain": [],
|
||||||
"data_directory": "",
|
"data_directory": "",
|
||||||
@ -195,8 +188,7 @@ By default, the maximum version is currently TLS 1.3.
|
|||||||
|
|
||||||
#### cipher_suites
|
#### cipher_suites
|
||||||
|
|
||||||
A list of enabled TLS 1.0–1.2 cipher suites. The order of the list is ignored.
|
A list of enabled TLS 1.0–1.2 cipher suites. The order of the list is ignored. Note that TLS 1.3 cipher suites are not configurable.
|
||||||
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.
|
If empty, a safe default list is used. The default cipher suites might change over time.
|
||||||
|
|
||||||
@ -228,50 +220,6 @@ The server private key line array, in PEM format.
|
|||||||
|
|
||||||
The path to the server private key, 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
|
## Custom TLS support
|
||||||
|
|
||||||
!!! info "QUIC support"
|
!!! info "QUIC support"
|
||||||
|
@ -2,11 +2,6 @@
|
|||||||
icon: material/alert-decagram
|
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 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [tls_fragment](#tls_fragment)
|
:material-plus: [tls_fragment](#tls_fragment)
|
||||||
@ -33,8 +28,6 @@ icon: material/alert-decagram
|
|||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
"key": [],
|
"key": [],
|
||||||
"key_path": "",
|
"key_path": "",
|
||||||
"kernel_tx": false,
|
|
||||||
"kernel_rx": false,
|
|
||||||
"acme": {
|
"acme": {
|
||||||
"domain": [],
|
"domain": [],
|
||||||
"data_directory": "",
|
"data_directory": "",
|
||||||
@ -223,56 +216,6 @@ TLS 版本值:
|
|||||||
|
|
||||||
服务器 PEM 私钥路径。
|
服务器 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
|
#### utls
|
||||||
|
|
||||||
==仅客户端==
|
==仅客户端==
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/compatible"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/clashapi/compatible"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
"github.com/sagernet/sing/common/x/list"
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
7
go.mod
7
go.mod
@ -4,7 +4,6 @@ go 1.23.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/anytls/sing-anytls v0.0.8
|
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/caddyserver/certmagic v0.23.0
|
||||||
github.com/coder/websocket v1.8.13
|
github.com/coder/websocket v1.8.13
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
@ -28,13 +27,13 @@ require (
|
|||||||
github.com/sagernet/gomobile v0.1.8
|
github.com/sagernet/gomobile v0.1.8
|
||||||
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237
|
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237
|
||||||
github.com/sagernet/quic-go v0.52.0-beta.1
|
github.com/sagernet/quic-go v0.52.0-beta.1
|
||||||
github.com/sagernet/sing v0.7.8-0.20250908063931-beb351e61b89
|
github.com/sagernet/sing v0.7.6-0.20250826155514-8bdb5fee4568
|
||||||
github.com/sagernet/sing-mux v0.3.3
|
github.com/sagernet/sing-mux v0.3.3
|
||||||
github.com/sagernet/sing-quic v0.5.2-0.20250908021228-186e280a524e
|
github.com/sagernet/sing-quic v0.5.0
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.1
|
github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250827122908-b76e852f59b0
|
||||||
github.com/sagernet/sing-vmess v0.2.7
|
github.com/sagernet/sing-vmess v0.2.7
|
||||||
github.com/sagernet/smux v1.5.34-mod.2
|
github.com/sagernet/smux v1.5.34-mod.2
|
||||||
github.com/sagernet/tailscale v1.80.3-sing-box-1.13-mod.1
|
github.com/sagernet/tailscale v1.80.3-sing-box-1.13-mod.1
|
||||||
|
14
go.sum
14
go.sum
@ -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/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 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
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 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
|
||||||
github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
|
github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
|
||||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||||
@ -169,20 +167,20 @@ 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 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
|
||||||
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
|
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.6.9/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.6-0.20250826155514-8bdb5fee4568 h1:0bBD73wG4Rmn1ZyMYsvHwgoDz9tFnX8BzvjbAEPoavg=
|
||||||
github.com/sagernet/sing v0.7.8-0.20250908063931-beb351e61b89/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.7.6-0.20250826155514-8bdb5fee4568/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 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
||||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
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=
|
github.com/sagernet/sing-quic v0.5.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak=
|
||||||
github.com/sagernet/sing-quic v0.5.2-0.20250908021228-186e280a524e/go.mod h1:gi/sGED8gTWgTAp3GlzXo2D7mXYY+ERoxtGvSkNx3sI=
|
github.com/sagernet/sing-quic v0.5.0/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
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-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.1 h1:k8DOTDMBBc42sUW0C91MBMOsqE5jAWtB0kmJq9TTBvU=
|
github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250827122908-b76e852f59b0 h1:Usid4HU1TKrtao2fv/wyubdOkBHpbHdwgU9KUzWXQMM=
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.1/go.mod h1:LokZYuEV3crByjQc/XRohLgfNvybtXdx5qe/I4W6S7k=
|
github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250827122908-b76e852f59b0/go.mod h1:LokZYuEV3crByjQc/XRohLgfNvybtXdx5qe/I4W6S7k=
|
||||||
github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk=
|
github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk=
|
||||||
github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
|
github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
||||||
|
@ -14,8 +14,6 @@ type InboundTLSOptions struct {
|
|||||||
CertificatePath string `json:"certificate_path,omitempty"`
|
CertificatePath string `json:"certificate_path,omitempty"`
|
||||||
Key badoption.Listable[string] `json:"key,omitempty"`
|
Key badoption.Listable[string] `json:"key,omitempty"`
|
||||||
KeyPath string `json:"key_path,omitempty"`
|
KeyPath string `json:"key_path,omitempty"`
|
||||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
|
||||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
|
||||||
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
ACME *InboundACMEOptions `json:"acme,omitempty"`
|
||||||
ECH *InboundECHOptions `json:"ech,omitempty"`
|
ECH *InboundECHOptions `json:"ech,omitempty"`
|
||||||
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
Reality *InboundRealityOptions `json:"reality,omitempty"`
|
||||||
@ -52,8 +50,6 @@ type OutboundTLSOptions struct {
|
|||||||
Fragment bool `json:"fragment,omitempty"`
|
Fragment bool `json:"fragment,omitempty"`
|
||||||
FragmentFallbackDelay badoption.Duration `json:"fragment_fallback_delay,omitempty"`
|
FragmentFallbackDelay badoption.Duration `json:"fragment_fallback_delay,omitempty"`
|
||||||
RecordFragment bool `json:"record_fragment,omitempty"`
|
RecordFragment bool `json:"record_fragment,omitempty"`
|
||||||
KernelTx bool `json:"kernel_tx,omitempty"`
|
|
||||||
KernelRx bool `json:"kernel_rx,omitempty"`
|
|
||||||
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
ECH *OutboundECHOptions `json:"ech,omitempty"`
|
||||||
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
UTLS *OutboundUTLSOptions `json:"utls,omitempty"`
|
||||||
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
Reality *OutboundRealityOptions `json:"reality,omitempty"`
|
||||||
|
@ -124,7 +124,7 @@ func (h *inboundHandler) NewConnectionEx(ctx context.Context, conn net.Conn, sou
|
|||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
metadata.Destination = destination.Unwrap()
|
metadata.Destination = destination
|
||||||
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
if userName, _ := auth.UserFromContext[string](ctx); userName != "" {
|
||||||
metadata.User = userName
|
metadata.User = userName
|
||||||
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||||
|
@ -44,7 +44,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,26 @@ func (h *Outbound) DialParallel(ctx context.Context, network string, destination
|
|||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
}
|
}
|
||||||
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, len(destinationAddresses) > 0 && destinationAddresses[0].Is6(), nil, nil, nil, h.fallbackDelay)
|
var domainStrategy C.DomainStrategy
|
||||||
|
if h.domainStrategy != C.DomainStrategyAsIS {
|
||||||
|
domainStrategy = h.domainStrategy
|
||||||
|
} else {
|
||||||
|
//nolint:staticcheck
|
||||||
|
domainStrategy = C.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
||||||
|
}
|
||||||
|
switch domainStrategy {
|
||||||
|
case C.DomainStrategyIPv4Only:
|
||||||
|
destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is4)
|
||||||
|
if len(destinationAddresses) == 0 {
|
||||||
|
return nil, E.New("no IPv4 address available for ", destination)
|
||||||
|
}
|
||||||
|
case C.DomainStrategyIPv6Only:
|
||||||
|
destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is6)
|
||||||
|
if len(destinationAddresses) == 0 {
|
||||||
|
return nil, E.New("no IPv6 address available for ", destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == C.DomainStrategyPreferIPv6, nil, nil, nil, h.fallbackDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||||
@ -200,7 +219,26 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest
|
|||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
}
|
}
|
||||||
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, len(destinationAddresses) > 0 && destinationAddresses[0].Is6(), networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
|
var domainStrategy C.DomainStrategy
|
||||||
|
if h.domainStrategy != C.DomainStrategyAsIS {
|
||||||
|
domainStrategy = h.domainStrategy
|
||||||
|
} else {
|
||||||
|
//nolint:staticcheck
|
||||||
|
domainStrategy = C.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
||||||
|
}
|
||||||
|
switch domainStrategy {
|
||||||
|
case C.DomainStrategyIPv4Only:
|
||||||
|
destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is4)
|
||||||
|
if len(destinationAddresses) == 0 {
|
||||||
|
return nil, E.New("no IPv4 address available for ", destination)
|
||||||
|
}
|
||||||
|
case C.DomainStrategyIPv6Only:
|
||||||
|
destinationAddresses = common.Filter(destinationAddresses, netip.Addr.Is6)
|
||||||
|
if len(destinationAddresses) == 0 {
|
||||||
|
return nil, E.New("no IPv6 address available for ", destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == C.DomainStrategyPreferIPv6, networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy *C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||||
|
@ -34,7 +34,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
detour, err := tls.NewDialerFromOptions(ctx, logger, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
detour, err := tls.NewDialerFromOptions(ctx, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
}
|
}
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
}
|
}
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ func (n *Inbound) Start(stage adapter.StartStage) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "create TLS config")
|
return E.Cause(err, "create TLS config")
|
||||||
}
|
}
|
||||||
tlsConfig, err = n.tlsConfig.STDConfig()
|
tlsConfig, err = n.tlsConfig.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ func (n *Inbound) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
|||||||
hostPort = request.Host
|
hostPort = request.Host
|
||||||
}
|
}
|
||||||
source := sHttp.SourceAddress(request)
|
source := sHttp.SourceAddress(request)
|
||||||
destination := M.ParseSocksaddr(hostPort).Unwrap()
|
destination := M.ParseSocksaddr(hostPort)
|
||||||
|
|
||||||
if hijacker, isHijacker := writer.(http.Hijacker); isHijacker {
|
if hijacker, isHijacker := writer.(http.Hijacker); isHijacker {
|
||||||
conn, _, err := hijacker.Hijack()
|
conn, _, err := hijacker.Hijack()
|
||||||
|
@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
options.TLS.MinVersion = "1.2"
|
options.TLS.MinVersion = "1.2"
|
||||||
options.TLS.MaxVersion = "1.2"
|
options.TLS.MaxVersion = "1.2"
|
||||||
}
|
}
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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))
|
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stdTLSConfig, err := tlsConfig.STDConfig()
|
stdTLSConfig, err := tlsConfig.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dn
|
|||||||
if serverAddr.Port == 0 {
|
if serverAddr.Port == 0 {
|
||||||
serverAddr.Port = 443
|
serverAddr.Port = 443
|
||||||
}
|
}
|
||||||
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.AddrString(), option.OutboundTLSOptions{
|
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.AddrString(), option.OutboundTLSOptions{
|
||||||
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
|
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
|
||||||
}))
|
}))
|
||||||
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
|
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
|
||||||
|
@ -51,7 +51,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
key: trojan.Key(options.Password),
|
key: trojan.Key(options.Password),
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
outbound.tlsConfig, err = tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
}
|
}
|
||||||
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
serverAddr: options.ServerOptions.Build(),
|
serverAddr: options.ServerOptions.Build(),
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
outbound.tlsConfig, err = tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
serverAddr: options.ServerOptions.Build(),
|
serverAddr: options.ServerOptions.Build(),
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
outbound.tlsConfig, err = tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
|
outbound.tlsConfig, err = tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ pushd $PROJECT
|
|||||||
git fetch
|
git fetch
|
||||||
git reset FETCH_HEAD --hard
|
git reset FETCH_HEAD --hard
|
||||||
git clean -fdx
|
git clean -fdx
|
||||||
go install -v -trimpath -ldflags "-s -w -buildid= -checklinkname=0" -tags with_quic,with_acme,debug ./cmd/sing-box
|
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_acme,debug ./cmd/sing-box
|
||||||
popd
|
popd
|
||||||
|
|
||||||
sudo systemctl stop sing-box
|
sudo systemctl stop sing-box
|
||||||
|
@ -10,7 +10,7 @@ DIR=$(dirname "$0")
|
|||||||
PROJECT=$DIR/../..
|
PROJECT=$DIR/../..
|
||||||
|
|
||||||
pushd $PROJECT
|
pushd $PROJECT
|
||||||
go install -v -trimpath -ldflags "-s -w -buildid= -checklinkname=0" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||||
popd
|
popd
|
||||||
|
|
||||||
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
|
sudo cp $(go env GOPATH)/bin/sing-box /usr/local/bin/
|
||||||
|
@ -10,7 +10,7 @@ DIR=$(dirname "$0")
|
|||||||
PROJECT=$DIR/../..
|
PROJECT=$DIR/../..
|
||||||
|
|
||||||
pushd $PROJECT
|
pushd $PROJECT
|
||||||
go install -v -trimpath -ldflags "-s -w -buildid= -checklinkname=0" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
go install -v -trimpath -ldflags "-s -w -buildid=" -tags with_quic,with_wireguard,with_acme ./cmd/sing-box
|
||||||
popd
|
popd
|
||||||
|
|
||||||
sudo systemctl stop sing-box
|
sudo systemctl stop sing-box
|
||||||
|
@ -102,8 +102,6 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co
|
|||||||
m.connections.Remove(element)
|
m.connections.Remove(element)
|
||||||
})
|
})
|
||||||
var done atomic.Bool
|
var done atomic.Bool
|
||||||
m.preConnectionCopy(ctx, conn, remoteConn, false, &done, onClose)
|
|
||||||
m.preConnectionCopy(ctx, remoteConn, conn, true, &done, onClose)
|
|
||||||
go m.connectionCopy(ctx, conn, remoteConn, false, &done, onClose)
|
go m.connectionCopy(ctx, conn, remoteConn, false, &done, onClose)
|
||||||
go m.connectionCopy(ctx, remoteConn, conn, true, &done, onClose)
|
go m.connectionCopy(ctx, remoteConn, conn, true, &done, onClose)
|
||||||
}
|
}
|
||||||
@ -226,24 +224,6 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
|||||||
go m.packetConnectionCopy(ctx, destination, conn, true, &done, onClose)
|
go m.packetConnectionCopy(ctx, destination, conn, true, &done, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConnectionManager) preConnectionCopy(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
|
||||||
if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](destination); isEarlyConn && earlyConn.NeedHandshake() {
|
|
||||||
err := m.connectionCopyEarly(source, destination)
|
|
||||||
if err != nil {
|
|
||||||
if done.Swap(true) {
|
|
||||||
onClose(err)
|
|
||||||
}
|
|
||||||
common.Close(source, destination)
|
|
||||||
if !direction {
|
|
||||||
m.logger.ErrorContext(ctx, "connection upload handshake: ", err)
|
|
||||||
} else {
|
|
||||||
m.logger.ErrorContext(ctx, "connection download handshake: ", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
||||||
var (
|
var (
|
||||||
sourceReader io.Reader = source
|
sourceReader io.Reader = source
|
||||||
@ -282,7 +262,21 @@ func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn,
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](destinationWriter); isEarlyConn && earlyConn.NeedHandshake() {
|
||||||
|
err := m.connectionCopyEarly(source, destination)
|
||||||
|
if err != nil {
|
||||||
|
if done.Swap(true) {
|
||||||
|
onClose(err)
|
||||||
|
}
|
||||||
|
common.Close(source, destination)
|
||||||
|
if !direction {
|
||||||
|
m.logger.ErrorContext(ctx, "connection upload handshake: ", err)
|
||||||
|
} else {
|
||||||
|
m.logger.ErrorContext(ctx, "connection download handshake: ", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
_, err := bufio.CopyWithCounters(destinationWriter, sourceReader, source, readCounters, writeCounters, bufio.DefaultIncreaseBufferAfter, bufio.DefaultBatchSize)
|
_, err := bufio.CopyWithCounters(destinationWriter, sourceReader, source, readCounters, writeCounters, bufio.DefaultIncreaseBufferAfter, bufio.DefaultBatchSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Close(source, destination)
|
common.Close(source, destination)
|
||||||
|
104
route/route.go
104
route/route.go
@ -28,8 +28,6 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/uot"
|
"github.com/sagernet/sing/common/uot"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Deprecated: use RouteConnectionEx instead.
|
// Deprecated: use RouteConnectionEx instead.
|
||||||
@ -382,16 +380,16 @@ func (r *Router) matchRule(
|
|||||||
newBuffer, newPackerBuffers, newErr := r.actionSniff(ctx, metadata, &R.RuleActionSniff{
|
newBuffer, newPackerBuffers, newErr := r.actionSniff(ctx, metadata, &R.RuleActionSniff{
|
||||||
OverrideDestination: metadata.InboundOptions.SniffOverrideDestination,
|
OverrideDestination: metadata.InboundOptions.SniffOverrideDestination,
|
||||||
Timeout: time.Duration(metadata.InboundOptions.SniffTimeout),
|
Timeout: time.Duration(metadata.InboundOptions.SniffTimeout),
|
||||||
}, inputConn, inputPacketConn, nil, nil)
|
}, inputConn, inputPacketConn, nil)
|
||||||
|
if newErr != nil {
|
||||||
|
fatalErr = newErr
|
||||||
|
return
|
||||||
|
}
|
||||||
if newBuffer != nil {
|
if newBuffer != nil {
|
||||||
buffers = []*buf.Buffer{newBuffer}
|
buffers = []*buf.Buffer{newBuffer}
|
||||||
} else if len(newPackerBuffers) > 0 {
|
} else if len(newPackerBuffers) > 0 {
|
||||||
packetBuffers = newPackerBuffers
|
packetBuffers = newPackerBuffers
|
||||||
}
|
}
|
||||||
if newErr != nil {
|
|
||||||
fatalErr = newErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if C.DomainStrategy(metadata.InboundOptions.DomainStrategy) != C.DomainStrategyAsIS {
|
if C.DomainStrategy(metadata.InboundOptions.DomainStrategy) != C.DomainStrategyAsIS {
|
||||||
fatalErr = r.actionResolve(ctx, metadata, &R.RuleActionResolve{
|
fatalErr = r.actionResolve(ctx, metadata, &R.RuleActionResolve{
|
||||||
@ -490,16 +488,16 @@ match:
|
|||||||
switch action := currentRule.Action().(type) {
|
switch action := currentRule.Action().(type) {
|
||||||
case *R.RuleActionSniff:
|
case *R.RuleActionSniff:
|
||||||
if !preMatch {
|
if !preMatch {
|
||||||
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn, buffers, packetBuffers)
|
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn, buffers)
|
||||||
|
if newErr != nil {
|
||||||
|
fatalErr = newErr
|
||||||
|
return
|
||||||
|
}
|
||||||
if newBuffer != nil {
|
if newBuffer != nil {
|
||||||
buffers = append(buffers, newBuffer)
|
buffers = append(buffers, newBuffer)
|
||||||
} else if len(newPacketBuffers) > 0 {
|
} else if len(newPacketBuffers) > 0 {
|
||||||
packetBuffers = append(packetBuffers, newPacketBuffers...)
|
packetBuffers = append(packetBuffers, newPacketBuffers...)
|
||||||
}
|
}
|
||||||
if newErr != nil {
|
|
||||||
fatalErr = newErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if metadata.Network != N.NetworkICMP {
|
} else if metadata.Network != N.NetworkICMP {
|
||||||
selectedRule = currentRule
|
selectedRule = currentRule
|
||||||
selectedRuleIndex = currentRuleIndex
|
selectedRuleIndex = currentRuleIndex
|
||||||
@ -525,7 +523,7 @@ match:
|
|||||||
|
|
||||||
func (r *Router) actionSniff(
|
func (r *Router) actionSniff(
|
||||||
ctx context.Context, metadata *adapter.InboundContext, action *R.RuleActionSniff,
|
ctx context.Context, metadata *adapter.InboundContext, action *R.RuleActionSniff,
|
||||||
inputConn net.Conn, inputPacketConn N.PacketConn, inputBuffers []*buf.Buffer, inputPacketBuffers []*N.PacketBuffer,
|
inputConn net.Conn, inputPacketConn N.PacketConn, inputBuffers []*buf.Buffer,
|
||||||
) (buffer *buf.Buffer, packetBuffers []*N.PacketBuffer, fatalErr error) {
|
) (buffer *buf.Buffer, packetBuffers []*N.PacketBuffer, fatalErr error) {
|
||||||
if sniff.Skip(metadata) {
|
if sniff.Skip(metadata) {
|
||||||
r.logger.DebugContext(ctx, "sniff skipped due to port considered as server-first")
|
r.logger.DebugContext(ctx, "sniff skipped due to port considered as server-first")
|
||||||
@ -537,7 +535,7 @@ func (r *Router) actionSniff(
|
|||||||
if inputConn != nil {
|
if inputConn != nil {
|
||||||
if len(action.StreamSniffers) == 0 && len(action.PacketSniffers) > 0 {
|
if len(action.StreamSniffers) == 0 && len(action.PacketSniffers) > 0 {
|
||||||
return
|
return
|
||||||
} else if slices.Equal(metadata.SnifferNames, action.SnifferNames) && metadata.SniffError != nil && !errors.Is(metadata.SniffError, sniff.ErrNeedMoreData) {
|
} else if metadata.SniffError != nil && !errors.Is(metadata.SniffError, sniff.ErrNeedMoreData) {
|
||||||
r.logger.DebugContext(ctx, "packet sniff skipped due to previous error: ", metadata.SniffError)
|
r.logger.DebugContext(ctx, "packet sniff skipped due to previous error: ", metadata.SniffError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -564,7 +562,6 @@ func (r *Router) actionSniff(
|
|||||||
action.Timeout,
|
action.Timeout,
|
||||||
streamSniffers...,
|
streamSniffers...,
|
||||||
)
|
)
|
||||||
metadata.SnifferNames = action.SnifferNames
|
|
||||||
metadata.SniffError = err
|
metadata.SniffError = err
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//goland:noinspection GoDeprecation
|
//goland:noinspection GoDeprecation
|
||||||
@ -590,13 +587,10 @@ func (r *Router) actionSniff(
|
|||||||
} else if inputPacketConn != nil {
|
} else if inputPacketConn != nil {
|
||||||
if len(action.PacketSniffers) == 0 && len(action.StreamSniffers) > 0 {
|
if len(action.PacketSniffers) == 0 && len(action.StreamSniffers) > 0 {
|
||||||
return
|
return
|
||||||
} else if slices.Equal(metadata.SnifferNames, action.SnifferNames) && metadata.SniffError != nil && !errors.Is(metadata.SniffError, sniff.ErrNeedMoreData) {
|
} else if metadata.SniffError != nil && !errors.Is(metadata.SniffError, sniff.ErrNeedMoreData) {
|
||||||
r.logger.DebugContext(ctx, "packet sniff skipped due to previous error: ", metadata.SniffError)
|
r.logger.DebugContext(ctx, "packet sniff skipped due to previous error: ", metadata.SniffError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
quicMoreData := func() bool {
|
|
||||||
return slices.Equal(metadata.SnifferNames, action.SnifferNames) && errors.Is(metadata.SniffError, sniff.ErrNeedMoreData)
|
|
||||||
}
|
|
||||||
var packetSniffers []sniff.PacketSniffer
|
var packetSniffers []sniff.PacketSniffer
|
||||||
if len(action.PacketSniffers) > 0 {
|
if len(action.PacketSniffers) > 0 {
|
||||||
packetSniffers = action.PacketSniffers
|
packetSniffers = action.PacketSniffers
|
||||||
@ -611,37 +605,12 @@ func (r *Router) actionSniff(
|
|||||||
sniff.NTP,
|
sniff.NTP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var err error
|
|
||||||
for _, packetBuffer := range inputPacketBuffers {
|
|
||||||
if quicMoreData() {
|
|
||||||
err = sniff.PeekPacket(
|
|
||||||
ctx,
|
|
||||||
metadata,
|
|
||||||
packetBuffer.Buffer.Bytes(),
|
|
||||||
sniff.QUICClientHello,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
err = sniff.PeekPacket(
|
|
||||||
ctx, metadata,
|
|
||||||
packetBuffer.Buffer.Bytes(),
|
|
||||||
packetSniffers...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
metadata.SnifferNames = action.SnifferNames
|
|
||||||
metadata.SniffError = err
|
|
||||||
if errors.Is(err, sniff.ErrNeedMoreData) {
|
|
||||||
// TODO: replace with generic message when there are more multi-packet protocols
|
|
||||||
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
goto finally
|
|
||||||
}
|
|
||||||
packetBuffers = inputPacketBuffers
|
|
||||||
for {
|
for {
|
||||||
var (
|
var (
|
||||||
sniffBuffer = buf.NewPacket()
|
sniffBuffer = buf.NewPacket()
|
||||||
destination M.Socksaddr
|
destination M.Socksaddr
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
go func() {
|
go func() {
|
||||||
sniffTimeout := C.ReadPayloadTimeout
|
sniffTimeout := C.ReadPayloadTimeout
|
||||||
@ -667,7 +636,7 @@ func (r *Router) actionSniff(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if quicMoreData() {
|
if len(packetBuffers) > 0 || metadata.SniffError != nil {
|
||||||
err = sniff.PeekPacket(
|
err = sniff.PeekPacket(
|
||||||
ctx,
|
ctx,
|
||||||
metadata,
|
metadata,
|
||||||
@ -687,34 +656,32 @@ func (r *Router) actionSniff(
|
|||||||
Destination: destination,
|
Destination: destination,
|
||||||
}
|
}
|
||||||
packetBuffers = append(packetBuffers, packetBuffer)
|
packetBuffers = append(packetBuffers, packetBuffer)
|
||||||
metadata.SnifferNames = action.SnifferNames
|
|
||||||
metadata.SniffError = err
|
metadata.SniffError = err
|
||||||
if errors.Is(err, sniff.ErrNeedMoreData) {
|
if errors.Is(err, sniff.ErrNeedMoreData) {
|
||||||
// TODO: replace with generic message when there are more multi-packet protocols
|
// TODO: replace with generic message when there are more multi-packet protocols
|
||||||
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
|
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
if metadata.Protocol != "" {
|
||||||
goto finally
|
//goland:noinspection GoDeprecation
|
||||||
}
|
if action.OverrideDestination && M.IsDomainName(metadata.Domain) {
|
||||||
finally:
|
metadata.Destination = M.Socksaddr{
|
||||||
if err == nil {
|
Fqdn: metadata.Domain,
|
||||||
//goland:noinspection GoDeprecation
|
Port: metadata.Destination.Port,
|
||||||
if action.OverrideDestination && M.IsDomainName(metadata.Domain) {
|
}
|
||||||
metadata.Destination = M.Socksaddr{
|
}
|
||||||
Fqdn: metadata.Domain,
|
if metadata.Domain != "" && metadata.Client != "" {
|
||||||
Port: metadata.Destination.Port,
|
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client)
|
||||||
|
} else if metadata.Domain != "" {
|
||||||
|
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
||||||
|
} else if metadata.Client != "" {
|
||||||
|
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", client: ", metadata.Client)
|
||||||
|
} else {
|
||||||
|
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if metadata.Domain != "" && metadata.Client != "" {
|
break
|
||||||
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client)
|
|
||||||
} else if metadata.Domain != "" {
|
|
||||||
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
|
||||||
} else if metadata.Client != "" {
|
|
||||||
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", client: ", metadata.Client)
|
|
||||||
} else {
|
|
||||||
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -742,6 +709,11 @@ func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundCon
|
|||||||
}
|
}
|
||||||
metadata.DestinationAddresses = addresses
|
metadata.DestinationAddresses = addresses
|
||||||
r.logger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
r.logger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]")
|
||||||
|
if metadata.Destination.IsIPv4() {
|
||||||
|
metadata.IPVersion = 4
|
||||||
|
} else if metadata.Destination.IsIPv6() {
|
||||||
|
metadata.IPVersion = 6
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
|||||||
return &RuleActionHijackDNS{}, nil
|
return &RuleActionHijackDNS{}, nil
|
||||||
case C.RuleActionTypeSniff:
|
case C.RuleActionTypeSniff:
|
||||||
sniffAction := &RuleActionSniff{
|
sniffAction := &RuleActionSniff{
|
||||||
SnifferNames: action.SniffOptions.Sniffer,
|
snifferNames: action.SniffOptions.Sniffer,
|
||||||
Timeout: time.Duration(action.SniffOptions.Timeout),
|
Timeout: time.Duration(action.SniffOptions.Timeout),
|
||||||
}
|
}
|
||||||
return sniffAction, sniffAction.build()
|
return sniffAction, sniffAction.build()
|
||||||
@ -362,7 +362,7 @@ func (r *RuleActionHijackDNS) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RuleActionSniff struct {
|
type RuleActionSniff struct {
|
||||||
SnifferNames []string
|
snifferNames []string
|
||||||
StreamSniffers []sniff.StreamSniffer
|
StreamSniffers []sniff.StreamSniffer
|
||||||
PacketSniffers []sniff.PacketSniffer
|
PacketSniffers []sniff.PacketSniffer
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
@ -375,7 +375,7 @@ func (r *RuleActionSniff) Type() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleActionSniff) build() error {
|
func (r *RuleActionSniff) build() error {
|
||||||
for _, name := range r.SnifferNames {
|
for _, name := range r.snifferNames {
|
||||||
switch name {
|
switch name {
|
||||||
case C.ProtocolTLS:
|
case C.ProtocolTLS:
|
||||||
r.StreamSniffers = append(r.StreamSniffers, sniff.TLSClientHello)
|
r.StreamSniffers = append(r.StreamSniffers, sniff.TLSClientHello)
|
||||||
@ -408,14 +408,14 @@ func (r *RuleActionSniff) build() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleActionSniff) String() string {
|
func (r *RuleActionSniff) String() string {
|
||||||
if len(r.SnifferNames) == 0 && r.Timeout == 0 {
|
if len(r.snifferNames) == 0 && r.Timeout == 0 {
|
||||||
return "sniff"
|
return "sniff"
|
||||||
} else if len(r.SnifferNames) > 0 && r.Timeout == 0 {
|
} else if len(r.snifferNames) > 0 && r.Timeout == 0 {
|
||||||
return F.ToString("sniff(", strings.Join(r.SnifferNames, ","), ")")
|
return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ")")
|
||||||
} else if len(r.SnifferNames) == 0 && r.Timeout > 0 {
|
} else if len(r.snifferNames) == 0 && r.Timeout > 0 {
|
||||||
return F.ToString("sniff(", r.Timeout.String(), ")")
|
return F.ToString("sniff(", r.Timeout.String(), ")")
|
||||||
} else {
|
} else {
|
||||||
return F.ToString("sniff(", strings.Join(r.SnifferNames, ","), ",", r.Timeout.String(), ")")
|
return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ",", r.Timeout.String(), ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,11 +301,11 @@ func (d *Service) startMeshWithHost(derpServer *derp.Server, server *option.DERP
|
|||||||
}
|
}
|
||||||
var stdConfig *tls.STDConfig
|
var stdConfig *tls.STDConfig
|
||||||
if server.TLS != nil && server.TLS.Enabled {
|
if server.TLS != nil && server.TLS.Enabled {
|
||||||
tlsConfig, err := tls.NewClient(d.ctx, d.logger, hostname, common.PtrValueOrDefault(server.TLS))
|
tlsConfig, err := tls.NewClient(d.ctx, hostname, common.PtrValueOrDefault(server.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stdConfig, err = tlsConfig.STDConfig()
|
stdConfig, err = tlsConfig.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ func (t *Transport) updateTransports(link *TransportLink) error {
|
|||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
if link.dnsOverTLS {
|
if link.dnsOverTLS {
|
||||||
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.String(), option.OutboundTLSOptions{
|
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.String(), option.OutboundTLSOptions{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
ServerName: serverAddr.String(),
|
ServerName: serverAddr.String(),
|
||||||
}))
|
}))
|
||||||
@ -151,7 +151,7 @@ func (t *Transport) updateTransports(link *TransportLink) error {
|
|||||||
} else {
|
} else {
|
||||||
serverName = serverAddr.String()
|
serverName = serverAddr.String()
|
||||||
}
|
}
|
||||||
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.String(), option.OutboundTLSOptions{
|
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.String(), option.OutboundTLSOptions{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
}))
|
}))
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/sagernet/sing-vmess"
|
"github.com/sagernet/sing-vmess"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json/badoption"
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
@ -56,7 +55,7 @@ func newV2RayPlugin(ctx context.Context, pluginOpts Args, router adapter.Router,
|
|||||||
var tlsClient tls.Config
|
var tlsClient tls.Config
|
||||||
var err error
|
var err error
|
||||||
if tlsOptions.Enabled {
|
if tlsOptions.Enabled {
|
||||||
tlsClient, err = tls.NewClient(ctx, logger.NOP(), serverAddr.AddrString(), tlsOptions)
|
tlsClient, err = tls.NewClient(ctx, serverAddr.AddrString(), tlsOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,14 +83,6 @@ func (c *ClientConn) Upstream() any {
|
|||||||
return c.ExtendedConn
|
return c.ExtendedConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientConn) ReaderReplaceable() bool {
|
|
||||||
return c.headerWritten
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ClientConn) WriterReplaceable() bool {
|
|
||||||
return c.headerWritten
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientPacketConn struct {
|
type ClientPacketConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
@ -300,7 +292,7 @@ func ReadPacket(conn net.Conn, buffer *buf.Buffer) (M.Socksaddr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = buffer.ReadFullFrom(conn, int(length))
|
_, err = buffer.ReadFullFrom(conn, int(length))
|
||||||
return destination, err
|
return destination.Unwrap(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func WritePacket(conn net.Conn, buffer *buf.Buffer, destination M.Socksaddr) error {
|
func WritePacket(conn net.Conn, buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||||
|
@ -138,7 +138,7 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint)
|
|||||||
b := packets[0]
|
b := packets[0]
|
||||||
common.ClearArray(b[1:4])
|
common.ClearArray(b[1:4])
|
||||||
}
|
}
|
||||||
eps[0] = remoteEndpoint(M.SocksaddrFromNet(addr).Unwrap().AddrPort())
|
eps[0] = remoteEndpoint(M.AddrPortFromNet(addr))
|
||||||
count = 1
|
count = 1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user