mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-09-10 21:34:07 +08:00
Compare commits
2 Commits
98eb0db4f8
...
8fd51eb2a2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8fd51eb2a2 | ||
![]() |
2b14292e6b |
2
.github/setup_legacy_go.sh
vendored
2
.github/setup_legacy_go.sh
vendored
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="1.23.12"
|
VERSION="1.23.6"
|
||||||
|
|
||||||
mkdir -p $HOME/go
|
mkdir -p $HOME/go
|
||||||
cd $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 }}
|
version: ${{ steps.outputs.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.0
|
go-version: ^1.24.6
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@ -88,14 +88,13 @@ jobs:
|
|||||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||||
|
|
||||||
- { os: windows, arch: amd64 }
|
- { 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" }
|
||||||
- { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" }
|
- { os: windows, arch: "386", legacy_go: true }
|
||||||
- { os: windows, arch: arm64 }
|
- { os: windows, arch: arm64 }
|
||||||
|
|
||||||
- { os: darwin, arch: amd64 }
|
- { os: darwin, arch: amd64 }
|
||||||
- { os: darwin, arch: arm64 }
|
- { 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: arm64, ndk: "aarch64-linux-android21" }
|
||||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
||||||
@ -103,33 +102,28 @@ jobs:
|
|||||||
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
if: ${{ ! matrix.legacy_go }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.0
|
go-version: ^1.24.6
|
||||||
- name: Setup Go 1.24
|
- name: Cache Legacy Go
|
||||||
if: matrix.legacy_go124
|
if: matrix.require_legacy_go
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ~1.24.6
|
|
||||||
- name: Cache Go 1.23
|
|
||||||
if: matrix.legacy_go123
|
|
||||||
id: cache-legacy-go
|
id: cache-legacy-go
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/go_legacy
|
~/go/go_legacy
|
||||||
key: go_legacy_12312
|
key: go_legacy_1236
|
||||||
- name: Setup Go 1.23
|
- name: Setup Legacy Go
|
||||||
if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
if: matrix.legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||||
run: |-
|
run: |-
|
||||||
.github/setup_legacy_go.sh
|
.github/setup_legacy_go.sh
|
||||||
- name: Setup Go 1.23
|
- name: Setup Legacy Go 2
|
||||||
if: matrix.legacy_go123
|
if: matrix.legacy_go
|
||||||
run: |-
|
run: |-
|
||||||
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
|
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
|
||||||
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
|
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
|
||||||
@ -190,8 +184,8 @@ jobs:
|
|||||||
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
|
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
|
||||||
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
|
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
|
||||||
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
|
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
|
||||||
elif [[ -n "${{ matrix.legacy_name }}" ]]; then
|
elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then
|
||||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
DIR_NAME="${DIR_NAME}-legacy"
|
||||||
fi
|
fi
|
||||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
||||||
@ -283,7 +277,7 @@ jobs:
|
|||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
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"
|
path: "dist"
|
||||||
build_android:
|
build_android:
|
||||||
name: Build Android
|
name: Build Android
|
||||||
@ -293,14 +287,14 @@ jobs:
|
|||||||
- calculate_version
|
- calculate_version
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.0
|
go-version: ^1.24.6
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@ -373,14 +367,14 @@ jobs:
|
|||||||
- calculate_version
|
- calculate_version
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.0
|
go-version: ^1.24.6
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@ -470,7 +464,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
@ -478,7 +472,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.0
|
go-version: ^1.24.6
|
||||||
- name: Setup Xcode stable
|
- name: Setup Xcode stable
|
||||||
if: matrix.if && github.ref == 'refs/heads/main-next'
|
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||||
run: |-
|
run: |-
|
||||||
@ -630,7 +624,7 @@ jobs:
|
|||||||
- build_apple
|
- build_apple
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Cache ghr
|
- name: Cache ghr
|
||||||
@ -653,7 +647,7 @@ jobs:
|
|||||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||||
- name: Download builds
|
- name: Download builds
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
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"
|
||||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.ref.outputs.ref }}
|
ref: ${{ steps.ref.outputs.ref }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -107,7 +107,7 @@ jobs:
|
|||||||
echo "latest=$latest"
|
echo "latest=$latest"
|
||||||
echo "latest=$latest" >> $GITHUB_OUTPUT
|
echo "latest=$latest" >> $GITHUB_OUTPUT
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
pattern: digests-*
|
pattern: digests-*
|
||||||
|
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@ -22,15 +22,15 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.24.6
|
go-version: ^1.24.6
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
|
12
.github/workflows/linux.yml
vendored
12
.github/workflows/linux.yml
vendored
@ -24,13 +24,13 @@ jobs:
|
|||||||
version: ${{ steps.outputs.outputs.version }}
|
version: ${{ steps.outputs.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.0
|
go-version: ^1.24.6
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@ -65,13 +65,13 @@ jobs:
|
|||||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
|
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.0
|
go-version: ^1.24.6
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
if: matrix.os == 'android'
|
if: matrix.os == 'android'
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@ -171,7 +171,7 @@ jobs:
|
|||||||
- build
|
- build
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
@ -180,7 +180,7 @@ jobs:
|
|||||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
||||||
- name: Download builds
|
- name: Download builds
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
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:
|
run:
|
||||||
go: "1.24"
|
go: "1.23"
|
||||||
build-tags:
|
build-tags:
|
||||||
- with_gvisor
|
- with_gvisor
|
||||||
- with_quic
|
- with_quic
|
||||||
@ -9,51 +30,7 @@ run:
|
|||||||
- with_utls
|
- with_utls
|
||||||
- with_acme
|
- with_acme
|
||||||
- with_clash_api
|
- with_clash_api
|
||||||
linters:
|
|
||||||
default: none
|
issues:
|
||||||
enable:
|
exclude-dirs:
|
||||||
- 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
|
- 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$
|
|
||||||
|
6
Makefile
6
Makefile
@ -45,7 +45,7 @@ lint:
|
|||||||
GOOS=freebsd golangci-lint run ./...
|
GOOS=freebsd golangci-lint run ./...
|
||||||
|
|
||||||
lint_install:
|
lint_install:
|
||||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
@go run ./cmd/internal/protogen
|
@go run ./cmd/internal/protogen
|
||||||
@ -245,8 +245,8 @@ lib:
|
|||||||
go run ./cmd/internal/build_libbox -target ios
|
go run ./cmd/internal/build_libbox -target ios
|
||||||
|
|
||||||
lib_install:
|
lib_install:
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.8
|
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.7
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8
|
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.7
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
venv/bin/mkdocs serve
|
venv/bin/mkdocs serve
|
||||||
|
@ -135,7 +135,8 @@ func ExtendContext(ctx context.Context) (context.Context, *InboundContext) {
|
|||||||
|
|
||||||
func OverrideContext(ctx context.Context) context.Context {
|
func OverrideContext(ctx context.Context) context.Context {
|
||||||
if metadata := ContextFrom(ctx); metadata != nil {
|
if metadata := ContextFrom(ctx); metadata != nil {
|
||||||
newMetadata := *metadata
|
var newMetadata InboundContext
|
||||||
|
newMetadata = *metadata
|
||||||
return WithContext(ctx, &newMetadata)
|
return WithContext(ctx, &newMetadata)
|
||||||
}
|
}
|
||||||
return ctx
|
return ctx
|
||||||
|
@ -151,7 +151,9 @@ func testOnce(boxPath string, stackName string, mtu int, multiThread bool, flags
|
|||||||
var sudoArgs []string
|
var sudoArgs []string
|
||||||
if len(flags) > 0 {
|
if len(flags) > 0 {
|
||||||
sudoArgs = append(sudoArgs, "env")
|
sudoArgs = append(sudoArgs, "env")
|
||||||
sudoArgs = append(sudoArgs, flags...)
|
for _, flag := range flags {
|
||||||
|
sudoArgs = append(sudoArgs, flag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name())
|
sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name())
|
||||||
boxProcess := shell.Exec("sudo", sudoArgs...)
|
boxProcess := shell.Exec("sudo", sudoArgs...)
|
||||||
|
@ -6,10 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/srs"
|
"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/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/route/rule"
|
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -71,7 +69,7 @@ func compileRuleSet(sourcePath string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = srs.Write(outputFile, plainRuleSet.Options, downgradeRuleSetVersion(plainRuleSet.Version, plainRuleSet.Options))
|
err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outputFile.Close()
|
outputFile.Close()
|
||||||
os.Remove(outputPath)
|
os.Remove(outputPath)
|
||||||
@ -80,18 +78,3 @@ func compileRuleSet(sourcePath string) error {
|
|||||||
outputFile.Close()
|
outputFile.Close()
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
@ -271,7 +271,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
|||||||
} else {
|
} else {
|
||||||
dialer = d.udpDialer4
|
dialer = d.udpDialer4
|
||||||
}
|
}
|
||||||
fastFallback := time.Since(d.networkLastFallback.Load()) < C.TCPTimeout
|
fastFallback := time.Now().Sub(d.networkLastFallback.Load()) < C.TCPTimeout
|
||||||
var (
|
var (
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
isPrimary bool
|
isPrimary bool
|
||||||
|
@ -10,8 +10,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/atomic"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@ -24,7 +22,7 @@ type slowOpenConn struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
network string
|
network string
|
||||||
destination M.Socksaddr
|
destination M.Socksaddr
|
||||||
conn atomic.Pointer[net.TCPConn]
|
conn net.Conn
|
||||||
create chan struct{}
|
create chan struct{}
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
access sync.Mutex
|
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) {
|
func (c *slowOpenConn) Read(b []byte) (n int, err error) {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn != nil {
|
|
||||||
return conn.Read(b)
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
case <-c.create:
|
case <-c.create:
|
||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
return 0, c.err
|
return 0, c.err
|
||||||
}
|
}
|
||||||
return c.conn.Load().Read(b)
|
|
||||||
case <-c.done:
|
case <-c.done:
|
||||||
return 0, os.ErrClosed
|
return 0, os.ErrClosed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return c.conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
||||||
tcpConn := c.conn.Load()
|
if c.conn != nil {
|
||||||
if tcpConn != nil {
|
return c.conn.Write(b)
|
||||||
return tcpConn.Write(b)
|
|
||||||
}
|
}
|
||||||
c.access.Lock()
|
c.access.Lock()
|
||||||
defer c.access.Unlock()
|
defer c.access.Unlock()
|
||||||
@ -79,7 +74,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
|||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
return 0, c.err
|
return 0, c.err
|
||||||
}
|
}
|
||||||
return c.conn.Load().Write(b)
|
return c.conn.Write(b)
|
||||||
case <-c.done:
|
case <-c.done:
|
||||||
return 0, os.ErrClosed
|
return 0, os.ErrClosed
|
||||||
default:
|
default:
|
||||||
@ -88,7 +83,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.err = err
|
c.err = err
|
||||||
} else {
|
} else {
|
||||||
c.conn.Store(conn.(*net.TCPConn))
|
c.conn = conn
|
||||||
}
|
}
|
||||||
n = len(b)
|
n = len(b)
|
||||||
close(c.create)
|
close(c.create)
|
||||||
@ -98,77 +93,70 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
|
|||||||
func (c *slowOpenConn) Close() error {
|
func (c *slowOpenConn) Close() error {
|
||||||
c.closeOnce.Do(func() {
|
c.closeOnce.Do(func() {
|
||||||
close(c.done)
|
close(c.done)
|
||||||
conn := c.conn.Load()
|
if c.conn != nil {
|
||||||
if conn != nil {
|
c.conn.Close()
|
||||||
conn.Close()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) LocalAddr() net.Addr {
|
func (c *slowOpenConn) LocalAddr() net.Addr {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return M.Socksaddr{}
|
return M.Socksaddr{}
|
||||||
}
|
}
|
||||||
return conn.LocalAddr()
|
return c.conn.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) RemoteAddr() net.Addr {
|
func (c *slowOpenConn) RemoteAddr() net.Addr {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return M.Socksaddr{}
|
return M.Socksaddr{}
|
||||||
}
|
}
|
||||||
return conn.RemoteAddr()
|
return c.conn.RemoteAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) SetDeadline(t time.Time) error {
|
func (c *slowOpenConn) SetDeadline(t time.Time) error {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
return conn.SetDeadline(t)
|
return c.conn.SetDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) SetReadDeadline(t time.Time) error {
|
func (c *slowOpenConn) SetReadDeadline(t time.Time) error {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
return conn.SetReadDeadline(t)
|
return c.conn.SetReadDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) SetWriteDeadline(t time.Time) error {
|
func (c *slowOpenConn) SetWriteDeadline(t time.Time) error {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
return conn.SetWriteDeadline(t)
|
return c.conn.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) Upstream() any {
|
func (c *slowOpenConn) Upstream() any {
|
||||||
return common.PtrOrNil(c.conn.Load())
|
return c.conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) ReaderReplaceable() bool {
|
func (c *slowOpenConn) ReaderReplaceable() bool {
|
||||||
return c.conn.Load() != nil
|
return c.conn != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) WriterReplaceable() bool {
|
func (c *slowOpenConn) WriterReplaceable() bool {
|
||||||
return c.conn.Load() != nil
|
return c.conn != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) LazyHeadroom() bool {
|
func (c *slowOpenConn) LazyHeadroom() bool {
|
||||||
return c.conn.Load() == nil
|
return c.conn == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *slowOpenConn) NeedHandshake() bool {
|
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) {
|
func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
select {
|
select {
|
||||||
case <-c.create:
|
case <-c.create:
|
||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
@ -178,5 +166,5 @@ func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
return 0, c.err
|
return 0, c.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bufio.Copy(w, c.conn.Load())
|
return bufio.Copy(w, c.conn)
|
||||||
}
|
}
|
||||||
|
@ -17,5 +17,8 @@ var uQUICChrome115 = &ja3.ClientHello{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
|
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"
|
||||||
"github.com/sagernet/sing/common/domain"
|
"github.com/sagernet/sing/common/domain"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
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"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
|
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
@ -43,8 +41,6 @@ const (
|
|||||||
ruleItemNetworkType
|
ruleItemNetworkType
|
||||||
ruleItemNetworkIsExpensive
|
ruleItemNetworkIsExpensive
|
||||||
ruleItemNetworkIsConstrained
|
ruleItemNetworkIsConstrained
|
||||||
ruleItemNetworkInterfaceAddress
|
|
||||||
ruleItemDefaultInterfaceAddress
|
|
||||||
ruleItemFinal uint8 = 0xFF
|
ruleItemFinal uint8 = 0xFF
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -234,51 +230,6 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
|||||||
rule.NetworkIsExpensive = true
|
rule.NetworkIsExpensive = true
|
||||||
case ruleItemNetworkIsConstrained:
|
case ruleItemNetworkIsConstrained:
|
||||||
rule.NetworkIsConstrained = true
|
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:
|
case ruleItemFinal:
|
||||||
err = binary.Read(reader, binary.BigEndian, &rule.Invert)
|
err = binary.Read(reader, binary.BigEndian, &rule.Invert)
|
||||||
return
|
return
|
||||||
@ -395,7 +346,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
|||||||
}
|
}
|
||||||
if len(rule.NetworkType) > 0 {
|
if len(rule.NetworkType) > 0 {
|
||||||
if generateVersion < C.RuleSetVersion3 {
|
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)
|
err = writeRuleItemUint8(writer, ruleItemNetworkType, rule.NetworkType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -403,67 +354,17 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rule.NetworkIsExpensive {
|
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)
|
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsExpensive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rule.NetworkIsConstrained {
|
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)
|
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsConstrained)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if len(rule.WIFISSID) > 0 {
|
||||||
err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
|
err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
|
||||||
if err != nil {
|
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
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
"github.com/sagernet/sing-box/common/badtls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
@ -15,7 +16,7 @@ import (
|
|||||||
aTLS "github.com/sagernet/sing/common/tls"
|
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 {
|
if !options.Enabled {
|
||||||
return dialer, nil
|
return dialer, nil
|
||||||
}
|
}
|
||||||
@ -42,6 +43,12 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e
|
|||||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
tlsConn, err := aTLS.ClientHandshake(ctx, conn, config)
|
tlsConn, err := aTLS.ClientHandshake(ctx, conn, config)
|
||||||
|
var echErr *tls.ECHRejectionError
|
||||||
|
if errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 {
|
||||||
|
if echConfig, isECH := config.(ECHCapableConfig); isECH {
|
||||||
|
echConfig.SetECHConfigList(echErr.RetryConfigList)
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -54,57 +61,26 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e
|
|||||||
return tlsConn, nil
|
return tlsConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dialer interface {
|
type Dialer struct {
|
||||||
N.Dialer
|
|
||||||
DialTLSContext(ctx context.Context, destination M.Socksaddr) (Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultDialer struct {
|
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
config Config
|
config Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDialer(dialer N.Dialer, config Config) Dialer {
|
func NewDialer(dialer N.Dialer, config Config) N.Dialer {
|
||||||
return &defaultDialer{dialer, config}
|
return &Dialer{dialer, config}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *defaultDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
if N.NetworkName(network) != N.NetworkTCP {
|
if network != N.NetworkTCP {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
return d.DialTLSContext(ctx, destination)
|
conn, err := d.dialer.DialContext(ctx, network, 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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tlsConn, err := ClientHandshake(ctx, conn, d.config)
|
return 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *defaultDialer) Upstream() any {
|
func (d *Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
return d.dialer
|
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) {
|
func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
||||||
s.access.Lock()
|
s.access.Lock()
|
||||||
defer s.access.Unlock()
|
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{
|
message := &mDNS.Msg{
|
||||||
MsgHdr: mDNS.MsgHdr{
|
MsgHdr: mDNS.MsgHdr{
|
||||||
RecursionDesired: true,
|
RecursionDesired: true,
|
||||||
|
@ -22,8 +22,7 @@ const (
|
|||||||
RuleSetVersion1 = 1 + iota
|
RuleSetVersion1 = 1 + iota
|
||||||
RuleSetVersion2
|
RuleSetVersion2
|
||||||
RuleSetVersion3
|
RuleSetVersion3
|
||||||
RuleSetVersion4
|
RuleSetVersionCurrent = RuleSetVersion3
|
||||||
RuleSetVersionCurrent = RuleSetVersion4
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -104,7 +104,7 @@ func (c *dnsConfig) serverOffset() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *dnsConfig) nameList(name string) []string {
|
func (conf *dnsConfig) nameList(name string) []string {
|
||||||
l := len(name)
|
l := len(name)
|
||||||
rooted := l > 0 && name[l-1] == '.'
|
rooted := l > 0 && name[l-1] == '.'
|
||||||
if l > 254 || l == 254 && !rooted {
|
if l > 254 || l == 254 && !rooted {
|
||||||
@ -118,15 +118,15 @@ func (c *dnsConfig) nameList(name string) []string {
|
|||||||
return []string{name}
|
return []string{name}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNdots := strings.Count(name, ".") >= c.ndots
|
hasNdots := strings.Count(name, ".") >= conf.ndots
|
||||||
name += "."
|
name += "."
|
||||||
// l++
|
// l++
|
||||||
|
|
||||||
names := make([]string, 0, 1+len(c.search))
|
names := make([]string, 0, 1+len(conf.search))
|
||||||
if hasNdots && !avoidDNS(name) {
|
if hasNdots && !avoidDNS(name) {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
for _, suffix := range c.search {
|
for _, suffix := range conf.search {
|
||||||
fqdn := name + suffix
|
fqdn := name + suffix
|
||||||
if !avoidDNS(fqdn) && len(fqdn) <= 254 {
|
if !avoidDNS(fqdn) && len(fqdn) <= 254 {
|
||||||
names = append(names, fqdn)
|
names = append(names, fqdn)
|
||||||
|
@ -30,7 +30,7 @@ func RegisterTLS(registry *dns.TransportRegistry) {
|
|||||||
type TLSTransport struct {
|
type TLSTransport struct {
|
||||||
dns.TransportAdapter
|
dns.TransportAdapter
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
dialer tls.Dialer
|
dialer N.Dialer
|
||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
@ -67,7 +67,7 @@ func NewTLSRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer
|
|||||||
return &TLSTransport{
|
return &TLSTransport{
|
||||||
TransportAdapter: adapter,
|
TransportAdapter: adapter,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
dialer: tls.NewDialer(dialer, tlsConfig),
|
dialer: dialer,
|
||||||
serverAddr: serverAddr,
|
serverAddr: serverAddr,
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
@ -100,10 +100,15 @@ func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tlsConn, err := t.dialer.DialTLSContext(ctx, t.serverAddr)
|
tcpConn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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})
|
return t.exchange(message, &tlsDNSConn{Conn: tlsConn})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,22 +2,11 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 1.13.0-alpha.1
|
#### 1.12.1
|
||||||
|
|
||||||
* Add interface address rule items **1**
|
|
||||||
|
|
||||||
**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
|
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
### 1.12.0
|
#### 1.12.0
|
||||||
|
|
||||||
* Refactor DNS servers **1**
|
* Refactor DNS servers **1**
|
||||||
* Add domain resolver options**2**
|
* Add domain resolver options**2**
|
||||||
|
@ -2,12 +2,6 @@
|
|||||||
icon: material/new-box
|
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)
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.11.0"
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
:material-plus: [action](#action)
|
:material-plus: [action](#action)
|
||||||
@ -134,19 +128,6 @@ icon: material/new-box
|
|||||||
],
|
],
|
||||||
"network_is_expensive": false,
|
"network_is_expensive": false,
|
||||||
"network_is_constrained": false,
|
"network_is_constrained": false,
|
||||||
"interface_address": {
|
|
||||||
"en0": [
|
|
||||||
"2000::/3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"network_interface_address": {
|
|
||||||
"wifi": [
|
|
||||||
"2000::/3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default_interface_address": [
|
|
||||||
"2000::/3"
|
|
||||||
],
|
|
||||||
"wifi_ssid": [
|
"wifi_ssid": [
|
||||||
"My WIFI"
|
"My WIFI"
|
||||||
],
|
],
|
||||||
@ -382,36 +363,6 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
|
|||||||
|
|
||||||
Match if network is in Low Data Mode.
|
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
|
#### wifi_ssid
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
@ -2,12 +2,6 @@
|
|||||||
icon: material/new-box
|
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)
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.11.0 中的更改"
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [action](#action)
|
:material-plus: [action](#action)
|
||||||
@ -131,19 +125,6 @@ icon: material/new-box
|
|||||||
],
|
],
|
||||||
"network_is_expensive": false,
|
"network_is_expensive": false,
|
||||||
"network_is_constrained": false,
|
"network_is_constrained": false,
|
||||||
"interface_address": {
|
|
||||||
"en0": [
|
|
||||||
"2000::/3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"network_interface_address": {
|
|
||||||
"wifi": [
|
|
||||||
"2000::/3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default_interface_address": [
|
|
||||||
"2000::/3"
|
|
||||||
],
|
|
||||||
"wifi_ssid": [
|
"wifi_ssid": [
|
||||||
"My WIFI"
|
"My WIFI"
|
||||||
],
|
],
|
||||||
@ -356,7 +337,7 @@ icon: material/new-box
|
|||||||
|
|
||||||
匹配网络类型。
|
匹配网络类型。
|
||||||
|
|
||||||
可用值: `wifi`, `cellular`, `ethernet` and `other`.
|
Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||||
|
|
||||||
#### network_is_expensive
|
#### network_is_expensive
|
||||||
|
|
||||||
@ -379,24 +360,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
匹配如果网络在低数据模式下。
|
匹配如果网络在低数据模式下。
|
||||||
|
|
||||||
#### interface_address
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
匹配接口地址。
|
|
||||||
|
|
||||||
#### network_interface_address
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
匹配网络接口(可用值同 `network_type`)地址。
|
|
||||||
|
|
||||||
#### default_interface_address
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
匹配默认接口地址。
|
|
||||||
|
|
||||||
#### wifi_ssid
|
#### wifi_ssid
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
@ -2,11 +2,6 @@
|
|||||||
icon: material/new-box
|
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"
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
:material-plus: [network_type](#network_type)
|
:material-plus: [network_type](#network_type)
|
||||||
@ -83,14 +78,6 @@ icon: material/new-box
|
|||||||
],
|
],
|
||||||
"network_is_expensive": false,
|
"network_is_expensive": false,
|
||||||
"network_is_constrained": false,
|
"network_is_constrained": false,
|
||||||
"network_interface_address": {
|
|
||||||
"wifi": [
|
|
||||||
"2000::/3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default_interface_address": [
|
|
||||||
"2000::/3"
|
|
||||||
],
|
|
||||||
"wifi_ssid": [
|
"wifi_ssid": [
|
||||||
"My WIFI"
|
"My WIFI"
|
||||||
],
|
],
|
||||||
@ -238,26 +225,6 @@ such as Cellular or a Personal Hotspot (on Apple platforms).
|
|||||||
|
|
||||||
Match if network is in Low Data Mode.
|
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
|
#### wifi_ssid
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
@ -2,11 +2,6 @@
|
|||||||
icon: material/new-box
|
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 中的更改"
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [network_type](#network_type)
|
:material-plus: [network_type](#network_type)
|
||||||
@ -83,14 +78,6 @@ icon: material/new-box
|
|||||||
],
|
],
|
||||||
"network_is_expensive": false,
|
"network_is_expensive": false,
|
||||||
"network_is_constrained": false,
|
"network_is_constrained": false,
|
||||||
"network_interface_address": {
|
|
||||||
"wifi": [
|
|
||||||
"2000::/3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default_interface_address": [
|
|
||||||
"2000::/3"
|
|
||||||
],
|
|
||||||
"wifi_ssid": [
|
"wifi_ssid": [
|
||||||
"My WIFI"
|
"My WIFI"
|
||||||
],
|
],
|
||||||
@ -234,18 +221,6 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
|||||||
|
|
||||||
匹配如果网络在低数据模式下。
|
匹配如果网络在低数据模式下。
|
||||||
|
|
||||||
#### network_interface_address
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
匹配网络接口(可用值同 `network_type`)地址。
|
|
||||||
|
|
||||||
#### default_interface_address
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
匹配默认接口地址。
|
|
||||||
|
|
||||||
#### wifi_ssid
|
#### wifi_ssid
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
@ -2,10 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.13.0"
|
|
||||||
|
|
||||||
:material-plus: version `4`
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.11.0"
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
:material-plus: version `3`
|
:material-plus: version `3`
|
||||||
@ -40,7 +36,6 @@ Version of rule-set.
|
|||||||
* 1: sing-box 1.8.0: Initial rule-set version.
|
* 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.
|
* 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.
|
* 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
|
#### rules
|
||||||
|
|
||||||
|
@ -2,10 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "sing-box 1.13.0 中的更改"
|
|
||||||
|
|
||||||
:material-plus: version `4`
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.11.0 中的更改"
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
:material-plus: version `3`
|
:material-plus: version `3`
|
||||||
@ -40,7 +36,6 @@ icon: material/new-box
|
|||||||
* 1: sing-box 1.8.0: 初始规则集版本。
|
* 1: sing-box 1.8.0: 初始规则集版本。
|
||||||
* 2: sing-box 1.10.0: 优化了二进制规则集中 `domain_suffix` 规则的内存使用。
|
* 2: sing-box 1.10.0: 优化了二进制规则集中 `domain_suffix` 规则的内存使用。
|
||||||
* 3: sing-box 1.11.0: 添加了 `network_type`、 `network_is_expensive` 和 `network_is_constrainted` 规则项。
|
* 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
|
#### rules
|
||||||
|
|
||||||
|
@ -62,7 +62,10 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_, isGroup := it.(adapter.OutboundGroup)
|
_, isGroup := it.(adapter.OutboundGroup)
|
||||||
return !isGroup
|
if isGroup {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
|
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
|
||||||
for _, detour := range outbounds {
|
for _, detour := range outbounds {
|
||||||
|
@ -18,7 +18,6 @@ func newIterator[T any](values []T) *iterator[T] {
|
|||||||
return &iterator[T]{values}
|
return &iterator[T]{values}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:noinline
|
|
||||||
func newPtrIterator[T any](values []T) *iterator[*T] {
|
func newPtrIterator[T any](values []T) *iterator[*T] {
|
||||||
return &iterator[*T]{common.Map(values, func(value T) *T { return &value })}
|
return &iterator[*T]{common.Map(values, func(value T) *T { return &value })}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ func (s *StatsService) RoutedPacketConnection(ctx context.Context, conn N.Packet
|
|||||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
||||||
}
|
}
|
||||||
s.access.Unlock()
|
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) {
|
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
|
var rtm runtime.MemStats
|
||||||
runtime.ReadMemStats(&rtm)
|
runtime.ReadMemStats(&rtm)
|
||||||
response := &SysStatsResponse{
|
response := &SysStatsResponse{
|
||||||
Uptime: uint32(time.Since(s.createdAt).Seconds()),
|
Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
|
||||||
NumGoroutine: uint32(runtime.NumGoroutine()),
|
NumGoroutine: uint32(runtime.NumGoroutine()),
|
||||||
Alloc: rtm.Alloc,
|
Alloc: rtm.Alloc,
|
||||||
TotalAlloc: rtm.TotalAlloc,
|
TotalAlloc: rtm.TotalAlloc,
|
||||||
|
22
go.mod
22
go.mod
@ -24,12 +24,12 @@ require (
|
|||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.8
|
github.com/sagernet/gomobile v0.1.7
|
||||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
|
||||||
github.com/sagernet/quic-go v0.52.0-beta.1
|
github.com/sagernet/quic-go v0.52.0-beta.1
|
||||||
github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d
|
github.com/sagernet/sing v0.7.5
|
||||||
github.com/sagernet/sing-mux v0.3.3
|
github.com/sagernet/sing-mux v0.3.2
|
||||||
github.com/sagernet/sing-quic v0.5.0
|
github.com/sagernet/sing-quic v0.5.0-beta.3
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
||||||
@ -44,11 +44,11 @@ require (
|
|||||||
github.com/vishvananda/netns v0.0.5
|
github.com/vishvananda/netns v0.0.5
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.41.0
|
golang.org/x/crypto v0.40.0
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
||||||
golang.org/x/mod v0.27.0
|
golang.org/x/mod v0.26.0
|
||||||
golang.org/x/net v0.43.0
|
golang.org/x/net v0.42.0
|
||||||
golang.org/x/sys v0.35.0
|
golang.org/x/sys v0.34.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||||
google.golang.org/grpc v1.73.0
|
google.golang.org/grpc v1.73.0
|
||||||
google.golang.org/protobuf v1.36.6
|
google.golang.org/protobuf v1.36.6
|
||||||
@ -123,10 +123,10 @@ require (
|
|||||||
go.uber.org/zap/exp v0.3.0 // indirect
|
go.uber.org/zap/exp v0.3.0 // indirect
|
||||||
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
|
||||||
golang.org/x/sync v0.16.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/term v0.34.0 // indirect
|
golang.org/x/term v0.33.0 // indirect
|
||||||
golang.org/x/text v0.28.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
golang.org/x/time v0.9.0 // indirect
|
golang.org/x/time v0.9.0 // indirect
|
||||||
golang.org/x/tools v0.36.0 // indirect
|
golang.org/x/tools v0.34.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 // 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-20250324211829-b45e905df463 // indirect
|
||||||
|
44
go.sum
44
go.sum
@ -156,8 +156,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/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 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
||||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
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.7 h1:I9jCJZTH0weP5MsuydvYHX5QfN/r6Fe8ptAIj1+SJVg=
|
||||||
github.com/sagernet/gomobile v0.1.8/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
|
github.com/sagernet/gomobile v0.1.7/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 h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38=
|
||||||
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4=
|
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=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||||
@ -167,12 +167,12 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
|
|||||||
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
|
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
|
||||||
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
|
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
|
||||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d h1:fTGA/vTcqceUqSiXeFAO99Iiai/nWBPV+yP8tPpHPf8=
|
github.com/sagernet/sing v0.7.5 h1:gNMwZCLPqR+4e0g6dwi0sSsrvOmoMjpZgqxKsuJZatc=
|
||||||
github.com/sagernet/sing v0.7.6-0.20250814022454-614cd851ca6d/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.7.5/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.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
||||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||||
github.com/sagernet/sing-quic v0.5.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak=
|
github.com/sagernet/sing-quic v0.5.0-beta.3 h1:X/acRNsqQNfDlmwE7SorHfaZiny5e67hqIzM/592ric=
|
||||||
github.com/sagernet/sing-quic v0.5.0/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
github.com/sagernet/sing-quic v0.5.0-beta.3/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
||||||
@ -262,18 +262,18 @@ 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 h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
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.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.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
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/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 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
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.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
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.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
@ -285,20 +285,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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
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/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.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -100,6 +100,7 @@ func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) {
|
|||||||
}
|
}
|
||||||
ruleAction.Action = C.RuleActionTypePredefined
|
ruleAction.Action = C.RuleActionTypePredefined
|
||||||
ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode))
|
ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSClientOptions struct {
|
type DNSClientOptions struct {
|
||||||
|
@ -100,9 +100,6 @@ type RawDefaultRule struct {
|
|||||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,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"`
|
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||||
Invert bool `json:"invert,omitempty"`
|
Invert bool `json:"invert,omitempty"`
|
||||||
|
@ -103,9 +103,6 @@ type RawDefaultDNSRule struct {
|
|||||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,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"`
|
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||||
|
@ -203,9 +203,6 @@ type DefaultHeadlessRule struct {
|
|||||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,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"`
|
Invert bool `json:"invert,omitempty"`
|
||||||
|
|
||||||
DomainMatcher *domain.Matcher `json:"-"`
|
DomainMatcher *domain.Matcher `json:"-"`
|
||||||
@ -243,7 +240,7 @@ type PlainRuleSetCompat _PlainRuleSetCompat
|
|||||||
func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
|
func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
|
||||||
var v any
|
var v any
|
||||||
switch r.Version {
|
switch r.Version {
|
||||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3, C.RuleSetVersion4:
|
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||||
v = r.Options
|
v = r.Options
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown rule-set version: ", r.Version)
|
return nil, E.New("unknown rule-set version: ", r.Version)
|
||||||
|
@ -26,7 +26,7 @@ func RegisterOutbound(registry *outbound.Registry) {
|
|||||||
|
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
dialer tls.Dialer
|
dialer N.Dialer
|
||||||
server M.Socksaddr
|
server M.Socksaddr
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
client *anytls.Client
|
client *anytls.Client
|
||||||
@ -58,8 +58,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
outbound.dialer = outboundDialer
|
||||||
outbound.dialer = tls.NewDialer(outboundDialer, tlsConfig)
|
|
||||||
|
|
||||||
client, err := anytls.NewClient(ctx, anytls.ClientConfig{
|
client, err := anytls.NewClient(ctx, anytls.ClientConfig{
|
||||||
Password: options.Password,
|
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) {
|
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) {
|
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() {
|
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.lastActive.Store(time.Now())
|
||||||
g.CheckOutbounds(false)
|
g.CheckOutbounds(false)
|
||||||
}
|
}
|
||||||
@ -323,7 +323,7 @@ func (g *URLTestGroup) loopCheck() {
|
|||||||
return
|
return
|
||||||
case <-g.ticker.C:
|
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.access.Lock()
|
||||||
g.ticker.Stop()
|
g.ticker.Stop()
|
||||||
g.ticker = nil
|
g.ticker = nil
|
||||||
@ -360,7 +360,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
history := g.history.LoadURLTestHistory(realTag)
|
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
|
continue
|
||||||
}
|
}
|
||||||
checked[realTag] = true
|
checked[realTag] = true
|
||||||
|
@ -34,7 +34,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
detour, err := tls.NewDialerFromOptions(ctx, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
detour, err := tls.NewDialerFromOptions(ctx, router, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,7 @@ func (h *Outbound) InterfaceUpdated() {
|
|||||||
if h.multiplexDialer != nil {
|
if h.multiplexDialer != nil {
|
||||||
h.multiplexDialer.Reset()
|
h.multiplexDialer.Reset()
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) Close() error {
|
func (h *Outbound) Close() error {
|
||||||
|
@ -180,6 +180,7 @@ func (s *Outbound) connect() (*ssh.Client, error) {
|
|||||||
|
|
||||||
func (s *Outbound) InterfaceUpdated() {
|
func (s *Outbound) InterfaceUpdated() {
|
||||||
common.Close(s.clientConn)
|
common.Close(s.clientConn)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Outbound) Close() error {
|
func (s *Outbound) Close() error {
|
||||||
|
@ -34,7 +34,6 @@ type Outbound struct {
|
|||||||
key [56]byte
|
key [56]byte
|
||||||
multiplexDialer *mux.Client
|
multiplexDialer *mux.Client
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
tlsDialer tls.Dialer
|
|
||||||
transport adapter.V2RayClientTransport
|
transport adapter.V2RayClientTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +54,6 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
|
||||||
}
|
}
|
||||||
if options.Transport != nil {
|
if options.Transport != nil {
|
||||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
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 {
|
if h.multiplexDialer != nil {
|
||||||
h.multiplexDialer.Reset()
|
h.multiplexDialer.Reset()
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) Close() error {
|
func (h *Outbound) Close() error {
|
||||||
@ -123,10 +122,11 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
|
|||||||
var err error
|
var err error
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
conn, err = h.transport.DialContext(ctx)
|
conn, err = h.transport.DialContext(ctx)
|
||||||
} else if h.tlsDialer != nil {
|
|
||||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
|
||||||
} else {
|
} else {
|
||||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
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 {
|
if err != nil {
|
||||||
common.Close(conn)
|
common.Close(conn)
|
||||||
|
@ -35,7 +35,6 @@ type Outbound struct {
|
|||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
multiplexDialer *mux.Client
|
multiplexDialer *mux.Client
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
tlsDialer tls.Dialer
|
|
||||||
transport adapter.V2RayClientTransport
|
transport adapter.V2RayClientTransport
|
||||||
packetAddr bool
|
packetAddr bool
|
||||||
xudp bool
|
xudp bool
|
||||||
@ -57,7 +56,6 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
|
||||||
}
|
}
|
||||||
if options.Transport != nil {
|
if options.Transport != nil {
|
||||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
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 {
|
if h.multiplexDialer != nil {
|
||||||
h.multiplexDialer.Reset()
|
h.multiplexDialer.Reset()
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) Close() error {
|
func (h *Outbound) Close() error {
|
||||||
@ -142,10 +141,11 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati
|
|||||||
var err error
|
var err error
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
conn, err = h.transport.DialContext(ctx)
|
conn, err = h.transport.DialContext(ctx)
|
||||||
} else if h.tlsDialer != nil {
|
|
||||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
|
||||||
} else {
|
} else {
|
||||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -184,10 +184,11 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
|||||||
var err error
|
var err error
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
conn, err = h.transport.DialContext(ctx)
|
conn, err = h.transport.DialContext(ctx)
|
||||||
} else if h.tlsDialer != nil {
|
|
||||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
|
||||||
} else {
|
} else {
|
||||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
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 {
|
if err != nil {
|
||||||
common.Close(conn)
|
common.Close(conn)
|
||||||
|
@ -35,7 +35,6 @@ type Outbound struct {
|
|||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
multiplexDialer *mux.Client
|
multiplexDialer *mux.Client
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
tlsDialer tls.Dialer
|
|
||||||
transport adapter.V2RayClientTransport
|
transport adapter.V2RayClientTransport
|
||||||
packetAddr bool
|
packetAddr bool
|
||||||
xudp bool
|
xudp bool
|
||||||
@ -57,7 +56,6 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
|
||||||
}
|
}
|
||||||
if options.Transport != nil {
|
if options.Transport != nil {
|
||||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
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 {
|
if h.multiplexDialer != nil {
|
||||||
h.multiplexDialer.Reset()
|
h.multiplexDialer.Reset()
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) Close() error {
|
func (h *Outbound) Close() error {
|
||||||
@ -156,10 +155,11 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
|
|||||||
var err error
|
var err error
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
conn, err = h.transport.DialContext(ctx)
|
conn, err = h.transport.DialContext(ctx)
|
||||||
} else if h.tlsDialer != nil {
|
|
||||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
|
||||||
} else {
|
} else {
|
||||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
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 {
|
if err != nil {
|
||||||
common.Close(conn)
|
common.Close(conn)
|
||||||
@ -183,10 +183,11 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
|||||||
var err error
|
var err error
|
||||||
if h.transport != nil {
|
if h.transport != nil {
|
||||||
conn, err = h.transport.DialContext(ctx)
|
conn, err = h.transport.DialContext(ctx)
|
||||||
} else if h.tlsDialer != nil {
|
|
||||||
conn, err = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
|
|
||||||
} else {
|
} else {
|
||||||
conn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -246,21 +246,6 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
|
|||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, 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 {
|
if len(options.RuleSet) > 0 {
|
||||||
var matchSource bool
|
var matchSource bool
|
||||||
if options.RuleSetIPCIDRMatchSource {
|
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.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, 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 {
|
if len(options.RuleSet) > 0 {
|
||||||
var matchSource bool
|
var matchSource bool
|
||||||
if options.RuleSetIPCIDRMatchSource {
|
if options.RuleSetIPCIDRMatchSource {
|
||||||
|
@ -164,21 +164,13 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
|
|||||||
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
|
|
||||||
}
|
}
|
||||||
if len(options.WIFIBSSID) > 0 {
|
if len(options.WIFIBSSID) > 0 {
|
||||||
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
|
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, 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 {
|
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,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 {
|
for _, rule := range rules {
|
||||||
switch rule.Type {
|
switch rule.Type {
|
||||||
case C.RuleTypeDefault:
|
case C.RuleTypeDefault:
|
||||||
@ -50,7 +50,7 @@ func HasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultH
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case C.RuleTypeLogical:
|
case C.RuleTypeLogical:
|
||||||
if HasHeadlessRule(rule.LogicalOptions.Rules, cond) {
|
if hasHeadlessRule(rule.LogicalOptions.Rules, cond) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,9 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var metadata adapter.RuleSetMetadata
|
var metadata adapter.RuleSetMetadata
|
||||||
metadata.ContainsProcessRule = HasHeadlessRule(headlessRules, isProcessHeadlessRule)
|
metadata.ContainsProcessRule = hasHeadlessRule(headlessRules, isProcessHeadlessRule)
|
||||||
metadata.ContainsWIFIRule = HasHeadlessRule(headlessRules, isWIFIHeadlessRule)
|
metadata.ContainsWIFIRule = hasHeadlessRule(headlessRules, isWIFIHeadlessRule)
|
||||||
metadata.ContainsIPCIDRRule = HasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
|
metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
|
||||||
s.rules = rules
|
s.rules = rules
|
||||||
s.metadata = metadata
|
s.metadata = metadata
|
||||||
s.callbackAccess.Lock()
|
s.callbackAccess.Lock()
|
||||||
|
@ -185,9 +185,9 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
|||||||
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.metadata.ContainsProcessRule = HasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
s.metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
||||||
s.metadata.ContainsWIFIRule = HasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
s.metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
||||||
s.metadata.ContainsIPCIDRRule = HasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
|
s.metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
|
||||||
s.rules = rules
|
s.rules = rules
|
||||||
s.callbackAccess.Lock()
|
s.callbackAccess.Lock()
|
||||||
callbacks := s.callbacks.Array()
|
callbacks := s.callbacks.Array()
|
||||||
|
@ -2,6 +2,7 @@ package ssmapi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
@ -156,14 +157,18 @@ func (s *APIServer) deleteUser(writer http.ResponseWriter, request *http.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *APIServer) getStats(writer http.ResponseWriter, request *http.Request) {
|
func (s *APIServer) getStats(writer http.ResponseWriter, request *http.Request) {
|
||||||
requireClear := request.URL.Query().Get("clear") == "true"
|
requireClear, _ := strconv.ParseBool(chi.URLParam(request, "clear"))
|
||||||
|
|
||||||
users := s.user.List()
|
users := s.user.List()
|
||||||
s.traffic.ReadUsers(users, requireClear)
|
s.traffic.ReadUsers(users)
|
||||||
for i := range users {
|
for i := range users {
|
||||||
users[i].Password = ""
|
users[i].Password = ""
|
||||||
}
|
}
|
||||||
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal(requireClear)
|
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal()
|
||||||
|
|
||||||
|
if requireClear {
|
||||||
|
s.traffic.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
render.JSON(writer, request, render.M{
|
render.JSON(writer, request, render.M{
|
||||||
"uplinkBytes": uplinkBytes,
|
"uplinkBytes": uplinkBytes,
|
||||||
|
@ -142,77 +142,53 @@ func (s *TrafficManager) TrackPacketConnection(conn N.PacketConn, metadata adapt
|
|||||||
readPacketCounter = append(readPacketCounter, upPacketsCounter)
|
readPacketCounter = append(readPacketCounter, upPacketsCounter)
|
||||||
writePacketCounter = append(writePacketCounter, downPacketsCounter)
|
writePacketCounter = append(writePacketCounter, downPacketsCounter)
|
||||||
udpSessionCounter.Add(1)
|
udpSessionCounter.Add(1)
|
||||||
return bufio.NewInt64CounterPacketConn(conn, readCounter, readPacketCounter, writeCounter, writePacketCounter)
|
return bufio.NewInt64CounterPacketConn(conn, append(readCounter, readPacketCounter...), append(writeCounter, writePacketCounter...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TrafficManager) ReadUser(user *UserObject) {
|
func (s *TrafficManager) ReadUser(user *UserObject) {
|
||||||
s.userAccess.Lock()
|
s.userAccess.Lock()
|
||||||
defer s.userAccess.Unlock()
|
defer s.userAccess.Unlock()
|
||||||
s.readUser(user, false)
|
s.readUser(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TrafficManager) readUser(user *UserObject, swap bool) {
|
func (s *TrafficManager) readUser(user *UserObject) {
|
||||||
if counter, loaded := s.userUplink[user.UserName]; loaded {
|
if counter, loaded := s.userUplink[user.UserName]; loaded {
|
||||||
if swap {
|
|
||||||
user.UplinkBytes = counter.Swap(0)
|
|
||||||
} else {
|
|
||||||
user.UplinkBytes = counter.Load()
|
user.UplinkBytes = counter.Load()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if counter, loaded := s.userDownlink[user.UserName]; loaded {
|
if counter, loaded := s.userDownlink[user.UserName]; loaded {
|
||||||
if swap {
|
|
||||||
user.DownlinkBytes = counter.Swap(0)
|
|
||||||
} else {
|
|
||||||
user.DownlinkBytes = counter.Load()
|
user.DownlinkBytes = counter.Load()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if counter, loaded := s.userUplinkPackets[user.UserName]; loaded {
|
if counter, loaded := s.userUplinkPackets[user.UserName]; loaded {
|
||||||
if swap {
|
|
||||||
user.UplinkPackets = counter.Swap(0)
|
|
||||||
} else {
|
|
||||||
user.UplinkPackets = counter.Load()
|
user.UplinkPackets = counter.Load()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded {
|
if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded {
|
||||||
if swap {
|
|
||||||
user.DownlinkPackets = counter.Swap(0)
|
|
||||||
} else {
|
|
||||||
user.DownlinkPackets = counter.Load()
|
user.DownlinkPackets = counter.Load()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if counter, loaded := s.userTCPSessions[user.UserName]; loaded {
|
if counter, loaded := s.userTCPSessions[user.UserName]; loaded {
|
||||||
if swap {
|
|
||||||
user.TCPSessions = counter.Swap(0)
|
|
||||||
} else {
|
|
||||||
user.TCPSessions = counter.Load()
|
user.TCPSessions = counter.Load()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if counter, loaded := s.userUDPSessions[user.UserName]; loaded {
|
if counter, loaded := s.userUDPSessions[user.UserName]; loaded {
|
||||||
if swap {
|
|
||||||
user.UDPSessions = counter.Swap(0)
|
|
||||||
} else {
|
|
||||||
user.UDPSessions = counter.Load()
|
user.UDPSessions = counter.Load()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TrafficManager) ReadUsers(users []*UserObject, swap bool) {
|
func (s *TrafficManager) ReadUsers(users []*UserObject) {
|
||||||
s.userAccess.Lock()
|
s.userAccess.Lock()
|
||||||
defer s.userAccess.Unlock()
|
defer s.userAccess.Unlock()
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
s.readUser(user, swap)
|
s.readUser(user)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TrafficManager) ReadGlobal(swap bool) (uplinkBytes int64, downlinkBytes int64, uplinkPackets int64, downlinkPackets int64, tcpSessions int64, udpSessions int64) {
|
func (s *TrafficManager) ReadGlobal() (
|
||||||
if swap {
|
uplinkBytes int64,
|
||||||
return s.globalUplink.Swap(0),
|
downlinkBytes int64,
|
||||||
s.globalDownlink.Swap(0),
|
uplinkPackets int64,
|
||||||
s.globalUplinkPackets.Swap(0),
|
downlinkPackets int64,
|
||||||
s.globalDownlinkPackets.Swap(0),
|
tcpSessions int64,
|
||||||
s.globalTCPSessions.Swap(0),
|
udpSessions int64,
|
||||||
s.globalUDPSessions.Swap(0)
|
) {
|
||||||
} else {
|
|
||||||
return s.globalUplink.Load(),
|
return s.globalUplink.Load(),
|
||||||
s.globalDownlink.Load(),
|
s.globalDownlink.Load(),
|
||||||
s.globalUplinkPackets.Load(),
|
s.globalUplinkPackets.Load(),
|
||||||
@ -220,4 +196,32 @@ func (s *TrafficManager) ReadGlobal(swap bool) (uplinkBytes int64, downlinkBytes
|
|||||||
s.globalTCPSessions.Load(),
|
s.globalTCPSessions.Load(),
|
||||||
s.globalUDPSessions.Load()
|
s.globalUDPSessions.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TrafficManager) Clear() {
|
||||||
|
s.globalUplink.Store(0)
|
||||||
|
s.globalDownlink.Store(0)
|
||||||
|
s.globalUplinkPackets.Store(0)
|
||||||
|
s.globalDownlinkPackets.Store(0)
|
||||||
|
s.globalTCPSessions.Store(0)
|
||||||
|
s.globalUDPSessions.Store(0)
|
||||||
|
s.userAccess.Lock()
|
||||||
|
defer s.userAccess.Unlock()
|
||||||
|
for _, counter := range s.userUplink {
|
||||||
|
counter.Store(0)
|
||||||
|
}
|
||||||
|
for _, counter := range s.userDownlink {
|
||||||
|
counter.Store(0)
|
||||||
|
}
|
||||||
|
for _, counter := range s.userUplinkPackets {
|
||||||
|
counter.Store(0)
|
||||||
|
}
|
||||||
|
for _, counter := range s.userDownlinkPackets {
|
||||||
|
counter.Store(0)
|
||||||
|
}
|
||||||
|
for _, counter := range s.userTCPSessions {
|
||||||
|
counter.Store(0)
|
||||||
|
}
|
||||||
|
for _, counter := range s.userUDPSessions {
|
||||||
|
counter.Store(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@ -30,7 +29,7 @@ type Client struct {
|
|||||||
serverAddr string
|
serverAddr string
|
||||||
serviceName string
|
serviceName string
|
||||||
dialOptions []grpc.DialOption
|
dialOptions []grpc.DialOption
|
||||||
conn atomic.Pointer[grpc.ClientConn]
|
conn *grpc.ClientConn
|
||||||
connAccess sync.Mutex
|
connAccess sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,13 +74,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) connect() (*grpc.ClientConn, error) {
|
func (c *Client) connect() (*grpc.ClientConn, error) {
|
||||||
conn := c.conn.Load()
|
conn := c.conn
|
||||||
if conn != nil && conn.GetState() != connectivity.Shutdown {
|
if conn != nil && conn.GetState() != connectivity.Shutdown {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
c.connAccess.Lock()
|
c.connAccess.Lock()
|
||||||
defer c.connAccess.Unlock()
|
defer c.connAccess.Unlock()
|
||||||
conn = c.conn.Load()
|
conn = c.conn
|
||||||
if conn != nil && conn.GetState() != connectivity.Shutdown {
|
if conn != nil && conn.GetState() != connectivity.Shutdown {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
@ -90,7 +89,7 @@ func (c *Client) connect() (*grpc.ClientConn, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.conn.Store(conn)
|
c.conn = conn
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,9 +109,11 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Close() error {
|
func (c *Client) Close() error {
|
||||||
conn := c.conn.Swap(nil)
|
c.connAccess.Lock()
|
||||||
if conn != nil {
|
defer c.connAccess.Unlock()
|
||||||
conn.Close()
|
if c.conn != nil {
|
||||||
|
c.conn.Close()
|
||||||
|
c.conn = nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ var defaultClientHeader = http.Header{
|
|||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
dialer N.Dialer
|
||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
transport *http2.Transport
|
transport *http2.Transport
|
||||||
options option.V2RayGRPCOptions
|
options option.V2RayGRPCOptions
|
||||||
@ -45,6 +46,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
}
|
}
|
||||||
client := &Client{
|
client := &Client{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
dialer: dialer,
|
||||||
serverAddr: serverAddr,
|
serverAddr: serverAddr,
|
||||||
options: options,
|
options: options,
|
||||||
transport: &http2.Transport{
|
transport: &http2.Transport{
|
||||||
@ -60,6 +62,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
},
|
},
|
||||||
host: host,
|
host: host,
|
||||||
}
|
}
|
||||||
|
|
||||||
if tlsConfig == nil {
|
if tlsConfig == nil {
|
||||||
client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
||||||
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||||
@ -68,9 +71,12 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
if len(tlsConfig.NextProtos()) == 0 {
|
if len(tlsConfig.NextProtos()) == 0 {
|
||||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||||
}
|
}
|
||||||
tlsDialer := tls.NewDialer(dialer, tlsConfig)
|
|
||||||
client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
client.transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
||||||
return tlsDialer.DialTLSContext(ctx, M.ParseSocksaddr(addr))
|
conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tls.ClientHandshake(ctx, conn, tlsConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +47,15 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
if len(tlsConfig.NextProtos()) == 0 {
|
if len(tlsConfig.NextProtos()) == 0 {
|
||||||
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||||
}
|
}
|
||||||
tlsDialer := tls.NewDialer(dialer, tlsConfig)
|
|
||||||
transport = &http2.Transport{
|
transport = &http2.Transport{
|
||||||
ReadIdleTimeout: time.Duration(options.IdleTimeout),
|
ReadIdleTimeout: time.Duration(options.IdleTimeout),
|
||||||
PingTimeout: time.Duration(options.PingTimeout),
|
PingTimeout: time.Duration(options.PingTimeout),
|
||||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
|
||||||
return tlsDialer.DialTLSContext(ctx, M.ParseSocksaddr(addr))
|
conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tls.ClientHandshake(ctx, conn, tlsConfig)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ var _ adapter.V2RayClientTransport = (*Client)(nil)
|
|||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
|
tlsConfig tls.Config
|
||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
requestURL url.URL
|
requestURL url.URL
|
||||||
headers http.Header
|
headers http.Header
|
||||||
@ -34,7 +35,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
if len(tlsConfig.NextProtos()) == 0 {
|
if len(tlsConfig.NextProtos()) == 0 {
|
||||||
tlsConfig.SetNextProtos([]string{"http/1.1"})
|
tlsConfig.SetNextProtos([]string{"http/1.1"})
|
||||||
}
|
}
|
||||||
dialer = tls.NewDialer(dialer, tlsConfig)
|
|
||||||
}
|
}
|
||||||
var host string
|
var host string
|
||||||
if options.Host != "" {
|
if options.Host != "" {
|
||||||
@ -65,6 +65,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
}
|
}
|
||||||
return &Client{
|
return &Client{
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
|
tlsConfig: tlsConfig,
|
||||||
serverAddr: serverAddr,
|
serverAddr: serverAddr,
|
||||||
requestURL: requestURL,
|
requestURL: requestURL,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
@ -77,6 +78,12 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if c.tlsConfig != nil {
|
||||||
|
conn, err = tls.ClientHandshake(ctx, conn, c.tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
request := &http.Request{
|
request := &http.Request{
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
URL: &c.requestURL,
|
URL: &c.requestURL,
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-quic"
|
"github.com/sagernet/sing-quic"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/atomic"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@ -30,7 +29,7 @@ type Client struct {
|
|||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
quicConfig *quic.Config
|
quicConfig *quic.Config
|
||||||
connAccess sync.Mutex
|
connAccess sync.Mutex
|
||||||
conn atomic.TypedValue[quic.Connection]
|
conn quic.Connection
|
||||||
rawConn net.Conn
|
rawConn net.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,13 +50,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) offer() (quic.Connection, error) {
|
func (c *Client) offer() (quic.Connection, error) {
|
||||||
conn := c.conn.Load()
|
conn := c.conn
|
||||||
if conn != nil && !common.Done(conn.Context()) {
|
if conn != nil && !common.Done(conn.Context()) {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
c.connAccess.Lock()
|
c.connAccess.Lock()
|
||||||
defer c.connAccess.Unlock()
|
defer c.connAccess.Unlock()
|
||||||
conn = c.conn.Load()
|
conn = c.conn
|
||||||
if conn != nil && !common.Done(conn.Context()) {
|
if conn != nil && !common.Done(conn.Context()) {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
@ -73,13 +72,14 @@ func (c *Client) offerNew() (quic.Connection, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
packetConn := bufio.NewUnbindPacketConn(udpConn)
|
var packetConn net.PacketConn
|
||||||
|
packetConn = bufio.NewUnbindPacketConn(udpConn)
|
||||||
quicConn, err := qtls.Dial(c.ctx, packetConn, udpConn.RemoteAddr(), c.tlsConfig, c.quicConfig)
|
quicConn, err := qtls.Dial(c.ctx, packetConn, udpConn.RemoteAddr(), c.tlsConfig, c.quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
packetConn.Close()
|
packetConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.conn.Store(quicConn)
|
c.conn = quicConn
|
||||||
c.rawConn = udpConn
|
c.rawConn = udpConn
|
||||||
return quicConn, nil
|
return quicConn, nil
|
||||||
}
|
}
|
||||||
@ -99,13 +99,13 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
|||||||
func (c *Client) Close() error {
|
func (c *Client) Close() error {
|
||||||
c.connAccess.Lock()
|
c.connAccess.Lock()
|
||||||
defer c.connAccess.Unlock()
|
defer c.connAccess.Unlock()
|
||||||
conn := c.conn.Swap(nil)
|
if c.conn != nil {
|
||||||
if conn != nil {
|
c.conn.CloseWithError(0, "")
|
||||||
conn.CloseWithError(0, "")
|
|
||||||
}
|
}
|
||||||
if c.rawConn != nil {
|
if c.rawConn != nil {
|
||||||
c.rawConn.Close()
|
c.rawConn.Close()
|
||||||
}
|
}
|
||||||
|
c.conn = nil
|
||||||
c.rawConn = nil
|
c.rawConn = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ var _ adapter.V2RayClientTransport = (*Client)(nil)
|
|||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
|
tlsConfig tls.Config
|
||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
requestURL url.URL
|
requestURL url.URL
|
||||||
headers http.Header
|
headers http.Header
|
||||||
@ -38,7 +39,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
if len(tlsConfig.NextProtos()) == 0 {
|
if len(tlsConfig.NextProtos()) == 0 {
|
||||||
tlsConfig.SetNextProtos([]string{"http/1.1"})
|
tlsConfig.SetNextProtos([]string{"http/1.1"})
|
||||||
}
|
}
|
||||||
dialer = tls.NewDialer(dialer, tlsConfig)
|
|
||||||
}
|
}
|
||||||
var requestURL url.URL
|
var requestURL url.URL
|
||||||
if tlsConfig == nil {
|
if tlsConfig == nil {
|
||||||
@ -65,6 +65,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
}
|
}
|
||||||
return &Client{
|
return &Client{
|
||||||
dialer,
|
dialer,
|
||||||
|
tlsConfig,
|
||||||
serverAddr,
|
serverAddr,
|
||||||
requestURL,
|
requestURL,
|
||||||
headers,
|
headers,
|
||||||
@ -78,6 +79,12 @@ func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers h
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if c.tlsConfig != nil {
|
||||||
|
conn, err = tls.ClientHandshake(ctx, conn, c.tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
var deadlineConn net.Conn
|
var deadlineConn net.Conn
|
||||||
if deadline.NeedAdditionalReadDeadline(conn) {
|
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||||
deadlineConn = deadline.NewConn(conn)
|
deadlineConn = deadline.NewConn(conn)
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@ -136,22 +135,20 @@ func (c *WebsocketConn) Upstream() any {
|
|||||||
type EarlyWebsocketConn struct {
|
type EarlyWebsocketConn struct {
|
||||||
*Client
|
*Client
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
conn atomic.Pointer[WebsocketConn]
|
conn *WebsocketConn
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
create chan struct{}
|
create chan struct{}
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) Read(b []byte) (n int, err error) {
|
func (c *EarlyWebsocketConn) Read(b []byte) (n int, err error) {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
<-c.create
|
<-c.create
|
||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
return 0, c.err
|
return 0, c.err
|
||||||
}
|
}
|
||||||
conn = c.conn.Load()
|
|
||||||
}
|
}
|
||||||
return wrapWsError0(conn.Read(b))
|
return wrapWsError0(c.conn.Read(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
|
func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
|
||||||
@ -190,23 +187,21 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.conn.Store(conn)
|
c.conn = conn
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
|
func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
|
||||||
conn := c.conn.Load()
|
if c.conn != nil {
|
||||||
if conn != nil {
|
return wrapWsError0(c.conn.Write(b))
|
||||||
return wrapWsError0(conn.Write(b))
|
|
||||||
}
|
}
|
||||||
c.access.Lock()
|
c.access.Lock()
|
||||||
defer c.access.Unlock()
|
defer c.access.Unlock()
|
||||||
conn = c.conn.Load()
|
|
||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
return 0, c.err
|
return 0, c.err
|
||||||
}
|
}
|
||||||
if conn != nil {
|
if c.conn != nil {
|
||||||
return wrapWsError0(conn.Write(b))
|
return wrapWsError0(c.conn.Write(b))
|
||||||
}
|
}
|
||||||
err = c.writeRequest(b)
|
err = c.writeRequest(b)
|
||||||
c.err = err
|
c.err = err
|
||||||
@ -218,19 +213,17 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
|
func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
conn := c.conn.Load()
|
if c.conn != nil {
|
||||||
if conn != nil {
|
return wrapWsError(c.conn.WriteBuffer(buffer))
|
||||||
return wrapWsError(conn.WriteBuffer(buffer))
|
|
||||||
}
|
}
|
||||||
c.access.Lock()
|
c.access.Lock()
|
||||||
defer c.access.Unlock()
|
defer c.access.Unlock()
|
||||||
|
if c.conn != nil {
|
||||||
|
return wrapWsError(c.conn.WriteBuffer(buffer))
|
||||||
|
}
|
||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
return c.err
|
return c.err
|
||||||
}
|
}
|
||||||
conn = c.conn.Load()
|
|
||||||
if conn != nil {
|
|
||||||
return wrapWsError(conn.WriteBuffer(buffer))
|
|
||||||
}
|
|
||||||
err := c.writeRequest(buffer.Bytes())
|
err := c.writeRequest(buffer.Bytes())
|
||||||
c.err = err
|
c.err = err
|
||||||
close(c.create)
|
close(c.create)
|
||||||
@ -238,27 +231,24 @@ func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) Close() error {
|
func (c *EarlyWebsocketConn) Close() error {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return conn.Close()
|
return c.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) LocalAddr() net.Addr {
|
func (c *EarlyWebsocketConn) LocalAddr() net.Addr {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return M.Socksaddr{}
|
return M.Socksaddr{}
|
||||||
}
|
}
|
||||||
return conn.LocalAddr()
|
return c.conn.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) RemoteAddr() net.Addr {
|
func (c *EarlyWebsocketConn) RemoteAddr() net.Addr {
|
||||||
conn := c.conn.Load()
|
if c.conn == nil {
|
||||||
if conn == nil {
|
|
||||||
return M.Socksaddr{}
|
return M.Socksaddr{}
|
||||||
}
|
}
|
||||||
return conn.RemoteAddr()
|
return c.conn.RemoteAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error {
|
func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error {
|
||||||
@ -278,11 +268,11 @@ func (c *EarlyWebsocketConn) NeedAdditionalReadDeadline() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) Upstream() any {
|
func (c *EarlyWebsocketConn) Upstream() any {
|
||||||
return common.PtrOrNil(c.conn.Load())
|
return common.PtrOrNil(c.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *EarlyWebsocketConn) LazyHeadroom() bool {
|
func (c *EarlyWebsocketConn) LazyHeadroom() bool {
|
||||||
return c.conn.Load() == nil
|
return c.conn == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapWsError(err error) error {
|
func wrapWsError(err error) error {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user