mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-08-16 07:17:36 +08:00
Compare commits
113 Commits
dev-next
...
v1.12.0-be
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f74074ff5 | ||
![]() |
e3735b6177 | ||
![]() |
4789846113 | ||
![]() |
ad94f94cfb | ||
![]() |
1f9de9f321 | ||
![]() |
344bbf9494 | ||
![]() |
b7e16e70ab | ||
![]() |
391153ecb8 | ||
![]() |
3d821db0b2 | ||
![]() |
344ecd3798 | ||
![]() |
a8a3f863cd | ||
![]() |
ea190ca428 | ||
![]() |
e65b78d1e4 | ||
![]() |
84f7d80da7 | ||
![]() |
2b57eb0b30 | ||
![]() |
9884f81298 | ||
![]() |
8493b4f1da | ||
![]() |
8f1a8add85 | ||
![]() |
85ee6bb266 | ||
![]() |
c4387f7c37 | ||
![]() |
dcb7a5caed | ||
![]() |
ae77cdeedd | ||
![]() |
ec05d4e5e3 | ||
![]() |
1f766d2b89 | ||
![]() |
a5a6c1f7d4 | ||
![]() |
cd43786279 | ||
![]() |
43a211db28 | ||
![]() |
d4f6bdf792 | ||
![]() |
99cf27bedd | ||
![]() |
0a14c5ab1f | ||
![]() |
533b31e1f6 | ||
![]() |
8ed6523872 | ||
![]() |
e5c162222d | ||
![]() |
88babbf3a7 | ||
![]() |
49a03e0b23 | ||
![]() |
9a8e9a34c0 | ||
![]() |
17551db7be | ||
![]() |
83201bb088 | ||
![]() |
f0f1942f1f | ||
![]() |
c7e318be61 | ||
![]() |
2a5e0d0c92 | ||
![]() |
956e485342 | ||
![]() |
23ede74e74 | ||
![]() |
03317e61dd | ||
![]() |
fc81bd9a5b | ||
![]() |
4d406cad84 | ||
![]() |
1f0282de9c | ||
![]() |
b097912418 | ||
![]() |
056c29e73a | ||
![]() |
7aa0a57e60 | ||
![]() |
2673e64bcb | ||
![]() |
3d3c1709d7 | ||
![]() |
9de29a590f | ||
![]() |
a5282b08ec | ||
![]() |
b26c2083bf | ||
![]() |
c1f4c691dc | ||
![]() |
c0ef6eb728 | ||
![]() |
074d61021f | ||
![]() |
246c9d4e40 | ||
![]() |
3e3466c8d7 | ||
![]() |
f73415a732 | ||
![]() |
ecabe9ffe1 | ||
![]() |
2dae1ee284 | ||
![]() |
2afb24d698 | ||
![]() |
ade83ee758 | ||
![]() |
ac9c300ca7 | ||
![]() |
e6eb3cec2b | ||
![]() |
601b79371b | ||
![]() |
56b957d30d | ||
![]() |
eb87b1a708 | ||
![]() |
c0a5561bd4 | ||
![]() |
4a3fe1d41c | ||
![]() |
4cbbcfb04d | ||
![]() |
b3ad4e0e39 | ||
![]() |
49f9c0011d | ||
![]() |
c8c165af87 | ||
![]() |
3f790ff8c9 | ||
![]() |
f2272ae1e7 | ||
![]() |
b40c264c0a | ||
![]() |
29fc8d6a86 | ||
![]() |
46ca27c926 | ||
![]() |
243a5dd477 | ||
![]() |
844e9f09a6 | ||
![]() |
9aa673f79e | ||
![]() |
5abd74ffe3 | ||
![]() |
68a32960bd | ||
![]() |
1e62e3e5d4 | ||
![]() |
40c03a9913 | ||
![]() |
2ea4029868 | ||
![]() |
66f5cdd014 | ||
![]() |
89da2b6355 | ||
![]() |
ee2b8498e6 | ||
![]() |
b1eaf537bd | ||
![]() |
63739c1621 | ||
![]() |
3c8ddee029 | ||
![]() |
23aad70045 | ||
![]() |
0d67b51267 | ||
![]() |
c02f939265 | ||
![]() |
36b84e25c2 | ||
![]() |
9fd4b0e9ae | ||
![]() |
e68cdcc98a | ||
![]() |
2691617c5e | ||
![]() |
04056a1357 | ||
![]() |
faac858e5d | ||
![]() |
a818c8abeb | ||
![]() |
15cbe9fc87 | ||
![]() |
e186f2d31e | ||
![]() |
e9323481a4 | ||
![]() |
a0a41ff2bb | ||
![]() |
549daf9d41 | ||
![]() |
fa370f7d04 | ||
![]() |
795cb17bfa | ||
![]() |
00d8add761 |
2
.github/setup_legacy_go.sh
vendored
2
.github/setup_legacy_go.sh
vendored
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION="1.23.12"
|
||||
VERSION="1.23.6"
|
||||
|
||||
mkdir -p $HOME/go
|
||||
cd $HOME/go
|
||||
|
56
.github/workflows/build.yml
vendored
56
.github/workflows/build.yml
vendored
@ -40,13 +40,13 @@ jobs:
|
||||
version: ${{ steps.outputs.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.0
|
||||
go-version: ^1.24
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@ -88,14 +88,13 @@ jobs:
|
||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||
|
||||
- { os: windows, arch: amd64 }
|
||||
- { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: amd64, legacy_go: true }
|
||||
- { os: windows, arch: "386" }
|
||||
- { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: "386", legacy_go: true }
|
||||
- { os: windows, arch: arm64 }
|
||||
|
||||
- { os: darwin, arch: amd64 }
|
||||
- { os: darwin, arch: arm64 }
|
||||
- { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||
|
||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
||||
@ -103,33 +102,28 @@ jobs:
|
||||
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||
if: ${{ ! matrix.legacy_go }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.0
|
||||
- name: Setup Go 1.24
|
||||
if: matrix.legacy_go124
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.24.6
|
||||
- name: Cache Go 1.23
|
||||
if: matrix.legacy_go123
|
||||
go-version: ^1.24
|
||||
- name: Cache Legacy Go
|
||||
if: matrix.require_legacy_go
|
||||
id: cache-legacy-go
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/go/go_legacy
|
||||
key: go_legacy_12312
|
||||
- name: Setup Go 1.23
|
||||
if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||
key: go_legacy_1236
|
||||
- name: Setup Legacy Go
|
||||
if: matrix.legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||
run: |-
|
||||
.github/setup_legacy_go.sh
|
||||
- name: Setup Go 1.23
|
||||
if: matrix.legacy_go123
|
||||
- name: Setup Legacy Go 2
|
||||
if: matrix.legacy_go
|
||||
run: |-
|
||||
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
|
||||
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
|
||||
@ -190,8 +184,8 @@ jobs:
|
||||
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
|
||||
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
|
||||
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
|
||||
elif [[ -n "${{ matrix.legacy_name }}" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
||||
elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy"
|
||||
fi
|
||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
||||
@ -283,7 +277,7 @@ jobs:
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_go && '-legacy' || '' }}
|
||||
path: "dist"
|
||||
build_android:
|
||||
name: Build Android
|
||||
@ -293,14 +287,14 @@ jobs:
|
||||
- calculate_version
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.0
|
||||
go-version: ^1.24
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@ -373,14 +367,14 @@ jobs:
|
||||
- calculate_version
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.0
|
||||
go-version: ^1.24
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@ -470,7 +464,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
if: matrix.if
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
@ -478,7 +472,7 @@ jobs:
|
||||
if: matrix.if
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.0
|
||||
go-version: ^1.24
|
||||
- name: Setup Xcode stable
|
||||
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||
run: |-
|
||||
@ -630,7 +624,7 @@ jobs:
|
||||
- build_apple
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Cache ghr
|
||||
@ -653,7 +647,7 @@ jobs:
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||
- name: Download builds
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
echo "ref=$ref"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
ref: ${{ steps.ref.outputs.ref }}
|
||||
fetch-depth: 0
|
||||
@ -107,7 +107,7 @@ jobs:
|
||||
echo "latest=$latest"
|
||||
echo "latest=$latest" >> $GITHUB_OUTPUT
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
|
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@ -22,15 +22,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.24.6
|
||||
go-version: ^1.24
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=30m
|
||||
|
21
.github/workflows/linux.yml
vendored
21
.github/workflows/linux.yml
vendored
@ -7,11 +7,6 @@ on:
|
||||
description: "Version name"
|
||||
required: true
|
||||
type: string
|
||||
forceBeta:
|
||||
description: "Force beta"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
@ -24,13 +19,13 @@ jobs:
|
||||
version: ${{ steps.outputs.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.0
|
||||
go-version: ^1.24
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@ -65,13 +60,13 @@ jobs:
|
||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.0
|
||||
go-version: ^1.24
|
||||
- name: Setup Android NDK
|
||||
if: matrix.os == 'android'
|
||||
uses: nttld/setup-ndk@v1
|
||||
@ -104,11 +99,11 @@ jobs:
|
||||
run: |-
|
||||
TZ=UTC touch -t '197001010000' dist/sing-box
|
||||
- name: Set name
|
||||
if: (! contains(needs.calculate_version.outputs.version, '-')) && !inputs.forceBeta
|
||||
if: ${{ ! contains(needs.calculate_version.outputs.version, '-') }}
|
||||
run: |-
|
||||
echo "NAME=sing-box" >> "$GITHUB_ENV"
|
||||
- name: Set beta name
|
||||
if: contains(needs.calculate_version.outputs.version, '-') || inputs.forceBeta
|
||||
if: contains(needs.calculate_version.outputs.version, '-')
|
||||
run: |-
|
||||
echo "NAME=sing-box-beta" >> "$GITHUB_ENV"
|
||||
- name: Set version
|
||||
@ -171,7 +166,7 @@ jobs:
|
||||
- build
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set tag
|
||||
@ -180,7 +175,7 @@ jobs:
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||
- name: Download builds
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
@ -1,6 +1,27 @@
|
||||
version: "2"
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- gofumpt
|
||||
- govet
|
||||
- gci
|
||||
- staticcheck
|
||||
- paralleltest
|
||||
- ineffassign
|
||||
|
||||
linters-settings:
|
||||
gci:
|
||||
custom-order: true
|
||||
sections:
|
||||
- standard
|
||||
- prefix(github.com/sagernet/)
|
||||
- default
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- -SA1003
|
||||
|
||||
run:
|
||||
go: "1.24"
|
||||
go: "1.23"
|
||||
build-tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
@ -9,51 +30,7 @@ run:
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- govet
|
||||
- ineffassign
|
||||
- paralleltest
|
||||
- staticcheck
|
||||
settings:
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- -S1000
|
||||
- -S1008
|
||||
- -S1017
|
||||
- -ST1003
|
||||
- -QF1001
|
||||
- -QF1003
|
||||
- -QF1008
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- transport/simple-obfs
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofumpt
|
||||
settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- prefix(github.com/sagernet/)
|
||||
- default
|
||||
custom-order: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- transport/simple-obfs
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- transport/simple-obfs
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
COPY . /go/src/github.com/sagernet/sing-box
|
||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||
|
6
Makefile
6
Makefile
@ -45,7 +45,7 @@ lint:
|
||||
GOOS=freebsd golangci-lint run ./...
|
||||
|
||||
lint_install:
|
||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||
go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
proto:
|
||||
@go run ./cmd/internal/protogen
|
||||
@ -245,8 +245,8 @@ lib:
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib_install:
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.8
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.6
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.6
|
||||
|
||||
docs:
|
||||
venv/bin/mkdocs serve
|
||||
|
@ -135,7 +135,8 @@ func ExtendContext(ctx context.Context) (context.Context, *InboundContext) {
|
||||
|
||||
func OverrideContext(ctx context.Context) context.Context {
|
||||
if metadata := ContextFrom(ctx); metadata != nil {
|
||||
newMetadata := *metadata
|
||||
var newMetadata InboundContext
|
||||
newMetadata = *metadata
|
||||
return WithContext(ctx, &newMetadata)
|
||||
}
|
||||
return ctx
|
||||
|
@ -2,7 +2,6 @@ package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@ -19,11 +18,6 @@ type Outbound interface {
|
||||
N.Dialer
|
||||
}
|
||||
|
||||
type OutboundWithPreferredRoutes interface {
|
||||
PreferredDomain(domain string) bool
|
||||
PreferredAddress(address netip.Addr) bool
|
||||
}
|
||||
|
||||
type OutboundRegistry interface {
|
||||
option.OutboundOptionsRegistry
|
||||
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
||||
|
@ -30,7 +30,7 @@ type Manager struct {
|
||||
outboundByTag map[string]adapter.Outbound
|
||||
dependByTag map[string][]string
|
||||
defaultOutbound adapter.Outbound
|
||||
defaultOutboundFallback func() (adapter.Outbound, error)
|
||||
defaultOutboundFallback adapter.Outbound
|
||||
}
|
||||
|
||||
func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry, endpoint adapter.EndpointManager, defaultTag string) *Manager {
|
||||
@ -44,7 +44,7 @@ func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Initialize(defaultOutboundFallback func() (adapter.Outbound, error)) {
|
||||
func (m *Manager) Initialize(defaultOutboundFallback adapter.Outbound) {
|
||||
m.defaultOutboundFallback = defaultOutboundFallback
|
||||
}
|
||||
|
||||
@ -55,31 +55,18 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
m.started = true
|
||||
m.stage = stage
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
if stage == adapter.StartStateStart {
|
||||
if m.defaultOutbound == nil {
|
||||
directOutbound, err := m.defaultOutboundFallback()
|
||||
if err != nil {
|
||||
m.access.Unlock()
|
||||
return E.Cause(err, "create direct outbound for fallback")
|
||||
}
|
||||
m.outbounds = append(m.outbounds, directOutbound)
|
||||
m.outboundByTag[directOutbound.Tag()] = directOutbound
|
||||
m.defaultOutbound = directOutbound
|
||||
}
|
||||
if m.defaultTag != "" && m.defaultOutbound == nil {
|
||||
defaultEndpoint, loaded := m.endpoint.Get(m.defaultTag)
|
||||
if !loaded {
|
||||
m.access.Unlock()
|
||||
return E.New("default outbound not found: ", m.defaultTag)
|
||||
}
|
||||
m.defaultOutbound = defaultEndpoint
|
||||
}
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
return m.startOutbounds(append(outbounds, common.Map(m.endpoint.Endpoints(), func(it adapter.Endpoint) adapter.Outbound { return it })...))
|
||||
} else {
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
for _, outbound := range outbounds {
|
||||
err := adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
@ -200,7 +187,11 @@ func (m *Manager) Outbound(tag string) (adapter.Outbound, bool) {
|
||||
func (m *Manager) Default() adapter.Outbound {
|
||||
m.access.RLock()
|
||||
defer m.access.RUnlock()
|
||||
return m.defaultOutbound
|
||||
if m.defaultOutbound != nil {
|
||||
return m.defaultOutbound
|
||||
} else {
|
||||
return m.defaultOutboundFallback
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Remove(tag string) error {
|
||||
|
8
box.go
8
box.go
@ -314,15 +314,15 @@ func New(options Options) (*Box, error) {
|
||||
return nil, E.Cause(err, "initialize service[", i, "]")
|
||||
}
|
||||
}
|
||||
outboundManager.Initialize(func() (adapter.Outbound, error) {
|
||||
return direct.NewOutbound(
|
||||
outboundManager.Initialize(common.Must1(
|
||||
direct.NewOutbound(
|
||||
ctx,
|
||||
router,
|
||||
logFactory.NewLogger("outbound/direct"),
|
||||
"direct",
|
||||
option.DirectOutboundOptions{},
|
||||
)
|
||||
})
|
||||
),
|
||||
))
|
||||
dnsTransportManager.Initialize(common.Must1(
|
||||
local.NewTransport(
|
||||
ctx,
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6db8e06e8d6c77648e79e3f93bdd41a4d48cc319
|
||||
Subproject commit eb2e13a6f9a8c03a35ae672395ccab0a6bdcd954
|
@ -1 +1 @@
|
||||
Subproject commit c5734677bdfcba5d2d4faf10c4f10475077a82ab
|
||||
Subproject commit ae5818ee5a24af965dc91f80bffa16e1e6c109c1
|
@ -177,7 +177,7 @@ func publishTestflight(ctx context.Context) error {
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " publish")
|
||||
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
|
||||
if response != nil && (response.StatusCode == http.StatusUnprocessableEntity || response.StatusCode == http.StatusNotFound) {
|
||||
if response != nil && response.StatusCode == http.StatusUnprocessableEntity {
|
||||
log.Info("waiting for process")
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
|
@ -46,9 +46,8 @@ var (
|
||||
sharedFlags []string
|
||||
debugFlags []string
|
||||
sharedTags []string
|
||||
darwinTags []string
|
||||
iosTags []string
|
||||
memcTags []string
|
||||
notMemcTags []string
|
||||
debugTags []string
|
||||
)
|
||||
|
||||
@ -63,9 +62,8 @@ func init() {
|
||||
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")
|
||||
darwinTags = append(darwinTags, "with_dhcp")
|
||||
iosTags = append(iosTags, "with_dhcp", "with_low_memory")
|
||||
memcTags = append(memcTags, "with_tailscale")
|
||||
notMemcTags = append(notMemcTags, "with_low_memory")
|
||||
debugTags = append(debugTags, "debug")
|
||||
}
|
||||
|
||||
@ -107,10 +105,8 @@ func buildAndroid() {
|
||||
}
|
||||
|
||||
if !debugEnabled {
|
||||
sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
|
||||
args = append(args, sharedFlags...)
|
||||
} else {
|
||||
debugFlags[1] = debugFlags[1] + " -checklinkname=0"
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
@ -157,7 +153,6 @@ func buildApple() {
|
||||
"-v",
|
||||
"-target", bindTarget,
|
||||
"-libname=box",
|
||||
"-tags-not-macos=with_low_memory",
|
||||
}
|
||||
if !withTailscale {
|
||||
args = append(args, "-tags-macos="+strings.Join(memcTags, ","))
|
||||
@ -169,7 +164,7 @@ func buildApple() {
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
tags := append(sharedTags, darwinTags...)
|
||||
tags := append(sharedTags, iosTags...)
|
||||
if withTailscale {
|
||||
tags = append(tags, memcTags...)
|
||||
}
|
||||
|
@ -1,284 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
)
|
||||
|
||||
var iperf3Path string
|
||||
|
||||
func main() {
|
||||
err := main0()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main0() error {
|
||||
err := shell.Exec("sudo", "ls").Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results, err := runTests()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoder := json.NewEncoder(os.Stdout)
|
||||
encoder.SetIndent("", " ")
|
||||
return encoder.Encode(results)
|
||||
}
|
||||
|
||||
func runTests() ([]TestResult, error) {
|
||||
boxPaths := []string{
|
||||
os.ExpandEnv("$HOME/Downloads/sing-box-1.11.15-darwin-arm64/sing-box"),
|
||||
//"/Users/sekai/Downloads/sing-box-1.11.15-linux-arm64/sing-box",
|
||||
"./sing-box",
|
||||
}
|
||||
stacks := []string{
|
||||
"gvisor",
|
||||
"system",
|
||||
}
|
||||
mtus := []int{
|
||||
1500,
|
||||
4064,
|
||||
// 16384,
|
||||
// 32768,
|
||||
// 49152,
|
||||
65535,
|
||||
}
|
||||
flagList := [][]string{
|
||||
{},
|
||||
}
|
||||
var results []TestResult
|
||||
for _, boxPath := range boxPaths {
|
||||
for _, stack := range stacks {
|
||||
for _, mtu := range mtus {
|
||||
if strings.HasPrefix(boxPath, ".") {
|
||||
for _, flags := range flagList {
|
||||
result, err := testOnce(boxPath, stack, mtu, false, flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, *result)
|
||||
}
|
||||
} else {
|
||||
result, err := testOnce(boxPath, stack, mtu, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, *result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
type TestResult struct {
|
||||
BoxPath string `json:"box_path"`
|
||||
Stack string `json:"stack"`
|
||||
MTU int `json:"mtu"`
|
||||
Flags []string `json:"flags"`
|
||||
MultiThread bool `json:"multi_thread"`
|
||||
UploadSpeed string `json:"upload_speed"`
|
||||
DownloadSpeed string `json:"download_speed"`
|
||||
}
|
||||
|
||||
func testOnce(boxPath string, stackName string, mtu int, multiThread bool, flags []string) (result *TestResult, err error) {
|
||||
testAddress := netip.MustParseAddr("1.1.1.1")
|
||||
testConfig := option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeTun,
|
||||
Options: &option.TunInboundOptions{
|
||||
Address: []netip.Prefix{netip.MustParsePrefix("172.18.0.1/30")},
|
||||
AutoRoute: true,
|
||||
MTU: uint32(mtu),
|
||||
Stack: stackName,
|
||||
RouteAddress: []netip.Prefix{netip.PrefixFrom(testAddress, testAddress.BitLen())},
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
RawDefaultRule: option.RawDefaultRule{
|
||||
IPCIDR: []string{testAddress.String()},
|
||||
},
|
||||
RuleAction: option.RuleAction{
|
||||
Action: C.RuleActionTypeRouteOptions,
|
||||
RouteOptionsOptions: option.RouteOptionsActionOptions{
|
||||
OverrideAddress: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
AutoDetectInterface: true,
|
||||
},
|
||||
}
|
||||
ctx := include.Context(context.Background())
|
||||
tempConfig, err := os.CreateTemp("", "tun-bench-*.json")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempConfig.Name())
|
||||
encoder := json.NewEncoderContext(ctx, tempConfig)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(testConfig)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "encode test config")
|
||||
}
|
||||
tempConfig.Close()
|
||||
var sudoArgs []string
|
||||
if len(flags) > 0 {
|
||||
sudoArgs = append(sudoArgs, "env")
|
||||
sudoArgs = append(sudoArgs, flags...)
|
||||
}
|
||||
sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name())
|
||||
boxProcess := shell.Exec("sudo", sudoArgs...)
|
||||
boxProcess.Stdout = &stderrWriter{}
|
||||
boxProcess.Stderr = io.Discard
|
||||
err = boxProcess.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if C.IsDarwin {
|
||||
iperf3Path, err = exec.LookPath("iperf3-darwin")
|
||||
} else {
|
||||
iperf3Path, err = exec.LookPath("iperf3")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
serverProcess := shell.Exec(iperf3Path, "-s")
|
||||
serverProcess.Stdout = io.Discard
|
||||
serverProcess.Stderr = io.Discard
|
||||
err = serverProcess.Start()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "start iperf3 server")
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
args := []string{"-c", testAddress.String()}
|
||||
if multiThread {
|
||||
args = append(args, "-P", "10")
|
||||
}
|
||||
|
||||
uploadProcess := shell.Exec(iperf3Path, args...)
|
||||
output, err := uploadProcess.Read()
|
||||
if err != nil {
|
||||
boxProcess.Process.Signal(syscall.SIGKILL)
|
||||
serverProcess.Process.Signal(syscall.SIGKILL)
|
||||
println(output)
|
||||
return
|
||||
}
|
||||
|
||||
uploadResult := common.SubstringBeforeLast(output, "iperf Done.")
|
||||
uploadResult = common.SubstringBeforeLast(uploadResult, "sender")
|
||||
uploadResult = common.SubstringBeforeLast(uploadResult, "bits/sec")
|
||||
uploadResult = common.SubstringAfterLast(uploadResult, "Bytes")
|
||||
uploadResult = strings.ReplaceAll(uploadResult, " ", "")
|
||||
|
||||
result = &TestResult{
|
||||
BoxPath: boxPath,
|
||||
Stack: stackName,
|
||||
MTU: mtu,
|
||||
Flags: flags,
|
||||
MultiThread: multiThread,
|
||||
UploadSpeed: uploadResult,
|
||||
}
|
||||
|
||||
downloadProcess := shell.Exec(iperf3Path, append(args, "-R")...)
|
||||
output, err = downloadProcess.Read()
|
||||
if err != nil {
|
||||
boxProcess.Process.Signal(syscall.SIGKILL)
|
||||
serverProcess.Process.Signal(syscall.SIGKILL)
|
||||
println(output)
|
||||
return
|
||||
}
|
||||
|
||||
downloadResult := common.SubstringBeforeLast(output, "iperf Done.")
|
||||
downloadResult = common.SubstringBeforeLast(downloadResult, "receiver")
|
||||
downloadResult = common.SubstringBeforeLast(downloadResult, "bits/sec")
|
||||
downloadResult = common.SubstringAfterLast(downloadResult, "Bytes")
|
||||
downloadResult = strings.ReplaceAll(downloadResult, " ", "")
|
||||
|
||||
result.DownloadSpeed = downloadResult
|
||||
|
||||
printArgs := []any{boxPath, stackName, mtu, "upload", uploadResult, "download", downloadResult}
|
||||
if len(flags) > 0 {
|
||||
printArgs = append(printArgs, "flags", strings.Join(flags, " "))
|
||||
}
|
||||
if multiThread {
|
||||
printArgs = append(printArgs, "(-P 10)")
|
||||
}
|
||||
fmt.Println(printArgs...)
|
||||
err = boxProcess.Process.Signal(syscall.SIGTERM)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = serverProcess.Process.Signal(syscall.SIGTERM)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
boxDone := make(chan struct{})
|
||||
go func() {
|
||||
boxProcess.Cmd.Wait()
|
||||
close(boxDone)
|
||||
}()
|
||||
|
||||
serverDone := make(chan struct{})
|
||||
go func() {
|
||||
serverProcess.Process.Wait()
|
||||
close(serverDone)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-boxDone:
|
||||
case <-time.After(2 * time.Second):
|
||||
boxProcess.Process.Kill()
|
||||
case <-time.After(4 * time.Second):
|
||||
println("box process did not close!")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-serverDone:
|
||||
case <-time.After(2 * time.Second):
|
||||
serverProcess.Process.Kill()
|
||||
case <-time.After(4 * time.Second):
|
||||
println("server process did not close!")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type stderrWriter struct{}
|
||||
|
||||
func (w *stderrWriter) Write(p []byte) (n int, err error) {
|
||||
return os.Stderr.Write(p)
|
||||
}
|
@ -6,10 +6,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/route/rule"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -71,7 +69,7 @@ func compileRuleSet(sourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = srs.Write(outputFile, plainRuleSet.Options, downgradeRuleSetVersion(plainRuleSet.Version, plainRuleSet.Options))
|
||||
err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputPath)
|
||||
@ -80,18 +78,3 @@ func compileRuleSet(sourcePath string) error {
|
||||
outputFile.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func downgradeRuleSetVersion(version uint8, options option.PlainRuleSet) uint8 {
|
||||
if version == C.RuleSetVersion4 && !rule.HasHeadlessRule(options.Rules, func(rule option.DefaultHeadlessRule) bool {
|
||||
return rule.NetworkInterfaceAddress != nil && rule.NetworkInterfaceAddress.Size() > 0 ||
|
||||
len(rule.DefaultInterfaceAddress) > 0
|
||||
}) {
|
||||
version = C.RuleSetVersion3
|
||||
}
|
||||
if version == C.RuleSetVersion3 && !rule.HasHeadlessRule(options.Rules, func(rule option.DefaultHeadlessRule) bool {
|
||||
return len(rule.NetworkType) > 0 || rule.NetworkIsExpensive || rule.NetworkIsConstrained
|
||||
}) {
|
||||
version = C.RuleSetVersion2
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
@ -454,5 +454,5 @@ func parseADGuardIPCIDRLine(ruleLine string) (netip.Prefix, error) {
|
||||
for len(ruleParts) < 4 {
|
||||
ruleParts = append(ruleParts, 0)
|
||||
}
|
||||
return netip.PrefixFrom(netip.AddrFrom4([4]byte(ruleParts)), bitLen), nil
|
||||
return netip.PrefixFrom(netip.AddrFrom4(*(*[4]byte)(ruleParts)), bitLen), nil
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||
} else {
|
||||
dialer = d.udpDialer4
|
||||
}
|
||||
fastFallback := time.Since(d.networkLastFallback.Load()) < C.TCPTimeout
|
||||
fastFallback := time.Now().Sub(d.networkLastFallback.Load()) < C.TCPTimeout
|
||||
var (
|
||||
conn net.Conn
|
||||
isPrimary bool
|
||||
|
@ -111,7 +111,7 @@ func NewWithOptions(options Options) (N.Dialer, error) {
|
||||
dnsQueryOptions.Transport = dnsTransport.Default()
|
||||
} else if options.NewDialer {
|
||||
return nil, E.New("missing domain resolver for domain server address")
|
||||
} else {
|
||||
} else if !options.DirectOutbound {
|
||||
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@ -24,7 +22,7 @@ type slowOpenConn struct {
|
||||
ctx context.Context
|
||||
network string
|
||||
destination M.Socksaddr
|
||||
conn atomic.Pointer[net.TCPConn]
|
||||
conn net.Conn
|
||||
create chan struct{}
|
||||
done chan struct{}
|
||||
access sync.Mutex
|
||||
@ -52,25 +50,22 @@ func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, des
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) Read(b []byte) (n int, err error) {
|
||||
conn := c.conn.Load()
|
||||
if conn != nil {
|
||||
return conn.Read(b)
|
||||
}
|
||||
select {
|
||||
case <-c.create:
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
if c.conn == nil {
|
||||
select {
|
||||
case <-c.create:
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
}
|
||||
case <-c.done:
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
return c.conn.Load().Read(b)
|
||||
case <-c.done:
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
tcpConn := c.conn.Load()
|
||||
if tcpConn != nil {
|
||||
return tcpConn.Write(b)
|
||||
if c.conn != nil {
|
||||
return c.conn.Write(b)
|
||||
}
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
@ -79,7 +74,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
}
|
||||
return c.conn.Load().Write(b)
|
||||
return c.conn.Write(b)
|
||||
case <-c.done:
|
||||
return 0, os.ErrClosed
|
||||
default:
|
||||
@ -88,7 +83,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
if err != nil {
|
||||
c.err = err
|
||||
} else {
|
||||
c.conn.Store(conn.(*net.TCPConn))
|
||||
c.conn = conn
|
||||
}
|
||||
n = len(b)
|
||||
close(c.create)
|
||||
@ -98,77 +93,70 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||
func (c *slowOpenConn) Close() error {
|
||||
c.closeOnce.Do(func() {
|
||||
close(c.done)
|
||||
conn := c.conn.Load()
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) LocalAddr() net.Addr {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
if c.conn == nil {
|
||||
return M.Socksaddr{}
|
||||
}
|
||||
return conn.LocalAddr()
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) RemoteAddr() net.Addr {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
if c.conn == nil {
|
||||
return M.Socksaddr{}
|
||||
}
|
||||
return conn.RemoteAddr()
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) SetDeadline(t time.Time) error {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
if c.conn == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return conn.SetDeadline(t)
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) SetReadDeadline(t time.Time) error {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
if c.conn == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return conn.SetReadDeadline(t)
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) SetWriteDeadline(t time.Time) error {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
if c.conn == nil {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
return conn.SetWriteDeadline(t)
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) Upstream() any {
|
||||
return common.PtrOrNil(c.conn.Load())
|
||||
return c.conn
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) ReaderReplaceable() bool {
|
||||
return c.conn.Load() != nil
|
||||
return c.conn != nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) WriterReplaceable() bool {
|
||||
return c.conn.Load() != nil
|
||||
return c.conn != nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) LazyHeadroom() bool {
|
||||
return c.conn.Load() == nil
|
||||
return c.conn == nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) NeedHandshake() bool {
|
||||
return c.conn.Load() == nil
|
||||
return c.conn == nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
conn := c.conn.Load()
|
||||
if conn == nil {
|
||||
if c.conn == nil {
|
||||
select {
|
||||
case <-c.create:
|
||||
if c.err != nil {
|
||||
@ -178,5 +166,5 @@ func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return 0, c.err
|
||||
}
|
||||
}
|
||||
return bufio.Copy(w, c.conn.Load())
|
||||
return bufio.Copy(w, c.conn)
|
||||
}
|
||||
|
@ -164,8 +164,9 @@ func (l *Listener) loopUDPOut() {
|
||||
if l.shutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
l.udpConn.Close()
|
||||
l.logger.Error("udp listener write back: ", destination, ": ", err)
|
||||
continue
|
||||
return
|
||||
}
|
||||
continue
|
||||
case <-l.packetOutboundClosed:
|
||||
|
@ -96,11 +96,11 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
|
||||
switch {
|
||||
case flag&0x1 > 0 && isIPv4:
|
||||
// ipv4
|
||||
srcIP = netip.AddrFrom4([4]byte(buf[inp+76 : inp+80]))
|
||||
srcIP = netip.AddrFrom4(*(*[4]byte)(buf[inp+76 : inp+80]))
|
||||
srcIsIPv4 = true
|
||||
case flag&0x2 > 0 && !isIPv4:
|
||||
// ipv6
|
||||
srcIP = netip.AddrFrom16([16]byte(buf[inp+64 : inp+80]))
|
||||
srcIP = netip.AddrFrom16(*(*[16]byte)(buf[inp+64 : inp+80]))
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
@ -17,5 +17,8 @@ var uQUICChrome115 = &ja3.ClientHello{
|
||||
}
|
||||
|
||||
func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
|
||||
return !uQUICChrome115.Equals(fingerprint, true)
|
||||
if uQUICChrome115.Equals(fingerprint, true) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -12,8 +12,6 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/domain"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
|
||||
"go4.org/netipx"
|
||||
@ -43,8 +41,6 @@ const (
|
||||
ruleItemNetworkType
|
||||
ruleItemNetworkIsExpensive
|
||||
ruleItemNetworkIsConstrained
|
||||
ruleItemNetworkInterfaceAddress
|
||||
ruleItemDefaultInterfaceAddress
|
||||
ruleItemFinal uint8 = 0xFF
|
||||
)
|
||||
|
||||
@ -234,51 +230,6 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
rule.NetworkIsExpensive = true
|
||||
case ruleItemNetworkIsConstrained:
|
||||
rule.NetworkIsConstrained = true
|
||||
case ruleItemNetworkInterfaceAddress:
|
||||
rule.NetworkInterfaceAddress = new(badjson.TypedMap[option.InterfaceType, badoption.Listable[badoption.Prefixable]])
|
||||
var size uint64
|
||||
size, err = binary.ReadUvarint(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := uint64(0); i < size; i++ {
|
||||
var key uint8
|
||||
err = binary.Read(reader, binary.BigEndian, &key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var value []badoption.Prefixable
|
||||
var prefixCount uint64
|
||||
prefixCount, err = binary.ReadUvarint(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for j := uint64(0); j < prefixCount; j++ {
|
||||
var prefix netip.Prefix
|
||||
prefix, err = readPrefix(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
value = append(value, badoption.Prefixable(prefix))
|
||||
}
|
||||
rule.NetworkInterfaceAddress.Put(option.InterfaceType(key), value)
|
||||
}
|
||||
case ruleItemDefaultInterfaceAddress:
|
||||
var value []badoption.Prefixable
|
||||
var prefixCount uint64
|
||||
prefixCount, err = binary.ReadUvarint(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for j := uint64(0); j < prefixCount; j++ {
|
||||
var prefix netip.Prefix
|
||||
prefix, err = readPrefix(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
value = append(value, badoption.Prefixable(prefix))
|
||||
}
|
||||
rule.DefaultInterfaceAddress = value
|
||||
case ruleItemFinal:
|
||||
err = binary.Read(reader, binary.BigEndian, &rule.Invert)
|
||||
return
|
||||
@ -395,7 +346,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||
}
|
||||
if len(rule.NetworkType) > 0 {
|
||||
if generateVersion < C.RuleSetVersion3 {
|
||||
return E.New("`network_type` rule item is only supported in version 3 or later")
|
||||
return E.New("network_type rule item is only supported in version 3 or later")
|
||||
}
|
||||
err = writeRuleItemUint8(writer, ruleItemNetworkType, rule.NetworkType)
|
||||
if err != nil {
|
||||
@ -403,67 +354,17 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||
}
|
||||
}
|
||||
if rule.NetworkIsExpensive {
|
||||
if generateVersion < C.RuleSetVersion3 {
|
||||
return E.New("`network_is_expensive` rule item is only supported in version 3 or later")
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsExpensive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rule.NetworkIsConstrained {
|
||||
if generateVersion < C.RuleSetVersion3 {
|
||||
return E.New("`network_is_constrained` rule item is only supported in version 3 or later")
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsConstrained)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rule.NetworkInterfaceAddress != nil && rule.NetworkInterfaceAddress.Size() > 0 {
|
||||
if generateVersion < C.RuleSetVersion4 {
|
||||
return E.New("`network_interface_address` rule item is only supported in version 4 or later")
|
||||
}
|
||||
err = writer.WriteByte(ruleItemNetworkInterfaceAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = varbin.WriteUvarint(writer, uint64(rule.NetworkInterfaceAddress.Size()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range rule.NetworkInterfaceAddress.Entries() {
|
||||
err = binary.Write(writer, binary.BigEndian, uint8(entry.Key.Build()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rawPrefix := range entry.Value {
|
||||
err = writePrefix(writer, rawPrefix.Build(netip.Prefix{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(rule.DefaultInterfaceAddress) > 0 {
|
||||
if generateVersion < C.RuleSetVersion4 {
|
||||
return E.New("`default_interface_address` rule item is only supported in version 4 or later")
|
||||
}
|
||||
err = writer.WriteByte(ruleItemDefaultInterfaceAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = varbin.WriteUvarint(writer, uint64(len(rule.DefaultInterfaceAddress)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rawPrefix := range rule.DefaultInterfaceAddress {
|
||||
err = writePrefix(writer, rawPrefix.Build(netip.Prefix{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(rule.WIFISSID) > 0 {
|
||||
err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
|
||||
if err != nil {
|
||||
|
@ -1,33 +0,0 @@
|
||||
package srs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net/netip"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
)
|
||||
|
||||
func readPrefix(reader varbin.Reader) (netip.Prefix, error) {
|
||||
addrSlice, err := varbin.ReadValue[[]byte](reader, binary.BigEndian)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, err
|
||||
}
|
||||
prefixBits, err := varbin.ReadValue[uint8](reader, binary.BigEndian)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, err
|
||||
}
|
||||
return netip.PrefixFrom(M.AddrFromIP(addrSlice), int(prefixBits)), nil
|
||||
}
|
||||
|
||||
func writePrefix(writer varbin.Writer, prefix netip.Prefix) error {
|
||||
err := varbin.Write(writer, binary.BigEndian, prefix.Addr().AsSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, uint8(prefix.Bits()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -2,11 +2,10 @@ package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/badtls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@ -15,7 +14,7 @@ import (
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
func NewDialerFromOptions(ctx context.Context, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||
func NewDialerFromOptions(ctx context.Context, router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||
if !options.Enabled {
|
||||
return dialer, nil
|
||||
}
|
||||
@ -54,57 +53,26 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
type Dialer interface {
|
||||
N.Dialer
|
||||
DialTLSContext(ctx context.Context, destination M.Socksaddr) (Conn, error)
|
||||
}
|
||||
|
||||
type defaultDialer struct {
|
||||
type Dialer struct {
|
||||
dialer N.Dialer
|
||||
config Config
|
||||
}
|
||||
|
||||
func NewDialer(dialer N.Dialer, config Config) Dialer {
|
||||
return &defaultDialer{dialer, config}
|
||||
func NewDialer(dialer N.Dialer, config Config) N.Dialer {
|
||||
return &Dialer{dialer, config}
|
||||
}
|
||||
|
||||
func (d *defaultDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if N.NetworkName(network) != N.NetworkTCP {
|
||||
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if network != N.NetworkTCP {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
return d.DialTLSContext(ctx, destination)
|
||||
}
|
||||
|
||||
func (d *defaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (d *defaultDialer) DialTLSContext(ctx context.Context, destination M.Socksaddr) (Conn, error) {
|
||||
return d.dialContext(ctx, destination, true)
|
||||
}
|
||||
|
||||
func (d *defaultDialer) dialContext(ctx context.Context, destination M.Socksaddr, echRetry bool) (Conn, error) {
|
||||
conn, err := d.dialer.DialContext(ctx, N.NetworkTCP, destination)
|
||||
conn, err := d.dialer.DialContext(ctx, network, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConn, err := ClientHandshake(ctx, conn, d.config)
|
||||
if err == nil {
|
||||
return tlsConn, nil
|
||||
}
|
||||
conn.Close()
|
||||
if echRetry {
|
||||
var echErr *tls.ECHRejectionError
|
||||
if errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 {
|
||||
if echConfig, isECH := d.config.(ECHCapableConfig); isECH {
|
||||
echConfig.SetECHConfigList(echErr.RetryConfigList)
|
||||
}
|
||||
}
|
||||
return d.dialContext(ctx, destination, false)
|
||||
}
|
||||
return nil, err
|
||||
return ClientHandshake(ctx, conn, d.config)
|
||||
}
|
||||
|
||||
func (d *defaultDialer) Upstream() any {
|
||||
return d.dialer
|
||||
func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (a
|
||||
func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
|
||||
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Now().Sub(s.lastUpdate) > s.lastTTL {
|
||||
message := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
RecursionDesired: true,
|
||||
|
@ -1,11 +1,14 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/pem"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/cloudflare/circl/hpke"
|
||||
"github.com/cloudflare/circl/kem"
|
||||
)
|
||||
|
||||
type ECHCapableConfig interface {
|
||||
@ -14,68 +17,145 @@ type ECHCapableConfig interface {
|
||||
SetECHConfigList([]byte)
|
||||
}
|
||||
|
||||
func ECHKeygenDefault(publicName string) (configPem string, keyPem string, err error) {
|
||||
echKey, err := ecdh.X25519().GenerateKey(rand.Reader)
|
||||
func ECHKeygenDefault(serverName string) (configPem string, keyPem string, err error) {
|
||||
cipherSuites := []echCipherSuite{
|
||||
{
|
||||
kdf: hpke.KDF_HKDF_SHA256,
|
||||
aead: hpke.AEAD_AES128GCM,
|
||||
}, {
|
||||
kdf: hpke.KDF_HKDF_SHA256,
|
||||
aead: hpke.AEAD_ChaCha20Poly1305,
|
||||
},
|
||||
}
|
||||
keyConfig := []myECHKeyConfig{
|
||||
{id: 0, kem: hpke.KEM_X25519_HKDF_SHA256},
|
||||
}
|
||||
|
||||
keyPairs, err := echKeygen(0xfe0d, serverName, keyConfig, cipherSuites)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
echConfig, err := marshalECHConfig(0, echKey.PublicKey().Bytes(), publicName, 0)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
var configBuffer bytes.Buffer
|
||||
var totalLen uint16
|
||||
for _, keyPair := range keyPairs {
|
||||
totalLen += uint16(len(keyPair.rawConf))
|
||||
}
|
||||
configBuilder := cryptobyte.NewBuilder(nil)
|
||||
configBuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
builder.AddBytes(echConfig)
|
||||
})
|
||||
configBytes, err := configBuilder.Bytes()
|
||||
if err != nil {
|
||||
return
|
||||
binary.Write(&configBuffer, binary.BigEndian, totalLen)
|
||||
for _, keyPair := range keyPairs {
|
||||
configBuffer.Write(keyPair.rawConf)
|
||||
}
|
||||
keyBuilder := cryptobyte.NewBuilder(nil)
|
||||
keyBuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
builder.AddBytes(echKey.Bytes())
|
||||
})
|
||||
keyBuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
builder.AddBytes(echConfig)
|
||||
})
|
||||
keyBytes, err := keyBuilder.Bytes()
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
var keyBuffer bytes.Buffer
|
||||
for _, keyPair := range keyPairs {
|
||||
keyBuffer.Write(keyPair.rawKey)
|
||||
}
|
||||
configPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBytes}))
|
||||
keyPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBytes}))
|
||||
|
||||
configPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer.Bytes()}))
|
||||
keyPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer.Bytes()}))
|
||||
return
|
||||
}
|
||||
|
||||
func marshalECHConfig(id uint8, pubKey []byte, publicName string, maxNameLen uint8) ([]byte, error) {
|
||||
const extensionEncryptedClientHello = 0xfe0d
|
||||
const DHKEM_X25519_HKDF_SHA256 = 0x0020
|
||||
const KDF_HKDF_SHA256 = 0x0001
|
||||
builder := cryptobyte.NewBuilder(nil)
|
||||
builder.AddUint16(extensionEncryptedClientHello)
|
||||
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
builder.AddUint8(id)
|
||||
|
||||
builder.AddUint16(DHKEM_X25519_HKDF_SHA256) // The only DHKEM we support
|
||||
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
builder.AddBytes(pubKey)
|
||||
})
|
||||
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
const (
|
||||
AEAD_AES_128_GCM = 0x0001
|
||||
AEAD_AES_256_GCM = 0x0002
|
||||
AEAD_ChaCha20Poly1305 = 0x0003
|
||||
)
|
||||
for _, aeadID := range []uint16{AEAD_AES_128_GCM, AEAD_AES_256_GCM, AEAD_ChaCha20Poly1305} {
|
||||
builder.AddUint16(KDF_HKDF_SHA256) // The only KDF we support
|
||||
builder.AddUint16(aeadID)
|
||||
}
|
||||
})
|
||||
builder.AddUint8(maxNameLen)
|
||||
builder.AddUint8LengthPrefixed(func(builder *cryptobyte.Builder) {
|
||||
builder.AddBytes([]byte(publicName))
|
||||
})
|
||||
builder.AddUint16(0) // extensions
|
||||
})
|
||||
return builder.Bytes()
|
||||
type echKeyConfigPair struct {
|
||||
id uint8
|
||||
rawKey []byte
|
||||
conf myECHKeyConfig
|
||||
rawConf []byte
|
||||
}
|
||||
|
||||
type echCipherSuite struct {
|
||||
kdf hpke.KDF
|
||||
aead hpke.AEAD
|
||||
}
|
||||
|
||||
type myECHKeyConfig struct {
|
||||
id uint8
|
||||
kem hpke.KEM
|
||||
seed []byte
|
||||
}
|
||||
|
||||
func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite []echCipherSuite) ([]echKeyConfigPair, error) {
|
||||
be := binary.BigEndian
|
||||
// prepare for future update
|
||||
if version != 0xfe0d {
|
||||
return nil, E.New("unsupported ECH version", version)
|
||||
}
|
||||
|
||||
suiteBuf := make([]byte, 0, len(suite)*4+2)
|
||||
suiteBuf = be.AppendUint16(suiteBuf, uint16(len(suite))*4)
|
||||
for _, s := range suite {
|
||||
if !s.kdf.IsValid() || !s.aead.IsValid() {
|
||||
return nil, E.New("invalid HPKE cipher suite")
|
||||
}
|
||||
suiteBuf = be.AppendUint16(suiteBuf, uint16(s.kdf))
|
||||
suiteBuf = be.AppendUint16(suiteBuf, uint16(s.aead))
|
||||
}
|
||||
|
||||
pairs := []echKeyConfigPair{}
|
||||
for _, c := range conf {
|
||||
pair := echKeyConfigPair{}
|
||||
pair.id = c.id
|
||||
pair.conf = c
|
||||
|
||||
if !c.kem.IsValid() {
|
||||
return nil, E.New("invalid HPKE KEM")
|
||||
}
|
||||
|
||||
kpGenerator := c.kem.Scheme().GenerateKeyPair
|
||||
if len(c.seed) > 0 {
|
||||
kpGenerator = func() (kem.PublicKey, kem.PrivateKey, error) {
|
||||
pub, sec := c.kem.Scheme().DeriveKeyPair(c.seed)
|
||||
return pub, sec, nil
|
||||
}
|
||||
if len(c.seed) < c.kem.Scheme().PrivateKeySize() {
|
||||
return nil, E.New("HPKE KEM seed too short")
|
||||
}
|
||||
}
|
||||
|
||||
pub, sec, err := kpGenerator()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "generate ECH config key pair")
|
||||
}
|
||||
b := []byte{}
|
||||
b = be.AppendUint16(b, version)
|
||||
b = be.AppendUint16(b, 0) // length field
|
||||
// contents
|
||||
// key config
|
||||
b = append(b, c.id)
|
||||
b = be.AppendUint16(b, uint16(c.kem))
|
||||
pubBuf, err := pub.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "serialize ECH public key")
|
||||
}
|
||||
b = be.AppendUint16(b, uint16(len(pubBuf)))
|
||||
b = append(b, pubBuf...)
|
||||
|
||||
b = append(b, suiteBuf...)
|
||||
// end key config
|
||||
// max name len, not supported
|
||||
b = append(b, 0)
|
||||
// server name
|
||||
b = append(b, byte(len(serverName)))
|
||||
b = append(b, []byte(serverName)...)
|
||||
// extensions, not supported
|
||||
b = be.AppendUint16(b, 0)
|
||||
|
||||
be.PutUint16(b[2:], uint16(len(b)-4))
|
||||
|
||||
pair.rawConf = b
|
||||
|
||||
secBuf, err := sec.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "serialize ECH private key")
|
||||
}
|
||||
sk := []byte{}
|
||||
sk = be.AppendUint16(sk, uint16(len(secBuf)))
|
||||
sk = append(sk, secBuf...)
|
||||
sk = be.AppendUint16(sk, uint16(len(b)))
|
||||
sk = append(sk, b...)
|
||||
pair.rawKey = sk
|
||||
|
||||
pairs = append(pairs, pair)
|
||||
}
|
||||
return pairs, nil
|
||||
}
|
||||
|
@ -86,16 +86,12 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
tlsConfig.VerifyConnection = func(state tls.ConnectionState) error {
|
||||
verifyOptions := x509.VerifyOptions{
|
||||
Roots: tlsConfig.RootCAs,
|
||||
DNSName: serverName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
for _, cert := range state.PeerCertificates[1:] {
|
||||
verifyOptions.Intermediates.AddCert(cert)
|
||||
}
|
||||
if tlsConfig.Time != nil {
|
||||
verifyOptions.CurrentTime = tlsConfig.Time()
|
||||
}
|
||||
_, err := state.PeerCertificates[0].Verify(verifyOptions)
|
||||
return err
|
||||
}
|
||||
|
@ -11,13 +11,10 @@ type TimeServiceWrapper struct {
|
||||
}
|
||||
|
||||
func (w *TimeServiceWrapper) TimeFunc() func() time.Time {
|
||||
return func() time.Time {
|
||||
if w.TimeService != nil {
|
||||
return w.TimeService.TimeFunc()()
|
||||
} else {
|
||||
return time.Now()
|
||||
}
|
||||
if w.TimeService == nil {
|
||||
return nil
|
||||
}
|
||||
return w.TimeService.TimeFunc()
|
||||
}
|
||||
|
||||
func (w *TimeServiceWrapper) Upstream() any {
|
||||
|
@ -145,16 +145,11 @@ func NewUTLSClient(ctx context.Context, serverAddress string, options option.Out
|
||||
var tlsConfig utls.Config
|
||||
tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
|
||||
tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx)
|
||||
if !options.DisableSNI {
|
||||
tlsConfig.ServerName = serverName
|
||||
}
|
||||
tlsConfig.ServerName = serverName
|
||||
if options.Insecure {
|
||||
tlsConfig.InsecureSkipVerify = options.Insecure
|
||||
} else if options.DisableSNI {
|
||||
if options.Reality != nil && options.Reality.Enabled {
|
||||
return nil, E.New("disable_sni is unsupported in reality")
|
||||
}
|
||||
tlsConfig.InsecureServerNameToVerify = serverName
|
||||
return nil, E.New("disable_sni is unsupported in uTLS")
|
||||
}
|
||||
if len(options.ALPN) > 0 {
|
||||
tlsConfig.NextProtos = options.ALPN
|
||||
|
@ -45,7 +45,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
defer func() {
|
||||
c.firstPacketWritten = true
|
||||
}()
|
||||
serverName := IndexTLSServerName(b)
|
||||
serverName := indexTLSServerName(b)
|
||||
if serverName != nil {
|
||||
if c.splitPacket {
|
||||
if c.tcpConn != nil {
|
||||
|
@ -22,13 +22,13 @@ const (
|
||||
tls13 uint16 = 0x0304
|
||||
)
|
||||
|
||||
type MyServerName struct {
|
||||
type myServerName struct {
|
||||
Index int
|
||||
Length int
|
||||
ServerName string
|
||||
}
|
||||
|
||||
func IndexTLSServerName(payload []byte) *MyServerName {
|
||||
func indexTLSServerName(payload []byte) *myServerName {
|
||||
if len(payload) < recordLayerHeaderLen || payload[0] != contentType {
|
||||
return nil
|
||||
}
|
||||
@ -36,48 +36,47 @@ func IndexTLSServerName(payload []byte) *MyServerName {
|
||||
if len(payload) < recordLayerHeaderLen+int(segmentLen) {
|
||||
return nil
|
||||
}
|
||||
serverName := indexTLSServerNameFromHandshake(payload[recordLayerHeaderLen:])
|
||||
serverName := indexTLSServerNameFromHandshake(payload[recordLayerHeaderLen : recordLayerHeaderLen+int(segmentLen)])
|
||||
if serverName == nil {
|
||||
return nil
|
||||
}
|
||||
serverName.Index += recordLayerHeaderLen
|
||||
serverName.Length += recordLayerHeaderLen
|
||||
return serverName
|
||||
}
|
||||
|
||||
func indexTLSServerNameFromHandshake(handshake []byte) *MyServerName {
|
||||
if len(handshake) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen {
|
||||
func indexTLSServerNameFromHandshake(hs []byte) *myServerName {
|
||||
if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen {
|
||||
return nil
|
||||
}
|
||||
if handshake[0] != handshakeType {
|
||||
if hs[0] != handshakeType {
|
||||
return nil
|
||||
}
|
||||
handshakeLen := uint32(handshake[1])<<16 | uint32(handshake[2])<<8 | uint32(handshake[3])
|
||||
if len(handshake[4:]) != int(handshakeLen) {
|
||||
handshakeLen := uint32(hs[1])<<16 | uint32(hs[2])<<8 | uint32(hs[3])
|
||||
if len(hs[4:]) != int(handshakeLen) {
|
||||
return nil
|
||||
}
|
||||
tlsVersion := uint16(handshake[4])<<8 | uint16(handshake[5])
|
||||
tlsVersion := uint16(hs[4])<<8 | uint16(hs[5])
|
||||
if tlsVersion&tlsVersionBitmask != 0x0300 && tlsVersion != tls13 {
|
||||
return nil
|
||||
}
|
||||
sessionIDLen := handshake[38]
|
||||
currentIndex := handshakeHeaderLen + randomDataLen + sessionIDHeaderLen + int(sessionIDLen)
|
||||
if len(handshake) < currentIndex {
|
||||
sessionIDLen := hs[38]
|
||||
if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen) {
|
||||
return nil
|
||||
}
|
||||
cipherSuites := handshake[currentIndex:]
|
||||
if len(cipherSuites) < cipherSuiteHeaderLen {
|
||||
cs := hs[handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen):]
|
||||
if len(cs) < cipherSuiteHeaderLen {
|
||||
return nil
|
||||
}
|
||||
csLen := uint16(cipherSuites[0])<<8 | uint16(cipherSuites[1])
|
||||
if len(cipherSuites) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen {
|
||||
csLen := uint16(cs[0])<<8 | uint16(cs[1])
|
||||
if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen {
|
||||
return nil
|
||||
}
|
||||
compressMethodLen := uint16(cipherSuites[cipherSuiteHeaderLen+int(csLen)])
|
||||
currentIndex += cipherSuiteHeaderLen + int(csLen) + compressMethodHeaderLen + int(compressMethodLen)
|
||||
if len(handshake) < currentIndex {
|
||||
compressMethodLen := uint16(cs[cipherSuiteHeaderLen+int(csLen)])
|
||||
if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen+int(compressMethodLen) {
|
||||
return nil
|
||||
}
|
||||
serverName := indexTLSServerNameFromExtensions(handshake[currentIndex:])
|
||||
currentIndex := cipherSuiteHeaderLen + int(csLen) + compressMethodHeaderLen + int(compressMethodLen)
|
||||
serverName := indexTLSServerNameFromExtensions(cs[currentIndex:])
|
||||
if serverName == nil {
|
||||
return nil
|
||||
}
|
||||
@ -85,7 +84,7 @@ func indexTLSServerNameFromHandshake(handshake []byte) *MyServerName {
|
||||
return serverName
|
||||
}
|
||||
|
||||
func indexTLSServerNameFromExtensions(exs []byte) *MyServerName {
|
||||
func indexTLSServerNameFromExtensions(exs []byte) *myServerName {
|
||||
if len(exs) == 0 {
|
||||
return nil
|
||||
}
|
||||
@ -119,8 +118,7 @@ func indexTLSServerNameFromExtensions(exs []byte) *MyServerName {
|
||||
}
|
||||
sniLen := uint16(sex[3])<<8 | uint16(sex[4])
|
||||
sex = sex[sniExtensionHeaderLen:]
|
||||
|
||||
return &MyServerName{
|
||||
return &myServerName{
|
||||
Index: currentIndex + extensionHeaderLen + sniExtensionHeaderLen,
|
||||
Length: int(sniLen),
|
||||
ServerName: string(sex),
|
||||
|
@ -1,20 +0,0 @@
|
||||
package tf_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/sagernet/sing-box/common/tlsfragment"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIndexTLSServerName(t *testing.T) {
|
||||
t.Parallel()
|
||||
payload, err := hex.DecodeString("16030105f8010005f403036e35de7389a679c54029cf452611f2211c70d9ac3897271de589ab6155f8e4ab20637d225f1ef969ad87ed78bfb9d171300bcb1703b6f314ccefb964f79b7d0961002a0a0a130213031301c02cc02bcca9c030c02fcca8c00ac009c014c013009d009c0035002fc008c012000a01000581baba00000000000f000d00000a6769746875622e636f6d00170000ff01000100000a000e000c3a3a11ec001d001700180019000b000201000010000e000c02683208687474702f312e31000500050100000000000d00160014040308040401050308050805050108060601020100120000003304ef04ed3a3a00010011ec04c0aeb2250c092a3463161cccb29d9183331a424964248579507ed23a180b0ceab2a5f5d9ce41547e497a89055471ea572867ba3a1fc3c9e45025274a20f60c6b60e62476b6afed0403af59ab83660ef4112ae20386a602010d0a5d454c0ed34c84ed4423e750213e6a2baab1bf9c4367a6007ab40a33d95220c2dcaa44f257024a5626b545db0510f4311b1a60714154909c6a61fdfca011fb2626d657aeb6070bf078508babe3b584555013e34acc56198ed4663742b3155a664a9901794c4586820a7dc162c01827291f3792e1237f801a8d1ef096013c181c4a58d2f6859ba75022d18cc4418bd4f351d5c18f83a58857d05af860c4b9ac018a5b63f17184e591532c6bc2cf2215d4a282c8a8a4f6f7aee110422c8bc9ebd3b1d609c568523aaae555db320e6c269473d87af38c256cbb9febc20aea6380c32a8916f7a373c8b1e37554e3260bf6621f6b804ee80b3c516b1d01985bf4c603b6daa9a5991de6a7a29f3a7122b8afb843a7660110fce62b43c615f5bcc2db688ba012649c0952b0a2c031e732d2b454c6b2968683cb8d244be2c9a7fa163222979eaf92722b92b862d81a3d94450c2b60c318421ebb4307c42d1f0473592a5c30e42039cc68cda9721e61aa63f49def17c15221680ed444896340133bbee67556f56b9f9d78a4df715f926a12add0cc9c862e46ea8b7316ae468282c18601b2771c9c9322f982228cf93effaacd3f80cbd12bce5fc36f56e2a3caf91e578a5fae00c9b23a8ed1a66764f4433c3628a70b8f0a6196adc60a4cb4226f07ba4c6b363fe9065563bfc1347452946386bab488686e837ab979c64f9047417fca635fe1bb4f074f256cc8af837c7b455e280426547755af90a61640169ef180aea3a77e662bb6dac1b6c3696027129b1a5edf495314e9c7f4b6110e16378ec893fa24642330a40aba1a85326101acb97c620fd8d71389e69eaed7bdb01bbe1fd428d66191150c7b2cd1ad4257391676a82ba8ce07fb2667c3b289f159003a7c7bc31d361b7b7f49a802961739d950dfcc0fa1c7abce5abdd2245101da391151490862028110465950b9e9c03d08a90998ab83267838d2e74a0593bc81f74cdf734519a05b351c0e5488c68dd810e6e9142ccc1e2f4a7f464297eb340e27acc6b9d64e12e38cce8492b3d939140b5a9e149a75597f10a23874c84323a07cdd657274378f887c85c4259b9c04cd33ba58ed630ef2a744f8e19dd34843dff331d2a6be7e2332c599289cd248a611c73d7481cd4a9bd43449a3836f14b2af18a1739e17999e4c67e85cc5bcecabb14185e5bcaff3c96098f03dc5aba819f29587758f49f940585354a2a780830528d68ccd166920dadcaa25cab5fc1907272a826aba3f08bc6b88757776812ecb6c7cec69a223ec0a13a7b62a2349a0f63ed7a27a3b15ba21d71fe6864ec6e089ae17cadd433fa3138f7ee24353c11365818f8fc34f43a05542d18efaac24bfccc1f748a0cc1a67ad379468b76fd34973dba785f5c91d618333cd810fe0700d1bbc8422029782628070a624c52c5309a4a64d625b11f8033ab28df34a1add297517fcc06b92b6817b3c5144438cf260867c57bde68c8c4b82e6a135ef676a52fbae5708002a404e6189a60e2836de565ad1b29e3819e5ed49f6810bcb28e1bd6de57306f94b79d9dae1cc4624d2a068499beef81cd5fe4b76dcbfff2a2008001d002001976128c6d5a934533f28b9914d2480aab2a8c1ab03d212529ce8b27640a716002d00020101002b000706caca03040303001b00030200015a5a000100")
|
||||
require.NoError(t, err)
|
||||
serverName := tf.IndexTLSServerName(payload)
|
||||
require.NotNil(t, serverName)
|
||||
require.Equal(t, serverName.ServerName, string(payload[serverName.Index:serverName.Index+serverName.Length]))
|
||||
require.Equal(t, "github.com", serverName.ServerName)
|
||||
}
|
@ -22,8 +22,7 @@ const (
|
||||
RuleSetVersion1 = 1 + iota
|
||||
RuleSetVersion2
|
||||
RuleSetVersion3
|
||||
RuleSetVersion4
|
||||
RuleSetVersionCurrent = RuleSetVersion4
|
||||
RuleSetVersionCurrent = RuleSetVersion3
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -195,13 +195,8 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
||||
}
|
||||
}*/
|
||||
if responseChecker != nil {
|
||||
var rejected bool
|
||||
if !(response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError) {
|
||||
rejected = true
|
||||
} else {
|
||||
rejected = !responseChecker(MessageToAddresses(response))
|
||||
}
|
||||
if rejected {
|
||||
addr, addrErr := MessageToAddresses(response)
|
||||
if addrErr != nil || !responseChecker(addr) {
|
||||
if c.rdrc != nil {
|
||||
c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger)
|
||||
}
|
||||
@ -425,10 +420,7 @@ func (c *Client) lookupToExchange(ctx context.Context, transport adapter.DNSTran
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.Rcode != dns.RcodeSuccess {
|
||||
return nil, RcodeError(response.Rcode)
|
||||
}
|
||||
return MessageToAddresses(response), nil
|
||||
return MessageToAddresses(response)
|
||||
}
|
||||
|
||||
func (c *Client) questionCache(question dns.Question, transport adapter.DNSTransport) ([]netip.Addr, error) {
|
||||
@ -436,10 +428,7 @@ func (c *Client) questionCache(question dns.Question, transport adapter.DNSTrans
|
||||
if response == nil {
|
||||
return nil, ErrNotCached
|
||||
}
|
||||
if response.Rcode != dns.RcodeSuccess {
|
||||
return nil, RcodeError(response.Rcode)
|
||||
}
|
||||
return MessageToAddresses(response), nil
|
||||
return MessageToAddresses(response)
|
||||
}
|
||||
|
||||
func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransport) (*dns.Msg, int) {
|
||||
@ -516,7 +505,10 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
|
||||
}
|
||||
}
|
||||
|
||||
func MessageToAddresses(response *dns.Msg) []netip.Addr {
|
||||
func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) {
|
||||
if response.Rcode != dns.RcodeSuccess {
|
||||
return nil, RcodeError(response.Rcode)
|
||||
}
|
||||
addresses := make([]netip.Addr, 0, len(response.Answer))
|
||||
for _, rawAnswer := range response.Answer {
|
||||
switch answer := rawAnswer.(type) {
|
||||
@ -532,7 +524,7 @@ func MessageToAddresses(response *dns.Msg) []netip.Addr {
|
||||
}
|
||||
}
|
||||
}
|
||||
return addresses
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func wrapError(err error) error {
|
||||
|
@ -3,15 +3,11 @@ package transport
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
@ -43,13 +39,11 @@ func RegisterHTTPS(registry *dns.TransportRegistry) {
|
||||
|
||||
type HTTPSTransport struct {
|
||||
dns.TransportAdapter
|
||||
logger logger.ContextLogger
|
||||
dialer N.Dialer
|
||||
destination *url.URL
|
||||
headers http.Header
|
||||
transportAccess sync.Mutex
|
||||
transport *http.Transport
|
||||
transportResetAt time.Time
|
||||
logger logger.ContextLogger
|
||||
dialer N.Dialer
|
||||
destination *url.URL
|
||||
headers http.Header
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteHTTPSDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
@ -128,6 +122,7 @@ func NewHTTPSRaw(
|
||||
var transport *http.Transport
|
||||
if tlsConfig != nil {
|
||||
transport = &http.Transport{
|
||||
IdleConnTimeout: C.TCPKeepAliveInitial,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
tcpConn, hErr := dialer.DialContext(ctx, network, serverAddr)
|
||||
@ -144,6 +139,7 @@ func NewHTTPSRaw(
|
||||
}
|
||||
} else {
|
||||
transport = &http.Transport{
|
||||
IdleConnTimeout: C.TCPKeepAliveInitial,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, network, serverAddr)
|
||||
},
|
||||
@ -167,33 +163,12 @@ func (t *HTTPSTransport) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) Close() error {
|
||||
t.transportAccess.Lock()
|
||||
defer t.transportAccess.Unlock()
|
||||
t.transport.CloseIdleConnections()
|
||||
t.transport = t.transport.Clone()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
startAt := time.Now()
|
||||
response, err := t.exchange(ctx, message)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
t.transportAccess.Lock()
|
||||
defer t.transportAccess.Unlock()
|
||||
if t.transportResetAt.After(startAt) {
|
||||
return nil, err
|
||||
}
|
||||
t.transport.CloseIdleConnections()
|
||||
t.transport = t.transport.Clone()
|
||||
t.transportResetAt = time.Now()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
exMessage := *message
|
||||
exMessage.Id = 0
|
||||
exMessage.Compress = true
|
||||
|
@ -3,6 +3,7 @@ package local
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@ -13,7 +14,6 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
@ -24,11 +24,9 @@ var _ adapter.DNSTransport = (*Transport)(nil)
|
||||
|
||||
type Transport struct {
|
||||
dns.TransportAdapter
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
hosts *hosts.File
|
||||
dialer N.Dialer
|
||||
resolved ResolvedResolver
|
||||
ctx context.Context
|
||||
hosts *hosts.File
|
||||
dialer N.Dialer
|
||||
}
|
||||
|
||||
func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.LocalDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
@ -39,39 +37,20 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
||||
return &Transport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
hosts: hosts.NewFile(hosts.DefaultPath),
|
||||
dialer: transportDialer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
switch stage {
|
||||
case adapter.StartStateInitialize:
|
||||
resolvedResolver, err := NewResolvedResolver(t.ctx, t.logger)
|
||||
if err == nil {
|
||||
err = resolvedResolver.Start()
|
||||
if err == nil {
|
||||
t.resolved = resolvedResolver
|
||||
} else {
|
||||
t.logger.Warn(E.Cause(err, "initialize resolved resolver"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Close() error {
|
||||
if t.resolved != nil {
|
||||
return t.resolved.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if t.resolved != nil && t.resolved.Available() {
|
||||
return t.resolved.Exchange(ctx, message)
|
||||
}
|
||||
question := message.Question[0]
|
||||
domain := dns.FqdnToDomain(question.Name)
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
@ -112,9 +91,9 @@ func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfi
|
||||
startRacer := func(ctx context.Context, fqdn string) {
|
||||
response, err := t.tryOneName(ctx, systemConfig, fqdn, message)
|
||||
if err == nil {
|
||||
if response.Rcode != mDNS.RcodeSuccess {
|
||||
err = dns.RcodeError(response.Rcode)
|
||||
} else if len(dns.MessageToAddresses(response)) == 0 {
|
||||
var addresses []netip.Addr
|
||||
addresses, err = dns.MessageToAddresses(response)
|
||||
if err == nil && len(addresses) == 0 {
|
||||
err = E.New(fqdn, ": empty result")
|
||||
}
|
||||
}
|
||||
|
@ -33,10 +33,6 @@ func NewFallbackTransport(ctx context.Context, logger log.ContextLogger, tag str
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
if platformInterface == nil {
|
||||
return transport, nil
|
||||
}
|
||||
return &FallbackTransport{
|
||||
DNSTransport: transport,
|
||||
ctx: ctx,
|
||||
@ -44,11 +40,11 @@ func NewFallbackTransport(ctx context.Context, logger log.ContextLogger, tag str
|
||||
}
|
||||
|
||||
func (f *FallbackTransport) Start(stage adapter.StartStage) error {
|
||||
err := f.DNSTransport.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
if stage != adapter.StartStatePostStart {
|
||||
platformInterface := service.FromContext[platform.Interface](f.ctx)
|
||||
if platformInterface == nil {
|
||||
return nil
|
||||
}
|
||||
inboundManager := service.FromContext[adapter.InboundManager](f.ctx)
|
||||
@ -63,7 +59,7 @@ func (f *FallbackTransport) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
|
||||
func (f *FallbackTransport) Close() error {
|
||||
return f.DNSTransport.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FallbackTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
|
@ -1,14 +0,0 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type ResolvedResolver interface {
|
||||
Start() error
|
||||
Close() error
|
||||
Available() bool
|
||||
Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error)
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/service/resolved"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type DBusResolvedResolver struct {
|
||||
logger logger.ContextLogger
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
systemBus *dbus.Conn
|
||||
resoledObject atomic.TypedValue[dbus.BusObject]
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
|
||||
interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor()
|
||||
if interfaceMonitor == nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
systemBus, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DBusResolvedResolver{
|
||||
logger: logger,
|
||||
interfaceMonitor: interfaceMonitor,
|
||||
systemBus: systemBus,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *DBusResolvedResolver) Start() error {
|
||||
t.updateStatus()
|
||||
err := t.systemBus.BusObject().AddMatchSignal(
|
||||
"org.freedesktop.DBus",
|
||||
"NameOwnerChanged",
|
||||
dbus.WithMatchSender("org.freedesktop.DBus"),
|
||||
dbus.WithMatchArg(0, "org.freedesktop.resolve1.Manager"),
|
||||
).Err
|
||||
if err != nil {
|
||||
return E.Cause(err, "configure resolved restart listener")
|
||||
}
|
||||
go t.loopUpdateStatus()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DBusResolvedResolver) Close() error {
|
||||
t.closeOnce.Do(func() {
|
||||
if t.systemBus != nil {
|
||||
_ = t.systemBus.Close()
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DBusResolvedResolver) Available() bool {
|
||||
return t.resoledObject.Load() == nil
|
||||
}
|
||||
|
||||
func (t *DBusResolvedResolver) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
defaultInterface := t.interfaceMonitor.DefaultInterface()
|
||||
if defaultInterface == nil {
|
||||
return nil, E.New("missing default interface")
|
||||
}
|
||||
resolvedObject := t.resoledObject.Load()
|
||||
if resolvedObject == nil {
|
||||
return nil, E.New("DBus interface for resolved is not available")
|
||||
}
|
||||
question := message.Question[0]
|
||||
call := resolvedObject.CallWithContext(
|
||||
ctx,
|
||||
"org.freedesktop.resolve1.Manager.ResolveRecord",
|
||||
0,
|
||||
int32(defaultInterface.Index),
|
||||
question.Name,
|
||||
question.Qclass,
|
||||
question.Qtype,
|
||||
uint64(0),
|
||||
)
|
||||
if call.Err != nil {
|
||||
return nil, E.Cause(call.Err, " resolve record via resolved")
|
||||
}
|
||||
var (
|
||||
records []resolved.ResourceRecord
|
||||
outflags uint64
|
||||
)
|
||||
err := call.Store(&records, &outflags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
Id: message.Id,
|
||||
Response: true,
|
||||
Authoritative: true,
|
||||
RecursionDesired: true,
|
||||
RecursionAvailable: true,
|
||||
Rcode: mDNS.RcodeSuccess,
|
||||
},
|
||||
Question: []mDNS.Question{question},
|
||||
}
|
||||
for _, record := range records {
|
||||
var rr mDNS.RR
|
||||
rr, _, err = mDNS.UnpackRR(record.Data, 0)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "unpack resource record")
|
||||
}
|
||||
response.Answer = append(response.Answer, rr)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (t *DBusResolvedResolver) loopUpdateStatus() {
|
||||
signalChan := make(chan *dbus.Signal, 1)
|
||||
t.systemBus.Signal(signalChan)
|
||||
for signal := range signalChan {
|
||||
var restarted bool
|
||||
if signal.Name == "org.freedesktop.DBus.NameOwnerChanged" {
|
||||
if len(signal.Body) != 3 || signal.Body[2].(string) == "" {
|
||||
continue
|
||||
} else {
|
||||
restarted = true
|
||||
}
|
||||
}
|
||||
if restarted {
|
||||
t.updateStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *DBusResolvedResolver) updateStatus() {
|
||||
dbusObject := t.systemBus.Object("org.freedesktop.resolve1", "/org/freedesktop/resolve1")
|
||||
err := dbusObject.Call("org.freedesktop.DBus.Peer.Ping", 0).Err
|
||||
if err != nil {
|
||||
if t.resoledObject.Swap(nil) != nil {
|
||||
t.logger.Debug("systemd-resolved service is gone")
|
||||
}
|
||||
return
|
||||
}
|
||||
t.resoledObject.Store(dbusObject)
|
||||
t.logger.Debug("using systemd-resolved service as resolver")
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
//go:build !linux
|
||||
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
@ -104,7 +104,7 @@ func (c *dnsConfig) serverOffset() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *dnsConfig) nameList(name string) []string {
|
||||
func (conf *dnsConfig) nameList(name string) []string {
|
||||
l := len(name)
|
||||
rooted := l > 0 && name[l-1] == '.'
|
||||
if l > 254 || l == 254 && !rooted {
|
||||
@ -118,15 +118,15 @@ func (c *dnsConfig) nameList(name string) []string {
|
||||
return []string{name}
|
||||
}
|
||||
|
||||
hasNdots := strings.Count(name, ".") >= c.ndots
|
||||
hasNdots := strings.Count(name, ".") >= conf.ndots
|
||||
name += "."
|
||||
// l++
|
||||
|
||||
names := make([]string, 0, 1+len(c.search))
|
||||
names := make([]string, 0, 1+len(conf.search))
|
||||
if hasNdots && !avoidDNS(name) {
|
||||
names = append(names, name)
|
||||
}
|
||||
for _, suffix := range c.search {
|
||||
for _, suffix := range conf.search {
|
||||
fqdn := name + suffix
|
||||
if !avoidDNS(fqdn) && len(fqdn) <= 254 {
|
||||
names = append(names, fqdn)
|
||||
|
@ -20,8 +20,8 @@ import (
|
||||
)
|
||||
|
||||
func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
|
||||
var state C.struct___res_state
|
||||
if C.res_ninit(&state) != 0 {
|
||||
var state C.res_state
|
||||
if C.res_ninit(state) != 0 {
|
||||
return &dnsConfig{
|
||||
servers: defaultNS,
|
||||
search: dnsDefaultSearch(),
|
||||
|
@ -1,13 +0,0 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDNSReadConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.NoError(t, dnsReadConfig(context.Background(), "/etc/resolv.conf").err)
|
||||
}
|
@ -30,7 +30,7 @@ func RegisterTLS(registry *dns.TransportRegistry) {
|
||||
type TLSTransport struct {
|
||||
dns.TransportAdapter
|
||||
logger logger.ContextLogger
|
||||
dialer tls.Dialer
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
access sync.Mutex
|
||||
@ -67,7 +67,7 @@ func NewTLSRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer
|
||||
return &TLSTransport{
|
||||
TransportAdapter: adapter,
|
||||
logger: logger,
|
||||
dialer: tls.NewDialer(dialer, tlsConfig),
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
@ -100,10 +100,15 @@ func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
||||
return response, nil
|
||||
}
|
||||
}
|
||||
tlsConn, err := t.dialer.DialTLSContext(ctx, t.serverAddr)
|
||||
tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConn, err := tls.ClientHandshake(ctx, tcpConn, t.tlsConfig)
|
||||
if err != nil {
|
||||
tcpConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return t.exchange(message, &tlsDNSConn{Conn: tlsConn})
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer
|
||||
logger: logger,
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
udpSize: 2048,
|
||||
udpSize: 512,
|
||||
tcpTransport: &TCPTransport{
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
@ -97,19 +97,15 @@ func (t *UDPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
||||
}
|
||||
|
||||
func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
t.access.Lock()
|
||||
if edns0Opt := message.IsEdns0(); edns0Opt != nil {
|
||||
if udpSize := int(edns0Opt.UDPSize()); udpSize > t.udpSize {
|
||||
t.udpSize = udpSize
|
||||
close(t.done)
|
||||
t.done = make(chan struct{})
|
||||
}
|
||||
}
|
||||
t.access.Unlock()
|
||||
conn, err := t.open(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if edns0Opt := message.IsEdns0(); edns0Opt != nil {
|
||||
if udpSize := int(edns0Opt.UDPSize()); udpSize > t.udpSize {
|
||||
t.udpSize = udpSize
|
||||
}
|
||||
}
|
||||
buffer := buf.NewSize(1 + message.Len())
|
||||
defer buffer.Release()
|
||||
exMessage := *message
|
||||
|
@ -2,202 +2,11 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.13.0-alpha.2
|
||||
|
||||
* Add `preferred_by` rule item **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
The new `preferred_by` routing rule item allows you to
|
||||
match preferred domains and addresses for specific outbounds.
|
||||
|
||||
See [Route Rule](/configuration/route/rule/#preferred_by).
|
||||
|
||||
#### 1.13.0-alpha.1
|
||||
|
||||
* Add interface address rule items **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
New interface address rules allow you to dynamically adjust rules based on your network environment.
|
||||
|
||||
See [Route Rule](/configuration/route/rule/), [DNS Route Rule](/configuration/dns/rule/)
|
||||
and [Headless Rule](/configuration/rule-set/headless-rule/).
|
||||
|
||||
#### 1.12.1
|
||||
#### 1.12.0-beta.29
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
### 1.12.0
|
||||
|
||||
* Refactor DNS servers **1**
|
||||
* Add domain resolver options**2**
|
||||
* Add TLS fragment/record fragment support to route options and outbound TLS options **3**
|
||||
* Add certificate options **4**
|
||||
* Add Tailscale endpoint and DNS server **5**
|
||||
* Drop support for go1.22 **6**
|
||||
* Add AnyTLS protocol **7**
|
||||
* Migrate to stdlib ECH implementation **8**
|
||||
* Add NTP sniffer **9**
|
||||
* Add wildcard SNI support for ShadowTLS inbound **10**
|
||||
* Improve `auto_redirect` **11**
|
||||
* Add control options for listeners **12**
|
||||
* Add DERP service **13**
|
||||
* Add Resolved service and DNS server **14**
|
||||
* Add SSM API service **15**
|
||||
* Add loopback address support for tun **16**
|
||||
* Improve tun performance on Apple platforms **17**
|
||||
* Update quic-go to v0.52.0
|
||||
* Update gVisor to 20250319.0
|
||||
* Update the status of graphical clients in stores **18**
|
||||
|
||||
**1**:
|
||||
|
||||
DNS servers are refactored for better performance and scalability.
|
||||
|
||||
See [DNS server](/configuration/dns/server/).
|
||||
|
||||
For migration, see [Migrate to new DNS server formats](/migration/#migrate-to-new-dns-servers).
|
||||
|
||||
Compatibility for old formats will be removed in sing-box 1.14.0.
|
||||
|
||||
**2**:
|
||||
|
||||
Legacy `outbound` DNS rules are deprecated
|
||||
and can be replaced by the new `domain_resolver` option.
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial/#domain_resolver) and
|
||||
[Route](/configuration/route/#default_domain_resolver).
|
||||
|
||||
For migration,
|
||||
see [Migrate outbound DNS rule items to domain resolver](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver).
|
||||
|
||||
**3**:
|
||||
|
||||
See [Route Action](/configuration/route/rule_action/#tls_fragment) and [TLS](/configuration/shared/tls/).
|
||||
|
||||
**4**:
|
||||
|
||||
New certificate options allow you to manage the default list of trusted X509 CA certificates.
|
||||
|
||||
For the system certificate list, fixed Go not reading Android trusted certificates correctly.
|
||||
|
||||
You can also use the Mozilla Included List instead, or add trusted certificates yourself.
|
||||
|
||||
See [Certificate](/configuration/certificate/).
|
||||
|
||||
**5**:
|
||||
|
||||
See [Tailscale](/configuration/endpoint/tailscale/).
|
||||
|
||||
**6**:
|
||||
|
||||
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
||||
|
||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||
|
||||
**7**:
|
||||
|
||||
The new AnyTLS protocol claims to mitigate TLS proxy traffic characteristics and comes with a new multiplexing scheme.
|
||||
|
||||
See [AnyTLS Inbound](/configuration/inbound/anytls/) and [AnyTLS Outbound](/configuration/outbound/anytls/).
|
||||
|
||||
**8**:
|
||||
|
||||
See [TLS](/configuration/shared/tls).
|
||||
|
||||
The build tag `with_ech` is no longer needed and has been removed.
|
||||
|
||||
**9**:
|
||||
|
||||
See [Protocol Sniff](/configuration/route/sniff/).
|
||||
|
||||
**10**:
|
||||
|
||||
See [ShadowTLS](/configuration/inbound/shadowtls/#wildcard_sni).
|
||||
|
||||
**11**:
|
||||
|
||||
Now `auto_redirect` fixes compatibility issues between tun and Docker bridge networks,
|
||||
see [Tun](/configuration/inbound/tun/#auto_redirect).
|
||||
|
||||
**12**:
|
||||
|
||||
You can now set `bind_interface`, `routing_mark` and `reuse_addr` in Listen Fields.
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen/).
|
||||
|
||||
**13**:
|
||||
|
||||
DERP service is a Tailscale DERP server, similar to [derper](https://pkg.go.dev/tailscale.com/cmd/derper).
|
||||
|
||||
See [DERP Service](/configuration/service/derp/).
|
||||
|
||||
**14**:
|
||||
|
||||
Resolved service is a fake systemd-resolved DBUS service to receive DNS settings from other programs
|
||||
(e.g. NetworkManager) and provide DNS resolution.
|
||||
|
||||
See [Resolved Service](/configuration/service/resolved/) and [Resolved DNS Server](/configuration/dns/server/resolved/).
|
||||
|
||||
**15**:
|
||||
|
||||
SSM API service is a RESTful API server for managing Shadowsocks servers.
|
||||
|
||||
See [SSM API Service](/configuration/service/ssm-api/).
|
||||
|
||||
**16**:
|
||||
|
||||
TUN now implements SideStore's StosVPN.
|
||||
|
||||
See [Tun](/configuration/inbound/tun/#loopback_address).
|
||||
|
||||
**17**:
|
||||
|
||||
We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack.
|
||||
|
||||
The following data was tested using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro.
|
||||
|
||||
| Version | Stack | MTU | Upload | Download |
|
||||
|-------------|--------|-------|--------|----------|
|
||||
| 1.11.15 | gvisor | 1500 | 852M | 2.57G |
|
||||
| 1.12.0-rc.4 | gvisor | 1500 | 2.90G | 4.68G |
|
||||
| 1.11.15 | gvisor | 4064 | 2.31G | 6.34G |
|
||||
| 1.12.0-rc.4 | gvisor | 4064 | 7.54G | 12.2G |
|
||||
| 1.11.15 | gvisor | 65535 | 27.6G | 18.1G |
|
||||
| 1.12.0-rc.4 | gvisor | 65535 | 39.8G | 34.7G |
|
||||
| 1.11.15 | system | 1500 | 664M | 706M |
|
||||
| 1.12.0-rc.4 | system | 1500 | 2.44G | 2.51G |
|
||||
| 1.11.15 | system | 4064 | 1.88G | 1.94G |
|
||||
| 1.12.0-rc.4 | system | 4064 | 6.45G | 6.27G |
|
||||
| 1.11.15 | system | 65535 | 26.2G | 17.4G |
|
||||
| 1.12.0-rc.4 | system | 65535 | 17.6G | 21.0G |
|
||||
|
||||
**18**:
|
||||
|
||||
We continue to experience issues updating our sing-box apps on the App Store and Play Store.
|
||||
Until we rewrite and resubmit the apps, they are considered irrecoverable.
|
||||
Therefore, after this release, we will not be repeating this notice unless there is new information.
|
||||
|
||||
#### 1.11.15
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
|
||||
violated the rules (TestFlight users are not affected)._
|
||||
|
||||
#### 1.12.0-beta.32
|
||||
|
||||
* Improve tun performance on Apple platforms **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack.
|
||||
|
||||
#### 1.11.14
|
||||
### 1.11.14
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -247,7 +56,7 @@ You can now choose what the DERP home page shows, just like with derper's `-home
|
||||
|
||||
See [DERP](/configuration/service/derp/#home).
|
||||
|
||||
#### 1.11.13
|
||||
### 1.11.13
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -285,7 +94,7 @@ SSM API service is a RESTful API server for managing Shadowsocks servers.
|
||||
|
||||
See [SSM API Service](/configuration/service/ssm-api/).
|
||||
|
||||
#### 1.11.11
|
||||
### 1.11.11
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -317,7 +126,7 @@ You can now set `bind_interface`, `routing_mark` and `reuse_addr` in Listen Fiel
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen/).
|
||||
|
||||
#### 1.11.10
|
||||
### 1.11.10
|
||||
|
||||
* Undeprecate the `block` outbound **1**
|
||||
* Fixes and improvements
|
||||
@ -335,7 +144,7 @@ violated the rules (TestFlight users are not affected)._
|
||||
* Update quic-go to v0.51.0
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.11.9
|
||||
### 1.11.9
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -346,7 +155,7 @@ violated the rules (TestFlight users are not affected)._
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.11.8
|
||||
### 1.11.8
|
||||
|
||||
* Improve `auto_redirect` **1**
|
||||
* Fixes and improvements
|
||||
@ -363,7 +172,7 @@ violated the rules (TestFlight users are not affected)._
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.11.7
|
||||
### 1.11.7
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -379,7 +188,7 @@ violated the rules (TestFlight users are not affected)._
|
||||
Now `auto_redirect` fixes compatibility issues between tun and Docker bridge networks,
|
||||
see [Tun](/configuration/inbound/tun/#auto_redirect).
|
||||
|
||||
#### 1.11.6
|
||||
### 1.11.6
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -420,7 +229,7 @@ See [Protocol Sniff](/configuration/route/sniff/).
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial/#domain_resolver).
|
||||
|
||||
#### 1.11.5
|
||||
### 1.11.5
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -436,7 +245,7 @@ violated the rules (TestFlight users are not affected)._
|
||||
|
||||
See [DNS Rule Action](/configuration/dns/rule_action/#predefined).
|
||||
|
||||
#### 1.11.4
|
||||
### 1.11.4
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -492,7 +301,7 @@ Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to co
|
||||
|
||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||
|
||||
#### 1.11.3
|
||||
### 1.11.3
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -503,7 +312,7 @@ process._
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.11.1
|
||||
### 1.11.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -682,7 +491,7 @@ See [Hysteria2](/configuration/outbound/hysteria2/).
|
||||
|
||||
When `up_mbps` and `down_mbps` are set, `ignore_client_bandwidth` instead denies clients from using BBR CC.
|
||||
|
||||
#### 1.10.7
|
||||
### 1.10.7
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@ -777,7 +586,7 @@ and the old outbound will be removed in sing-box 1.13.0.
|
||||
See [Endpoint](/configuration/endpoint/), [WireGuard Endpoint](/configuration/endpoint/wireguard/)
|
||||
and [Migrate WireGuard outbound fields to route options](/migration/#migrate-wireguard-outbound-to-endpoint).
|
||||
|
||||
#### 1.10.2
|
||||
### 1.10.2
|
||||
|
||||
* Add deprecated warnings
|
||||
* Fix proxying websocket connections in HTTP/mixed inbounds
|
||||
@ -914,7 +723,7 @@ See [Rule Action](/configuration/route/rule_action/).
|
||||
* Update quic-go to v0.48.0
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.10.1
|
||||
### 1.10.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
|
@ -2,12 +2,6 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [interface_address](#interface_address)
|
||||
:material-plus: [network_interface_address](#network_interface_address)
|
||||
:material-plus: [default_interface_address](#default_interface_address)
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [ip_accept_any](#ip_accept_any)
|
||||
@ -136,19 +130,6 @@ icon: material/alert-decagram
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"interface_address": {
|
||||
"en0": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"network_interface_address": {
|
||||
"wifi": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"default_interface_address": [
|
||||
"2000::/3"
|
||||
],
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@ -378,36 +359,6 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
|
||||
|
||||
Match if network is in Low Data Mode.
|
||||
|
||||
#### interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux, Windows, and macOS.
|
||||
|
||||
Match interface address.
|
||||
|
||||
#### network_interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Matches network interface (same values as `network_type`) address.
|
||||
|
||||
#### default_interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux, Windows, and macOS.
|
||||
|
||||
Match default interface address.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
@ -2,12 +2,6 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [interface_address](#interface_address)
|
||||
:material-plus: [network_interface_address](#network_interface_address)
|
||||
:material-plus: [default_interface_address](#default_interface_address)
|
||||
|
||||
!!! quote "sing-box 1.12.0 中的更改"
|
||||
|
||||
:material-plus: [ip_accept_any](#ip_accept_any)
|
||||
@ -136,19 +130,6 @@ icon: material/alert-decagram
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"interface_address": {
|
||||
"en0": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"network_interface_address": {
|
||||
"wifi": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"default_interface_address": [
|
||||
"2000::/3"
|
||||
],
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@ -377,36 +358,6 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
匹配如果网络在低数据模式下。
|
||||
|
||||
#### interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS.
|
||||
|
||||
匹配接口地址。
|
||||
|
||||
#### network_interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配网络接口(可用值同 `network_type`)地址。
|
||||
|
||||
#### default_interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS.
|
||||
|
||||
匹配默认接口地址。
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
@ -61,7 +61,7 @@ WireGuard MTU。
|
||||
|
||||
==必填==
|
||||
|
||||
接口的 IPv4/IPv6 地址或地址段的列表。
|
||||
接口的 IPv4/IPv6 地址或地址段的列表您。
|
||||
|
||||
要分配给接口的 IP(v4 或 v6)地址段列表。
|
||||
|
||||
|
@ -88,7 +88,7 @@ icon: material/delete-clock
|
||||
|
||||
==必填==
|
||||
|
||||
接口的 IPv4/IPv6 地址或地址段的列表。
|
||||
接口的 IPv4/IPv6 地址或地址段的列表您。
|
||||
|
||||
要分配给接口的 IP(v4 或 v6)地址段列表。
|
||||
|
||||
|
@ -2,13 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [interface_address](#interface_address)
|
||||
:material-plus: [network_interface_address](#network_interface_address)
|
||||
:material-plus: [default_interface_address](#default_interface_address)
|
||||
:material-plus: [preferred_by](#preferred_by)
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
@ -135,29 +128,12 @@ icon: material/new-box
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"interface_address": {
|
||||
"en0": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"network_interface_address": {
|
||||
"wifi": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"default_interface_address": [
|
||||
"2000::/3"
|
||||
],
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
"wifi_bssid": [
|
||||
"00:00:00:00:00:00"
|
||||
],
|
||||
"preferred_by": [
|
||||
"tailscale",
|
||||
"wireguard"
|
||||
],
|
||||
"rule_set": [
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
@ -387,36 +363,6 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
|
||||
|
||||
Match if network is in Low Data Mode.
|
||||
|
||||
#### interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux, Windows, and macOS.
|
||||
|
||||
Match interface address.
|
||||
|
||||
#### network_interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Matches network interface (same values as `network_type`) address.
|
||||
|
||||
#### default_interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux, Windows, and macOS.
|
||||
|
||||
Match default interface address.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
@ -433,17 +379,6 @@ Match WiFi SSID.
|
||||
|
||||
Match WiFi BSSID.
|
||||
|
||||
#### preferred_by
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
Match specified outbounds' preferred routes.
|
||||
|
||||
| Type | Match |
|
||||
|-------------|-----------------------------------------------|
|
||||
| `tailscale` | Match MagicDNS domains and peers' allowed IPs |
|
||||
| `wireguard` | Match peers's allowed IPs |
|
||||
|
||||
#### rule_set
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
@ -2,13 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [interface_address](#interface_address)
|
||||
:material-plus: [network_interface_address](#network_interface_address)
|
||||
:material-plus: [default_interface_address](#default_interface_address)
|
||||
:material-plus: [preferred_by](#preferred_by)
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: [action](#action)
|
||||
@ -132,29 +125,12 @@ icon: material/new-box
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"interface_address": {
|
||||
"en0": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"network_interface_address": {
|
||||
"wifi": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"default_interface_address": [
|
||||
"2000::/3"
|
||||
],
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
"wifi_bssid": [
|
||||
"00:00:00:00:00:00"
|
||||
],
|
||||
"preferred_by": [
|
||||
"tailscale",
|
||||
"wireguard"
|
||||
],
|
||||
"rule_set": [
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
@ -361,7 +337,7 @@ icon: material/new-box
|
||||
|
||||
匹配网络类型。
|
||||
|
||||
可用值: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
#### network_is_expensive
|
||||
|
||||
@ -384,36 +360,6 @@ icon: material/new-box
|
||||
|
||||
匹配如果网络在低数据模式下。
|
||||
|
||||
#### interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS.
|
||||
|
||||
匹配接口地址。
|
||||
|
||||
#### network_interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配网络接口(可用值同 `network_type`)地址。
|
||||
|
||||
#### default_interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS.
|
||||
|
||||
匹配默认接口地址。
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
@ -430,17 +376,6 @@ icon: material/new-box
|
||||
|
||||
匹配 WiFi BSSID。
|
||||
|
||||
#### preferred_by
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
匹配制定出站的首选路由。
|
||||
|
||||
| 类型 | 匹配 |
|
||||
|-------------|--------------------------------|
|
||||
| `tailscale` | 匹配 MagicDNS 域名和对端的 allowed IPs |
|
||||
| `wireguard` | 匹配对端的 allowed IPs |
|
||||
|
||||
#### rule_set
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
@ -2,11 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [network_interface_address](#network_interface_address)
|
||||
:material-plus: [default_interface_address](#default_interface_address)
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: [network_type](#network_type)
|
||||
@ -83,14 +78,6 @@ icon: material/new-box
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"network_interface_address": {
|
||||
"wifi": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"default_interface_address": [
|
||||
"2000::/3"
|
||||
],
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@ -238,26 +225,6 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
|
||||
|
||||
Match if network is in Low Data Mode.
|
||||
|
||||
#### network_interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Matches network interface (same values as `network_type`) address.
|
||||
|
||||
#### default_interface_address
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux, Windows, and macOS.
|
||||
|
||||
Match default interface address.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
@ -2,11 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [network_interface_address](#network_interface_address)
|
||||
:material-plus: [default_interface_address](#default_interface_address)
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: [network_type](#network_type)
|
||||
@ -83,14 +78,6 @@ icon: material/new-box
|
||||
],
|
||||
"network_is_expensive": false,
|
||||
"network_is_constrained": false,
|
||||
"network_interface_address": {
|
||||
"wifi": [
|
||||
"2000::/3"
|
||||
]
|
||||
},
|
||||
"default_interface_address": [
|
||||
"2000::/3"
|
||||
],
|
||||
"wifi_ssid": [
|
||||
"My WIFI"
|
||||
],
|
||||
@ -234,26 +221,6 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
匹配如果网络在低数据模式下。
|
||||
|
||||
#### network_interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配网络接口(可用值同 `network_type`)地址。
|
||||
|
||||
#### default_interface_address
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux、Windows 和 macOS.
|
||||
|
||||
匹配默认接口地址。
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
!!! quote ""
|
||||
|
@ -2,10 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: version `4`
|
||||
|
||||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
:material-plus: version `3`
|
||||
@ -40,7 +36,6 @@ Version of rule-set.
|
||||
* 1: sing-box 1.8.0: Initial rule-set version.
|
||||
* 2: sing-box 1.10.0: Optimized memory usages of `domain_suffix` rules in binary rule-sets.
|
||||
* 3: sing-box 1.11.0: Added `network_type`, `network_is_expensive` and `network_is_constrainted` rule items.
|
||||
* 4: sing-box 1.13.0: Added `network_interface_address` and `default_interface_address` rule items.
|
||||
|
||||
#### rules
|
||||
|
||||
|
@ -2,10 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: version `4`
|
||||
|
||||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
:material-plus: version `3`
|
||||
@ -40,7 +36,6 @@ icon: material/new-box
|
||||
* 1: sing-box 1.8.0: 初始规则集版本。
|
||||
* 2: sing-box 1.10.0: 优化了二进制规则集中 `domain_suffix` 规则的内存使用。
|
||||
* 3: sing-box 1.11.0: 添加了 `network_type`、 `network_is_expensive` 和 `network_is_constrainted` 规则项。
|
||||
* 4: sing-box 1.13.0: 添加了 `network_interface_address` 和 `default_interface_address` 规则项。
|
||||
|
||||
#### rules
|
||||
|
||||
|
@ -108,7 +108,7 @@ flowchart TB
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"address": ["172.19.0.1/30"],
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"auto_route": true,
|
||||
// "auto_redirect": true, // On linux
|
||||
"strict_route": true
|
||||
@ -162,7 +162,8 @@ flowchart TB
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"],
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||
"auto_route": true,
|
||||
// "auto_redirect": true, // On linux
|
||||
"strict_route": true
|
||||
@ -232,7 +233,8 @@ flowchart TB
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"address": ["172.19.0.1/30","fdfe:dcba:9876::1/126"],
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"inet6_address": "fdfe:dcba:9876::1/126",
|
||||
"auto_route": true,
|
||||
// "auto_redirect": true, // On linux
|
||||
"strict_route": true
|
||||
|
@ -351,15 +351,14 @@ DNS servers are refactored for better performance and scalability.
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
"servers": [
|
||||
{
|
||||
"domain": [
|
||||
"example.com"
|
||||
],
|
||||
// other rules
|
||||
|
||||
"action": "predefined",
|
||||
"rcode": "REFUSED"
|
||||
"type": "predefined",
|
||||
"responses": [
|
||||
{
|
||||
"rcode": "REFUSED"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1188,4 +1187,4 @@ which will disrupt the existing `process_path` use cases in Windows.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
@ -8,7 +8,7 @@ icon: material/arrange-bring-forward
|
||||
|
||||
DNS 服务器已经重构。
|
||||
|
||||
!!! info "引用"
|
||||
!!! info "饮用"
|
||||
|
||||
[DNS 服务器](/configuration/dns/server/) /
|
||||
[旧 DNS 服务器](/configuration/dns/server/legacy/)
|
||||
@ -351,15 +351,14 @@ DNS 服务器已经重构。
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"rules": [
|
||||
"servers": [
|
||||
{
|
||||
"domain": [
|
||||
"example.com"
|
||||
],
|
||||
// 其它规则
|
||||
|
||||
"action": "predefined",
|
||||
"rcode": "REFUSED"
|
||||
"type": "predefined",
|
||||
"responses": [
|
||||
{
|
||||
"rcode": "REFUSED"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
func cacheRouter(ctx context.Context) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Post("/fakeip/flush", flushFakeip(ctx))
|
||||
r.Post("/dns/flush", flushDNS(ctx))
|
||||
return r
|
||||
}
|
||||
|
||||
@ -32,13 +31,3 @@ func flushFakeip(ctx context.Context) func(w http.ResponseWriter, r *http.Reques
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func flushDNS(ctx context.Context) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
|
||||
if dnsRouter != nil {
|
||||
dnsRouter.ClearCache()
|
||||
}
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,10 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||
return false
|
||||
}
|
||||
_, isGroup := it.(adapter.OutboundGroup)
|
||||
return !isGroup
|
||||
if isGroup {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
|
||||
for _, detour := range outbounds {
|
||||
|
@ -22,7 +22,6 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
)
|
||||
|
||||
func BaseContext(platformInterface PlatformInterface) context.Context {
|
||||
@ -34,9 +33,7 @@ func BaseContext(platformInterface PlatformInterface) context.Context {
|
||||
})
|
||||
}
|
||||
}
|
||||
ctx := context.Background()
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
return box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||
return box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||
}
|
||||
|
||||
func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
|
||||
|
@ -18,7 +18,6 @@ func newIterator[T any](values []T) *iterator[T] {
|
||||
return &iterator[T]{values}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func newPtrIterator[T any](values []T) *iterator[*T] {
|
||||
return &iterator[*T]{common.Map(values, func(value T) *T { return &value })}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"os"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// https://github.com/SagerNet/sing-box/issues/3233
|
||||
// https://github.com/golang/go/issues/70508
|
||||
// https://github.com/tailscale/tailscale/issues/13452
|
||||
|
||||
//go:linkname checkPidfdOnce os.checkPidfdOnce
|
||||
var checkPidfdOnce func() error
|
||||
|
||||
func init() {
|
||||
checkPidfdOnce = func() error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
)
|
||||
|
||||
@ -43,6 +44,7 @@ type BoxService struct {
|
||||
|
||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||
ctx := BaseContext(platformInterface)
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
|
||||
options, err := parseConfig(ctx, configContent)
|
||||
if err != nil {
|
||||
|
@ -115,7 +115,7 @@ func (s *StatsService) RoutedPacketConnection(ctx context.Context, conn N.Packet
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
||||
}
|
||||
s.access.Unlock()
|
||||
return bufio.NewInt64CounterPacketConn(conn, readCounter, nil, writeCounter, nil)
|
||||
return bufio.NewInt64CounterPacketConn(conn, readCounter, writeCounter)
|
||||
}
|
||||
|
||||
func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
||||
@ -192,7 +192,7 @@ func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest
|
||||
var rtm runtime.MemStats
|
||||
runtime.ReadMemStats(&rtm)
|
||||
response := &SysStatsResponse{
|
||||
Uptime: uint32(time.Since(s.createdAt).Seconds()),
|
||||
Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
|
||||
NumGoroutine: uint32(runtime.NumGoroutine()),
|
||||
Alloc: rtm.Alloc,
|
||||
TotalAlloc: rtm.TotalAlloc,
|
||||
|
49
go.mod
49
go.mod
@ -5,36 +5,37 @@ go 1.23.1
|
||||
require (
|
||||
github.com/anytls/sing-anytls v0.0.8
|
||||
github.com/caddyserver/certmagic v0.23.0
|
||||
github.com/coder/websocket v1.8.13
|
||||
github.com/cloudflare/circl v1.6.1
|
||||
github.com/coder/websocket v1.8.12
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
github.com/go-chi/chi/v5 v5.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466
|
||||
github.com/gofrs/uuid/v5 v5.3.2
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f
|
||||
github.com/libdns/alidns v1.0.5-libdns.v1.beta1
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6
|
||||
github.com/libdns/alidns v1.0.4-libdns.v1.beta1
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422
|
||||
github.com/metacubex/utls v1.8.0
|
||||
github.com/metacubex/utls v1.7.0-alpha.3
|
||||
github.com/mholt/acmez/v3 v3.1.2
|
||||
github.com/miekg/dns v1.1.67
|
||||
github.com/miekg/dns v1.1.66
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/cors v1.2.1
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
github.com/sagernet/gomobile v0.1.8
|
||||
github.com/sagernet/gomobile v0.1.6
|
||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
||||
github.com/sagernet/quic-go v0.52.0-beta.1
|
||||
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f
|
||||
github.com/sagernet/sing-mux v0.3.3
|
||||
github.com/sagernet/sing-quic v0.5.0
|
||||
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b
|
||||
github.com/sagernet/sing-mux v0.3.2
|
||||
github.com/sagernet/sing-quic v0.5.0-beta.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
||||
github.com/sagernet/sing-tun v0.7.0-beta.1
|
||||
github.com/sagernet/sing-vmess v0.2.6
|
||||
github.com/sagernet/sing-tun v0.6.10-0.20250620051458-5e343c4b66b2
|
||||
github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88
|
||||
github.com/sagernet/smux v1.5.34-mod.2
|
||||
github.com/sagernet/tailscale v1.80.3-mod.5
|
||||
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
||||
@ -44,13 +45,13 @@ require (
|
||||
github.com/vishvananda/netns v0.0.5
|
||||
go.uber.org/zap v1.27.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/crypto v0.38.0
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
||||
golang.org/x/mod v0.27.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/grpc v1.72.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
@ -80,7 +81,7 @@ require (
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
@ -94,7 +95,7 @@ require (
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
|
||||
github.com/libdns/libdns v1.1.0 // indirect
|
||||
github.com/libdns/libdns v1.0.0-beta.1 // indirect
|
||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
|
||||
github.com/mdlayher/sdnotify v1.0.0 // indirect
|
||||
@ -122,14 +123,14 @@ require (
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
)
|
||||
|
117
go.sum
117
go.sum
@ -20,8 +20,10 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
|
||||
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
|
||||
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
|
||||
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
|
||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
@ -45,8 +47,8 @@ github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
|
||||
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
||||
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
|
||||
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
|
||||
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
|
||||
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84=
|
||||
@ -72,8 +74,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
@ -105,13 +107,12 @@ github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
|
||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
|
||||
github.com/libdns/alidns v1.0.5-libdns.v1.beta1 h1:txHK7UxDed3WFBDjrTZPuMn8X+WmhjBTTAMW5xdy5pQ=
|
||||
github.com/libdns/alidns v1.0.5-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g=
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 h1:3MGrVWs2COjMkQR17oUw1zMIPbm2YAzxDC3oGVZvQs8=
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60=
|
||||
github.com/libdns/alidns v1.0.4-libdns.v1.beta1 h1:ods22gD4PcT0g4qRX77ucykjz7Rppnkz3vQoxDbbKTM=
|
||||
github.com/libdns/alidns v1.0.4-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g=
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6 h1:0dlpPjNr8TaYZbkpwCiee4udBNrYrWG8EZPYEbjHEn8=
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6/go.mod h1:Aq4IXdjalB6mD0ELvKqJiIGim8zSC6mlIshRPMOAb5w=
|
||||
github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
|
||||
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/libdns v1.1.0 h1:9ze/tWvt7Df6sbhOJRB8jT33GHEHpEQXdtkE3hPthbU=
|
||||
github.com/libdns/libdns v1.1.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||
@ -124,12 +125,12 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac=
|
||||
github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ=
|
||||
github.com/metacubex/utls v1.7.0-alpha.3 h1:cp1cEMUnoifiWrGHRzo+nCwPRveN9yPD8QaRFmfcYxA=
|
||||
github.com/metacubex/utls v1.7.0-alpha.3/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
|
||||
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/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
|
||||
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
@ -156,8 +157,8 @@ github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
||||
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||
github.com/sagernet/gomobile v0.1.8 h1:vXgoN0pjsMONAaYCTdsKBX2T1kxuS7sbT/mZ7PElGoo=
|
||||
github.com/sagernet/gomobile v0.1.8/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
|
||||
github.com/sagernet/gomobile v0.1.6 h1:JkR1ToKOrdoiwULte4pYS5HYdPBzl2N+JNuuwVuLs0k=
|
||||
github.com/sagernet/gomobile v0.1.6/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38=
|
||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||
@ -167,22 +168,22 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
|
||||
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
|
||||
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
|
||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f h1:HIBo8l+tsS3wLwuI1E56uRTQw46QytXSUpZTP3vwG/U=
|
||||
github.com/sagernet/sing v0.7.6-0.20250815070458-d33ece7a184f/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||
github.com/sagernet/sing-quic v0.5.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak=
|
||||
github.com/sagernet/sing-quic v0.5.0/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
||||
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b h1:ZjTCYPb5f7aHdf1UpUvE22dVmf7BL8eQ/zLZhjgh7Wo=
|
||||
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
||||
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||
github.com/sagernet/sing-quic v0.5.0-beta.2 h1:j7KAbBuGmsKwSxVAQL5soJ+wDqxim4/llK2kxB0hSKk=
|
||||
github.com/sagernet/sing-quic v0.5.0-beta.2/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/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/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/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||
github.com/sagernet/sing-tun v0.7.0-beta.1 h1:mBIFXYAnGO5ey/HcCYanqnBx61E7yF8zTFGRZonGYmY=
|
||||
github.com/sagernet/sing-tun v0.7.0-beta.1/go.mod h1:AHJuRrLbNRJuivuFZ2VhXwDj4ViYp14szG5EkkKAqRQ=
|
||||
github.com/sagernet/sing-vmess v0.2.6 h1:1c4dGzeGy0kpBXXrT1sgiMZtHhdJylIT8eWrGhJYZec=
|
||||
github.com/sagernet/sing-vmess v0.2.6/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
|
||||
github.com/sagernet/sing-tun v0.6.10-0.20250620051458-5e343c4b66b2 h1:ykbqGFHDNVvp0jhgLime/XBAtQpcOcFpT8Rs5Hcc5n4=
|
||||
github.com/sagernet/sing-tun v0.6.10-0.20250620051458-5e343c4b66b2/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||
github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88 h1:0pVm8sPOel+BoiCddW3pV3cKDKEaSioVTYDdTSKjyFI=
|
||||
github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
|
||||
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
||||
github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
|
||||
@ -239,16 +240,16 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
@ -262,21 +263,21 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wus
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -285,20 +286,20 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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.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/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -308,10 +309,10 @@ golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdI
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
@ -100,6 +100,7 @@ func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) {
|
||||
}
|
||||
ruleAction.Action = C.RuleActionTypePredefined
|
||||
ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode))
|
||||
return
|
||||
}
|
||||
|
||||
type DNSClientOptions struct {
|
||||
@ -179,7 +180,7 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
options := o.Options.(*LegacyDNSServerOptions)
|
||||
serverURL, _ := url.Parse(options.Address)
|
||||
var serverType string
|
||||
if serverURL != nil && serverURL.Scheme != "" {
|
||||
if serverURL.Scheme != "" {
|
||||
serverType = serverURL.Scheme
|
||||
} else {
|
||||
switch options.Address {
|
||||
@ -216,7 +217,7 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
o.Type = C.DNSTypeUDP
|
||||
o.Options = &remoteOptions
|
||||
var serverAddr M.Socksaddr
|
||||
if serverURL == nil || serverURL.Scheme == "" {
|
||||
if serverURL.Scheme == "" {
|
||||
serverAddr = M.ParseSocksaddr(options.Address)
|
||||
} else {
|
||||
serverAddr = M.ParseSocksaddr(serverURL.Host)
|
||||
@ -231,9 +232,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
case C.DNSTypeTCP:
|
||||
o.Type = C.DNSTypeTCP
|
||||
o.Options = &remoteOptions
|
||||
if serverURL == nil {
|
||||
return E.New("invalid server address")
|
||||
}
|
||||
serverAddr := M.ParseSocksaddr(serverURL.Host)
|
||||
if !serverAddr.IsValid() {
|
||||
return E.New("invalid server address")
|
||||
@ -244,9 +242,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
}
|
||||
case C.DNSTypeTLS, C.DNSTypeQUIC:
|
||||
o.Type = serverType
|
||||
if serverURL == nil {
|
||||
return E.New("invalid server address")
|
||||
}
|
||||
serverAddr := M.ParseSocksaddr(serverURL.Host)
|
||||
if !serverAddr.IsValid() {
|
||||
return E.New("invalid server address")
|
||||
@ -266,9 +261,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
},
|
||||
}
|
||||
o.Options = &httpsOptions
|
||||
if serverURL == nil {
|
||||
return E.New("invalid server address")
|
||||
}
|
||||
serverAddr := M.ParseSocksaddr(serverURL.Host)
|
||||
if !serverAddr.IsValid() {
|
||||
return E.New("invalid server address")
|
||||
@ -282,9 +274,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
}
|
||||
case "rcode":
|
||||
var rcode int
|
||||
if serverURL == nil {
|
||||
return E.New("invalid server address")
|
||||
}
|
||||
switch serverURL.Host {
|
||||
case "success":
|
||||
rcode = dns.RcodeSuccess
|
||||
@ -306,9 +295,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
|
||||
case C.DNSTypeDHCP:
|
||||
o.Type = C.DNSTypeDHCP
|
||||
dhcpOptions := DHCPDNSServerOptions{}
|
||||
if serverURL == nil {
|
||||
return E.New("invalid server address")
|
||||
}
|
||||
if serverURL.Host != "" && serverURL.Host != "auto" {
|
||||
dhcpOptions.Interface = serverURL.Host
|
||||
}
|
||||
|
@ -67,46 +67,42 @@ func (r Rule) IsValid() bool {
|
||||
}
|
||||
|
||||
type RawDefaultRule struct {
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Client badoption.Listable[string] `json:"client,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"`
|
||||
NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
|
||||
DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"`
|
||||
PreferredBy badoption.Listable[string] `json:"preferred_by,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Client badoption.Listable[string] `json:"client,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
|
@ -68,48 +68,45 @@ func (r DNSRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type RawDefaultDNSRule struct {
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
IPAcceptAny bool `json:"ip_accept_any,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"`
|
||||
NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
|
||||
DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
IPAcceptAny bool `json:"ip_accept_any,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
|
@ -182,31 +182,28 @@ func (r HeadlessRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type DefaultHeadlessRule struct {
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
|
||||
DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"`
|
||||
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
DomainMatcher *domain.Matcher `json:"-"`
|
||||
SourceIPSet *netipx.IPSet `json:"-"`
|
||||
@ -243,7 +240,7 @@ type PlainRuleSetCompat _PlainRuleSetCompat
|
||||
func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
|
||||
var v any
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3, C.RuleSetVersion4:
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||
v = r.Options
|
||||
default:
|
||||
return nil, E.New("unknown rule-set version: ", r.Version)
|
||||
@ -258,7 +255,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||
}
|
||||
var v any
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3, C.RuleSetVersion4:
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||
v = &r.Options
|
||||
case 0:
|
||||
return E.New("missing rule-set version")
|
||||
@ -275,7 +272,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||
|
||||
func (r PlainRuleSetCompat) Upgrade() (PlainRuleSet, error) {
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3, C.RuleSetVersion4:
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||
default:
|
||||
return PlainRuleSet{}, E.New("unknown rule-set version: " + F.ToString(r.Version))
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ func RegisterOutbound(registry *outbound.Registry) {
|
||||
|
||||
type Outbound struct {
|
||||
outbound.Adapter
|
||||
dialer tls.Dialer
|
||||
dialer N.Dialer
|
||||
server M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
client *anytls.Client
|
||||
@ -58,8 +58,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outbound.dialer = tls.NewDialer(outboundDialer, tlsConfig)
|
||||
outbound.dialer = outboundDialer
|
||||
|
||||
client, err := anytls.NewClient(ctx, anytls.ClientConfig{
|
||||
Password: options.Password,
|
||||
@ -92,7 +91,16 @@ func (d anytlsDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
}
|
||||
|
||||
func (h *Outbound) dialOut(ctx context.Context) (net.Conn, error) {
|
||||
return h.dialer.DialTLSContext(ctx, h.server)
|
||||
conn, err := h.dialer.DialContext(ctx, N.NetworkTCP, h.server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConn, err := tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
common.Close(tlsConn, conn)
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
|
@ -313,7 +313,7 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) loopCheck() {
|
||||
if time.Since(g.lastActive.Load()) > g.interval {
|
||||
if time.Now().Sub(g.lastActive.Load()) > g.interval {
|
||||
g.lastActive.Store(time.Now())
|
||||
g.CheckOutbounds(false)
|
||||
}
|
||||
@ -323,7 +323,7 @@ func (g *URLTestGroup) loopCheck() {
|
||||
return
|
||||
case <-g.ticker.C:
|
||||
}
|
||||
if time.Since(g.lastActive.Load()) > g.idleTimeout {
|
||||
if time.Now().Sub(g.lastActive.Load()) > g.idleTimeout {
|
||||
g.access.Lock()
|
||||
g.ticker.Stop()
|
||||
g.ticker = nil
|
||||
@ -360,7 +360,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
||||
continue
|
||||
}
|
||||
history := g.history.LoadURLTestHistory(realTag)
|
||||
if !force && history != nil && time.Since(history.Time) < g.interval {
|
||||
if !force && history != nil && time.Now().Sub(history.Time) < g.interval {
|
||||
continue
|
||||
}
|
||||
checked[realTag] = true
|
||||
|
@ -34,7 +34,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
detour, err := tls.NewDialerFromOptions(ctx, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
detour, err := tls.NewDialerFromOptions(ctx, router, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -128,6 +128,7 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
|
@ -180,6 +180,7 @@ func (s *Outbound) connect() (*ssh.Client, error) {
|
||||
|
||||
func (s *Outbound) InterfaceUpdated() {
|
||||
common.Close(s.clientConn)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Outbound) Close() error {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@ -46,6 +47,8 @@ type DNSTransport struct {
|
||||
acceptDefaultResolvers bool
|
||||
dnsRouter adapter.DNSRouter
|
||||
endpointManager adapter.EndpointManager
|
||||
cfg *wgcfg.Config
|
||||
dnsCfg *nDNS.Config
|
||||
endpoint *Endpoint
|
||||
routePrefixes []netip.Prefix
|
||||
routes map[string][]adapter.DNSTransport
|
||||
@ -80,10 +83,10 @@ func (t *DNSTransport) Start(stage adapter.StartStage) error {
|
||||
if !isTailscale {
|
||||
return E.New("endpoint is not Tailscale: ", t.endpointTag)
|
||||
}
|
||||
if ep.onReconfigHook != nil {
|
||||
if ep.onReconfig != nil {
|
||||
return E.New("only one Tailscale DNS server is allowed for single endpoint")
|
||||
}
|
||||
ep.onReconfigHook = t.onReconfig
|
||||
ep.onReconfig = t.onReconfig
|
||||
t.endpoint = ep
|
||||
return nil
|
||||
}
|
||||
@ -92,6 +95,14 @@ func (t *DNSTransport) Reset() {
|
||||
}
|
||||
|
||||
func (t *DNSTransport) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *nDNS.Config) {
|
||||
if cfg == nil || dnsCfg == nil {
|
||||
return
|
||||
}
|
||||
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
|
||||
return
|
||||
}
|
||||
t.cfg = cfg
|
||||
t.dnsCfg = dnsCfg
|
||||
err := t.updateDNSServers(routerCfg, dnsCfg)
|
||||
if err != nil {
|
||||
t.logger.Error(E.Cause(err, "update DNS servers"))
|
||||
|
@ -10,9 +10,9 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@ -31,7 +31,6 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@ -50,14 +49,8 @@ import (
|
||||
"github.com/sagernet/tailscale/version"
|
||||
"github.com/sagernet/tailscale/wgengine"
|
||||
"github.com/sagernet/tailscale/wgengine/filter"
|
||||
"github.com/sagernet/tailscale/wgengine/router"
|
||||
"github.com/sagernet/tailscale/wgengine/wgcfg"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
var _ adapter.OutboundWithPreferredRoutes = (*Endpoint)(nil)
|
||||
|
||||
func init() {
|
||||
version.SetVersion("sing-box " + C.Version)
|
||||
}
|
||||
@ -77,12 +70,7 @@ type Endpoint struct {
|
||||
server *tsnet.Server
|
||||
stack *stack.Stack
|
||||
filter *atomic.Pointer[filter.Filter]
|
||||
onReconfigHook wgengine.ReconfigListener
|
||||
|
||||
cfg *wgcfg.Config
|
||||
dnsCfg *tsDNS.Config
|
||||
routeDomains atomic.TypedValue[map[string]bool]
|
||||
routePrefixes atomic.Pointer[netipx.IPSet]
|
||||
onReconfig wgengine.ReconfigListener
|
||||
|
||||
acceptRoutes bool
|
||||
exitNode string
|
||||
@ -228,7 +216,9 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
|
||||
if t.onReconfig != nil {
|
||||
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
|
||||
}
|
||||
|
||||
ipStack := t.server.ExportNetstack().ExportIPStack()
|
||||
gErr := ipStack.SetSpoofing(tun.DefaultNIC, true)
|
||||
@ -263,7 +253,8 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
||||
if err != nil {
|
||||
return E.Cause(err, "update prefs")
|
||||
}
|
||||
t.filter = atomic.PointerForm(localBackend.ExportFilter())
|
||||
t.filter = localBackend.ExportFilter()
|
||||
|
||||
go t.watchState()
|
||||
return nil
|
||||
}
|
||||
@ -482,58 +473,10 @@ func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
||||
}
|
||||
|
||||
func (t *Endpoint) PreferredDomain(domain string) bool {
|
||||
routeDomains := t.routeDomains.Load()
|
||||
if routeDomains == nil {
|
||||
return false
|
||||
}
|
||||
return routeDomains[strings.ToLower(domain)]
|
||||
}
|
||||
|
||||
func (t *Endpoint) PreferredAddress(address netip.Addr) bool {
|
||||
routePrefixes := t.routePrefixes.Load()
|
||||
if routePrefixes == nil {
|
||||
return false
|
||||
}
|
||||
return routePrefixes.Contains(address)
|
||||
}
|
||||
|
||||
func (t *Endpoint) Server() *tsnet.Server {
|
||||
return t.server
|
||||
}
|
||||
|
||||
func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *tsDNS.Config) {
|
||||
if cfg == nil || dnsCfg == nil {
|
||||
return
|
||||
}
|
||||
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
|
||||
return
|
||||
}
|
||||
t.cfg = cfg
|
||||
t.dnsCfg = dnsCfg
|
||||
|
||||
routeDomains := make(map[string]bool)
|
||||
for fqdn := range dnsCfg.Routes {
|
||||
routeDomains[fqdn.WithoutTrailingDot()] = true
|
||||
}
|
||||
for _, fqdn := range dnsCfg.SearchDomains {
|
||||
routeDomains[fqdn.WithoutTrailingDot()] = true
|
||||
}
|
||||
t.routeDomains.Store(routeDomains)
|
||||
|
||||
var builder netipx.IPSetBuilder
|
||||
for _, peer := range cfg.Peers {
|
||||
for _, allowedIP := range peer.AllowedIPs {
|
||||
builder.AddPrefix(allowedIP)
|
||||
}
|
||||
}
|
||||
t.routePrefixes.Store(common.Must1(builder.IPSet()))
|
||||
|
||||
if t.onReconfigHook != nil {
|
||||
t.onReconfigHook(cfg, routerCfg, dnsCfg)
|
||||
}
|
||||
}
|
||||
|
||||
func addressFromAddr(destination netip.Addr) tcpip.Address {
|
||||
if destination.Is6() {
|
||||
return tcpip.AddrFrom16(destination.As16())
|
||||
|
@ -34,7 +34,6 @@ type Outbound struct {
|
||||
key [56]byte
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
tlsDialer tls.Dialer
|
||||
transport adapter.V2RayClientTransport
|
||||
}
|
||||
|
||||
@ -55,7 +54,6 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
||||
}
|
||||
if options.Transport != nil {
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
@ -107,6 +105,7 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
@ -123,10 +122,11 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
common.Close(conn)
|
||||
|
@ -130,19 +130,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
deprecated.Report(ctx, deprecated.OptionTUNGSO)
|
||||
}
|
||||
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
tunMTU := options.MTU
|
||||
enableGSO := C.IsLinux && options.Stack == "gvisor" && platformInterface == nil && tunMTU > 0 && tunMTU < 49152
|
||||
if tunMTU == 0 {
|
||||
if platformInterface != nil && platformInterface.UnderNetworkExtension() {
|
||||
// In Network Extension, when MTU exceeds 4064 (4096-UTUN_IF_HEADROOM_SIZE), the performance of tun will drop significantly, which may be a system bug.
|
||||
tunMTU = 4064
|
||||
} else if C.IsAndroid {
|
||||
// Some Android devices report ENOBUFS when using MTU 65535
|
||||
tunMTU = 9000
|
||||
} else {
|
||||
tunMTU = 65535
|
||||
}
|
||||
tunMTU = 9000
|
||||
}
|
||||
var udpTimeout time.Duration
|
||||
if options.UDPTimeout != 0 {
|
||||
@ -183,7 +173,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
outputMark = tun.DefaultAutoRedirectOutputMark
|
||||
}
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
multiPendingPackets := C.IsDarwin && ((options.Stack == "gvisor" && tunMTU < 32768) || (options.Stack != "gvisor" && options.MTU <= 9000))
|
||||
inbound := &Inbound{
|
||||
tag: tag,
|
||||
ctx: ctx,
|
||||
@ -194,7 +183,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
tunOptions: tun.Options{
|
||||
Name: options.InterfaceName,
|
||||
MTU: tunMTU,
|
||||
GSO: enableGSO,
|
||||
Inet4Address: inet4Address,
|
||||
Inet6Address: inet6Address,
|
||||
AutoRoute: options.AutoRoute,
|
||||
@ -217,11 +205,10 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
IncludePackage: options.IncludePackage,
|
||||
ExcludePackage: options.ExcludePackage,
|
||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||
EXP_MultiPendingPackets: multiPendingPackets,
|
||||
},
|
||||
udpTimeout: udpTimeout,
|
||||
stack: options.Stack,
|
||||
platformInterface: platformInterface,
|
||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||
platformOptions: common.PtrValueOrDefault(options.Platform),
|
||||
}
|
||||
for _, routeAddressSet := range options.RouteAddressSet {
|
||||
|
@ -15,11 +15,11 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing-vmess/vless"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
@ -189,7 +189,7 @@ func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
}
|
||||
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
conn = packetaddr.NewConn(bufio.NewNetPacketConn(conn), metadata.Destination)
|
||||
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
|
@ -35,7 +35,6 @@ type Outbound struct {
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
tlsDialer tls.Dialer
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
@ -57,7 +56,6 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
||||
}
|
||||
if options.Transport != nil {
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
@ -126,6 +124,7 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
@ -142,10 +141,11 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -184,10 +184,11 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
common.Close(conn)
|
||||
|
@ -19,7 +19,6 @@ import (
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
@ -204,7 +203,7 @@ func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn,
|
||||
}
|
||||
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
conn = packetaddr.NewConn(bufio.NewNetPacketConn(conn), metadata.Destination)
|
||||
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
|
@ -35,7 +35,6 @@ type Outbound struct {
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
tlsDialer tls.Dialer
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
@ -57,7 +56,6 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
||||
}
|
||||
if options.Transport != nil {
|
||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||
@ -110,6 +108,7 @@ func (h *Outbound) InterfaceUpdated() {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Outbound) Close() error {
|
||||
@ -156,10 +155,11 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
common.Close(conn)
|
||||
@ -183,10 +183,11 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
var err error
|
||||
if h.transport != nil {
|
||||
conn, err = h.transport.DialContext(ctx)
|
||||
} else if h.tlsDialer != nil {
|
||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
||||
} else {
|
||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
||||
if err == nil && h.tlsConfig != nil {
|
||||
conn, err = tls.ClientHandshake(ctx, conn, h.tlsConfig)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -22,8 +22,6 @@ import (
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
var _ adapter.OutboundWithPreferredRoutes = (*Endpoint)(nil)
|
||||
|
||||
func RegisterEndpoint(registry *endpoint.Registry) {
|
||||
endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)
|
||||
}
|
||||
@ -212,11 +210,3 @@ func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
||||
}
|
||||
return w.endpoint.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
||||
func (w *Endpoint) PreferredDomain(domain string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *Endpoint) PreferredAddress(address netip.Addr) bool {
|
||||
return w.endpoint.Lookup(address) != nil
|
||||
}
|
||||
|
@ -21,8 +21,6 @@ import (
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
var _ adapter.OutboundWithPreferredRoutes = (*Outbound)(nil)
|
||||
|
||||
func RegisterOutbound(registry *outbound.Registry) {
|
||||
outbound.Register[option.LegacyWireGuardOutboundOptions](registry, C.TypeWireGuard, NewOutbound)
|
||||
}
|
||||
@ -160,11 +158,3 @@ func (o *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
||||
}
|
||||
return o.endpoint.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
||||
func (o *Outbound) PreferredDomain(domain string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (o *Outbound) PreferredAddress(address netip.Addr) bool {
|
||||
return o.endpoint.Lookup(address) != nil
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn,
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err := bufio.CopyWithCounters(destinationWriter, sourceReader, source, readCounters, writeCounters, bufio.DefaultIncreaseBufferAfter, bufio.DefaultBatchSize)
|
||||
_, err := bufio.CopyWithCounters(destinationWriter, sourceReader, source, readCounters, writeCounters)
|
||||
if err != nil {
|
||||
common.Close(source, destination)
|
||||
} else if duplexDst, isDuplex := destination.(N.WriteCloser); isDuplex {
|
||||
|
@ -117,7 +117,7 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
|
||||
if len(options.DomainRegex) > 0 {
|
||||
item, err := NewDomainRegexItem(options.DomainRegex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, E.Cause(err, "domain_regex")
|
||||
}
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
@ -246,26 +246,6 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.InterfaceAddress != nil && options.InterfaceAddress.Size() > 0 {
|
||||
item := NewInterfaceAddressItem(networkManager, options.InterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 {
|
||||
item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DefaultInterfaceAddress) > 0 {
|
||||
item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.PreferredBy) > 0 {
|
||||
item := NewPreferredByItem(ctx, options.PreferredBy)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.RuleSet) > 0 {
|
||||
var matchSource bool
|
||||
if options.RuleSetIPCIDRMatchSource {
|
||||
|
@ -1,56 +0,0 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*DefaultInterfaceAddressItem)(nil)
|
||||
|
||||
type DefaultInterfaceAddressItem struct {
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
interfaceAddresses []netip.Prefix
|
||||
}
|
||||
|
||||
func NewDefaultInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses badoption.Listable[badoption.Prefixable]) *DefaultInterfaceAddressItem {
|
||||
item := &DefaultInterfaceAddressItem{
|
||||
interfaceMonitor: networkManager.InterfaceMonitor(),
|
||||
interfaceAddresses: make([]netip.Prefix, 0, len(interfaceAddresses)),
|
||||
}
|
||||
for _, prefixable := range interfaceAddresses {
|
||||
item.interfaceAddresses = append(item.interfaceAddresses, prefixable.Build(netip.Prefix{}))
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func (r *DefaultInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
||||
defaultInterface := r.interfaceMonitor.DefaultInterface()
|
||||
if defaultInterface == nil {
|
||||
return false
|
||||
}
|
||||
for _, address := range r.interfaceAddresses {
|
||||
if common.All(defaultInterface.Addresses, func(it netip.Prefix) bool {
|
||||
return !address.Overlaps(it)
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *DefaultInterfaceAddressItem) String() string {
|
||||
addressLen := len(r.interfaceAddresses)
|
||||
switch {
|
||||
case addressLen == 1:
|
||||
return "default_interface_address=" + r.interfaceAddresses[0].String()
|
||||
case addressLen > 3:
|
||||
return "default_interface_address=[" + strings.Join(common.Map(r.interfaceAddresses[:3], netip.Prefix.String), " ") + "...]"
|
||||
default:
|
||||
return "default_interface_address=[" + strings.Join(common.Map(r.interfaceAddresses, netip.Prefix.String), " ") + "]"
|
||||
}
|
||||
}
|
@ -247,21 +247,6 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.InterfaceAddress != nil && options.InterfaceAddress.Size() > 0 {
|
||||
item := NewInterfaceAddressItem(networkManager, options.InterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 {
|
||||
item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DefaultInterfaceAddress) > 0 {
|
||||
item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.RuleSet) > 0 {
|
||||
var matchSource bool
|
||||
if options.RuleSetIPCIDRMatchSource {
|
||||
|
@ -164,21 +164,13 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
|
||||
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
}
|
||||
if len(options.WIFIBSSID) > 0 {
|
||||
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 {
|
||||
item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DefaultInterfaceAddress) > 0 {
|
||||
item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
}
|
||||
}
|
||||
if len(options.AdGuardDomain) > 0 {
|
||||
|
@ -1,62 +0,0 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*InterfaceAddressItem)(nil)
|
||||
|
||||
type InterfaceAddressItem struct {
|
||||
networkManager adapter.NetworkManager
|
||||
interfaceAddresses map[string][]netip.Prefix
|
||||
description string
|
||||
}
|
||||
|
||||
func NewInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]]) *InterfaceAddressItem {
|
||||
item := &InterfaceAddressItem{
|
||||
networkManager: networkManager,
|
||||
interfaceAddresses: make(map[string][]netip.Prefix, interfaceAddresses.Size()),
|
||||
}
|
||||
var entryDescriptions []string
|
||||
for _, entry := range interfaceAddresses.Entries() {
|
||||
prefixes := make([]netip.Prefix, 0, len(entry.Value))
|
||||
for _, prefixable := range entry.Value {
|
||||
prefixes = append(prefixes, prefixable.Build(netip.Prefix{}))
|
||||
}
|
||||
item.interfaceAddresses[entry.Key] = prefixes
|
||||
entryDescriptions = append(entryDescriptions, entry.Key+"="+strings.Join(common.Map(prefixes, netip.Prefix.String), ","))
|
||||
}
|
||||
item.description = "interface_address=[" + strings.Join(entryDescriptions, " ") + "]"
|
||||
return item
|
||||
}
|
||||
|
||||
func (r *InterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
||||
interfaces := r.networkManager.InterfaceFinder().Interfaces()
|
||||
for ifName, addresses := range r.interfaceAddresses {
|
||||
iface := common.Find(interfaces, func(it control.Interface) bool {
|
||||
return it.Name == ifName
|
||||
})
|
||||
if iface.Name == "" {
|
||||
return false
|
||||
}
|
||||
if common.All(addresses, func(address netip.Prefix) bool {
|
||||
return common.All(iface.Addresses, func(it netip.Prefix) bool {
|
||||
return !address.Overlaps(it)
|
||||
})
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *InterfaceAddressItem) String() string {
|
||||
return r.description
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*PreferredByItem)(nil)
|
||||
|
||||
type PreferredByItem struct {
|
||||
ctx context.Context
|
||||
outboundTags []string
|
||||
outbounds []adapter.OutboundWithPreferredRoutes
|
||||
}
|
||||
|
||||
func NewPreferredByItem(ctx context.Context, outboundTags []string) *PreferredByItem {
|
||||
return &PreferredByItem{
|
||||
ctx: ctx,
|
||||
outboundTags: outboundTags,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PreferredByItem) Start() error {
|
||||
outboundManager := service.FromContext[adapter.OutboundManager](r.ctx)
|
||||
for _, outboundTag := range r.outboundTags {
|
||||
rawOutbound, loaded := outboundManager.Outbound(outboundTag)
|
||||
if !loaded {
|
||||
return E.New("outbound not found: ", outboundTag)
|
||||
}
|
||||
outboundWithPreferredRoutes, withRoutes := rawOutbound.(adapter.OutboundWithPreferredRoutes)
|
||||
if !withRoutes {
|
||||
return E.New("outbound type does not support preferred routes: ", rawOutbound.Type())
|
||||
}
|
||||
r.outbounds = append(r.outbounds, outboundWithPreferredRoutes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PreferredByItem) Match(metadata *adapter.InboundContext) bool {
|
||||
var domainHost string
|
||||
if metadata.Domain != "" {
|
||||
domainHost = metadata.Domain
|
||||
} else {
|
||||
domainHost = metadata.Destination.Fqdn
|
||||
}
|
||||
if domainHost != "" {
|
||||
for _, outbound := range r.outbounds {
|
||||
if outbound.PreferredDomain(domainHost) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if metadata.Destination.IsIP() {
|
||||
for _, outbound := range r.outbounds {
|
||||
if outbound.PreferredAddress(metadata.Destination.Addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
for _, address := range metadata.DestinationAddresses {
|
||||
for _, outbound := range r.outbounds {
|
||||
if outbound.PreferredAddress(address) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *PreferredByItem) String() string {
|
||||
description := "preferred_by="
|
||||
pLen := len(r.outboundTags)
|
||||
if pLen == 1 {
|
||||
description += F.ToString(r.outboundTags[0])
|
||||
} else {
|
||||
description += "[" + strings.Join(F.MapToString(r.outboundTags), " ") + "]"
|
||||
}
|
||||
return description
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*NetworkInterfaceAddressItem)(nil)
|
||||
|
||||
type NetworkInterfaceAddressItem struct {
|
||||
networkManager adapter.NetworkManager
|
||||
interfaceAddresses map[C.InterfaceType][]netip.Prefix
|
||||
description string
|
||||
}
|
||||
|
||||
func NewNetworkInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses *badjson.TypedMap[option.InterfaceType, badoption.Listable[badoption.Prefixable]]) *NetworkInterfaceAddressItem {
|
||||
item := &NetworkInterfaceAddressItem{
|
||||
networkManager: networkManager,
|
||||
interfaceAddresses: make(map[C.InterfaceType][]netip.Prefix, interfaceAddresses.Size()),
|
||||
}
|
||||
var entryDescriptions []string
|
||||
for _, entry := range interfaceAddresses.Entries() {
|
||||
prefixes := make([]netip.Prefix, 0, len(entry.Value))
|
||||
for _, prefixable := range entry.Value {
|
||||
prefixes = append(prefixes, prefixable.Build(netip.Prefix{}))
|
||||
}
|
||||
item.interfaceAddresses[entry.Key.Build()] = prefixes
|
||||
entryDescriptions = append(entryDescriptions, entry.Key.Build().String()+"="+strings.Join(common.Map(prefixes, netip.Prefix.String), ","))
|
||||
}
|
||||
item.description = "network_interface_address=[" + strings.Join(entryDescriptions, " ") + "]"
|
||||
return item
|
||||
}
|
||||
|
||||
func (r *NetworkInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
||||
interfaces := r.networkManager.NetworkInterfaces()
|
||||
match:
|
||||
for ifType, addresses := range r.interfaceAddresses {
|
||||
for _, networkInterface := range interfaces {
|
||||
if networkInterface.Type != ifType {
|
||||
continue
|
||||
}
|
||||
if common.Any(networkInterface.Addresses, func(it netip.Prefix) bool {
|
||||
return common.Any(addresses, func(prefix netip.Prefix) bool {
|
||||
return prefix.Overlaps(it)
|
||||
})
|
||||
}) {
|
||||
continue match
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *NetworkInterfaceAddressItem) String() string {
|
||||
return r.description
|
||||
}
|
@ -42,7 +42,7 @@ func extractIPSetFromRule(rawRule adapter.HeadlessRule) []*netipx.IPSet {
|
||||
}
|
||||
}
|
||||
|
||||
func HasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool {
|
||||
func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool {
|
||||
for _, rule := range rules {
|
||||
switch rule.Type {
|
||||
case C.RuleTypeDefault:
|
||||
@ -50,7 +50,7 @@ func HasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultH
|
||||
return true
|
||||
}
|
||||
case C.RuleTypeLogical:
|
||||
if HasHeadlessRule(rule.LogicalOptions.Rules, cond) {
|
||||
if hasHeadlessRule(rule.LogicalOptions.Rules, cond) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user