mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
28 Commits
dev-ts-rel
...
v1.12.21
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d18d2b352a | ||
|
|
534128bba9 | ||
|
|
736a7368c6 | ||
|
|
e7a9c90213 | ||
|
|
0f3774e501 | ||
|
|
2f8e656522 | ||
|
|
3ba30e3f00 | ||
|
|
f2639a5829 | ||
|
|
69bebbda82 | ||
|
|
00b2c042ee | ||
|
|
d9eb8f3ab6 | ||
|
|
58025a01f8 | ||
|
|
99cad72ea8 | ||
|
|
6e96d620fe | ||
|
|
51ce402dbb | ||
|
|
8b404b5a4c | ||
|
|
3ce94d50dd | ||
|
|
29d56fca9c | ||
|
|
ab18010ee1 | ||
|
|
e69c202c79 | ||
|
|
0a812f2a46 | ||
|
|
fffe9fc566 | ||
|
|
6fdf27a701 | ||
|
|
7fa7d4f0a9 | ||
|
|
f511ebc1d4 | ||
|
|
84bbdc2eba | ||
|
|
568612fc70 | ||
|
|
d78828fd81 |
2
.github/setup_go_for_windows7.sh
vendored
2
.github/setup_go_for_windows7.sh
vendored
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="1.25.5"
|
VERSION="1.25.7"
|
||||||
|
|
||||||
mkdir -p $HOME/go
|
mkdir -p $HOME/go
|
||||||
cd $HOME/go
|
cd $HOME/go
|
||||||
|
|||||||
10
.github/workflows/build.yml
vendored
10
.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.5
|
go-version: ^1.25.7
|
||||||
- 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_win7 || matrix.legacy_go124) }}
|
if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- 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
|
||||||
@@ -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.5
|
go-version: ^1.25.7
|
||||||
- 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.5
|
go-version: ^1.25.7
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -479,7 +479,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
if: matrix.if
|
if: matrix.if
|
||||||
run: |-
|
run: |-
|
||||||
|
|||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
with:
|
with:
|
||||||
version: v2.4.0
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
install-mode: binary
|
install-mode: binary
|
||||||
verify: false
|
verify: false
|
||||||
|
|||||||
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.5
|
go-version: ^1.25.7
|
||||||
- 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.5
|
go-version: ^1.25.7
|
||||||
- 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
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -38,7 +38,7 @@ fmt:
|
|||||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||||
|
|
||||||
fmt_install:
|
fmt_install:
|
||||||
go install -v mvdan.cc/gofumpt@v0.8.0
|
go install -v mvdan.cc/gofumpt@latest
|
||||||
go install -v github.com/daixiang0/gci@latest
|
go install -v github.com/daixiang0/gci@latest
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@@ -49,7 +49,7 @@ lint:
|
|||||||
GOOS=freebsd golangci-lint run ./...
|
GOOS=freebsd golangci-lint run ./...
|
||||||
|
|
||||||
lint_install:
|
lint_install:
|
||||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
@go run ./cmd/internal/protogen
|
@go run ./cmd/internal/protogen
|
||||||
|
|||||||
Submodule clients/android updated: fe128a6cd7...5e1339b933
Submodule clients/apple updated: 532c140f05...97402ba8b6
@@ -303,8 +303,6 @@ find:
|
|||||||
metadata.Protocol = C.ProtocolQUIC
|
metadata.Protocol = C.ProtocolQUIC
|
||||||
fingerprint, err := ja3.Compute(buffer.Bytes())
|
fingerprint, err := ja3.Compute(buffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
metadata.Protocol = C.ProtocolQUIC
|
|
||||||
metadata.Client = C.ClientChromium
|
|
||||||
metadata.SniffContext = fragments
|
metadata.SniffContext = fragments
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return E.Cause1(ErrNeedMoreData, err)
|
||||||
}
|
}
|
||||||
@@ -334,7 +332,7 @@ find:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if count(frameTypeList, frameTypeCrypto) > 1 || count(frameTypeList, frameTypePing) > 0 {
|
if count(frameTypeList, frameTypeCrypto) > 1 || count(frameTypeList, frameTypePing) > 0 {
|
||||||
if maybeUQUIC(fingerprint) {
|
if isQUICGo(fingerprint) {
|
||||||
metadata.Client = C.ClientQUICGo
|
metadata.Client = C.ClientQUICGo
|
||||||
} else {
|
} else {
|
||||||
metadata.Client = C.ClientChromium
|
metadata.Client = C.ClientChromium
|
||||||
|
|||||||
@@ -1,21 +1,29 @@
|
|||||||
package sniff
|
package sniff
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/ja3"
|
"github.com/sagernet/sing-box/common/ja3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chromium sends separate client hello packets, but UQUIC has not yet implemented this behavior
|
const (
|
||||||
// The cronet without this behavior does not have version 115
|
// X25519Kyber768Draft00 - post-quantum curve used by Go crypto/tls
|
||||||
var uQUICChrome115 = &ja3.ClientHello{
|
x25519Kyber768Draft00 uint16 = 0x11EC // 4588
|
||||||
Version: tls.VersionTLS12,
|
// renegotiation_info extension used by Go crypto/tls
|
||||||
CipherSuites: []uint16{4865, 4866, 4867},
|
extensionRenegotiationInfo uint16 = 0xFF01 // 65281
|
||||||
Extensions: []uint16{0, 10, 13, 16, 27, 43, 45, 51, 57, 17513},
|
)
|
||||||
EllipticCurves: []uint16{29, 23, 24},
|
|
||||||
SignatureAlgorithms: []uint16{1027, 2052, 1025, 1283, 2053, 1281, 2054, 1537, 513},
|
|
||||||
}
|
|
||||||
|
|
||||||
func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
|
// isQUICGo detects native quic-go by checking for Go crypto/tls specific features.
|
||||||
return !uQUICChrome115.Equals(fingerprint, true)
|
// Note: uQUIC with Chromium mimicry cannot be reliably distinguished from real Chromium
|
||||||
|
// since it uses the same TLS fingerprint, so it will be identified as Chromium.
|
||||||
|
func isQUICGo(fingerprint *ja3.ClientHello) bool {
|
||||||
|
for _, curve := range fingerprint.EllipticCurves {
|
||||||
|
if curve == x25519Kyber768Draft00 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ext := range fingerprint.Extensions {
|
||||||
|
if ext == extensionRenegotiationInfo {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
188
common/sniff/quic_capture_test.go
Normal file
188
common/sniff/quic_capture_test.go
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
package sniff_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/quic-go"
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/sniff"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSniffQUICQuicGoFingerprint(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
const testSNI = "test.example.com"
|
||||||
|
|
||||||
|
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer udpConn.Close()
|
||||||
|
|
||||||
|
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
|
||||||
|
packetsChan := make(chan [][]byte, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var packets [][]byte
|
||||||
|
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
buf := make([]byte, 2048)
|
||||||
|
n, _, err := udpConn.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
packets = append(packets, buf[:n])
|
||||||
|
}
|
||||||
|
packetsChan <- packets
|
||||||
|
}()
|
||||||
|
|
||||||
|
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
ServerName: testSNI,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
NextProtos: []string{"h3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
|
||||||
|
|
||||||
|
select {
|
||||||
|
case packets := <-packetsChan:
|
||||||
|
t.Logf("Captured %d packets", len(packets))
|
||||||
|
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
for i, pkt := range packets {
|
||||||
|
err := sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
|
t.Logf("Packet %d: err=%v, domain=%s, client=%s", i, err, metadata.Domain, metadata.Client)
|
||||||
|
if metadata.Domain != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("\n=== quic-go TLS Fingerprint Analysis ===")
|
||||||
|
t.Logf("Domain: %s", metadata.Domain)
|
||||||
|
t.Logf("Client: %s", metadata.Client)
|
||||||
|
t.Logf("Protocol: %s", metadata.Protocol)
|
||||||
|
|
||||||
|
// The client should be identified as quic-go, not chromium
|
||||||
|
// Current issue: it's being identified as chromium
|
||||||
|
if metadata.Client == "chromium" {
|
||||||
|
t.Log("WARNING: quic-go is being misidentified as chromium!")
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("Timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSniffQUICInitialFromQuicGo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const testSNI = "test.example.com"
|
||||||
|
|
||||||
|
// Create UDP listener to capture ALL initial packets
|
||||||
|
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer udpConn.Close()
|
||||||
|
|
||||||
|
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
|
||||||
|
|
||||||
|
// Channel to receive captured packets
|
||||||
|
packetsChan := make(chan [][]byte, 1)
|
||||||
|
|
||||||
|
// Start goroutine to capture packets
|
||||||
|
go func() {
|
||||||
|
var packets [][]byte
|
||||||
|
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||||
|
for i := 0; i < 5; i++ { // Capture up to 5 packets
|
||||||
|
buf := make([]byte, 2048)
|
||||||
|
n, _, err := udpConn.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
packets = append(packets, buf[:n])
|
||||||
|
}
|
||||||
|
packetsChan <- packets
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create QUIC client connection (will fail but we capture the initial packet)
|
||||||
|
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
ServerName: testSNI,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
NextProtos: []string{"h3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// This will fail (no server) but sends initial packet
|
||||||
|
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
|
||||||
|
|
||||||
|
// Wait for captured packets
|
||||||
|
select {
|
||||||
|
case packets := <-packetsChan:
|
||||||
|
t.Logf("Captured %d QUIC packets", len(packets))
|
||||||
|
|
||||||
|
for i, packet := range packets {
|
||||||
|
t.Logf("Packet %d: length=%d, first 30 bytes: %x", i, len(packet), packet[:min(30, len(packet))])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test sniffer with first packet
|
||||||
|
if len(packets) > 0 {
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
err := sniff.QUICClientHello(context.Background(), &metadata, packets[0])
|
||||||
|
|
||||||
|
t.Logf("First packet sniff error: %v", err)
|
||||||
|
t.Logf("Protocol: %s", metadata.Protocol)
|
||||||
|
t.Logf("Domain: %s", metadata.Domain)
|
||||||
|
t.Logf("Client: %s", metadata.Client)
|
||||||
|
|
||||||
|
// If first packet needs more data, try with subsequent packets
|
||||||
|
// IMPORTANT: reuse metadata to accumulate CRYPTO fragments via SniffContext
|
||||||
|
if errors.Is(err, sniff.ErrNeedMoreData) && len(packets) > 1 {
|
||||||
|
t.Log("First packet needs more data, trying subsequent packets with shared context...")
|
||||||
|
for i := 1; i < len(packets); i++ {
|
||||||
|
// Reuse same metadata to accumulate fragments
|
||||||
|
err = sniff.QUICClientHello(context.Background(), &metadata, packets[i])
|
||||||
|
t.Logf("Packet %d sniff result: err=%v, domain=%s, sniffCtx=%v", i, err, metadata.Domain, metadata.SniffContext != nil)
|
||||||
|
if metadata.Domain != "" || (err != nil && !errors.Is(err, sniff.ErrNeedMoreData)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print hex dump for debugging
|
||||||
|
t.Logf("First packet hex:\n%s", hex.Dump(packets[0][:min(256, len(packets[0]))]))
|
||||||
|
|
||||||
|
// Log final results
|
||||||
|
t.Logf("Final: Protocol=%s, Domain=%s, Client=%s", metadata.Protocol, metadata.Domain, metadata.Client)
|
||||||
|
|
||||||
|
// Verify SNI extraction
|
||||||
|
if metadata.Domain == "" {
|
||||||
|
t.Errorf("Failed to extract SNI, expected: %s", testSNI)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, testSNI, metadata.Domain, "SNI should match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check client identification - quic-go should be identified as quic-go, not chromium
|
||||||
|
t.Logf("Client identified as: %s (expected: quic-go)", metadata.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("Timeout waiting for QUIC packets")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ func TestSniffQUICChromeNew(t *testing.T) {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
require.Empty(t, metadata.Client)
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
||||||
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
|
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -39,7 +39,7 @@ func TestSniffQUICChromium(t *testing.T) {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
require.Empty(t, metadata.Client)
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
||||||
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
|
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -144,7 +144,11 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
if c.cache != nil {
|
if c.cache != nil {
|
||||||
cond, loaded := c.cacheLock.LoadOrStore(question, make(chan struct{}))
|
cond, loaded := c.cacheLock.LoadOrStore(question, make(chan struct{}))
|
||||||
if loaded {
|
if loaded {
|
||||||
<-cond
|
select {
|
||||||
|
case <-cond:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defer func() {
|
defer func() {
|
||||||
c.cacheLock.Delete(question)
|
c.cacheLock.Delete(question)
|
||||||
@@ -154,7 +158,11 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
} else if c.transportCache != nil {
|
} else if c.transportCache != nil {
|
||||||
cond, loaded := c.transportCacheLock.LoadOrStore(question, make(chan struct{}))
|
cond, loaded := c.transportCacheLock.LoadOrStore(question, make(chan struct{}))
|
||||||
if loaded {
|
if loaded {
|
||||||
<-cond
|
select {
|
||||||
|
case <-cond:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defer func() {
|
defer func() {
|
||||||
c.transportCacheLock.Delete(question)
|
c.transportCacheLock.Delete(question)
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn ne
|
|||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
buffer.Reset()
|
||||||
_, _, err := buffer.ReadPacketFrom(packetConn)
|
_, _, err := buffer.ReadPacketFrom(packetConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.ErrShortBuffer) {
|
if errors.Is(err, io.ErrShortBuffer) {
|
||||||
|
|||||||
@@ -2,6 +2,51 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### 1.12.21
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.20
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.19
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.18
|
||||||
|
|
||||||
|
* Add fallback routing rule for `auto_redirect` **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
Adds a fallback iproute2 rule checked after system default rules (32766: main, 32767: default),
|
||||||
|
ensuring traffic is routed to the sing-box table when no route is found in system tables.
|
||||||
|
|
||||||
|
The rule index can be customized via `auto_redirect_iproute2_fallback_rule_index` (default: 32768).
|
||||||
|
|
||||||
|
#### 1.12.17
|
||||||
|
|
||||||
|
* Update uTLS to v1.8.2 **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
This update fixes missing padding extension for Chrome 120+ fingerprints.
|
||||||
|
|
||||||
|
Also, documentation has been updated with a warning about uTLS fingerprinting vulnerabilities.
|
||||||
|
uTLS is not recommended for censorship circumvention due to fundamental architectural limitations;
|
||||||
|
use NaiveProxy instead for TLS fingerprint resistance.
|
||||||
|
|
||||||
|
#### 1.12.16
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.15
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.12.14
|
#### 1.12.14
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
"method": "2022-blake3-aes-128-gcm",
|
"method": "2022-blake3-aes-128-gcm",
|
||||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
|
"managed": false,
|
||||||
"multiplex": {}
|
"multiplex": {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -86,6 +87,10 @@ Both if empty.
|
|||||||
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
|
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
|
||||||
| other methods | any string |
|
| other methods | any string |
|
||||||
|
|
||||||
|
#### managed
|
||||||
|
|
||||||
|
Defaults to `false`. Enable this when the inbound is managed by the [SSM API](/configuration/service/ssm-api) for dynamic user.
|
||||||
|
|
||||||
#### multiplex
|
#### multiplex
|
||||||
|
|
||||||
See [Multiplex](/configuration/shared/multiplex#inbound) for details.
|
See [Multiplex](/configuration/shared/multiplex#inbound) for details.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
"method": "2022-blake3-aes-128-gcm",
|
"method": "2022-blake3-aes-128-gcm",
|
||||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
|
"managed": false,
|
||||||
"multiplex": {}
|
"multiplex": {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -86,6 +87,10 @@ See [Listen Fields](/configuration/shared/listen/) for details.
|
|||||||
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
|
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
|
||||||
| other methods | 任意字符串 |
|
| other methods | 任意字符串 |
|
||||||
|
|
||||||
|
#### managed
|
||||||
|
|
||||||
|
默认为 `false`。当该入站需要由 [SSM API](/zh/configuration/service/ssm-api) 管理用户时必须启用此字段。
|
||||||
|
|
||||||
#### multiplex
|
#### multiplex
|
||||||
|
|
||||||
参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。
|
参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.12.18"
|
||||||
|
|
||||||
|
:material-plus: [auto_redirect_iproute2_fallback_rule_index](#auto_redirect_iproute2_fallback_rule_index)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
:material-plus: [loopback_address](#loopback_address)
|
:material-plus: [loopback_address](#loopback_address)
|
||||||
@@ -63,6 +67,7 @@ icon: material/new-box
|
|||||||
"auto_redirect": true,
|
"auto_redirect": true,
|
||||||
"auto_redirect_input_mark": "0x2023",
|
"auto_redirect_input_mark": "0x2023",
|
||||||
"auto_redirect_output_mark": "0x2024",
|
"auto_redirect_output_mark": "0x2024",
|
||||||
|
"auto_redirect_iproute2_fallback_rule_index": 32768,
|
||||||
"loopback_address": [
|
"loopback_address": [
|
||||||
"10.7.0.1"
|
"10.7.0.1"
|
||||||
],
|
],
|
||||||
@@ -278,6 +283,17 @@ Connection output mark used by `auto_redirect`.
|
|||||||
|
|
||||||
`0x2024` is used by default.
|
`0x2024` is used by default.
|
||||||
|
|
||||||
|
#### auto_redirect_iproute2_fallback_rule_index
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.12.18"
|
||||||
|
|
||||||
|
Linux iproute2 fallback rule index generated by `auto_redirect`.
|
||||||
|
|
||||||
|
This rule is checked after system default rules (32766: main, 32767: default),
|
||||||
|
routing traffic to the sing-box table only when no route is found in system tables.
|
||||||
|
|
||||||
|
`32768` is used by default.
|
||||||
|
|
||||||
#### loopback_address
|
#### loopback_address
|
||||||
|
|
||||||
!!! question "Since sing-box 1.12.0"
|
!!! question "Since sing-box 1.12.0"
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.12.18 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [auto_redirect_iproute2_fallback_rule_index](#auto_redirect_iproute2_fallback_rule_index)
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [loopback_address](#loopback_address)
|
:material-plus: [loopback_address](#loopback_address)
|
||||||
@@ -63,6 +67,7 @@ icon: material/new-box
|
|||||||
"auto_redirect": true,
|
"auto_redirect": true,
|
||||||
"auto_redirect_input_mark": "0x2023",
|
"auto_redirect_input_mark": "0x2023",
|
||||||
"auto_redirect_output_mark": "0x2024",
|
"auto_redirect_output_mark": "0x2024",
|
||||||
|
"auto_redirect_iproute2_fallback_rule_index": 32768,
|
||||||
"loopback_address": [
|
"loopback_address": [
|
||||||
"10.7.0.1"
|
"10.7.0.1"
|
||||||
],
|
],
|
||||||
@@ -277,6 +282,17 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
默认使用 `0x2024`。
|
默认使用 `0x2024`。
|
||||||
|
|
||||||
|
#### auto_redirect_iproute2_fallback_rule_index
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.18 起"
|
||||||
|
|
||||||
|
`auto_redirect` 生成的 iproute2 回退规则索引。
|
||||||
|
|
||||||
|
此规则在系统默认规则(32766: main,32767: default)之后检查,
|
||||||
|
仅当系统路由表中未找到路由时才将流量路由到 sing-box 路由表。
|
||||||
|
|
||||||
|
默认使用 `32768`。
|
||||||
|
|
||||||
#### loopback_address
|
#### loopback_address
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|||||||
@@ -230,9 +230,18 @@ The path to the server private key, in PEM format.
|
|||||||
|
|
||||||
==Client only==
|
==Client only==
|
||||||
|
|
||||||
!!! failure ""
|
!!! failure "Not Recommended"
|
||||||
|
|
||||||
There is no evidence that GFW detects and blocks servers based on TLS client fingerprinting, and using an imperfect emulation that has not been security reviewed could pose security risks.
|
uTLS has had repeated fingerprinting vulnerabilities discovered by researchers.
|
||||||
|
|
||||||
|
uTLS is a Go library that attempts to imitate browser TLS fingerprints by copying
|
||||||
|
ClientHello structure. However, browsers use completely different TLS stacks
|
||||||
|
(Chrome uses BoringSSL, Firefox uses NSS) with distinct implementation behaviors
|
||||||
|
that cannot be replicated by simply copying the handshake format, making detection possible.
|
||||||
|
Additionally, the library lacks active maintenance and has poor code quality,
|
||||||
|
making it unsuitable for censorship circumvention.
|
||||||
|
|
||||||
|
For TLS fingerprint resistance, use [NaiveProxy](/configuration/inbound/naive/) instead.
|
||||||
|
|
||||||
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance.
|
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance.
|
||||||
|
|
||||||
|
|||||||
@@ -220,9 +220,16 @@ TLS 版本值:
|
|||||||
|
|
||||||
==仅客户端==
|
==仅客户端==
|
||||||
|
|
||||||
!!! failure ""
|
!!! failure "不推荐"
|
||||||
|
|
||||||
没有证据表明 GFW 根据 TLS 客户端指纹检测并阻止服务器,并且,使用一个未经安全审查的不完美模拟可能带来安全隐患。
|
uTLS 已被研究人员多次发现其指纹可被识别的漏洞。
|
||||||
|
|
||||||
|
uTLS 是一个试图通过复制 ClientHello 结构来模仿浏览器 TLS 指纹的 Go 库。
|
||||||
|
然而,浏览器使用完全不同的 TLS 实现(Chrome 使用 BoringSSL,Firefox 使用 NSS),
|
||||||
|
其实现行为无法通过简单复制握手格式来复现,其行为细节必然存在差异,使得检测成为可能。
|
||||||
|
此外,此库缺乏积极维护,且代码质量较差,不建议用于反审查场景。
|
||||||
|
|
||||||
|
如需 TLS 指纹抵抗,请改用 [NaiveProxy](/configuration/inbound/naive/)。
|
||||||
|
|
||||||
uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻力。
|
uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻力。
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ icon: material/horse
|
|||||||
|
|
||||||
# Trojan
|
# Trojan
|
||||||
|
|
||||||
Torjan is the most commonly used TLS proxy made in China. It can be used in various combinations,
|
Trojan is the most commonly used TLS proxy made in China. It can be used in various combinations.
|
||||||
but only the combination of uTLS and multiplexing is recommended.
|
|
||||||
|
|
||||||
| Protocol and implementation combination | Specification | Resists passive detection | Resists active probes |
|
| Protocol and implementation combination | Specification | Resists passive detection | Resists active probes |
|
||||||
|-----------------------------------------|----------------------------------------------------------------------|---------------------------|-----------------------|
|
|-----------------------------------------|----------------------------------------------------------------------|---------------------------|-----------------------|
|
||||||
@@ -140,11 +139,7 @@ but only the combination of uTLS and multiplexing is recommended.
|
|||||||
"password": "password",
|
"password": "password",
|
||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"server_name": "example.org",
|
"server_name": "example.org"
|
||||||
"utls": {
|
|
||||||
"enabled": true,
|
|
||||||
"fingerprint": "firefox"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"multiplex": {
|
"multiplex": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
@@ -171,11 +166,7 @@ but only the combination of uTLS and multiplexing is recommended.
|
|||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"server_name": "example.org",
|
"server_name": "example.org",
|
||||||
"certificate_path": "/path/to/certificate.pem",
|
"certificate_path": "/path/to/certificate.pem"
|
||||||
"utls": {
|
|
||||||
"enabled": true,
|
|
||||||
"fingerprint": "firefox"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"multiplex": {
|
"multiplex": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
@@ -198,11 +189,7 @@ but only the combination of uTLS and multiplexing is recommended.
|
|||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"server_name": "example.org",
|
"server_name": "example.org",
|
||||||
"insecure": true,
|
"insecure": true
|
||||||
"utls": {
|
|
||||||
"enabled": true,
|
|
||||||
"fingerprint": "firefox"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"multiplex": {
|
"multiplex": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
|||||||
12
go.mod
12
go.mod
@@ -16,7 +16,7 @@ require (
|
|||||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6
|
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0
|
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0
|
||||||
github.com/metacubex/utls v1.8.3
|
github.com/metacubex/utls v1.8.4
|
||||||
github.com/mholt/acmez/v3 v3.1.2
|
github.com/mholt/acmez/v3 v3.1.2
|
||||||
github.com/miekg/dns v1.1.67
|
github.com/miekg/dns v1.1.67
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1
|
github.com/oschwald/maxminddb-golang v1.13.1
|
||||||
@@ -27,15 +27,15 @@ require (
|
|||||||
github.com/sagernet/gomobile v0.1.8
|
github.com/sagernet/gomobile v0.1.8
|
||||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
||||||
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3
|
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3
|
||||||
github.com/sagernet/sing v0.7.14
|
github.com/sagernet/sing v0.7.18
|
||||||
github.com/sagernet/sing-mux v0.3.3
|
github.com/sagernet/sing-mux v0.3.4
|
||||||
github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb
|
github.com/sagernet/sing-quic v0.5.3
|
||||||
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.7.3
|
github.com/sagernet/sing-tun v0.7.11
|
||||||
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.50-sing-box-mod.1
|
||||||
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2
|
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
|
|||||||
34
go.sum
34
go.sum
@@ -28,7 +28,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
|
|||||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
||||||
@@ -124,8 +123,8 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos
|
|||||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 h1:Ui+/2s5Qz0lSnDUBmEL12M5Oi/PzvFxGTNohm8ZcsmE=
|
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0 h1:Ui+/2s5Qz0lSnDUBmEL12M5Oi/PzvFxGTNohm8ZcsmE=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
github.com/metacubex/tfo-go v0.0.0-20250921095601-b102db4216c0/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||||
github.com/metacubex/utls v1.8.3 h1:0m/yCxm3SK6kWve2lKiFb1pue1wHitJ8sQQD4Ikqde4=
|
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
|
||||||
github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
||||||
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
||||||
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||||
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
|
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
|
||||||
@@ -166,25 +165,24 @@ github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNen
|
|||||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||||
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 h1:ySqffGm82rPqI1TUPqmtHIYd12pfEGScygnOxjTL56w=
|
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 h1:ySqffGm82rPqI1TUPqmtHIYd12pfEGScygnOxjTL56w=
|
||||||
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
|
github.com/sagernet/quic-go v0.52.0-sing-box-mod.3/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
|
||||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.7.18 h1:iZHkaru1/MoHugx3G+9S3WG4owMewKO/KvieE2Pzk4E=
|
||||||
github.com/sagernet/sing v0.7.14 h1:5QQRDCUvYNOMyVp3LuK/hYEBAIv0VsbD3x/l9zH467s=
|
github.com/sagernet/sing v0.7.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing v0.7.14/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
|
||||||
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
||||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-quic v0.5.3 h1:K937DKJN98xqyztijRkLJqbBfyV4rEZcYxFyP3EBikU=
|
||||||
github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb h1:5Wx3XeTiKrrrcrAky7Hc1bO3CGxrvho2Vu5b/adlEIM=
|
github.com/sagernet/sing-quic v0.5.3/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI=
|
||||||
github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI=
|
|
||||||
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.7.3 h1:MFnAir+l24ElEyxdfwtY8mqvUUL9nPnL9TDYLkOmVes=
|
github.com/sagernet/sing-tun v0.7.11 h1:qB7jy8JKqXg73fYBsDkBSy4ulRSbLrFut0e+y+QPhqU=
|
||||||
github.com/sagernet/sing-tun v0.7.3/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
|
github.com/sagernet/sing-tun v0.7.11/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
|
||||||
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.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
|
||||||
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2 h1:MO7s4ni2bSfAOhcan2rdQSWCztkMXmqyg6jYPZp8bEE=
|
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2 h1:MO7s4ni2bSfAOhcan2rdQSWCztkMXmqyg6jYPZp8bEE=
|
||||||
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
|
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
|
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
|
||||||
@@ -196,14 +194,7 @@ github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wx
|
|||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
||||||
@@ -284,7 +275,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,37 +61,40 @@ func checkOptions(options *Options) error {
|
|||||||
|
|
||||||
func checkInbounds(inbounds []Inbound) error {
|
func checkInbounds(inbounds []Inbound) error {
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
for _, inbound := range inbounds {
|
for i, inbound := range inbounds {
|
||||||
if inbound.Tag == "" {
|
tag := inbound.Tag
|
||||||
continue
|
if tag == "" {
|
||||||
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
if seen[inbound.Tag] {
|
if seen[tag] {
|
||||||
return E.New("duplicate inbound tag: ", inbound.Tag)
|
return E.New("duplicate inbound tag: ", tag)
|
||||||
}
|
}
|
||||||
seen[inbound.Tag] = true
|
seen[tag] = true
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkOutbounds(outbounds []Outbound, endpoints []Endpoint) error {
|
func checkOutbounds(outbounds []Outbound, endpoints []Endpoint) error {
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
for _, outbound := range outbounds {
|
for i, outbound := range outbounds {
|
||||||
if outbound.Tag == "" {
|
tag := outbound.Tag
|
||||||
continue
|
if tag == "" {
|
||||||
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
if seen[outbound.Tag] {
|
if seen[tag] {
|
||||||
return E.New("duplicate outbound/endpoint tag: ", outbound.Tag)
|
return E.New("duplicate outbound/endpoint tag: ", tag)
|
||||||
}
|
}
|
||||||
seen[outbound.Tag] = true
|
seen[tag] = true
|
||||||
}
|
}
|
||||||
for _, endpoint := range endpoints {
|
for i, endpoint := range endpoints {
|
||||||
if endpoint.Tag == "" {
|
tag := endpoint.Tag
|
||||||
continue
|
if tag == "" {
|
||||||
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
if seen[endpoint.Tag] {
|
if seen[tag] {
|
||||||
return E.New("duplicate outbound/endpoint tag: ", endpoint.Tag)
|
return E.New("duplicate outbound/endpoint tag: ", tag)
|
||||||
}
|
}
|
||||||
seen[endpoint.Tag] = true
|
seen[tag] = true
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,33 +11,34 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TunInboundOptions struct {
|
type TunInboundOptions struct {
|
||||||
InterfaceName string `json:"interface_name,omitempty"`
|
InterfaceName string `json:"interface_name,omitempty"`
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
|
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
|
||||||
AutoRoute bool `json:"auto_route,omitempty"`
|
AutoRoute bool `json:"auto_route,omitempty"`
|
||||||
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
||||||
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
|
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
|
||||||
AutoRedirect bool `json:"auto_redirect,omitempty"`
|
AutoRedirect bool `json:"auto_redirect,omitempty"`
|
||||||
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
|
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
|
||||||
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
|
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
|
||||||
LoopbackAddress badoption.Listable[netip.Addr] `json:"loopback_address,omitempty"`
|
AutoRedirectIPRoute2FallbackRuleIndex int `json:"auto_redirect_iproute2_fallback_rule_index,omitempty"`
|
||||||
StrictRoute bool `json:"strict_route,omitempty"`
|
LoopbackAddress badoption.Listable[netip.Addr] `json:"loopback_address,omitempty"`
|
||||||
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
|
StrictRoute bool `json:"strict_route,omitempty"`
|
||||||
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`
|
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
|
||||||
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
|
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`
|
||||||
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"`
|
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
|
||||||
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"`
|
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"`
|
||||||
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"`
|
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"`
|
||||||
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"`
|
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"`
|
||||||
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"`
|
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"`
|
||||||
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"`
|
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"`
|
||||||
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"`
|
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"`
|
||||||
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
|
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"`
|
||||||
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
|
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
|
||||||
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
|
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
|
||||||
Stack string `json:"stack,omitempty"`
|
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||||
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
Stack string `json:"stack,omitempty"`
|
||||||
|
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||||
InboundOptions
|
InboundOptions
|
||||||
|
|
||||||
// Deprecated: removed
|
// Deprecated: removed
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ func (p *paddingConn) writeWithPadding(writer io.Writer, data []byte) (n int, er
|
|||||||
binary.BigEndian.PutUint16(header, uint16(len(data)))
|
binary.BigEndian.PutUint16(header, uint16(len(data)))
|
||||||
header[2] = byte(paddingSize)
|
header[2] = byte(paddingSize)
|
||||||
common.Must1(buffer.Write(data))
|
common.Must1(buffer.Write(data))
|
||||||
|
buffer.Extend(paddingSize)
|
||||||
_, err = writer.Write(buffer.Bytes())
|
_, err = writer.Write(buffer.Bytes())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
n = len(data)
|
n = len(data)
|
||||||
|
|||||||
@@ -341,26 +341,42 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
|
|||||||
}
|
}
|
||||||
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
|
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
|
||||||
}
|
}
|
||||||
addr := tcpip.FullAddress{
|
addr4, addr6 := t.server.TailscaleIPs()
|
||||||
|
remoteAddr := tcpip.FullAddress{
|
||||||
NIC: 1,
|
NIC: 1,
|
||||||
Port: destination.Port,
|
Port: destination.Port,
|
||||||
Addr: addressFromAddr(destination.Addr),
|
Addr: addressFromAddr(destination.Addr),
|
||||||
}
|
}
|
||||||
|
var localAddr tcpip.FullAddress
|
||||||
var networkProtocol tcpip.NetworkProtocolNumber
|
var networkProtocol tcpip.NetworkProtocolNumber
|
||||||
if destination.IsIPv4() {
|
if destination.IsIPv4() {
|
||||||
|
if !addr4.IsValid() {
|
||||||
|
return nil, E.New("missing Tailscale IPv4 address")
|
||||||
|
}
|
||||||
networkProtocol = header.IPv4ProtocolNumber
|
networkProtocol = header.IPv4ProtocolNumber
|
||||||
|
localAddr = tcpip.FullAddress{
|
||||||
|
NIC: 1,
|
||||||
|
Addr: addressFromAddr(addr4),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if !addr6.IsValid() {
|
||||||
|
return nil, E.New("missing Tailscale IPv6 address")
|
||||||
|
}
|
||||||
networkProtocol = header.IPv6ProtocolNumber
|
networkProtocol = header.IPv6ProtocolNumber
|
||||||
|
localAddr = tcpip.FullAddress{
|
||||||
|
NIC: 1,
|
||||||
|
Addr: addressFromAddr(addr6),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
tcpConn, err := gonet.DialContextTCP(ctx, t.stack, addr, networkProtocol)
|
tcpConn, err := gonet.DialTCPWithBind(ctx, t.stack, localAddr, remoteAddr, networkProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return tcpConn, nil
|
return tcpConn, nil
|
||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
udpConn, err := gonet.DialUDP(t.stack, nil, &addr, networkProtocol)
|
udpConn, err := gonet.DialUDP(t.stack, &localAddr, &remoteAddr, networkProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if ruleIndex == 0 {
|
if ruleIndex == 0 {
|
||||||
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
||||||
}
|
}
|
||||||
|
autoRedirectFallbackRuleIndex := options.AutoRedirectIPRoute2FallbackRuleIndex
|
||||||
|
if autoRedirectFallbackRuleIndex == 0 {
|
||||||
|
autoRedirectFallbackRuleIndex = tun.DefaultIPRoute2AutoRedirectFallbackRuleIndex
|
||||||
|
}
|
||||||
inputMark := uint32(options.AutoRedirectInputMark)
|
inputMark := uint32(options.AutoRedirectInputMark)
|
||||||
if inputMark == 0 {
|
if inputMark == 0 {
|
||||||
inputMark = tun.DefaultAutoRedirectInputMark
|
inputMark = tun.DefaultAutoRedirectInputMark
|
||||||
@@ -192,32 +196,33 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
inboundOptions: options.InboundOptions,
|
inboundOptions: options.InboundOptions,
|
||||||
tunOptions: tun.Options{
|
tunOptions: tun.Options{
|
||||||
Name: options.InterfaceName,
|
Name: options.InterfaceName,
|
||||||
MTU: tunMTU,
|
MTU: tunMTU,
|
||||||
GSO: enableGSO,
|
GSO: enableGSO,
|
||||||
Inet4Address: inet4Address,
|
Inet4Address: inet4Address,
|
||||||
Inet6Address: inet6Address,
|
Inet6Address: inet6Address,
|
||||||
AutoRoute: options.AutoRoute,
|
AutoRoute: options.AutoRoute,
|
||||||
IPRoute2TableIndex: tableIndex,
|
IPRoute2TableIndex: tableIndex,
|
||||||
IPRoute2RuleIndex: ruleIndex,
|
IPRoute2RuleIndex: ruleIndex,
|
||||||
AutoRedirectInputMark: inputMark,
|
IPRoute2AutoRedirectFallbackRuleIndex: autoRedirectFallbackRuleIndex,
|
||||||
AutoRedirectOutputMark: outputMark,
|
AutoRedirectInputMark: inputMark,
|
||||||
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
|
AutoRedirectOutputMark: outputMark,
|
||||||
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
|
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
|
||||||
StrictRoute: options.StrictRoute,
|
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
|
||||||
IncludeInterface: options.IncludeInterface,
|
StrictRoute: options.StrictRoute,
|
||||||
ExcludeInterface: options.ExcludeInterface,
|
IncludeInterface: options.IncludeInterface,
|
||||||
Inet4RouteAddress: inet4RouteAddress,
|
ExcludeInterface: options.ExcludeInterface,
|
||||||
Inet6RouteAddress: inet6RouteAddress,
|
Inet4RouteAddress: inet4RouteAddress,
|
||||||
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
Inet6RouteAddress: inet6RouteAddress,
|
||||||
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
||||||
IncludeUID: includeUID,
|
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
||||||
ExcludeUID: excludeUID,
|
IncludeUID: includeUID,
|
||||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
ExcludeUID: excludeUID,
|
||||||
IncludePackage: options.IncludePackage,
|
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||||
ExcludePackage: options.ExcludePackage,
|
IncludePackage: options.IncludePackage,
|
||||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
ExcludePackage: options.ExcludePackage,
|
||||||
EXP_MultiPendingPackets: multiPendingPackets,
|
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||||
|
EXP_MultiPendingPackets: multiPendingPackets,
|
||||||
},
|
},
|
||||||
udpTimeout: udpTimeout,
|
udpTimeout: udpTimeout,
|
||||||
stack: options.Stack,
|
stack: options.Stack,
|
||||||
@@ -319,7 +324,6 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
|
|||||||
t.tunOptions.Name = tun.CalculateInterfaceName("")
|
t.tunOptions.Name = tun.CalculateInterfaceName("")
|
||||||
}
|
}
|
||||||
if t.platformInterface == nil {
|
if t.platformInterface == nil {
|
||||||
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
|
|
||||||
for _, routeRuleSet := range t.routeRuleSet {
|
for _, routeRuleSet := range t.routeRuleSet {
|
||||||
ipSets := routeRuleSet.ExtractIPSet()
|
ipSets := routeRuleSet.ExtractIPSet()
|
||||||
if len(ipSets) == 0 {
|
if len(ipSets) == 0 {
|
||||||
@@ -331,7 +335,6 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
|
|||||||
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
|
|
||||||
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
|
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
|
||||||
ipSets := routeExcludeRuleSet.ExtractIPSet()
|
ipSets := routeExcludeRuleSet.ExtractIPSet()
|
||||||
if len(ipSets) == 0 {
|
if len(ipSets) == 0 {
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ type StreamWrapper struct {
|
|||||||
|
|
||||||
func (s *StreamWrapper) Read(p []byte) (n int, err error) {
|
func (s *StreamWrapper) Read(p []byte) (n int, err error) {
|
||||||
n, err = s.Stream.Read(p)
|
n, err = s.Stream.Read(p)
|
||||||
|
//nolint:staticcheck
|
||||||
return n, baderror.WrapQUIC(err)
|
return n, baderror.WrapQUIC(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StreamWrapper) Write(p []byte) (n int, err error) {
|
func (s *StreamWrapper) Write(p []byte) (n int, err error) {
|
||||||
n, err = s.Stream.Write(p)
|
n, err = s.Stream.Write(p)
|
||||||
|
//nolint:staticcheck
|
||||||
return n, baderror.WrapQUIC(err)
|
return n, baderror.WrapQUIC(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user