mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
39 Commits
v1.13.0-be
...
dev-wifi-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
766972ce52 | ||
|
|
f1e48a1cad | ||
|
|
ba496ae300 | ||
|
|
4488148322 | ||
|
|
f086454f81 | ||
|
|
f9b6a068ee | ||
|
|
0ad2a441d9 | ||
|
|
c714b59c87 | ||
|
|
bd6b125707 | ||
|
|
d1109cee90 | ||
|
|
b48002b4db | ||
|
|
67cedfd927 | ||
|
|
853b576d12 | ||
|
|
05cd7f6192 | ||
|
|
e4cf55a86d | ||
|
|
2bf605fad4 | ||
|
|
b913899d43 | ||
|
|
e1a22c0cc2 | ||
|
|
d890c8e8d7 | ||
|
|
1b4ffff67c | ||
|
|
6d012c04cd | ||
|
|
c1309b63c9 | ||
|
|
6674b252bf | ||
|
|
8c7b2e4ac3 | ||
|
|
f400c927c4 | ||
|
|
587d330b58 | ||
|
|
719a28920a | ||
|
|
2e0d344a3d | ||
|
|
387084b7c7 | ||
|
|
e15c02ee33 | ||
|
|
2648b80c6b | ||
|
|
391f7a73d2 | ||
|
|
c1565e2bb0 | ||
|
|
605d8b41f9 | ||
|
|
03d8243d41 | ||
|
|
30f1beb071 | ||
|
|
3841d9cb5a | ||
|
|
dbb7345ec9 | ||
|
|
88f000412b |
@@ -14,7 +14,6 @@
|
||||
--depends kmod-inet-diag
|
||||
--depends kmod-tun
|
||||
--depends firewall4
|
||||
--depends kmod-nft-queue
|
||||
|
||||
--before-remove release/config/openwrt.prerm
|
||||
|
||||
|
||||
1
.github/CRONET_GO_VERSION
vendored
1
.github/CRONET_GO_VERSION
vendored
@@ -1 +0,0 @@
|
||||
e16b8bf4662d2bc02842408ab3e22845c41e0569
|
||||
2
.github/setup_go_for_windows7.sh
vendored
2
.github/setup_go_for_windows7.sh
vendored
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION="1.25.6"
|
||||
VERSION="1.25.1"
|
||||
|
||||
mkdir -p $HOME/go
|
||||
cd $HOME/go
|
||||
|
||||
13
.github/update_cronet.sh
vendored
13
.github/update_cronet.sh
vendored
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
PROJECTS=$SCRIPT_DIR/../..
|
||||
|
||||
git -C $PROJECTS/cronet-go fetch origin main
|
||||
git -C $PROJECTS/cronet-go fetch origin go
|
||||
go get -x github.com/sagernet/cronet-go/all@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
||||
go get -x github.com/sagernet/cronet-go@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
||||
go mod tidy
|
||||
git -C $PROJECTS/cronet-go rev-parse origin/HEAD > "$SCRIPT_DIR/CRONET_GO_VERSION"
|
||||
355
.github/workflows/build.yml
vendored
355
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.6
|
||||
go-version: ^1.25.1
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@@ -69,25 +69,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { os: linux, arch: amd64, variant: purego, naive: true }
|
||||
- { os: linux, arch: amd64, variant: glibc, naive: true }
|
||||
- { os: linux, arch: amd64, variant: musl, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
||||
|
||||
- { os: linux, arch: arm64, variant: purego, naive: true }
|
||||
- { os: linux, arch: arm64, variant: glibc, naive: true }
|
||||
- { os: linux, arch: arm64, variant: musl, naive: true, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
||||
|
||||
- { os: linux, arch: "386", go386: sse2 }
|
||||
- { os: linux, arch: "386", variant: glibc, naive: true, go386: sse2 }
|
||||
- { os: linux, arch: "386", variant: musl, naive: true, go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
||||
|
||||
- { os: linux, arch: arm, goarm: "7" }
|
||||
- { os: linux, arch: arm, variant: glibc, naive: true, goarm: "7" }
|
||||
- { os: linux, arch: arm, variant: musl, naive: true, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
||||
|
||||
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
||||
- { os: linux, arch: "386", go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
||||
- { os: linux, arch: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
|
||||
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
||||
- { os: linux, arch: arm, goarm: "5", openwrt: "arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" }
|
||||
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl, openwrt: "arm_arm1176jzf-s_vfp" }
|
||||
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
||||
- { os: linux, arch: mips, gomips: softfloat, openwrt: "mips_24kc mips_4kec mips_mips32" }
|
||||
- { os: linux, arch: mipsle, gomips: hardfloat, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc_24kf" }
|
||||
- { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
|
||||
@@ -99,28 +87,35 @@ jobs:
|
||||
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
|
||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||
|
||||
- { os: windows, arch: amd64 }
|
||||
- { os: windows, arch: amd64, legacy_win7: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: "386" }
|
||||
- { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: arm64 }
|
||||
|
||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android23" }
|
||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi23" }
|
||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android23" }
|
||||
- { os: android, arch: "386", ndk: "i686-linux-android23" }
|
||||
- { os: darwin, arch: amd64 }
|
||||
- { os: darwin, arch: arm64 }
|
||||
- { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||
|
||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
|
||||
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
|
||||
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.6
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Go 1.24
|
||||
if: matrix.legacy_go124
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.24.10
|
||||
go-version: ~1.24.6
|
||||
- name: Cache Go for Windows 7
|
||||
if: matrix.legacy_win7
|
||||
id: cache-go-for-windows7
|
||||
@@ -128,7 +123,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
~/go/go_win7
|
||||
key: go_win7_1255
|
||||
key: go_win7_1251
|
||||
- name: Setup Go for Windows 7
|
||||
if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
|
||||
run: |-
|
||||
@@ -144,45 +139,6 @@ jobs:
|
||||
with:
|
||||
ndk-version: r28
|
||||
local-cache: true
|
||||
- name: Clone cronet-go
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
|
||||
git init ~/cronet-go
|
||||
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
|
||||
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION"
|
||||
git -C ~/cronet-go checkout FETCH_HEAD
|
||||
git -C ~/cronet-go submodule update --init --recursive --depth=1
|
||||
- name: Cache Chromium toolchain
|
||||
if: matrix.naive
|
||||
id: cache-chromium-toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/cronet-go/naiveproxy/src/third_party/llvm-build/Release+Asserts
|
||||
~/cronet-go/naiveproxy/src/out/sysroot-build
|
||||
key: chromium-toolchain-${{ matrix.arch }}-${{ matrix.variant }}-${{ hashFiles('.github/CRONET_GO_VERSION') }}
|
||||
- name: Download Chromium toolchain
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
if [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl download-toolchain
|
||||
else
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} download-toolchain
|
||||
fi
|
||||
- name: Set Chromium toolchain environment
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
if [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl env >> $GITHUB_ENV
|
||||
else
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} env >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||
@@ -190,75 +146,15 @@ jobs:
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.naive }}" == "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound"
|
||||
fi
|
||||
if [[ "${{ matrix.variant }}" == "purego" ]]; then
|
||||
TAGS="${TAGS},with_purego"
|
||||
elif [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
TAGS="${TAGS},with_musl"
|
||||
fi
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0'
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build (purego)
|
||||
if: matrix.variant == 'purego'
|
||||
- name: Build
|
||||
if: matrix.os != 'android'
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
GOOS: ${{ matrix.os }}
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GO386: ${{ matrix.go386 }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GOMIPS: ${{ matrix.gomips }}
|
||||
GOMIPS64: ${{ matrix.gomips }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract libcronet.so
|
||||
if: matrix.variant == 'purego' && matrix.naive
|
||||
run: |
|
||||
cd ~/cronet-go
|
||||
CGO_ENABLED=0 go run -v ./cmd/build-naive extract-lib --target ${{ matrix.os }}/${{ matrix.arch }} -o $GITHUB_WORKSPACE/dist
|
||||
- name: Build (glibc)
|
||||
if: matrix.variant == 'glibc'
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
GOOS: linux
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GO386: ${{ matrix.go386 }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build (musl)
|
||||
if: matrix.variant == 'musl'
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
GOOS: linux
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GO386: ${{ matrix.go386 }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build (non-variant)
|
||||
if: matrix.os != 'android' && matrix.variant == ''
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
@@ -278,7 +174,7 @@ jobs:
|
||||
export CXX="${CC}++"
|
||||
mkdir -p dist
|
||||
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
@@ -297,11 +193,6 @@ jobs:
|
||||
elif [[ -n "${{ matrix.legacy_name }}" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
||||
fi
|
||||
if [[ "${{ matrix.variant }}" == "glibc" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-glibc"
|
||||
elif [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-musl"
|
||||
fi
|
||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
||||
PKG_VERSION="${PKG_VERSION//-/\~}"
|
||||
@@ -384,177 +275,15 @@ jobs:
|
||||
zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
|
||||
else
|
||||
cp sing-box "${DIR_NAME}"
|
||||
if [ -f libcronet.so ]; then
|
||||
cp libcronet.so "${DIR_NAME}"
|
||||
fi
|
||||
tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
|
||||
fi
|
||||
rm -r "${DIR_NAME}"
|
||||
- name: Cleanup
|
||||
run: rm -f dist/sing-box dist/libcronet.so
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}${{ matrix.variant && format('-{0}', matrix.variant) }}
|
||||
path: "dist"
|
||||
build_darwin:
|
||||
name: Build Darwin binaries
|
||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
||||
runs-on: macos-latest
|
||||
needs:
|
||||
- calculate_version
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { arch: amd64 }
|
||||
- { arch: arm64 }
|
||||
- { arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
if: ${{ ! matrix.legacy_go124 }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.3
|
||||
- name: Setup Go 1.24
|
||||
if: matrix.legacy_go124
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.24.6
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.legacy_go124 }}" != "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound"
|
||||
fi
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
GOOS: darwin
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set name
|
||||
run: |-
|
||||
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-darwin-${{ matrix.arch }}"
|
||||
if [[ -n "${{ matrix.legacy_name }}" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
||||
fi
|
||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||
- name: Archive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd dist
|
||||
mkdir -p "${DIR_NAME}"
|
||||
cp ../LICENSE "${DIR_NAME}"
|
||||
cp sing-box "${DIR_NAME}"
|
||||
tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
|
||||
rm -r "${DIR_NAME}"
|
||||
- name: Cleanup
|
||||
run: rm dist/sing-box
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-darwin_${{ matrix.arch }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
||||
path: "dist"
|
||||
build_windows:
|
||||
name: Build Windows binaries
|
||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
||||
runs-on: windows-latest
|
||||
needs:
|
||||
- calculate_version
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { arch: amd64, naive: true }
|
||||
- { arch: "386" }
|
||||
- { arch: arm64, naive: true }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.4
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$env:GITHUB_ENV"
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
- name: Build
|
||||
if: matrix.naive
|
||||
run: |
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_naive_outbound,with_purego,badlinkname,tfogo_checklinkname0" `
|
||||
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0" `
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
GOOS: windows
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build
|
||||
if: ${{ !matrix.naive }}
|
||||
run: |
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" `
|
||||
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0" `
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
GOOS: windows
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract libcronet.dll
|
||||
if: matrix.naive
|
||||
run: |
|
||||
$CRONET_GO_VERSION = Get-Content .github/CRONET_GO_VERSION
|
||||
$env:CGO_ENABLED = "0"
|
||||
go run -v "github.com/sagernet/cronet-go/cmd/build-naive@$CRONET_GO_VERSION" extract-lib --target windows/${{ matrix.arch }} -o dist
|
||||
- name: Archive
|
||||
if: matrix.naive
|
||||
run: |
|
||||
$DIR_NAME = "sing-box-${{ needs.calculate_version.outputs.version }}-windows-${{ matrix.arch }}"
|
||||
mkdir "dist/$DIR_NAME"
|
||||
Copy-Item LICENSE "dist/$DIR_NAME"
|
||||
Copy-Item "dist/sing-box.exe" "dist/$DIR_NAME"
|
||||
Copy-Item "dist/libcronet.dll" "dist/$DIR_NAME"
|
||||
Compress-Archive -Path "dist/$DIR_NAME" -DestinationPath "dist/$DIR_NAME.zip"
|
||||
Remove-Item -Recurse "dist/$DIR_NAME"
|
||||
- name: Archive
|
||||
if: ${{ !matrix.naive }}
|
||||
run: |
|
||||
$DIR_NAME = "sing-box-${{ needs.calculate_version.outputs.version }}-windows-${{ matrix.arch }}"
|
||||
mkdir "dist/$DIR_NAME"
|
||||
Copy-Item LICENSE "dist/$DIR_NAME"
|
||||
Copy-Item "dist/sing-box.exe" "dist/$DIR_NAME"
|
||||
Compress-Archive -Path "dist/$DIR_NAME" -DestinationPath "dist/$DIR_NAME.zip"
|
||||
Remove-Item -Recurse "dist/$DIR_NAME"
|
||||
- name: Cleanup
|
||||
if: matrix.naive
|
||||
run: Remove-Item dist/sing-box.exe, dist/libcronet.dll
|
||||
- name: Cleanup
|
||||
if: ${{ !matrix.naive }}
|
||||
run: Remove-Item dist/sing-box.exe
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-windows_${{ matrix.arch }}
|
||||
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) }}
|
||||
path: "dist"
|
||||
build_android:
|
||||
name: Build Android
|
||||
@@ -571,7 +300,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.6
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@@ -619,9 +348,9 @@ jobs:
|
||||
- name: Build
|
||||
run: |-
|
||||
mkdir clients/android/app/libs
|
||||
cp *.aar clients/android/app/libs
|
||||
cp libbox.aar clients/android/app/libs
|
||||
cd clients/android
|
||||
./gradlew :app:assembleOtherRelease :app:assembleOtherLegacyRelease
|
||||
./gradlew :app:assemblePlayRelease :app:assembleOtherRelease
|
||||
env:
|
||||
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
@@ -629,18 +358,8 @@ jobs:
|
||||
- name: Prepare upload
|
||||
run: |-
|
||||
mkdir -p dist
|
||||
#cp clients/android/app/build/outputs/apk/play/release/*.apk dist
|
||||
cp clients/android/app/build/outputs/apk/other/release/*.apk dist
|
||||
cp clients/android/app/build/outputs/apk/otherLegacy/release/*.apk dist
|
||||
VERSION_CODE=$(grep VERSION_CODE clients/android/version.properties | cut -d= -f2)
|
||||
VERSION_NAME=$(grep VERSION_NAME clients/android/version.properties | cut -d= -f2)
|
||||
cat > dist/SFA-version-metadata.json << EOF
|
||||
{
|
||||
"version_code": ${VERSION_CODE},
|
||||
"version_name": "${VERSION_NAME}"
|
||||
}
|
||||
EOF
|
||||
cat dist/SFA-version-metadata.json
|
||||
cp clients/android/app/build/outputs/apk/play/release/*.apk dist
|
||||
cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -661,7 +380,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.6
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@@ -702,7 +421,7 @@ jobs:
|
||||
run: |-
|
||||
go run -v ./cmd/internal/update_android_version --ci
|
||||
mkdir clients/android/app/libs
|
||||
cp *.aar clients/android/app/libs
|
||||
cp libbox.aar clients/android/app/libs
|
||||
cd clients/android
|
||||
echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
|
||||
./gradlew :app:publishPlayReleaseBundle
|
||||
@@ -714,7 +433,7 @@ jobs:
|
||||
build_apple:
|
||||
name: Build Apple clients
|
||||
runs-on: macos-26
|
||||
if: false # github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store' || inputs.build == 'iOS' || inputs.build == 'macOS' || inputs.build == 'tvOS' || inputs.build == 'macOS-standalone'
|
||||
if: false
|
||||
needs:
|
||||
- calculate_version
|
||||
strategy:
|
||||
@@ -760,7 +479,7 @@ jobs:
|
||||
if: matrix.if
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.6
|
||||
go-version: ^1.25.1
|
||||
- name: Set tag
|
||||
if: matrix.if
|
||||
run: |-
|
||||
@@ -879,7 +598,7 @@ jobs:
|
||||
--app-drop-link 0 0 \
|
||||
--skip-jenkins \
|
||||
SFM.dmg "${{ matrix.export_path }}/SFM.app"
|
||||
xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
|
||||
xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
|
||||
cd "${{ matrix.archive }}"
|
||||
zip -r SFM.dSYMs.zip dSYMs
|
||||
popd
|
||||
@@ -900,8 +619,6 @@ jobs:
|
||||
needs:
|
||||
- calculate_version
|
||||
- build
|
||||
- build_darwin
|
||||
- build_windows
|
||||
- build_android
|
||||
- build_apple
|
||||
steps:
|
||||
|
||||
149
.github/workflows/docker.yml
vendored
149
.github/workflows/docker.yml
vendored
@@ -1,10 +1,6 @@
|
||||
name: Publish Docker Images
|
||||
|
||||
on:
|
||||
#push:
|
||||
# branches:
|
||||
# - main-next
|
||||
# - dev-next
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
@@ -17,134 +13,8 @@ env:
|
||||
REGISTRY_IMAGE: ghcr.io/sagernet/sing-box
|
||||
|
||||
jobs:
|
||||
build_binary:
|
||||
name: Build binary
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
# Naive-enabled builds (musl)
|
||||
- { arch: amd64, naive: true, docker_platform: "linux/amd64" }
|
||||
- { arch: arm64, naive: true, docker_platform: "linux/arm64" }
|
||||
- { arch: "386", naive: true, docker_platform: "linux/386" }
|
||||
- { arch: arm, goarm: "7", naive: true, docker_platform: "linux/arm/v7" }
|
||||
# Non-naive builds
|
||||
- { arch: arm, goarm: "6", docker_platform: "linux/arm/v6" }
|
||||
- { arch: ppc64le, docker_platform: "linux/ppc64le" }
|
||||
- { arch: riscv64, docker_platform: "linux/riscv64" }
|
||||
- { arch: s390x, docker_platform: "linux/s390x" }
|
||||
steps:
|
||||
- name: Get commit to build
|
||||
id: ref
|
||||
run: |-
|
||||
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
|
||||
ref="${{ github.ref_name }}"
|
||||
else
|
||||
ref="${{ github.event.inputs.tag }}"
|
||||
fi
|
||||
echo "ref=$ref"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
ref: ${{ steps.ref.outputs.ref }}
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.4
|
||||
- name: Clone cronet-go
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
|
||||
git init ~/cronet-go
|
||||
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
|
||||
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION"
|
||||
git -C ~/cronet-go checkout FETCH_HEAD
|
||||
git -C ~/cronet-go submodule update --init --recursive --depth=1
|
||||
- name: Cache Chromium toolchain
|
||||
if: matrix.naive
|
||||
id: cache-chromium-toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/cronet-go/naiveproxy/src/third_party/llvm-build/Release+Asserts
|
||||
~/cronet-go/naiveproxy/src/out/sysroot-build
|
||||
key: chromium-toolchain-${{ matrix.arch }}-musl-${{ hashFiles('.github/CRONET_GO_VERSION') }}
|
||||
- name: Download Chromium toolchain
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl download-toolchain
|
||||
- name: Set version
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
VERSION=$(go run ./cmd/internal/read_tag)
|
||||
echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"
|
||||
- name: Set Chromium toolchain environment
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl env >> $GITHUB_ENV
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.naive }}" == "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound,with_musl"
|
||||
fi
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build (naive)
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
GOOS: linux
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
- name: Build (non-naive)
|
||||
if: ${{ ! matrix.naive }}
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
GOOS: linux
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
- name: Prepare artifact
|
||||
run: |
|
||||
platform=${{ matrix.docker_platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
# Rename binary to include arch info for Dockerfile.binary
|
||||
BINARY_NAME="sing-box-${{ matrix.arch }}"
|
||||
if [[ -n "${{ matrix.goarm }}" ]]; then
|
||||
BINARY_NAME="${BINARY_NAME}v${{ matrix.goarm }}"
|
||||
fi
|
||||
mv sing-box "${BINARY_NAME}"
|
||||
echo "BINARY_NAME=${BINARY_NAME}" >> $GITHUB_ENV
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ env.BINARY_NAME }}
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
build_docker:
|
||||
name: Build Docker image
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build_binary
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
@@ -177,16 +47,6 @@ jobs:
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
- name: Download binary
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: binary-${{ env.PLATFORM_PAIR }}
|
||||
path: .
|
||||
- name: Prepare binary
|
||||
run: |
|
||||
# Find and make the binary executable
|
||||
chmod +x sing-box-*
|
||||
ls -la sing-box-*
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Setup Docker Buildx
|
||||
@@ -208,7 +68,8 @@ jobs:
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
context: .
|
||||
file: Dockerfile.binary
|
||||
build-args: |
|
||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
- name: Export digest
|
||||
@@ -226,7 +87,7 @@ jobs:
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build_docker
|
||||
- build
|
||||
steps:
|
||||
- name: Get commit to build
|
||||
id: ref
|
||||
@@ -260,7 +121,6 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Create manifest list and push
|
||||
if: github.event_name != 'push'
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
@@ -268,7 +128,6 @@ jobs:
|
||||
-t "${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}" \
|
||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
||||
- name: Inspect image
|
||||
if: github.event_name != 'push'
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.latest }}
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
version: latest
|
||||
version: v2.4.0
|
||||
args: --timeout=30m
|
||||
install-mode: binary
|
||||
verify: false
|
||||
|
||||
78
.github/workflows/linux.yml
vendored
78
.github/workflows/linux.yml
vendored
@@ -1,10 +1,6 @@
|
||||
name: Build Linux Packages
|
||||
|
||||
on:
|
||||
#push:
|
||||
# branches:
|
||||
# - main-next
|
||||
# - dev-next
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
@@ -34,7 +30,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.6
|
||||
go-version: ^1.25.1
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@@ -56,13 +52,11 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Naive-enabled builds (musl)
|
||||
- { os: linux, arch: amd64, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
||||
- { os: linux, arch: arm64, naive: true, debian: arm64, rpm: aarch64, pacman: aarch64 }
|
||||
- { os: linux, arch: "386", naive: true, debian: i386, rpm: i386 }
|
||||
- { os: linux, arch: arm, goarm: "7", naive: true, debian: armhf, rpm: armv7hl, pacman: armv7hl }
|
||||
# Non-naive builds (unsupported architectures)
|
||||
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
||||
- { os: linux, arch: "386", debian: i386, rpm: i386 }
|
||||
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl }
|
||||
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl }
|
||||
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64 }
|
||||
- { os: linux, arch: mips64le, debian: mips64el, rpm: mips64el }
|
||||
- { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel }
|
||||
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
||||
@@ -77,38 +71,13 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.6
|
||||
- name: Clone cronet-go
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
|
||||
git init ~/cronet-go
|
||||
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
|
||||
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION"
|
||||
git -C ~/cronet-go checkout FETCH_HEAD
|
||||
git -C ~/cronet-go submodule update --init --recursive --depth=1
|
||||
- name: Cache Chromium toolchain
|
||||
if: matrix.naive
|
||||
id: cache-chromium-toolchain
|
||||
uses: actions/cache@v4
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Android NDK
|
||||
if: matrix.os == 'android'
|
||||
uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
path: |
|
||||
~/cronet-go/naiveproxy/src/third_party/llvm-build/Release+Asserts
|
||||
~/cronet-go/naiveproxy/src/out/sysroot-build
|
||||
key: chromium-toolchain-${{ matrix.arch }}-musl-${{ hashFiles('.github/CRONET_GO_VERSION') }}
|
||||
- name: Download Chromium toolchain
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl download-toolchain
|
||||
- name: Set Chromium toolchain environment
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl env >> $GITHUB_ENV
|
||||
ndk-version: r28
|
||||
local-cache: true
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||
@@ -116,32 +85,14 @@ jobs:
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.naive }}" == "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound,with_musl"
|
||||
fi
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0'
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build (naive)
|
||||
if: matrix.naive
|
||||
- name: Build
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
GOOS: linux
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build (non-naive)
|
||||
if: ${{ ! matrix.naive }}
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "0"
|
||||
@@ -234,6 +185,5 @@ jobs:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
- name: Publish packages
|
||||
if: github.event_name != 'push'
|
||||
run: |-
|
||||
ls dist | xargs -I {} curl -F "package=@dist/{}" https://${{ secrets.FURY_TOKEN }}@push.fury.io/sagernet/
|
||||
|
||||
103
.goreleaser.fury.yaml
Normal file
103
.goreleaser.fury.yaml
Normal file
@@ -0,0 +1,103 @@
|
||||
project_name: sing-box
|
||||
builds:
|
||||
- id: main
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
|
||||
- -s
|
||||
- -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
targets:
|
||||
- linux_386
|
||||
- linux_amd64_v1
|
||||
- linux_arm64
|
||||
- linux_arm_7
|
||||
- linux_s390x
|
||||
- linux_riscv64
|
||||
- linux_mips64le
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
snapshot:
|
||||
name_template: "{{ .Version }}.{{ .ShortCommit }}"
|
||||
nfpms:
|
||||
- &template
|
||||
id: package
|
||||
package_name: sing-box
|
||||
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
builds:
|
||||
- main
|
||||
homepage: https://sing-box.sagernet.org/
|
||||
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||
description: The universal proxy platform.
|
||||
license: GPLv3 or later
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
priority: extra
|
||||
contents:
|
||||
- src: release/config/config.json
|
||||
dst: /etc/sing-box/config.json
|
||||
type: "config|noreplace"
|
||||
|
||||
- src: release/config/sing-box.service
|
||||
dst: /usr/lib/systemd/system/sing-box.service
|
||||
- src: release/config/sing-box@.service
|
||||
dst: /usr/lib/systemd/system/sing-box@.service
|
||||
- src: release/config/sing-box.sysusers
|
||||
dst: /usr/lib/sysusers.d/sing-box.conf
|
||||
- src: release/config/sing-box.rules
|
||||
dst: /usr/share/polkit-1/rules.d/sing-box.rules
|
||||
- src: release/config/sing-box-split-dns.xml
|
||||
dst: /usr/share/dbus-1/system.d/sing-box-split-dns.conf
|
||||
|
||||
- src: release/completions/sing-box.bash
|
||||
dst: /usr/share/bash-completion/completions/sing-box.bash
|
||||
- src: release/completions/sing-box.fish
|
||||
dst: /usr/share/fish/vendor_completions.d/sing-box.fish
|
||||
- src: release/completions/sing-box.zsh
|
||||
dst: /usr/share/zsh/site-functions/_sing-box
|
||||
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
deb:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
fields:
|
||||
Bugs: https://github.com/SagerNet/sing-box/issues
|
||||
rpm:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
conflicts:
|
||||
- sing-box-beta
|
||||
- id: package_beta
|
||||
<<: *template
|
||||
package_name: sing-box-beta
|
||||
file_name_template: '{{ .ProjectName }}-beta_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
conflicts:
|
||||
- sing-box
|
||||
release:
|
||||
disable: true
|
||||
furies:
|
||||
- account: sagernet
|
||||
ids:
|
||||
- package
|
||||
disable: "{{ not (not .Prerelease) }}"
|
||||
- account: sagernet
|
||||
ids:
|
||||
- package_beta
|
||||
disable: "{{ not .Prerelease }}"
|
||||
213
.goreleaser.yaml
Normal file
213
.goreleaser.yaml
Normal file
@@ -0,0 +1,213 @@
|
||||
version: 2
|
||||
project_name: sing-box
|
||||
builds:
|
||||
- &template
|
||||
id: main
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
|
||||
- -s
|
||||
- -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOTOOLCHAIN=local
|
||||
targets:
|
||||
- linux_386
|
||||
- linux_amd64_v1
|
||||
- linux_arm64
|
||||
- linux_arm_6
|
||||
- linux_arm_7
|
||||
- linux_s390x
|
||||
- linux_riscv64
|
||||
- linux_mips64le
|
||||
- windows_amd64_v1
|
||||
- windows_386
|
||||
- windows_arm64
|
||||
- darwin_amd64_v1
|
||||
- darwin_arm64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
- id: legacy
|
||||
<<: *template
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_utls
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
- with_tailscale
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOROOT={{ .Env.GOPATH }}/go_legacy
|
||||
tool: "{{ .Env.GOPATH }}/go_legacy/bin/go"
|
||||
targets:
|
||||
- windows_amd64_v1
|
||||
- windows_386
|
||||
- id: android
|
||||
<<: *template
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- GOTOOLCHAIN=local
|
||||
overrides:
|
||||
- goos: android
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
env:
|
||||
- CC=armv7a-linux-androideabi21-clang
|
||||
- CXX=armv7a-linux-androideabi21-clang++
|
||||
- goos: android
|
||||
goarch: arm64
|
||||
env:
|
||||
- CC=aarch64-linux-android21-clang
|
||||
- CXX=aarch64-linux-android21-clang++
|
||||
- goos: android
|
||||
goarch: 386
|
||||
env:
|
||||
- CC=i686-linux-android21-clang
|
||||
- CXX=i686-linux-android21-clang++
|
||||
- goos: android
|
||||
goarch: amd64
|
||||
goamd64: v1
|
||||
env:
|
||||
- CC=x86_64-linux-android21-clang
|
||||
- CXX=x86_64-linux-android21-clang++
|
||||
targets:
|
||||
- android_arm_7
|
||||
- android_arm64
|
||||
- android_386
|
||||
- android_amd64
|
||||
archives:
|
||||
- &template
|
||||
id: archive
|
||||
builds:
|
||||
- main
|
||||
- android
|
||||
formats:
|
||||
- tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats:
|
||||
- zip
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- LICENSE
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}_{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
- id: archive-legacy
|
||||
<<: *template
|
||||
builds:
|
||||
- legacy
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-legacy'
|
||||
nfpms:
|
||||
- id: package
|
||||
package_name: sing-box
|
||||
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}_{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
builds:
|
||||
- main
|
||||
homepage: https://sing-box.sagernet.org/
|
||||
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||
description: The universal proxy platform.
|
||||
license: GPLv3 or later
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
- archlinux
|
||||
# - apk
|
||||
# - ipk
|
||||
priority: extra
|
||||
contents:
|
||||
- src: release/config/config.json
|
||||
dst: /etc/sing-box/config.json
|
||||
type: "config|noreplace"
|
||||
|
||||
- src: release/config/sing-box.service
|
||||
dst: /usr/lib/systemd/system/sing-box.service
|
||||
- src: release/config/sing-box@.service
|
||||
dst: /usr/lib/systemd/system/sing-box@.service
|
||||
- src: release/config/sing-box.sysusers
|
||||
dst: /usr/lib/sysusers.d/sing-box.conf
|
||||
- src: release/config/sing-box.rules
|
||||
dst: /usr/share/polkit-1/rules.d/sing-box.rules
|
||||
- src: release/config/sing-box-split-dns.xml
|
||||
dst: /usr/share/dbus-1/system.d/sing-box-split-dns.conf
|
||||
|
||||
- src: release/completions/sing-box.bash
|
||||
dst: /usr/share/bash-completion/completions/sing-box.bash
|
||||
- src: release/completions/sing-box.fish
|
||||
dst: /usr/share/fish/vendor_completions.d/sing-box.fish
|
||||
- src: release/completions/sing-box.zsh
|
||||
dst: /usr/share/zsh/site-functions/_sing-box
|
||||
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
deb:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
fields:
|
||||
Bugs: https://github.com/SagerNet/sing-box/issues
|
||||
rpm:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
overrides:
|
||||
apk:
|
||||
contents:
|
||||
- src: release/config/config.json
|
||||
dst: /etc/sing-box/config.json
|
||||
type: config
|
||||
|
||||
- src: release/config/sing-box.initd
|
||||
dst: /etc/init.d/sing-box
|
||||
|
||||
- src: release/completions/sing-box.bash
|
||||
dst: /usr/share/bash-completion/completions/sing-box.bash
|
||||
- src: release/completions/sing-box.fish
|
||||
dst: /usr/share/fish/vendor_completions.d/sing-box.fish
|
||||
- src: release/completions/sing-box.zsh
|
||||
dst: /usr/share/zsh/site-functions/_sing-box
|
||||
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
ipk:
|
||||
contents:
|
||||
- src: release/config/config.json
|
||||
dst: /etc/sing-box/config.json
|
||||
type: config
|
||||
|
||||
- src: release/config/openwrt.init
|
||||
dst: /etc/init.d/sing-box
|
||||
- src: release/config/openwrt.conf
|
||||
dst: /etc/config/sing-box
|
||||
source:
|
||||
enabled: false
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}.source'
|
||||
prefix_template: '{{ .ProjectName }}-{{ .Version }}/'
|
||||
checksum:
|
||||
disable: true
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}.checksum'
|
||||
signs:
|
||||
- artifacts: checksum
|
||||
release:
|
||||
github:
|
||||
owner: SagerNet
|
||||
name: sing-box
|
||||
draft: true
|
||||
prerelease: auto
|
||||
mode: replace
|
||||
ids:
|
||||
- archive
|
||||
- package
|
||||
skip_upload: true
|
||||
partial:
|
||||
by: target
|
||||
@@ -13,13 +13,15 @@ RUN set -ex \
|
||||
&& export COMMIT=$(git rev-parse --short HEAD) \
|
||||
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
||||
&& go build -v -trimpath -tags \
|
||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" \
|
||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0" \
|
||||
-o /go/bin/sing-box \
|
||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
|
||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
||||
./cmd/sing-box
|
||||
FROM --platform=$TARGETPLATFORM alpine AS dist
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache --upgrade bash tzdata ca-certificates nftables
|
||||
&& apk upgrade \
|
||||
&& apk add bash tzdata ca-certificates nftables \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
COPY --from=builder /go/bin/sing-box /usr/local/bin/sing-box
|
||||
ENTRYPOINT ["sing-box"]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache --upgrade bash tzdata ca-certificates nftables
|
||||
COPY sing-box-${TARGETARCH}${TARGETVARIANT} /usr/local/bin/sing-box
|
||||
ENTRYPOINT ["sing-box"]
|
||||
90
Makefile
90
Makefile
@@ -1,12 +1,12 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0
|
||||
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0
|
||||
|
||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest)
|
||||
|
||||
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0"
|
||||
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid= -checklinkname=0"
|
||||
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS)"
|
||||
MAIN = ./cmd/sing-box
|
||||
PREFIX ?= $(shell go env GOPATH)
|
||||
@@ -37,11 +37,8 @@ fmt:
|
||||
@gofmt -s -w .
|
||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||
|
||||
fmt_docs:
|
||||
go run ./cmd/internal/format_docs
|
||||
|
||||
fmt_install:
|
||||
go install -v mvdan.cc/gofumpt@latest
|
||||
go install -v mvdan.cc/gofumpt@v0.8.0
|
||||
go install -v github.com/daixiang0/gci@latest
|
||||
|
||||
lint:
|
||||
@@ -52,7 +49,7 @@ lint:
|
||||
GOOS=freebsd golangci-lint run ./...
|
||||
|
||||
lint_install:
|
||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
||||
|
||||
proto:
|
||||
@go run ./cmd/internal/protogen
|
||||
@@ -109,7 +106,7 @@ build_ios:
|
||||
cd ../sing-box-for-apple && \
|
||||
rm -rf build/SFI.xcarchive && \
|
||||
xcodebuild clean -scheme SFI && \
|
||||
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
|
||||
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
|
||||
|
||||
upload_ios_app_store:
|
||||
cd ../sing-box-for-apple && \
|
||||
@@ -130,7 +127,7 @@ release_ios: build_ios upload_ios_app_store
|
||||
build_macos:
|
||||
cd ../sing-box-for-apple && \
|
||||
rm -rf build/SFM.xcarchive && \
|
||||
xcodebuild archive -scheme SFM -configuration Release -archivePath build/SFM.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
|
||||
xcodebuild archive -scheme SFM -configuration Release -archivePath build/SFM.xcarchive -allowProvisioningUpdates
|
||||
|
||||
upload_macos_app_store:
|
||||
cd ../sing-box-for-apple && \
|
||||
@@ -139,50 +136,54 @@ upload_macos_app_store:
|
||||
release_macos: build_macos upload_macos_app_store
|
||||
|
||||
build_macos_standalone:
|
||||
$(MAKE) -C ../sing-box-for-apple archive_macos_standalone
|
||||
cd ../sing-box-for-apple && \
|
||||
rm -rf build/SFM.System.xcarchive && \
|
||||
xcodebuild archive -scheme SFM.System -configuration Release -archivePath build/SFM.System.xcarchive -allowProvisioningUpdates
|
||||
|
||||
build_macos_dmg:
|
||||
$(MAKE) -C ../sing-box-for-apple build_macos_dmg
|
||||
|
||||
build_macos_pkg:
|
||||
$(MAKE) -C ../sing-box-for-apple build_macos_pkg
|
||||
rm -rf dist/SFM
|
||||
mkdir -p dist/SFM
|
||||
cd ../sing-box-for-apple && \
|
||||
rm -rf build/SFM.System && \
|
||||
rm -rf build/SFM.dmg && \
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath "build/SFM.System.xcarchive" \
|
||||
-exportOptionsPlist SFM.System/Export.plist -allowProvisioningUpdates \
|
||||
-exportPath "build/SFM.System" && \
|
||||
create-dmg \
|
||||
--volname "sing-box" \
|
||||
--volicon "build/SFM.System/SFM.app/Contents/Resources/AppIcon.icns" \
|
||||
--icon "SFM.app" 0 0 \
|
||||
--hide-extension "SFM.app" \
|
||||
--app-drop-link 0 0 \
|
||||
--skip-jenkins \
|
||||
"../sing-box/dist/SFM/SFM.dmg" "build/SFM.System/SFM.app"
|
||||
|
||||
notarize_macos_dmg:
|
||||
$(MAKE) -C ../sing-box-for-apple notarize_macos_dmg
|
||||
|
||||
notarize_macos_pkg:
|
||||
$(MAKE) -C ../sing-box-for-apple notarize_macos_pkg
|
||||
xcrun notarytool submit "dist/SFM/SFM.dmg" --wait \
|
||||
--keychain-profile "notarytool-password" \
|
||||
--no-s3-acceleration
|
||||
|
||||
upload_macos_dmg:
|
||||
mkdir -p dist/SFM
|
||||
cp ../sing-box-for-apple/build/SFM-Apple.dmg "dist/SFM/SFM-${VERSION}-Apple.dmg"
|
||||
cp ../sing-box-for-apple/build/SFM-Intel.dmg "dist/SFM/SFM-${VERSION}-Intel.dmg"
|
||||
cp ../sing-box-for-apple/build/SFM-Universal.dmg "dist/SFM/SFM-${VERSION}-Universal.dmg"
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Apple.dmg"
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.dmg"
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.dmg"
|
||||
|
||||
upload_macos_pkg:
|
||||
mkdir -p dist/SFM
|
||||
cp ../sing-box-for-apple/build/SFM-Apple.pkg "dist/SFM/SFM-${VERSION}-Apple.pkg"
|
||||
cp ../sing-box-for-apple/build/SFM-Intel.pkg "dist/SFM/SFM-${VERSION}-Intel.pkg"
|
||||
cp ../sing-box-for-apple/build/SFM-Universal.pkg "dist/SFM/SFM-${VERSION}-Universal.pkg"
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Apple.pkg"
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.pkg"
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.pkg"
|
||||
cd dist/SFM && \
|
||||
cp SFM.dmg "SFM-${VERSION}-universal.dmg" && \
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dmg"
|
||||
|
||||
upload_macos_dsyms:
|
||||
mkdir -p dist/SFM
|
||||
cd ../sing-box-for-apple/build/SFM.System-universal.xcarchive && zip -r SFM.dSYMs.zip dSYMs
|
||||
cp ../sing-box-for-apple/build/SFM.System-universal.xcarchive/SFM.dSYMs.zip "dist/SFM/SFM-${VERSION}.dSYMs.zip"
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}.dSYMs.zip"
|
||||
pushd ../sing-box-for-apple/build/SFM.System.xcarchive && \
|
||||
zip -r SFM.dSYMs.zip dSYMs && \
|
||||
mv SFM.dSYMs.zip ../../../sing-box/dist/SFM && \
|
||||
popd && \
|
||||
cd dist/SFM && \
|
||||
cp SFM.dSYMs.zip "SFM-${VERSION}-universal.dSYMs.zip" && \
|
||||
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dSYMs.zip"
|
||||
|
||||
release_macos_standalone: build_macos_pkg notarize_macos_pkg upload_macos_pkg upload_macos_dsyms
|
||||
release_macos_standalone: build_macos_standalone build_macos_dmg notarize_macos_dmg upload_macos_dmg upload_macos_dsyms
|
||||
|
||||
build_tvos:
|
||||
cd ../sing-box-for-apple && \
|
||||
rm -rf build/SFT.xcarchive && \
|
||||
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
|
||||
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive -allowProvisioningUpdates
|
||||
|
||||
upload_tvos_app_store:
|
||||
cd ../sing-box-for-apple && \
|
||||
@@ -211,7 +212,7 @@ release_apple: lib_ios update_apple_version release_ios release_macos release_tv
|
||||
release_apple_beta: update_apple_version release_ios release_macos release_tvos
|
||||
|
||||
publish_testflight:
|
||||
go run -v ./cmd/internal/app_store_connect publish_testflight $(filter-out $@,$(MAKECMDGOALS))
|
||||
go run -v ./cmd/internal/app_store_connect publish_testflight
|
||||
|
||||
prepare_app_store:
|
||||
go run -v ./cmd/internal/app_store_connect prepare_app_store
|
||||
@@ -248,8 +249,8 @@ lib:
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib_install:
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.11
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.11
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.8
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8
|
||||
|
||||
docs:
|
||||
venv/bin/mkdocs serve
|
||||
@@ -269,6 +270,3 @@ update:
|
||||
git fetch
|
||||
git reset FETCH_HEAD --hard
|
||||
git clean -fdx
|
||||
|
||||
%:
|
||||
@:
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
> Sponsored by [Warp](https://go.warp.dev/sing-box), built for coding with multiple AI agents
|
||||
|
||||
<a href="https://go.warp.dev/sing-box">
|
||||
<img alt="Warp sponsorship" width="400" src="https://github.com/warpdotdev/brand-assets/raw/refs/heads/main/Github/Sponsor/Warp-Github-LG-02.png">
|
||||
</a>
|
||||
|
||||
---
|
||||
|
||||
# sing-box
|
||||
|
||||
The universal proxy platform.
|
||||
|
||||
@@ -27,6 +27,8 @@ type DNSClient interface {
|
||||
Start()
|
||||
Exchange(ctx context.Context, transport DNSTransport, message *dns.Msg, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error)
|
||||
Lookup(ctx context.Context, transport DNSTransport, domain string, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error)
|
||||
LookupCache(domain string, strategy C.DomainStrategy) ([]netip.Addr, bool)
|
||||
ExchangeCache(ctx context.Context, message *dns.Msg) (*dns.Msg, bool)
|
||||
ClearCache()
|
||||
}
|
||||
|
||||
@@ -68,7 +70,6 @@ type DNSTransport interface {
|
||||
Type() string
|
||||
Tag() string
|
||||
Dependencies() []string
|
||||
Reset()
|
||||
Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ adapter.EndpointManager = (*Manager)(nil)
|
||||
@@ -48,14 +46,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
for _, endpoint := range m.endpoints {
|
||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(endpoint, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -72,15 +66,11 @@ func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
var err error
|
||||
for _, endpoint := range endpoints {
|
||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
err = E.Append(err, endpoint.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -129,15 +119,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(endpoint, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if existsEndpoint, loaded := m.endpointByTag[tag]; loaded {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
)
|
||||
|
||||
@@ -15,7 +14,6 @@ type ClashServer interface {
|
||||
ConnectionTracker
|
||||
Mode() string
|
||||
ModeList() []string
|
||||
SetModeUpdateHook(hook *observable.Subscriber[struct{}])
|
||||
HistoryStorage() URLTestHistoryStorage
|
||||
}
|
||||
|
||||
@@ -25,7 +23,7 @@ type URLTestHistory struct {
|
||||
}
|
||||
|
||||
type URLTestHistoryStorage interface {
|
||||
SetHook(hook *observable.Subscriber[struct{}])
|
||||
SetHook(hook chan<- struct{})
|
||||
LoadURLTestHistory(tag string) *URLTestHistory
|
||||
DeleteURLTestHistory(tag string)
|
||||
StoreURLTestHistory(tag string, history *URLTestHistory)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ adapter.InboundManager = (*Manager)(nil)
|
||||
@@ -47,14 +45,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
inbounds := m.inbounds
|
||||
m.access.Unlock()
|
||||
for _, inbound := range inbounds {
|
||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -71,15 +65,11 @@ func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
var err error
|
||||
for _, inbound := range inbounds {
|
||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
err = E.Append(err, inbound.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -131,15 +121,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if existsInbound, loaded := m.inboundByTag[tag]; loaded {
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
type SimpleLifecycle interface {
|
||||
Start() error
|
||||
@@ -56,47 +48,22 @@ type LifecycleService interface {
|
||||
Lifecycle
|
||||
}
|
||||
|
||||
func getServiceName(service any) string {
|
||||
if named, ok := service.(interface {
|
||||
Type() string
|
||||
Tag() string
|
||||
}); ok {
|
||||
tag := named.Tag()
|
||||
if tag != "" {
|
||||
return named.Type() + "[" + tag + "]"
|
||||
}
|
||||
return named.Type()
|
||||
}
|
||||
t := reflect.TypeOf(service)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
return strings.ToLower(t.Name())
|
||||
}
|
||||
|
||||
func Start(logger log.ContextLogger, stage StartStage, services ...Lifecycle) error {
|
||||
func Start(stage StartStage, services ...Lifecycle) error {
|
||||
for _, service := range services {
|
||||
name := getServiceName(service)
|
||||
logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := service.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartNamed(logger log.ContextLogger, stage StartStage, services []LifecycleService) error {
|
||||
func StartNamed(stage StartStage, services []LifecycleService) error {
|
||||
for _, service := range services {
|
||||
logger.Trace(stage, " ", service.Name())
|
||||
startTime := time.Now()
|
||||
err := service.Start(stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.String(), " ", service.Name())
|
||||
}
|
||||
logger.Trace(stage, " ", service.Name(), " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
@@ -83,14 +81,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
for _, outbound := range outbounds {
|
||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -115,29 +109,22 @@ func (m *Manager) startOutbounds(outbounds []adapter.Outbound) error {
|
||||
}
|
||||
started[outboundTag] = true
|
||||
canContinue = true
|
||||
name := "outbound/" + outboundToStart.Type() + "[" + outboundTag + "]"
|
||||
if starter, isStarter := outboundToStart.(adapter.Lifecycle); isStarter {
|
||||
m.logger.Trace("start ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("start ", name)
|
||||
monitor.Start("start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
err := starter.Start(adapter.StartStateStart)
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start ", name)
|
||||
return E.Cause(err, "start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
}
|
||||
m.logger.Trace("start ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
} else if starter, isStarter := outboundToStart.(interface {
|
||||
Start() error
|
||||
}); isStarter {
|
||||
m.logger.Trace("start ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("start ", name)
|
||||
monitor.Start("start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
err := starter.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start ", name)
|
||||
return E.Cause(err, "start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
}
|
||||
m.logger.Trace("start ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if len(started) == len(outbounds) {
|
||||
@@ -184,15 +171,11 @@ func (m *Manager) Close() error {
|
||||
var err error
|
||||
for _, outbound := range outbounds {
|
||||
if closer, isCloser := outbound.(io.Closer); isCloser {
|
||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
err = E.Append(err, closer.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -273,15 +256,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
return err
|
||||
}
|
||||
if m.started {
|
||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
m.access.Lock()
|
||||
|
||||
@@ -21,11 +21,10 @@ import (
|
||||
type Router interface {
|
||||
Lifecycle
|
||||
ConnectionRouter
|
||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration, supportBypass bool) (tun.DirectRouteDestination, error)
|
||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
||||
ConnectionRouterEx
|
||||
RuleSet(tag string) (RuleSet, bool)
|
||||
Rules() []Rule
|
||||
NeedFindProcess() bool
|
||||
AppendTracker(tracker ConnectionTracker)
|
||||
ResetNetwork()
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ adapter.ServiceManager = (*Manager)(nil)
|
||||
@@ -45,14 +43,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
services := m.services
|
||||
m.access.Unlock()
|
||||
for _, service := range services {
|
||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(service, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " service/", service.Type(), "[", service.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -69,15 +63,11 @@ func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
var err error
|
||||
for _, service := range services {
|
||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close service/", service.Type(), "[", service.Tag(), "]")
|
||||
err = E.Append(err, service.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close service/", service.Type(), "[", service.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -126,15 +116,11 @@ func (m *Manager) Create(ctx context.Context, logger log.ContextLogger, tag stri
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(service, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " service/", service.Type(), "[", service.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if existsService, loaded := m.serviceByTag[tag]; loaded {
|
||||
|
||||
@@ -73,7 +73,7 @@ func NewUpstreamContextHandlerEx(
|
||||
}
|
||||
|
||||
func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, myMetadata := ExtendContext(ctx)
|
||||
myMetadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context,
|
||||
}
|
||||
|
||||
func (w *myUpstreamContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, myMetadata := ExtendContext(ctx)
|
||||
myMetadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
@@ -146,7 +146,7 @@ type routeContextHandlerWrapperEx struct {
|
||||
}
|
||||
|
||||
func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, metadata := ExtendContext(ctx)
|
||||
metadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
metadata.Source = source
|
||||
}
|
||||
@@ -157,7 +157,7 @@ func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn
|
||||
}
|
||||
|
||||
func (r *routeContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, metadata := ExtendContext(ctx)
|
||||
metadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
metadata.Source = source
|
||||
}
|
||||
|
||||
49
box.go
49
box.go
@@ -443,15 +443,15 @@ func (s *Box) preStart() error {
|
||||
if err != nil {
|
||||
return E.Cause(err, "start logger")
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStateInitialize, s.internalService) // cache-file clash-api v2ray-api
|
||||
err = adapter.StartNamed(adapter.StartStateInitialize, s.internalService) // cache-file clash-api v2ray-api
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -463,27 +463,27 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStateStart, s.internalService)
|
||||
err = adapter.StartNamed(adapter.StartStateStart, s.internalService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStart, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStateStart, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStatePostStart, s.internalService)
|
||||
err = adapter.StartNamed(adapter.StartStatePostStart, s.internalService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStateStarted, s.internalService)
|
||||
err = adapter.StartNamed(adapter.StartStateStarted, s.internalService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -497,42 +497,17 @@ func (s *Box) Close() error {
|
||||
default:
|
||||
close(s.done)
|
||||
}
|
||||
var err error
|
||||
for _, closeItem := range []struct {
|
||||
name string
|
||||
service adapter.Lifecycle
|
||||
}{
|
||||
{"service", s.service},
|
||||
{"endpoint", s.endpoint},
|
||||
{"inbound", s.inbound},
|
||||
{"outbound", s.outbound},
|
||||
{"router", s.router},
|
||||
{"connection", s.connection},
|
||||
{"dns-router", s.dnsRouter},
|
||||
{"dns-transport", s.dnsTransport},
|
||||
{"network", s.network},
|
||||
} {
|
||||
s.logger.Trace("close ", closeItem.name)
|
||||
startTime := time.Now()
|
||||
err = E.Append(err, closeItem.service.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", closeItem.name)
|
||||
})
|
||||
s.logger.Trace("close ", closeItem.name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
err := common.Close(
|
||||
s.service, s.endpoint, s.inbound, s.outbound, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
|
||||
)
|
||||
for _, lifecycleService := range s.internalService {
|
||||
s.logger.Trace("close ", lifecycleService.Name())
|
||||
startTime := time.Now()
|
||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", lifecycleService.Name())
|
||||
})
|
||||
s.logger.Trace("close ", lifecycleService.Name(), " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
s.logger.Trace("close logger")
|
||||
startTime := time.Now()
|
||||
err = E.Append(err, s.logFactory.Close(), func(err error) error {
|
||||
return E.Cause(err, "close logger")
|
||||
})
|
||||
s.logger.Trace("close logger completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Submodule clients/android updated: 39e961fcbc...e08fbfcfea
Submodule clients/apple updated: 97402ba8b6...84d8cf1757
@@ -100,32 +100,11 @@ findVersion:
|
||||
}
|
||||
|
||||
func publishTestflight(ctx context.Context) error {
|
||||
if len(os.Args) < 3 {
|
||||
return E.New("platform required: ios, macos, or tvos")
|
||||
}
|
||||
var platform asc.Platform
|
||||
switch os.Args[2] {
|
||||
case "ios":
|
||||
platform = asc.PlatformIOS
|
||||
case "macos":
|
||||
platform = asc.PlatformMACOS
|
||||
case "tvos":
|
||||
platform = asc.PlatformTVOS
|
||||
default:
|
||||
return E.New("unknown platform: ", os.Args[2])
|
||||
}
|
||||
|
||||
tagVersion, err := build_shared.ReadTagVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tag := tagVersion.VersionString()
|
||||
|
||||
releaseNotes := F.ToString("sing-box ", tagVersion.String())
|
||||
if len(os.Args) >= 4 {
|
||||
releaseNotes = strings.Join(os.Args[3:], " ")
|
||||
}
|
||||
|
||||
client := createClient(20 * time.Minute)
|
||||
|
||||
log.Info(tag, " list build IDs")
|
||||
@@ -136,75 +115,97 @@ func publishTestflight(ctx context.Context) error {
|
||||
buildIDs := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string {
|
||||
return it.ID
|
||||
})
|
||||
|
||||
var platforms []asc.Platform
|
||||
if len(os.Args) == 3 {
|
||||
switch os.Args[2] {
|
||||
case "ios":
|
||||
platforms = []asc.Platform{asc.PlatformIOS}
|
||||
case "macos":
|
||||
platforms = []asc.Platform{asc.PlatformMACOS}
|
||||
case "tvos":
|
||||
platforms = []asc.Platform{asc.PlatformTVOS}
|
||||
default:
|
||||
return E.New("unknown platform: ", os.Args[2])
|
||||
}
|
||||
} else {
|
||||
platforms = []asc.Platform{
|
||||
asc.PlatformIOS,
|
||||
asc.PlatformMACOS,
|
||||
asc.PlatformTVOS,
|
||||
}
|
||||
}
|
||||
waitingForProcess := false
|
||||
log.Info(string(platform), " list builds")
|
||||
for {
|
||||
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
||||
FilterApp: []string{appID},
|
||||
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
build := builds.Data[0]
|
||||
if !waitingForProcess && (common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute) {
|
||||
log.Info(string(platform), " ", tag, " waiting for process")
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
}
|
||||
if *build.Attributes.ProcessingState != "VALID" {
|
||||
waitingForProcess = true
|
||||
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " list localizations")
|
||||
localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, build.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool {
|
||||
return *it.Attributes.Locale == "en-US"
|
||||
})
|
||||
if localization.ID == "" {
|
||||
log.Fatal(string(platform), " ", tag, " no en-US localization found")
|
||||
}
|
||||
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
|
||||
log.Info(string(platform), " ", tag, " update localization")
|
||||
_, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr(releaseNotes))
|
||||
for _, platform := range platforms {
|
||||
log.Info(string(platform), " list builds")
|
||||
for {
|
||||
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
||||
FilterApp: []string{appID},
|
||||
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " publish")
|
||||
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
|
||||
if response != nil && (response.StatusCode == http.StatusUnprocessableEntity || response.StatusCode == http.StatusNotFound) {
|
||||
log.Info("waiting for process")
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " list submissions")
|
||||
betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{
|
||||
FilterBuild: []string{build.ID},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(betaSubmissions.Data) == 0 {
|
||||
log.Info(string(platform), " ", tag, " create submission")
|
||||
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
||||
build := builds.Data[0]
|
||||
if !waitingForProcess && (common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute) {
|
||||
log.Info(string(platform), " ", tag, " waiting for process")
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
}
|
||||
if *build.Attributes.ProcessingState != "VALID" {
|
||||
waitingForProcess = true
|
||||
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " list localizations")
|
||||
localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, build.ID, nil)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "ANOTHER_BUILD_IN_REVIEW") {
|
||||
log.Error(err)
|
||||
break
|
||||
return err
|
||||
}
|
||||
localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool {
|
||||
return *it.Attributes.Locale == "en-US"
|
||||
})
|
||||
if localization.ID == "" {
|
||||
log.Fatal(string(platform), " ", tag, " no en-US localization found")
|
||||
}
|
||||
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
|
||||
log.Info(string(platform), " ", tag, " update localization")
|
||||
_, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr(
|
||||
F.ToString("sing-box ", tagVersion.String()),
|
||||
))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " publish")
|
||||
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
|
||||
if response != nil && (response.StatusCode == http.StatusUnprocessableEntity || response.StatusCode == http.StatusNotFound) {
|
||||
log.Info("waiting for process")
|
||||
time.Sleep(15 * time.Second)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(string(platform), " ", tag, " list submissions")
|
||||
betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{
|
||||
FilterBuild: []string{build.ID},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(betaSubmissions.Data) == 0 {
|
||||
log.Info(string(platform), " ", tag, " create submission")
|
||||
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "ANOTHER_BUILD_IN_REVIEW") {
|
||||
log.Error(err)
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/sagernet/gomobile"
|
||||
@@ -47,7 +46,7 @@ var (
|
||||
sharedFlags []string
|
||||
debugFlags []string
|
||||
sharedTags []string
|
||||
darwinTags []string
|
||||
macOSTags []string
|
||||
memcTags []string
|
||||
notMemcTags []string
|
||||
debugTags []string
|
||||
@@ -60,37 +59,19 @@ func init() {
|
||||
if err != nil {
|
||||
currentTag = "unknown"
|
||||
}
|
||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0")
|
||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0")
|
||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
||||
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
||||
darwinTags = append(darwinTags, "with_dhcp")
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
||||
macOSTags = append(macOSTags, "with_dhcp")
|
||||
memcTags = append(memcTags, "with_tailscale")
|
||||
notMemcTags = append(notMemcTags, "with_low_memory")
|
||||
debugTags = append(debugTags, "debug")
|
||||
}
|
||||
|
||||
type AndroidBuildConfig struct {
|
||||
AndroidAPI int
|
||||
OutputName string
|
||||
Tags []string
|
||||
}
|
||||
func buildAndroid() {
|
||||
build_shared.FindSDK()
|
||||
|
||||
func filterTags(tags []string, exclude ...string) []string {
|
||||
excludeMap := make(map[string]bool)
|
||||
for _, tag := range exclude {
|
||||
excludeMap[tag] = true
|
||||
}
|
||||
var result []string
|
||||
for _, tag := range tags {
|
||||
if !excludeMap[tag] {
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func checkJavaVersion() {
|
||||
var javaPath string
|
||||
javaHome := os.Getenv("JAVA_HOME")
|
||||
if javaHome == "" {
|
||||
@@ -106,24 +87,21 @@ func checkJavaVersion() {
|
||||
if !strings.Contains(javaVersion, "openjdk 17") {
|
||||
log.Fatal("java version should be openjdk 17")
|
||||
}
|
||||
}
|
||||
|
||||
func getAndroidBindTarget() string {
|
||||
var bindTarget string
|
||||
if platform != "" {
|
||||
return platform
|
||||
bindTarget = platform
|
||||
} else if debugEnabled {
|
||||
return "android/arm64"
|
||||
bindTarget = "android/arm64"
|
||||
} else {
|
||||
bindTarget = "android"
|
||||
}
|
||||
return "android"
|
||||
}
|
||||
|
||||
func buildAndroidVariant(config AndroidBuildConfig, bindTarget string) {
|
||||
args := []string{
|
||||
"bind",
|
||||
"-v",
|
||||
"-o", config.OutputName,
|
||||
"-target", bindTarget,
|
||||
"-androidapi", strconv.Itoa(config.AndroidAPI),
|
||||
"-androidapi", "21",
|
||||
"-javapkg=io.nekohasekai",
|
||||
"-libname=box",
|
||||
}
|
||||
@@ -134,59 +112,34 @@ func buildAndroidVariant(config AndroidBuildConfig, bindTarget string) {
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
args = append(args, "-tags", strings.Join(config.Tags, ","))
|
||||
tags := append(sharedTags, memcTags...)
|
||||
if debugEnabled {
|
||||
tags = append(tags, debugTags...)
|
||||
}
|
||||
|
||||
args = append(args, "-tags", strings.Join(tags, ","))
|
||||
args = append(args, "./experimental/libbox")
|
||||
|
||||
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||
command.Stdout = os.Stdout
|
||||
command.Stderr = os.Stderr
|
||||
err := command.Run()
|
||||
err = command.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
const name = "libbox.aar"
|
||||
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
||||
if rw.IsDir(copyPath) {
|
||||
copyPath, _ = filepath.Abs(copyPath)
|
||||
err = rw.CopyFile(config.OutputName, filepath.Join(copyPath, config.OutputName))
|
||||
err = rw.CopyFile(name, filepath.Join(copyPath, name))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Info("copied ", config.OutputName, " to ", copyPath)
|
||||
log.Info("copied to ", copyPath)
|
||||
}
|
||||
}
|
||||
|
||||
func buildAndroid() {
|
||||
build_shared.FindSDK()
|
||||
checkJavaVersion()
|
||||
|
||||
bindTarget := getAndroidBindTarget()
|
||||
|
||||
// Build main variant (SDK 23)
|
||||
mainTags := append([]string{}, sharedTags...)
|
||||
mainTags = append(mainTags, memcTags...)
|
||||
if debugEnabled {
|
||||
mainTags = append(mainTags, debugTags...)
|
||||
}
|
||||
buildAndroidVariant(AndroidBuildConfig{
|
||||
AndroidAPI: 23,
|
||||
OutputName: "libbox.aar",
|
||||
Tags: mainTags,
|
||||
}, bindTarget)
|
||||
|
||||
// Build legacy variant (SDK 21, no naive outbound)
|
||||
legacyTags := filterTags(sharedTags, "with_naive_outbound")
|
||||
legacyTags = append(legacyTags, memcTags...)
|
||||
if debugEnabled {
|
||||
legacyTags = append(legacyTags, debugTags...)
|
||||
}
|
||||
buildAndroidVariant(AndroidBuildConfig{
|
||||
AndroidAPI: 21,
|
||||
OutputName: "libbox-legacy.aar",
|
||||
Tags: legacyTags,
|
||||
}, bindTarget)
|
||||
}
|
||||
|
||||
func buildApple() {
|
||||
var bindTarget string
|
||||
if platform != "" {
|
||||
@@ -194,7 +147,7 @@ func buildApple() {
|
||||
} else if debugEnabled {
|
||||
bindTarget = "ios"
|
||||
} else {
|
||||
bindTarget = "ios,iossimulator,tvos,tvossimulator,macos"
|
||||
bindTarget = "ios,tvos,macos"
|
||||
}
|
||||
|
||||
args := []string{
|
||||
@@ -205,7 +158,9 @@ func buildApple() {
|
||||
"-tags-not-macos=with_low_memory",
|
||||
}
|
||||
if !withTailscale {
|
||||
args = append(args, "-tags-macos="+strings.Join(memcTags, ","))
|
||||
args = append(args, "-tags-macos="+strings.Join(append(macOSTags, memcTags...), ","))
|
||||
} else {
|
||||
args = append(args, "-tags-macos="+strings.Join(macOSTags, ","))
|
||||
}
|
||||
|
||||
if !debugEnabled {
|
||||
@@ -214,7 +169,7 @@ func buildApple() {
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
tags := append(sharedTags, darwinTags...)
|
||||
tags := sharedTags
|
||||
if withTailscale {
|
||||
tags = append(tags, memcTags...)
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := filepath.Walk("docs", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(path, ".md") {
|
||||
return nil
|
||||
}
|
||||
return processFile(path)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processFile(path string) error {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
modified := false
|
||||
result := make([]string, 0, len(lines))
|
||||
|
||||
inQuoteBlock := false
|
||||
materialLines := []int{} // indices of :material- lines in the block
|
||||
|
||||
for _, line := range lines {
|
||||
// Check for quote block start
|
||||
if strings.HasPrefix(line, "!!! quote \"") && strings.Contains(line, "sing-box") {
|
||||
inQuoteBlock = true
|
||||
materialLines = nil
|
||||
result = append(result, line)
|
||||
continue
|
||||
}
|
||||
|
||||
// Inside a quote block
|
||||
if inQuoteBlock {
|
||||
trimmed := strings.TrimPrefix(line, " ")
|
||||
isMaterialLine := strings.HasPrefix(trimmed, ":material-")
|
||||
isEmpty := strings.TrimSpace(line) == ""
|
||||
isIndented := strings.HasPrefix(line, " ")
|
||||
|
||||
if isMaterialLine {
|
||||
materialLines = append(materialLines, len(result))
|
||||
result = append(result, line)
|
||||
continue
|
||||
}
|
||||
|
||||
// Block ends when:
|
||||
// - Empty line AFTER we've seen material lines, OR
|
||||
// - Non-indented, non-empty line
|
||||
blockEnds := (isEmpty && len(materialLines) > 0) || (!isEmpty && !isIndented)
|
||||
if blockEnds {
|
||||
// Process collected material lines
|
||||
if len(materialLines) > 0 {
|
||||
for j, idx := range materialLines {
|
||||
isLast := j == len(materialLines)-1
|
||||
resultLine := strings.TrimRight(result[idx], " ")
|
||||
if !isLast {
|
||||
// Add trailing two spaces for non-last lines
|
||||
resultLine += " "
|
||||
}
|
||||
if result[idx] != resultLine {
|
||||
modified = true
|
||||
result[idx] = resultLine
|
||||
}
|
||||
}
|
||||
}
|
||||
inQuoteBlock = false
|
||||
materialLines = nil
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, line)
|
||||
}
|
||||
|
||||
// Handle case where file ends while still in a block
|
||||
if inQuoteBlock && len(materialLines) > 0 {
|
||||
for j, idx := range materialLines {
|
||||
isLast := j == len(materialLines)-1
|
||||
resultLine := strings.TrimRight(result[idx], " ")
|
||||
if !isLast {
|
||||
resultLine += " "
|
||||
}
|
||||
if result[idx] != resultLine {
|
||||
modified = true
|
||||
result[idx] = resultLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if modified {
|
||||
newContent := strings.Join(result, "\n")
|
||||
if !bytes.Equal(content, []byte(newContent)) {
|
||||
log.Info("formatted: ", path)
|
||||
return os.WriteFile(path, []byte(newContent), 0o644)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -17,10 +17,6 @@ func main() {
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
err = updateChromeIncludedRootCAs()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func updateMozillaIncludedRootCAs() error {
|
||||
@@ -73,94 +69,3 @@ func init() {
|
||||
generated.WriteString("}\n")
|
||||
return os.WriteFile("common/certificate/mozilla.go", []byte(generated.String()), 0o644)
|
||||
}
|
||||
|
||||
func fetchChinaFingerprints() (map[string]bool, error) {
|
||||
response, err := http.Get("https://ccadb.my.salesforce-sites.com/ccadb/AllCertificateRecordsCSVFormatv4")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
reader := csv.NewReader(response.Body)
|
||||
header, err := reader.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
countryIndex := slices.Index(header, "Country")
|
||||
fingerprintIndex := slices.Index(header, "SHA-256 Fingerprint")
|
||||
|
||||
chinaFingerprints := make(map[string]bool)
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if record[countryIndex] == "China" {
|
||||
chinaFingerprints[record[fingerprintIndex]] = true
|
||||
}
|
||||
}
|
||||
return chinaFingerprints, nil
|
||||
}
|
||||
|
||||
func updateChromeIncludedRootCAs() error {
|
||||
chinaFingerprints, err := fetchChinaFingerprints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := http.Get("https://ccadb.my.salesforce-sites.com/ccadb/RootCACertificatesIncludedByRSReportCSV")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
reader := csv.NewReader(response.Body)
|
||||
header, err := reader.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subjectIndex := slices.Index(header, "Subject")
|
||||
statusIndex := slices.Index(header, "Google Chrome Status")
|
||||
certIndex := slices.Index(header, "X.509 Certificate (PEM)")
|
||||
fingerprintIndex := slices.Index(header, "SHA-256 Fingerprint")
|
||||
|
||||
generated := strings.Builder{}
|
||||
generated.WriteString(`// Code generated by 'make update_certificates'. DO NOT EDIT.
|
||||
|
||||
package certificate
|
||||
|
||||
import "crypto/x509"
|
||||
|
||||
var chromeIncluded *x509.CertPool
|
||||
|
||||
func init() {
|
||||
chromeIncluded = x509.NewCertPool()
|
||||
`)
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if record[statusIndex] != "Included" {
|
||||
continue
|
||||
}
|
||||
if chinaFingerprints[record[fingerprintIndex]] {
|
||||
continue
|
||||
}
|
||||
generated.WriteString("\n // ")
|
||||
generated.WriteString(record[subjectIndex])
|
||||
generated.WriteString("\n")
|
||||
generated.WriteString(" chromeIncluded.AppendCertsFromPEM([]byte(`")
|
||||
cert := record[certIndex]
|
||||
// Remove single quotes if present
|
||||
if len(cert) > 0 && cert[0] == '\'' {
|
||||
cert = cert[1 : len(cert)-1]
|
||||
}
|
||||
generated.WriteString(cert)
|
||||
generated.WriteString("`))\n")
|
||||
}
|
||||
generated.WriteString("}\n")
|
||||
return os.WriteFile("common/certificate/chrome.go", []byte(generated.String()), 0o644)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -53,8 +53,6 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
||||
}
|
||||
case C.CertificateStoreMozilla:
|
||||
systemPool = mozillaIncluded
|
||||
case C.CertificateStoreChrome:
|
||||
systemPool = chromeIncluded
|
||||
case C.CertificateStoreNone:
|
||||
systemPool = nil
|
||||
default:
|
||||
|
||||
@@ -137,29 +137,14 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath))
|
||||
listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath))
|
||||
}
|
||||
if options.BindAddressNoPort {
|
||||
if !C.IsLinux {
|
||||
return nil, E.New("`bind_address_no_port` is only supported on Linux")
|
||||
}
|
||||
dialer.Control = control.Append(dialer.Control, control.BindAddressNoPort())
|
||||
}
|
||||
if options.ConnectTimeout != 0 {
|
||||
dialer.Timeout = time.Duration(options.ConnectTimeout)
|
||||
} else {
|
||||
dialer.Timeout = C.TCPConnectTimeout
|
||||
}
|
||||
if !options.DisableTCPKeepAlive {
|
||||
keepIdle := time.Duration(options.TCPKeepAlive)
|
||||
if keepIdle == 0 {
|
||||
keepIdle = C.TCPKeepAliveInitial
|
||||
}
|
||||
keepInterval := time.Duration(options.TCPKeepAliveInterval)
|
||||
if keepInterval == 0 {
|
||||
keepInterval = C.TCPKeepAliveInterval
|
||||
}
|
||||
dialer.KeepAlive = keepIdle
|
||||
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(keepIdle, keepInterval))
|
||||
}
|
||||
// TODO: Add an option to customize the keep alive period
|
||||
dialer.KeepAlive = C.TCPKeepAliveInitial
|
||||
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(C.TCPKeepAliveInitial, C.TCPKeepAliveInterval))
|
||||
var udpFragment bool
|
||||
if options.UDPFragment != nil {
|
||||
udpFragment = *options.UDPFragment
|
||||
|
||||
@@ -37,7 +37,7 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
if l.listenOptions.ReuseAddr {
|
||||
listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
|
||||
}
|
||||
if !l.listenOptions.DisableTCPKeepAlive {
|
||||
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
||||
if keepIdle == 0 {
|
||||
keepIdle = C.TCPKeepAliveInitial
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
@@ -136,9 +134,7 @@ func (m *connmanMonitor) monitorSignals(ctx context.Context, signalChan chan *db
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// godbus Signal.Name uses "interface.member" format (e.g. "net.connman.Service.PropertyChanged"),
|
||||
// not just the member name. This differs from the D-Bus signal member in the match rule.
|
||||
if signal.Name == "net.connman.Service.PropertyChanged" {
|
||||
if signal.Name == "PropertyChanged" {
|
||||
state := m.ReadWIFIState()
|
||||
if state != lastState {
|
||||
lastState = state
|
||||
@@ -158,10 +154,6 @@ func (m *connmanMonitor) Close() error {
|
||||
close(m.signalChan)
|
||||
}
|
||||
if m.conn != nil {
|
||||
m.conn.RemoveMatchSignal(
|
||||
dbus.WithMatchInterface("net.connman.Service"),
|
||||
dbus.WithMatchSender("net.connman"),
|
||||
)
|
||||
return m.conn.Close()
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
@@ -180,10 +178,6 @@ func (m *iwdMonitor) Close() error {
|
||||
close(m.signalChan)
|
||||
}
|
||||
if m.conn != nil {
|
||||
m.conn.RemoveMatchSignal(
|
||||
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
||||
dbus.WithMatchSender("net.connman.iwd"),
|
||||
)
|
||||
return m.conn.Close()
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build linux
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
@@ -42,59 +40,57 @@ func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState {
|
||||
|
||||
nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
|
||||
|
||||
var activeConnectionPaths []dbus.ObjectPath
|
||||
err := nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "ActiveConnections").Store(&activeConnectionPaths)
|
||||
if err != nil || len(activeConnectionPaths) == 0 {
|
||||
var primaryConnectionPath dbus.ObjectPath
|
||||
err := nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "PrimaryConnection").Store(&primaryConnectionPath)
|
||||
if err != nil || primaryConnectionPath == "/" {
|
||||
return adapter.WIFIState{}
|
||||
}
|
||||
|
||||
for _, connectionPath := range activeConnectionPaths {
|
||||
connObj := m.conn.Object("org.freedesktop.NetworkManager", connectionPath)
|
||||
connObj := m.conn.Object("org.freedesktop.NetworkManager", primaryConnectionPath)
|
||||
|
||||
var devicePaths []dbus.ObjectPath
|
||||
err = connObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Connection.Active", "Devices").Store(&devicePaths)
|
||||
if err != nil || len(devicePaths) == 0 {
|
||||
var devicePaths []dbus.ObjectPath
|
||||
err = connObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Connection.Active", "Devices").Store(&devicePaths)
|
||||
if err != nil || len(devicePaths) == 0 {
|
||||
return adapter.WIFIState{}
|
||||
}
|
||||
|
||||
for _, devicePath := range devicePaths {
|
||||
deviceObj := m.conn.Object("org.freedesktop.NetworkManager", devicePath)
|
||||
|
||||
var deviceType uint32
|
||||
err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device", "DeviceType").Store(&deviceType)
|
||||
if err != nil || deviceType != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, devicePath := range devicePaths {
|
||||
deviceObj := m.conn.Object("org.freedesktop.NetworkManager", devicePath)
|
||||
var accessPointPath dbus.ObjectPath
|
||||
err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device.Wireless", "ActiveAccessPoint").Store(&accessPointPath)
|
||||
if err != nil || accessPointPath == "/" {
|
||||
continue
|
||||
}
|
||||
|
||||
var deviceType uint32
|
||||
err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device", "DeviceType").Store(&deviceType)
|
||||
if err != nil || deviceType != 2 {
|
||||
continue
|
||||
}
|
||||
apObj := m.conn.Object("org.freedesktop.NetworkManager", accessPointPath)
|
||||
|
||||
var accessPointPath dbus.ObjectPath
|
||||
err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device.Wireless", "ActiveAccessPoint").Store(&accessPointPath)
|
||||
if err != nil || accessPointPath == "/" {
|
||||
continue
|
||||
}
|
||||
var ssidBytes []byte
|
||||
err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "Ssid").Store(&ssidBytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
apObj := m.conn.Object("org.freedesktop.NetworkManager", accessPointPath)
|
||||
var hwAddress string
|
||||
err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "HwAddress").Store(&hwAddress)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var ssidBytes []byte
|
||||
err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "Ssid").Store(&ssidBytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ssid := strings.TrimSpace(string(ssidBytes))
|
||||
if ssid == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var hwAddress string
|
||||
err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "HwAddress").Store(&hwAddress)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ssid := strings.TrimSpace(string(ssidBytes))
|
||||
if ssid == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
return adapter.WIFIState{
|
||||
SSID: ssid,
|
||||
BSSID: strings.ToUpper(strings.ReplaceAll(hwAddress, ":", "")),
|
||||
}
|
||||
return adapter.WIFIState{
|
||||
SSID: ssid,
|
||||
BSSID: strings.ToUpper(strings.ReplaceAll(hwAddress, ":", "")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,10 +151,6 @@ func (m *networkManagerMonitor) Close() error {
|
||||
close(m.signalChan)
|
||||
}
|
||||
if m.conn != nil {
|
||||
m.conn.RemoveMatchSignal(
|
||||
dbus.WithMatchSender("org.freedesktop.NetworkManager"),
|
||||
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
||||
)
|
||||
return m.conn.Close()
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -8,21 +8,15 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
)
|
||||
|
||||
var wpaSocketCounter atomic.Uint64
|
||||
|
||||
type wpaSupplicantMonitor struct {
|
||||
socketPath string
|
||||
callback func(adapter.WIFIState)
|
||||
cancel context.CancelFunc
|
||||
monitorConn *net.UnixConn
|
||||
connMutex sync.Mutex
|
||||
socketPath string
|
||||
callback func(adapter.WIFIState)
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||
@@ -37,8 +31,7 @@ func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, err
|
||||
continue
|
||||
}
|
||||
socketPath := filepath.Join(socketDir, entry.Name())
|
||||
id := wpaSocketCounter.Add(1)
|
||||
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d-%d", os.Getpid(), id), Net: "unixgram"}
|
||||
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
|
||||
remoteAddr := &net.UnixAddr{Name: socketPath, Net: "unixgram"}
|
||||
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
||||
if err != nil {
|
||||
@@ -52,8 +45,7 @@ func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, err
|
||||
}
|
||||
|
||||
func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
|
||||
id := wpaSocketCounter.Add(1)
|
||||
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d-%d", os.Getpid(), id), Net: "unixgram"}
|
||||
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
|
||||
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
||||
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
||||
if err != nil {
|
||||
@@ -93,11 +85,8 @@ func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
|
||||
}
|
||||
}
|
||||
|
||||
// sendCommand sends a command to wpa_supplicant and returns the response.
|
||||
// Commands are sent without trailing newlines per the wpa_supplicant control
|
||||
// interface protocol - the official wpa_ctrl.c sends raw command strings.
|
||||
func (m *wpaSupplicantMonitor) sendCommand(conn *net.UnixConn, command string) (string, error) {
|
||||
_, err := conn.Write([]byte(command))
|
||||
_, err := conn.Write([]byte(command + "\n"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -132,8 +121,6 @@ func (m *wpaSupplicantMonitor) Start() error {
|
||||
|
||||
func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adapter.WIFIState) {
|
||||
var consecutiveErrors int
|
||||
var debounceTimer *time.Timer
|
||||
var debounceMutex sync.Mutex
|
||||
|
||||
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-mon-%d", os.Getpid()), Net: "unixgram"}
|
||||
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
||||
@@ -143,14 +130,7 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
m.connMutex.Lock()
|
||||
m.monitorConn = conn
|
||||
m.connMutex.Unlock()
|
||||
|
||||
// ATTACH/DETACH commands use os_strcmp() for exact matching in wpa_supplicant,
|
||||
// so they must be sent without trailing newlines.
|
||||
// See: https://w1.fi/cgit/hostap/tree/wpa_supplicant/ctrl_iface_unix.c
|
||||
_, err = conn.Write([]byte("ATTACH"))
|
||||
_, err = conn.Write([]byte("ATTACH\n"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -164,12 +144,6 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
debounceMutex.Lock()
|
||||
if debounceTimer != nil {
|
||||
debounceTimer.Stop()
|
||||
}
|
||||
debounceMutex.Unlock()
|
||||
conn.Write([]byte("DETACH"))
|
||||
return
|
||||
default:
|
||||
}
|
||||
@@ -177,14 +151,6 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
||||
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
continue
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
consecutiveErrors++
|
||||
if consecutiveErrors > 10 {
|
||||
return
|
||||
@@ -196,18 +162,11 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
||||
|
||||
msg := string(buf[:n])
|
||||
if strings.Contains(msg, "CTRL-EVENT-CONNECTED") || strings.Contains(msg, "CTRL-EVENT-DISCONNECTED") {
|
||||
debounceMutex.Lock()
|
||||
if debounceTimer != nil {
|
||||
debounceTimer.Stop()
|
||||
state := m.ReadWIFIState()
|
||||
if state != lastState {
|
||||
lastState = state
|
||||
m.callback(state)
|
||||
}
|
||||
debounceTimer = time.AfterFunc(500*time.Millisecond, func() {
|
||||
state := m.ReadWIFIState()
|
||||
if state != lastState {
|
||||
lastState = state
|
||||
m.callback(state)
|
||||
}
|
||||
})
|
||||
debounceMutex.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,10 +175,5 @@ func (m *wpaSupplicantMonitor) Close() error {
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
}
|
||||
m.connMutex.Lock()
|
||||
if m.monitorConn != nil {
|
||||
m.monitorConn.Close()
|
||||
}
|
||||
m.connMutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !linux && !windows
|
||||
//go:build !linux
|
||||
|
||||
package settings
|
||||
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
//go:build windows
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common/winwlanapi"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type windowsWIFIMonitor struct {
|
||||
handle windows.Handle
|
||||
callback func(adapter.WIFIState)
|
||||
cancel context.CancelFunc
|
||||
lastState adapter.WIFIState
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||
handle, err := winwlanapi.OpenHandle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
interfaces, err := winwlanapi.EnumInterfaces(handle)
|
||||
if err != nil {
|
||||
winwlanapi.CloseHandle(handle)
|
||||
return nil, err
|
||||
}
|
||||
if len(interfaces) == 0 {
|
||||
winwlanapi.CloseHandle(handle)
|
||||
return nil, fmt.Errorf("no wireless interfaces found")
|
||||
}
|
||||
|
||||
return &windowsWIFIMonitor{
|
||||
handle: handle,
|
||||
callback: callback,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *windowsWIFIMonitor) ReadWIFIState() adapter.WIFIState {
|
||||
interfaces, err := winwlanapi.EnumInterfaces(m.handle)
|
||||
if err != nil || len(interfaces) == 0 {
|
||||
return adapter.WIFIState{}
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.InterfaceState != winwlanapi.InterfaceStateConnected {
|
||||
continue
|
||||
}
|
||||
|
||||
guid := iface.InterfaceGUID
|
||||
attrs, err := winwlanapi.QueryCurrentConnection(m.handle, &guid)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ssidLength := attrs.AssociationAttributes.SSID.Length
|
||||
if ssidLength == 0 || ssidLength > winwlanapi.Dot11SSIDMaxLength {
|
||||
continue
|
||||
}
|
||||
|
||||
ssid := string(attrs.AssociationAttributes.SSID.SSID[:ssidLength])
|
||||
bssid := formatBSSID(attrs.AssociationAttributes.BSSID)
|
||||
|
||||
return adapter.WIFIState{
|
||||
SSID: strings.TrimSpace(ssid),
|
||||
BSSID: bssid,
|
||||
}
|
||||
}
|
||||
|
||||
return adapter.WIFIState{}
|
||||
}
|
||||
|
||||
func formatBSSID(mac winwlanapi.Dot11MacAddress) string {
|
||||
return fmt.Sprintf("%02X%02X%02X%02X%02X%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])
|
||||
}
|
||||
|
||||
func (m *windowsWIFIMonitor) Start() error {
|
||||
if m.callback == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
m.cancel = cancel
|
||||
|
||||
m.lastState = m.ReadWIFIState()
|
||||
|
||||
callbackFunc := func(data *winwlanapi.NotificationData, callbackContext uintptr) uintptr {
|
||||
if data.NotificationSource != winwlanapi.NotificationSourceACM {
|
||||
return 0
|
||||
}
|
||||
switch data.NotificationCode {
|
||||
case winwlanapi.NotificationACMConnectionComplete,
|
||||
winwlanapi.NotificationACMDisconnected:
|
||||
m.checkAndNotify()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
callbackPointer := syscall.NewCallback(callbackFunc)
|
||||
|
||||
err := winwlanapi.RegisterNotification(m.handle, winwlanapi.NotificationSourceACM, callbackPointer, 0)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
}()
|
||||
|
||||
m.callback(m.lastState)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *windowsWIFIMonitor) checkAndNotify() {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
state := m.ReadWIFIState()
|
||||
if state != m.lastState {
|
||||
m.lastState = state
|
||||
if m.callback != nil {
|
||||
m.callback(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *windowsWIFIMonitor) Close() error {
|
||||
if m.cancel != nil {
|
||||
m.cancel()
|
||||
}
|
||||
winwlanapi.UnregisterNotification(m.handle)
|
||||
return winwlanapi.CloseHandle(m.handle)
|
||||
}
|
||||
@@ -303,6 +303,8 @@ find:
|
||||
metadata.Protocol = C.ProtocolQUIC
|
||||
fingerprint, err := ja3.Compute(buffer.Bytes())
|
||||
if err != nil {
|
||||
metadata.Protocol = C.ProtocolQUIC
|
||||
metadata.Client = C.ClientChromium
|
||||
metadata.SniffContext = fragments
|
||||
return E.Cause1(ErrNeedMoreData, err)
|
||||
}
|
||||
@@ -332,7 +334,7 @@ find:
|
||||
}
|
||||
|
||||
if count(frameTypeList, frameTypeCrypto) > 1 || count(frameTypeList, frameTypePing) > 0 {
|
||||
if isQUICGo(fingerprint) {
|
||||
if maybeUQUIC(fingerprint) {
|
||||
metadata.Client = C.ClientQUICGo
|
||||
} else {
|
||||
metadata.Client = C.ClientChromium
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
package sniff
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/sagernet/sing-box/common/ja3"
|
||||
)
|
||||
|
||||
const (
|
||||
// X25519Kyber768Draft00 - post-quantum curve used by Go crypto/tls
|
||||
x25519Kyber768Draft00 uint16 = 0x11EC // 4588
|
||||
// renegotiation_info extension used by Go crypto/tls
|
||||
extensionRenegotiationInfo uint16 = 0xFF01 // 65281
|
||||
)
|
||||
|
||||
// isQUICGo detects native quic-go by checking for Go crypto/tls specific features.
|
||||
// Note: uQUIC with Chromium mimicry cannot be reliably distinguished from real Chromium
|
||||
// since it uses the same TLS fingerprint, so it will be identified as Chromium.
|
||||
func isQUICGo(fingerprint *ja3.ClientHello) bool {
|
||||
for _, curve := range fingerprint.EllipticCurves {
|
||||
if curve == x25519Kyber768Draft00 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, ext := range fingerprint.Extensions {
|
||||
if ext == extensionRenegotiationInfo {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
// Chromium sends separate client hello packets, but UQUIC has not yet implemented this behavior
|
||||
// The cronet without this behavior does not have version 115
|
||||
var uQUICChrome115 = &ja3.ClientHello{
|
||||
Version: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{4865, 4866, 4867},
|
||||
Extensions: []uint16{0, 10, 13, 16, 27, 43, 45, 51, 57, 17513},
|
||||
EllipticCurves: []uint16{29, 23, 24},
|
||||
SignatureAlgorithms: []uint16{1027, 2052, 1025, 1283, 2053, 1281, 2054, 1537, 513},
|
||||
}
|
||||
|
||||
func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
|
||||
return !uQUICChrome115.Equals(fingerprint, true)
|
||||
}
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
package sniff_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSniffQUICQuicGoFingerprint(t *testing.T) {
|
||||
t.Parallel()
|
||||
const testSNI = "test.example.com"
|
||||
|
||||
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
require.NoError(t, err)
|
||||
defer udpConn.Close()
|
||||
|
||||
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
|
||||
packetsChan := make(chan [][]byte, 1)
|
||||
|
||||
go func() {
|
||||
var packets [][]byte
|
||||
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
for i := 0; i < 10; i++ {
|
||||
buf := make([]byte, 2048)
|
||||
n, _, err := udpConn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
packets = append(packets, buf[:n])
|
||||
}
|
||||
packetsChan <- packets
|
||||
}()
|
||||
|
||||
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
require.NoError(t, err)
|
||||
defer clientConn.Close()
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: testSNI,
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h3"},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
|
||||
|
||||
select {
|
||||
case packets := <-packetsChan:
|
||||
t.Logf("Captured %d packets", len(packets))
|
||||
|
||||
var metadata adapter.InboundContext
|
||||
for i, pkt := range packets {
|
||||
err := sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||
t.Logf("Packet %d: err=%v, domain=%s, client=%s", i, err, metadata.Domain, metadata.Client)
|
||||
if metadata.Domain != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("\n=== quic-go TLS Fingerprint Analysis ===")
|
||||
t.Logf("Domain: %s", metadata.Domain)
|
||||
t.Logf("Client: %s", metadata.Client)
|
||||
t.Logf("Protocol: %s", metadata.Protocol)
|
||||
|
||||
// The client should be identified as quic-go, not chromium
|
||||
// Current issue: it's being identified as chromium
|
||||
if metadata.Client == "chromium" {
|
||||
t.Log("WARNING: quic-go is being misidentified as chromium!")
|
||||
}
|
||||
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Timeout")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSniffQUICInitialFromQuicGo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const testSNI = "test.example.com"
|
||||
|
||||
// Create UDP listener to capture ALL initial packets
|
||||
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
require.NoError(t, err)
|
||||
defer udpConn.Close()
|
||||
|
||||
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
|
||||
|
||||
// Channel to receive captured packets
|
||||
packetsChan := make(chan [][]byte, 1)
|
||||
|
||||
// Start goroutine to capture packets
|
||||
go func() {
|
||||
var packets [][]byte
|
||||
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
for i := 0; i < 5; i++ { // Capture up to 5 packets
|
||||
buf := make([]byte, 2048)
|
||||
n, _, err := udpConn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
packets = append(packets, buf[:n])
|
||||
}
|
||||
packetsChan <- packets
|
||||
}()
|
||||
|
||||
// Create QUIC client connection (will fail but we capture the initial packet)
|
||||
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
require.NoError(t, err)
|
||||
defer clientConn.Close()
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: testSNI,
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h3"},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// This will fail (no server) but sends initial packet
|
||||
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
|
||||
|
||||
// Wait for captured packets
|
||||
select {
|
||||
case packets := <-packetsChan:
|
||||
t.Logf("Captured %d QUIC packets", len(packets))
|
||||
|
||||
for i, packet := range packets {
|
||||
t.Logf("Packet %d: length=%d, first 30 bytes: %x", i, len(packet), packet[:min(30, len(packet))])
|
||||
}
|
||||
|
||||
// Test sniffer with first packet
|
||||
if len(packets) > 0 {
|
||||
var metadata adapter.InboundContext
|
||||
err := sniff.QUICClientHello(context.Background(), &metadata, packets[0])
|
||||
|
||||
t.Logf("First packet sniff error: %v", err)
|
||||
t.Logf("Protocol: %s", metadata.Protocol)
|
||||
t.Logf("Domain: %s", metadata.Domain)
|
||||
t.Logf("Client: %s", metadata.Client)
|
||||
|
||||
// If first packet needs more data, try with subsequent packets
|
||||
// IMPORTANT: reuse metadata to accumulate CRYPTO fragments via SniffContext
|
||||
if errors.Is(err, sniff.ErrNeedMoreData) && len(packets) > 1 {
|
||||
t.Log("First packet needs more data, trying subsequent packets with shared context...")
|
||||
for i := 1; i < len(packets); i++ {
|
||||
// Reuse same metadata to accumulate fragments
|
||||
err = sniff.QUICClientHello(context.Background(), &metadata, packets[i])
|
||||
t.Logf("Packet %d sniff result: err=%v, domain=%s, sniffCtx=%v", i, err, metadata.Domain, metadata.SniffContext != nil)
|
||||
if metadata.Domain != "" || (err != nil && !errors.Is(err, sniff.ErrNeedMoreData)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print hex dump for debugging
|
||||
t.Logf("First packet hex:\n%s", hex.Dump(packets[0][:min(256, len(packets[0]))]))
|
||||
|
||||
// Log final results
|
||||
t.Logf("Final: Protocol=%s, Domain=%s, Client=%s", metadata.Protocol, metadata.Domain, metadata.Client)
|
||||
|
||||
// Verify SNI extraction
|
||||
if metadata.Domain == "" {
|
||||
t.Errorf("Failed to extract SNI, expected: %s", testSNI)
|
||||
} else {
|
||||
require.Equal(t, testSNI, metadata.Domain, "SNI should match")
|
||||
}
|
||||
|
||||
// Check client identification - quic-go should be identified as quic-go, not chromium
|
||||
t.Logf("Client identified as: %s (expected: quic-go)", metadata.Client)
|
||||
}
|
||||
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Timeout waiting for QUIC packets")
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func TestSniffQUICChromeNew(t *testing.T) {
|
||||
var metadata adapter.InboundContext
|
||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||
require.Empty(t, metadata.Client)
|
||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
||||
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
|
||||
require.NoError(t, err)
|
||||
@@ -39,7 +39,7 @@ func TestSniffQUICChromium(t *testing.T) {
|
||||
var metadata adapter.InboundContext
|
||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||
require.Empty(t, metadata.Client)
|
||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
||||
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -114,17 +114,13 @@ func startACME(ctx context.Context, logger logger.Logger, options option.Inbound
|
||||
switch dnsOptions.Provider {
|
||||
case C.DNSProviderAliDNS:
|
||||
solver.DNSProvider = &alidns.Provider{
|
||||
CredentialInfo: alidns.CredentialInfo{
|
||||
AccessKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
||||
AccessKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
||||
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
||||
SecurityToken: dnsOptions.AliDNSOptions.SecurityToken,
|
||||
},
|
||||
AccKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
||||
AccKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
||||
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
||||
}
|
||||
case C.DNSProviderCloudflare:
|
||||
solver.DNSProvider = &cloudflare.Provider{
|
||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||
}
|
||||
default:
|
||||
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
|
||||
|
||||
@@ -51,7 +51,6 @@ func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, op
|
||||
return &ECHClientConfig{
|
||||
ECHCapableConfig: clientConfig,
|
||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
||||
queryServerName: options.ECH.QueryServerName,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -109,11 +108,10 @@ func parseECHKeys(echKey []byte) ([]tls.EncryptedClientHelloKey, error) {
|
||||
|
||||
type ECHClientConfig struct {
|
||||
ECHCapableConfig
|
||||
access sync.Mutex
|
||||
dnsRouter adapter.DNSRouter
|
||||
queryServerName string
|
||||
lastTTL time.Duration
|
||||
lastUpdate time.Time
|
||||
access sync.Mutex
|
||||
dnsRouter adapter.DNSRouter
|
||||
lastTTL time.Duration
|
||||
lastUpdate time.Time
|
||||
}
|
||||
|
||||
func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
||||
@@ -132,17 +130,13 @@ func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn)
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
|
||||
queryServerName := s.queryServerName
|
||||
if queryServerName == "" {
|
||||
queryServerName = s.ServerName()
|
||||
}
|
||||
message := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
RecursionDesired: true,
|
||||
},
|
||||
Question: []mDNS.Question{
|
||||
{
|
||||
Name: mDNS.Fqdn(queryServerName),
|
||||
Name: mDNS.Fqdn(s.ServerName()),
|
||||
Qtype: mDNS.TypeHTTPS,
|
||||
Qclass: mDNS.ClassINET,
|
||||
},
|
||||
@@ -181,12 +175,7 @@ func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn)
|
||||
}
|
||||
|
||||
func (s *ECHClientConfig) Clone() Config {
|
||||
return &ECHClientConfig{
|
||||
ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig),
|
||||
dnsRouter: s.dnsRouter,
|
||||
queryServerName: s.queryServerName,
|
||||
lastUpdate: s.lastUpdate,
|
||||
}
|
||||
return &ECHClientConfig{ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig), dnsRouter: s.dnsRouter, lastUpdate: s.lastUpdate}
|
||||
}
|
||||
|
||||
func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
|
||||
|
||||
@@ -169,35 +169,6 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
var clientCertificate []byte
|
||||
if len(options.ClientCertificate) > 0 {
|
||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
||||
} else if options.ClientCertificatePath != "" {
|
||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client certificate")
|
||||
}
|
||||
clientCertificate = content
|
||||
}
|
||||
var clientKey []byte
|
||||
if len(options.ClientKey) > 0 {
|
||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
||||
} else if options.ClientKeyPath != "" {
|
||||
content, err := os.ReadFile(options.ClientKeyPath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client key")
|
||||
}
|
||||
clientKey = content
|
||||
}
|
||||
if len(clientCertificate) > 0 && len(clientKey) > 0 {
|
||||
keyPair, err := tls.X509KeyPair(clientCertificate, clientKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse client x509 key pair")
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||
} else if len(clientCertificate) > 0 || len(clientKey) > 0 {
|
||||
return nil, E.New("client certificate and client key must be provided together")
|
||||
}
|
||||
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||
if options.ECH != nil && options.ECH.Enabled {
|
||||
var err error
|
||||
|
||||
@@ -222,35 +222,6 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
var clientCertificate []byte
|
||||
if len(options.ClientCertificate) > 0 {
|
||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
||||
} else if options.ClientCertificatePath != "" {
|
||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client certificate")
|
||||
}
|
||||
clientCertificate = content
|
||||
}
|
||||
var clientKey []byte
|
||||
if len(options.ClientKey) > 0 {
|
||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
||||
} else if options.ClientKeyPath != "" {
|
||||
content, err := os.ReadFile(options.ClientKeyPath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client key")
|
||||
}
|
||||
clientKey = content
|
||||
}
|
||||
if len(clientCertificate) > 0 && len(clientKey) > 0 {
|
||||
keyPair, err := utls.X509KeyPair(clientCertificate, clientKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse client x509 key pair")
|
||||
}
|
||||
tlsConfig.Certificates = []utls.Certificate{keyPair}
|
||||
} else if len(clientCertificate) > 0 || len(clientKey) > 0 {
|
||||
return nil, E.New("client certificate and client key must be provided together")
|
||||
}
|
||||
id, err := uTLSClientHelloID(options.UTLS.Fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
)
|
||||
|
||||
var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil)
|
||||
@@ -22,7 +21,7 @@ var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil)
|
||||
type HistoryStorage struct {
|
||||
access sync.RWMutex
|
||||
delayHistory map[string]*adapter.URLTestHistory
|
||||
updateHook *observable.Subscriber[struct{}]
|
||||
updateHook chan<- struct{}
|
||||
}
|
||||
|
||||
func NewHistoryStorage() *HistoryStorage {
|
||||
@@ -31,7 +30,7 @@ func NewHistoryStorage() *HistoryStorage {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HistoryStorage) SetHook(hook *observable.Subscriber[struct{}]) {
|
||||
func (s *HistoryStorage) SetHook(hook chan<- struct{}) {
|
||||
s.updateHook = hook
|
||||
}
|
||||
|
||||
@@ -61,7 +60,10 @@ func (s *HistoryStorage) StoreURLTestHistory(tag string, history *adapter.URLTes
|
||||
func (s *HistoryStorage) notifyUpdated() {
|
||||
updateHook := s.updateHook
|
||||
if updateHook != nil {
|
||||
updateHook.Emit(struct{}{})
|
||||
select {
|
||||
case updateHook <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,5 @@ package constant
|
||||
const (
|
||||
CertificateStoreSystem = "system"
|
||||
CertificateStoreMozilla = "mozilla"
|
||||
CertificateStoreChrome = "chrome"
|
||||
CertificateStoreNone = "none"
|
||||
)
|
||||
|
||||
@@ -4,5 +4,5 @@ import "time"
|
||||
|
||||
const (
|
||||
DHCPTTL = time.Hour
|
||||
DHCPTimeout = 5 * time.Second
|
||||
DHCPTimeout = time.Minute
|
||||
)
|
||||
|
||||
@@ -28,8 +28,6 @@ const (
|
||||
TypeDERP = "derp"
|
||||
TypeResolved = "resolved"
|
||||
TypeSSMAPI = "ssm-api"
|
||||
TypeCCM = "ccm"
|
||||
TypeOCM = "ocm"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -30,7 +30,6 @@ const (
|
||||
RuleActionTypeRoute = "route"
|
||||
RuleActionTypeRouteOptions = "route-options"
|
||||
RuleActionTypeDirect = "direct"
|
||||
RuleActionTypeBypass = "bypass"
|
||||
RuleActionTypeReject = "reject"
|
||||
RuleActionTypeHijackDNS = "hijack-dns"
|
||||
RuleActionTypeSniff = "sniff"
|
||||
|
||||
@@ -3,7 +3,7 @@ package constant
|
||||
import "time"
|
||||
|
||||
const (
|
||||
TCPKeepAliveInitial = 5 * time.Minute
|
||||
TCPKeepAliveInitial = 10 * time.Minute
|
||||
TCPKeepAliveInterval = 75 * time.Second
|
||||
TCPConnectTimeout = 5 * time.Second
|
||||
TCPTimeout = 15 * time.Second
|
||||
|
||||
702
daemon/helper.pb.go
Normal file
702
daemon/helper.pb.go
Normal file
@@ -0,0 +1,702 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type SubscribeHelperRequestRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
AcceptGetWIFIStateRequests bool `protobuf:"varint,1,opt,name=acceptGetWIFIStateRequests,proto3" json:"acceptGetWIFIStateRequests,omitempty"`
|
||||
AcceptFindConnectionOwnerRequests bool `protobuf:"varint,2,opt,name=acceptFindConnectionOwnerRequests,proto3" json:"acceptFindConnectionOwnerRequests,omitempty"`
|
||||
AcceptSendNotificationRequests bool `protobuf:"varint,3,opt,name=acceptSendNotificationRequests,proto3" json:"acceptSendNotificationRequests,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SubscribeHelperRequestRequest) Reset() {
|
||||
*x = SubscribeHelperRequestRequest{}
|
||||
mi := &file_daemon_helper_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SubscribeHelperRequestRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SubscribeHelperRequestRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SubscribeHelperRequestRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_helper_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SubscribeHelperRequestRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SubscribeHelperRequestRequest) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_helper_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SubscribeHelperRequestRequest) GetAcceptGetWIFIStateRequests() bool {
|
||||
if x != nil {
|
||||
return x.AcceptGetWIFIStateRequests
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *SubscribeHelperRequestRequest) GetAcceptFindConnectionOwnerRequests() bool {
|
||||
if x != nil {
|
||||
return x.AcceptFindConnectionOwnerRequests
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *SubscribeHelperRequestRequest) GetAcceptSendNotificationRequests() bool {
|
||||
if x != nil {
|
||||
return x.AcceptSendNotificationRequests
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type HelperRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
// Types that are valid to be assigned to Request:
|
||||
//
|
||||
// *HelperRequest_GetWIFIState
|
||||
// *HelperRequest_FindConnectionOwner
|
||||
// *HelperRequest_SendNotification
|
||||
Request isHelperRequest_Request `protobuf_oneof:"request"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *HelperRequest) Reset() {
|
||||
*x = HelperRequest{}
|
||||
mi := &file_daemon_helper_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *HelperRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HelperRequest) ProtoMessage() {}
|
||||
|
||||
func (x *HelperRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_helper_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HelperRequest.ProtoReflect.Descriptor instead.
|
||||
func (*HelperRequest) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_helper_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *HelperRequest) GetId() int64 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HelperRequest) GetRequest() isHelperRequest_Request {
|
||||
if x != nil {
|
||||
return x.Request
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HelperRequest) GetGetWIFIState() *emptypb.Empty {
|
||||
if x != nil {
|
||||
if x, ok := x.Request.(*HelperRequest_GetWIFIState); ok {
|
||||
return x.GetWIFIState
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HelperRequest) GetFindConnectionOwner() *FindConnectionOwnerRequest {
|
||||
if x != nil {
|
||||
if x, ok := x.Request.(*HelperRequest_FindConnectionOwner); ok {
|
||||
return x.FindConnectionOwner
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HelperRequest) GetSendNotification() *Notification {
|
||||
if x != nil {
|
||||
if x, ok := x.Request.(*HelperRequest_SendNotification); ok {
|
||||
return x.SendNotification
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isHelperRequest_Request interface {
|
||||
isHelperRequest_Request()
|
||||
}
|
||||
|
||||
type HelperRequest_GetWIFIState struct {
|
||||
GetWIFIState *emptypb.Empty `protobuf:"bytes,2,opt,name=getWIFIState,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HelperRequest_FindConnectionOwner struct {
|
||||
FindConnectionOwner *FindConnectionOwnerRequest `protobuf:"bytes,3,opt,name=findConnectionOwner,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HelperRequest_SendNotification struct {
|
||||
SendNotification *Notification `protobuf:"bytes,4,opt,name=sendNotification,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*HelperRequest_GetWIFIState) isHelperRequest_Request() {}
|
||||
|
||||
func (*HelperRequest_FindConnectionOwner) isHelperRequest_Request() {}
|
||||
|
||||
func (*HelperRequest_SendNotification) isHelperRequest_Request() {}
|
||||
|
||||
type FindConnectionOwnerRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
IpProtocol int32 `protobuf:"varint,1,opt,name=ipProtocol,proto3" json:"ipProtocol,omitempty"`
|
||||
SourceAddress string `protobuf:"bytes,2,opt,name=sourceAddress,proto3" json:"sourceAddress,omitempty"`
|
||||
SourcePort int32 `protobuf:"varint,3,opt,name=sourcePort,proto3" json:"sourcePort,omitempty"`
|
||||
DestinationAddress string `protobuf:"bytes,4,opt,name=destinationAddress,proto3" json:"destinationAddress,omitempty"`
|
||||
DestinationPort int32 `protobuf:"varint,5,opt,name=destinationPort,proto3" json:"destinationPort,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) Reset() {
|
||||
*x = FindConnectionOwnerRequest{}
|
||||
mi := &file_daemon_helper_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FindConnectionOwnerRequest) ProtoMessage() {}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_helper_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FindConnectionOwnerRequest.ProtoReflect.Descriptor instead.
|
||||
func (*FindConnectionOwnerRequest) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_helper_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) GetIpProtocol() int32 {
|
||||
if x != nil {
|
||||
return x.IpProtocol
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) GetSourceAddress() string {
|
||||
if x != nil {
|
||||
return x.SourceAddress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) GetSourcePort() int32 {
|
||||
if x != nil {
|
||||
return x.SourcePort
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) GetDestinationAddress() string {
|
||||
if x != nil {
|
||||
return x.DestinationAddress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *FindConnectionOwnerRequest) GetDestinationPort() int32 {
|
||||
if x != nil {
|
||||
return x.DestinationPort
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
|
||||
TypeName string `protobuf:"bytes,2,opt,name=typeName,proto3" json:"typeName,omitempty"`
|
||||
TypeId int32 `protobuf:"varint,3,opt,name=typeId,proto3" json:"typeId,omitempty"`
|
||||
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Subtitle string `protobuf:"bytes,5,opt,name=subtitle,proto3" json:"subtitle,omitempty"`
|
||||
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
|
||||
OpenURL string `protobuf:"bytes,7,opt,name=openURL,proto3" json:"openURL,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Notification) Reset() {
|
||||
*x = Notification{}
|
||||
mi := &file_daemon_helper_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Notification) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Notification) ProtoMessage() {}
|
||||
|
||||
func (x *Notification) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_helper_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
||||
func (*Notification) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_helper_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *Notification) GetIdentifier() string {
|
||||
if x != nil {
|
||||
return x.Identifier
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetTypeName() string {
|
||||
if x != nil {
|
||||
return x.TypeName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetTypeId() int32 {
|
||||
if x != nil {
|
||||
return x.TypeId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Notification) GetTitle() string {
|
||||
if x != nil {
|
||||
return x.Title
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetSubtitle() string {
|
||||
if x != nil {
|
||||
return x.Subtitle
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetBody() string {
|
||||
if x != nil {
|
||||
return x.Body
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Notification) GetOpenURL() string {
|
||||
if x != nil {
|
||||
return x.OpenURL
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type HelperResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
// Types that are valid to be assigned to Response:
|
||||
//
|
||||
// *HelperResponse_WifiState
|
||||
// *HelperResponse_Error
|
||||
// *HelperResponse_ConnectionOwner
|
||||
Response isHelperResponse_Response `protobuf_oneof:"response"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *HelperResponse) Reset() {
|
||||
*x = HelperResponse{}
|
||||
mi := &file_daemon_helper_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *HelperResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HelperResponse) ProtoMessage() {}
|
||||
|
||||
func (x *HelperResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_helper_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HelperResponse.ProtoReflect.Descriptor instead.
|
||||
func (*HelperResponse) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_helper_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *HelperResponse) GetId() int64 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HelperResponse) GetResponse() isHelperResponse_Response {
|
||||
if x != nil {
|
||||
return x.Response
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HelperResponse) GetWifiState() *WIFIState {
|
||||
if x != nil {
|
||||
if x, ok := x.Response.(*HelperResponse_WifiState); ok {
|
||||
return x.WifiState
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HelperResponse) GetError() string {
|
||||
if x != nil {
|
||||
if x, ok := x.Response.(*HelperResponse_Error); ok {
|
||||
return x.Error
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HelperResponse) GetConnectionOwner() *ConnectionOwner {
|
||||
if x != nil {
|
||||
if x, ok := x.Response.(*HelperResponse_ConnectionOwner); ok {
|
||||
return x.ConnectionOwner
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isHelperResponse_Response interface {
|
||||
isHelperResponse_Response()
|
||||
}
|
||||
|
||||
type HelperResponse_WifiState struct {
|
||||
WifiState *WIFIState `protobuf:"bytes,2,opt,name=wifiState,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HelperResponse_Error struct {
|
||||
Error string `protobuf:"bytes,3,opt,name=error,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HelperResponse_ConnectionOwner struct {
|
||||
ConnectionOwner *ConnectionOwner `protobuf:"bytes,4,opt,name=connectionOwner,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*HelperResponse_WifiState) isHelperResponse_Response() {}
|
||||
|
||||
func (*HelperResponse_Error) isHelperResponse_Response() {}
|
||||
|
||||
func (*HelperResponse_ConnectionOwner) isHelperResponse_Response() {}
|
||||
|
||||
type ConnectionOwner struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
UserId int32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"`
|
||||
UserName string `protobuf:"bytes,2,opt,name=userName,proto3" json:"userName,omitempty"`
|
||||
ProcessPath string `protobuf:"bytes,3,opt,name=processPath,proto3" json:"processPath,omitempty"`
|
||||
AndroidPackageName string `protobuf:"bytes,4,opt,name=androidPackageName,proto3" json:"androidPackageName,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ConnectionOwner) Reset() {
|
||||
*x = ConnectionOwner{}
|
||||
mi := &file_daemon_helper_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ConnectionOwner) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConnectionOwner) ProtoMessage() {}
|
||||
|
||||
func (x *ConnectionOwner) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_helper_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnectionOwner.ProtoReflect.Descriptor instead.
|
||||
func (*ConnectionOwner) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_helper_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *ConnectionOwner) GetUserId() int32 {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ConnectionOwner) GetUserName() string {
|
||||
if x != nil {
|
||||
return x.UserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConnectionOwner) GetProcessPath() string {
|
||||
if x != nil {
|
||||
return x.ProcessPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConnectionOwner) GetAndroidPackageName() string {
|
||||
if x != nil {
|
||||
return x.AndroidPackageName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type WIFIState struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Ssid string `protobuf:"bytes,1,opt,name=ssid,proto3" json:"ssid,omitempty"`
|
||||
Bssid string `protobuf:"bytes,2,opt,name=bssid,proto3" json:"bssid,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *WIFIState) Reset() {
|
||||
*x = WIFIState{}
|
||||
mi := &file_daemon_helper_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *WIFIState) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*WIFIState) ProtoMessage() {}
|
||||
|
||||
func (x *WIFIState) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_helper_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use WIFIState.ProtoReflect.Descriptor instead.
|
||||
func (*WIFIState) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_helper_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *WIFIState) GetSsid() string {
|
||||
if x != nil {
|
||||
return x.Ssid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *WIFIState) GetBssid() string {
|
||||
if x != nil {
|
||||
return x.Bssid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_daemon_helper_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_daemon_helper_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x13daemon/helper.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xf5\x01\n" +
|
||||
"\x1dSubscribeHelperRequestRequest\x12>\n" +
|
||||
"\x1aacceptGetWIFIStateRequests\x18\x01 \x01(\bR\x1aacceptGetWIFIStateRequests\x12L\n" +
|
||||
"!acceptFindConnectionOwnerRequests\x18\x02 \x01(\bR!acceptFindConnectionOwnerRequests\x12F\n" +
|
||||
"\x1eacceptSendNotificationRequests\x18\x03 \x01(\bR\x1eacceptSendNotificationRequests\"\x84\x02\n" +
|
||||
"\rHelperRequest\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12<\n" +
|
||||
"\fgetWIFIState\x18\x02 \x01(\v2\x16.google.protobuf.EmptyH\x00R\fgetWIFIState\x12V\n" +
|
||||
"\x13findConnectionOwner\x18\x03 \x01(\v2\".daemon.FindConnectionOwnerRequestH\x00R\x13findConnectionOwner\x12B\n" +
|
||||
"\x10sendNotification\x18\x04 \x01(\v2\x14.daemon.NotificationH\x00R\x10sendNotificationB\t\n" +
|
||||
"\arequest\"\xdc\x01\n" +
|
||||
"\x1aFindConnectionOwnerRequest\x12\x1e\n" +
|
||||
"\n" +
|
||||
"ipProtocol\x18\x01 \x01(\x05R\n" +
|
||||
"ipProtocol\x12$\n" +
|
||||
"\rsourceAddress\x18\x02 \x01(\tR\rsourceAddress\x12\x1e\n" +
|
||||
"\n" +
|
||||
"sourcePort\x18\x03 \x01(\x05R\n" +
|
||||
"sourcePort\x12.\n" +
|
||||
"\x12destinationAddress\x18\x04 \x01(\tR\x12destinationAddress\x12(\n" +
|
||||
"\x0fdestinationPort\x18\x05 \x01(\x05R\x0fdestinationPort\"\xc2\x01\n" +
|
||||
"\fNotification\x12\x1e\n" +
|
||||
"\n" +
|
||||
"identifier\x18\x01 \x01(\tR\n" +
|
||||
"identifier\x12\x1a\n" +
|
||||
"\btypeName\x18\x02 \x01(\tR\btypeName\x12\x16\n" +
|
||||
"\x06typeId\x18\x03 \x01(\x05R\x06typeId\x12\x14\n" +
|
||||
"\x05title\x18\x04 \x01(\tR\x05title\x12\x1a\n" +
|
||||
"\bsubtitle\x18\x05 \x01(\tR\bsubtitle\x12\x12\n" +
|
||||
"\x04body\x18\x06 \x01(\tR\x04body\x12\x18\n" +
|
||||
"\aopenURL\x18\a \x01(\tR\aopenURL\"\xbc\x01\n" +
|
||||
"\x0eHelperResponse\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x121\n" +
|
||||
"\twifiState\x18\x02 \x01(\v2\x11.daemon.WIFIStateH\x00R\twifiState\x12\x16\n" +
|
||||
"\x05error\x18\x03 \x01(\tH\x00R\x05error\x12C\n" +
|
||||
"\x0fconnectionOwner\x18\x04 \x01(\v2\x17.daemon.ConnectionOwnerH\x00R\x0fconnectionOwnerB\n" +
|
||||
"\n" +
|
||||
"\bresponse\"\x97\x01\n" +
|
||||
"\x0fConnectionOwner\x12\x16\n" +
|
||||
"\x06userId\x18\x01 \x01(\x05R\x06userId\x12\x1a\n" +
|
||||
"\buserName\x18\x02 \x01(\tR\buserName\x12 \n" +
|
||||
"\vprocessPath\x18\x03 \x01(\tR\vprocessPath\x12.\n" +
|
||||
"\x12androidPackageName\x18\x04 \x01(\tR\x12androidPackageName\"5\n" +
|
||||
"\tWIFIState\x12\x12\n" +
|
||||
"\x04ssid\x18\x01 \x01(\tR\x04ssid\x12\x14\n" +
|
||||
"\x05bssid\x18\x02 \x01(\tR\x05bssidB%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||
|
||||
var (
|
||||
file_daemon_helper_proto_rawDescOnce sync.Once
|
||||
file_daemon_helper_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_daemon_helper_proto_rawDescGZIP() []byte {
|
||||
file_daemon_helper_proto_rawDescOnce.Do(func() {
|
||||
file_daemon_helper_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)))
|
||||
})
|
||||
return file_daemon_helper_proto_rawDescData
|
||||
}
|
||||
|
||||
var (
|
||||
file_daemon_helper_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
file_daemon_helper_proto_goTypes = []any{
|
||||
(*SubscribeHelperRequestRequest)(nil), // 0: daemon.SubscribeHelperRequestRequest
|
||||
(*HelperRequest)(nil), // 1: daemon.HelperRequest
|
||||
(*FindConnectionOwnerRequest)(nil), // 2: daemon.FindConnectionOwnerRequest
|
||||
(*Notification)(nil), // 3: daemon.Notification
|
||||
(*HelperResponse)(nil), // 4: daemon.HelperResponse
|
||||
(*ConnectionOwner)(nil), // 5: daemon.ConnectionOwner
|
||||
(*WIFIState)(nil), // 6: daemon.WIFIState
|
||||
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
|
||||
}
|
||||
)
|
||||
|
||||
var file_daemon_helper_proto_depIdxs = []int32{
|
||||
7, // 0: daemon.HelperRequest.getWIFIState:type_name -> google.protobuf.Empty
|
||||
2, // 1: daemon.HelperRequest.findConnectionOwner:type_name -> daemon.FindConnectionOwnerRequest
|
||||
3, // 2: daemon.HelperRequest.sendNotification:type_name -> daemon.Notification
|
||||
6, // 3: daemon.HelperResponse.wifiState:type_name -> daemon.WIFIState
|
||||
5, // 4: daemon.HelperResponse.connectionOwner:type_name -> daemon.ConnectionOwner
|
||||
5, // [5:5] is the sub-list for method output_type
|
||||
5, // [5:5] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_daemon_helper_proto_init() }
|
||||
func file_daemon_helper_proto_init() {
|
||||
if File_daemon_helper_proto != nil {
|
||||
return
|
||||
}
|
||||
file_daemon_helper_proto_msgTypes[1].OneofWrappers = []any{
|
||||
(*HelperRequest_GetWIFIState)(nil),
|
||||
(*HelperRequest_FindConnectionOwner)(nil),
|
||||
(*HelperRequest_SendNotification)(nil),
|
||||
}
|
||||
file_daemon_helper_proto_msgTypes[4].OneofWrappers = []any{
|
||||
(*HelperResponse_WifiState)(nil),
|
||||
(*HelperResponse_Error)(nil),
|
||||
(*HelperResponse_ConnectionOwner)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_daemon_helper_proto_goTypes,
|
||||
DependencyIndexes: file_daemon_helper_proto_depIdxs,
|
||||
MessageInfos: file_daemon_helper_proto_msgTypes,
|
||||
}.Build()
|
||||
File_daemon_helper_proto = out.File
|
||||
file_daemon_helper_proto_goTypes = nil
|
||||
file_daemon_helper_proto_depIdxs = nil
|
||||
}
|
||||
61
daemon/helper.proto
Normal file
61
daemon/helper.proto
Normal file
@@ -0,0 +1,61 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package daemon;
|
||||
option go_package = "github.com/sagernet/sing-box/daemon";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
message SubscribeHelperRequestRequest {
|
||||
bool acceptGetWIFIStateRequests = 1;
|
||||
bool acceptFindConnectionOwnerRequests = 2;
|
||||
bool acceptSendNotificationRequests = 3;
|
||||
}
|
||||
|
||||
message HelperRequest {
|
||||
int64 id = 1;
|
||||
oneof request {
|
||||
google.protobuf.Empty getWIFIState = 2;
|
||||
FindConnectionOwnerRequest findConnectionOwner = 3;
|
||||
Notification sendNotification = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message FindConnectionOwnerRequest {
|
||||
int32 ipProtocol = 1;
|
||||
string sourceAddress = 2;
|
||||
int32 sourcePort = 3;
|
||||
string destinationAddress = 4;
|
||||
int32 destinationPort = 5;
|
||||
}
|
||||
|
||||
message Notification {
|
||||
string identifier = 1;
|
||||
string typeName = 2;
|
||||
int32 typeId = 3;
|
||||
string title = 4;
|
||||
string subtitle = 5;
|
||||
string body = 6;
|
||||
string openURL = 7;
|
||||
}
|
||||
|
||||
message HelperResponse {
|
||||
int64 id = 1;
|
||||
oneof response {
|
||||
WIFIState wifiState = 2;
|
||||
string error = 3;
|
||||
ConnectionOwner connectionOwner = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message ConnectionOwner {
|
||||
int32 userId = 1;
|
||||
string userName = 2;
|
||||
string processPath = 3;
|
||||
string androidPackageName = 4;
|
||||
}
|
||||
|
||||
message WIFIState {
|
||||
string ssid = 1;
|
||||
string bssid = 2;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,15 @@ import (
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
)
|
||||
|
||||
@@ -26,12 +29,23 @@ type Instance struct {
|
||||
urlTestHistoryStorage *urltest.HistoryStorage
|
||||
}
|
||||
|
||||
func (s *StartedService) baseContext() context.Context {
|
||||
dnsRegistry := include.DNSTransportRegistry()
|
||||
if s.platform != nil && s.platform.UsePlatformLocalDNSTransport() {
|
||||
dns.RegisterTransport[option.LocalDNSServerOptions](dnsRegistry, C.DNSTypeLocal, s.platform.LocalDNSTransport())
|
||||
}
|
||||
ctx := box.Context(s.ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||
ctx = filemanager.WithDefault(ctx, s.workingDirectory, s.tempDirectory, s.userID, s.groupID)
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (s *StartedService) CheckConfig(configContent string) error {
|
||||
options, err := parseConfig(s.ctx, configContent)
|
||||
ctx := s.baseContext()
|
||||
options, err := parseConfig(ctx, configContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(s.ctx)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
@@ -44,7 +58,7 @@ func (s *StartedService) CheckConfig(configContent string) error {
|
||||
}
|
||||
|
||||
func (s *StartedService) FormatConfig(configContent string) (string, error) {
|
||||
options, err := parseConfig(s.ctx, configContent)
|
||||
options, err := parseConfig(s.baseContext(), configContent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -65,7 +79,7 @@ type OverrideOptions struct {
|
||||
}
|
||||
|
||||
func (s *StartedService) newInstance(profileContent string, overrideOptions *OverrideOptions) (*Instance, error) {
|
||||
ctx := s.ctx
|
||||
ctx := s.baseContext()
|
||||
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
|
||||
ctx, cancel := context.WithCancel(include.Context(ctx))
|
||||
options, err := parseConfig(ctx, profileContent)
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
type PlatformHandler interface {
|
||||
ServiceStop() error
|
||||
ServiceReload() error
|
||||
@@ -7,3 +13,10 @@ type PlatformHandler interface {
|
||||
SetSystemProxyEnabled(enabled bool) error
|
||||
WriteDebugMessage(message string)
|
||||
}
|
||||
|
||||
type PlatformInterface interface {
|
||||
adapter.PlatformInterface
|
||||
|
||||
UsePlatformLocalDNSTransport() bool
|
||||
LocalDNSTransport() dns.TransportConstructorFunc[option.LocalDNSServerOptions]
|
||||
}
|
||||
|
||||
@@ -31,16 +31,16 @@ import (
|
||||
var _ StartedServiceServer = (*StartedService)(nil)
|
||||
|
||||
type StartedService struct {
|
||||
ctx context.Context
|
||||
// platform adapter.PlatformInterface
|
||||
handler PlatformHandler
|
||||
debug bool
|
||||
logMaxLines int
|
||||
// workingDirectory string
|
||||
// tempDirectory string
|
||||
// userID int
|
||||
// groupID int
|
||||
// systemProxyEnabled bool
|
||||
ctx context.Context
|
||||
platform PlatformInterface
|
||||
platformHandler PlatformHandler
|
||||
debug bool
|
||||
logMaxLines int
|
||||
workingDirectory string
|
||||
tempDirectory string
|
||||
userID int
|
||||
groupID int
|
||||
systemProxyEnabled bool
|
||||
serviceAccess sync.RWMutex
|
||||
serviceStatus *ServiceStatus
|
||||
serviceStatusSubscriber *observable.Subscriber[*ServiceStatus]
|
||||
@@ -50,55 +50,49 @@ type StartedService struct {
|
||||
logSubscriber *observable.Subscriber[*log.Entry]
|
||||
logObserver *observable.Observer[*log.Entry]
|
||||
instance *Instance
|
||||
startedAt time.Time
|
||||
urlTestSubscriber *observable.Subscriber[struct{}]
|
||||
urlTestObserver *observable.Observer[struct{}]
|
||||
urlTestHistoryStorage *urltest.HistoryStorage
|
||||
clashModeSubscriber *observable.Subscriber[struct{}]
|
||||
clashModeObserver *observable.Observer[struct{}]
|
||||
|
||||
connectionEventSubscriber *observable.Subscriber[trafficontrol.ConnectionEvent]
|
||||
connectionEventObserver *observable.Observer[trafficontrol.ConnectionEvent]
|
||||
}
|
||||
|
||||
type ServiceOptions struct {
|
||||
Context context.Context
|
||||
// Platform adapter.PlatformInterface
|
||||
Handler PlatformHandler
|
||||
Debug bool
|
||||
LogMaxLines int
|
||||
// WorkingDirectory string
|
||||
// TempDirectory string
|
||||
// UserID int
|
||||
// GroupID int
|
||||
// SystemProxyEnabled bool
|
||||
Context context.Context
|
||||
Platform PlatformInterface
|
||||
PlatformHandler PlatformHandler
|
||||
Debug bool
|
||||
LogMaxLines int
|
||||
WorkingDirectory string
|
||||
TempDirectory string
|
||||
UserID int
|
||||
GroupID int
|
||||
SystemProxyEnabled bool
|
||||
}
|
||||
|
||||
func NewStartedService(options ServiceOptions) *StartedService {
|
||||
s := &StartedService{
|
||||
ctx: options.Context,
|
||||
// platform: options.Platform,
|
||||
handler: options.Handler,
|
||||
debug: options.Debug,
|
||||
logMaxLines: options.LogMaxLines,
|
||||
// workingDirectory: options.WorkingDirectory,
|
||||
// tempDirectory: options.TempDirectory,
|
||||
// userID: options.UserID,
|
||||
// groupID: options.GroupID,
|
||||
// systemProxyEnabled: options.SystemProxyEnabled,
|
||||
serviceStatus: &ServiceStatus{Status: ServiceStatus_IDLE},
|
||||
serviceStatusSubscriber: observable.NewSubscriber[*ServiceStatus](4),
|
||||
logSubscriber: observable.NewSubscriber[*log.Entry](128),
|
||||
urlTestSubscriber: observable.NewSubscriber[struct{}](1),
|
||||
urlTestHistoryStorage: urltest.NewHistoryStorage(),
|
||||
clashModeSubscriber: observable.NewSubscriber[struct{}](1),
|
||||
connectionEventSubscriber: observable.NewSubscriber[trafficontrol.ConnectionEvent](256),
|
||||
ctx: options.Context,
|
||||
platform: options.Platform,
|
||||
platformHandler: options.PlatformHandler,
|
||||
debug: options.Debug,
|
||||
logMaxLines: options.LogMaxLines,
|
||||
workingDirectory: options.WorkingDirectory,
|
||||
tempDirectory: options.TempDirectory,
|
||||
userID: options.UserID,
|
||||
groupID: options.GroupID,
|
||||
systemProxyEnabled: options.SystemProxyEnabled,
|
||||
serviceStatus: &ServiceStatus{Status: ServiceStatus_IDLE},
|
||||
serviceStatusSubscriber: observable.NewSubscriber[*ServiceStatus](4),
|
||||
logSubscriber: observable.NewSubscriber[*log.Entry](128),
|
||||
urlTestSubscriber: observable.NewSubscriber[struct{}](1),
|
||||
urlTestHistoryStorage: urltest.NewHistoryStorage(),
|
||||
clashModeSubscriber: observable.NewSubscriber[struct{}](1),
|
||||
}
|
||||
s.serviceStatusObserver = observable.NewObserver(s.serviceStatusSubscriber, 2)
|
||||
s.logObserver = observable.NewObserver(s.logSubscriber, 64)
|
||||
s.urlTestObserver = observable.NewObserver(s.urlTestSubscriber, 1)
|
||||
s.clashModeObserver = observable.NewObserver(s.clashModeSubscriber, 1)
|
||||
s.connectionEventObserver = observable.NewObserver(s.connectionEventSubscriber, 64)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -123,46 +117,6 @@ func (s *StartedService) updateStatusError(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StartedService) waitForStarted(ctx context.Context) error {
|
||||
s.serviceAccess.RLock()
|
||||
currentStatus := s.serviceStatus.Status
|
||||
s.serviceAccess.RUnlock()
|
||||
|
||||
switch currentStatus {
|
||||
case ServiceStatus_STARTED:
|
||||
return nil
|
||||
case ServiceStatus_STARTING:
|
||||
default:
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
subscription, done, err := s.serviceStatusObserver.Subscribe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.serviceStatusObserver.UnSubscribe(subscription)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-s.ctx.Done():
|
||||
return s.ctx.Err()
|
||||
case status := <-subscription:
|
||||
switch status.Status {
|
||||
case ServiceStatus_STARTED:
|
||||
return nil
|
||||
case ServiceStatus_FATAL:
|
||||
return E.New(status.ErrorMessage)
|
||||
case ServiceStatus_IDLE, ServiceStatus_STOPPING:
|
||||
return os.ErrInvalid
|
||||
}
|
||||
case <-done:
|
||||
return os.ErrClosed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StartedService) StartOrReloadService(profileContent string, options *OverrideOptions) error {
|
||||
s.serviceAccess.Lock()
|
||||
switch s.serviceStatus.Status {
|
||||
@@ -171,13 +125,6 @@ func (s *StartedService) StartOrReloadService(profileContent string, options *Ov
|
||||
s.serviceAccess.Unlock()
|
||||
return os.ErrInvalid
|
||||
}
|
||||
oldInstance := s.instance
|
||||
if oldInstance != nil {
|
||||
s.updateStatus(ServiceStatus_STOPPING)
|
||||
s.serviceAccess.Unlock()
|
||||
_ = oldInstance.Close()
|
||||
s.serviceAccess.Lock()
|
||||
}
|
||||
s.updateStatus(ServiceStatus_STARTING)
|
||||
s.resetLogs()
|
||||
instance, err := s.newInstance(profileContent, options)
|
||||
@@ -185,11 +132,6 @@ func (s *StartedService) StartOrReloadService(profileContent string, options *Ov
|
||||
return s.updateStatusError(err)
|
||||
}
|
||||
s.instance = instance
|
||||
instance.urlTestHistoryStorage.SetHook(s.urlTestSubscriber)
|
||||
if instance.clashServer != nil {
|
||||
instance.clashServer.SetModeUpdateHook(s.clashModeSubscriber)
|
||||
instance.clashServer.(*clashapi.Server).TrafficManager().SetEventHook(s.connectionEventSubscriber)
|
||||
}
|
||||
s.serviceAccess.Unlock()
|
||||
err = instance.Start()
|
||||
s.serviceAccess.Lock()
|
||||
@@ -200,7 +142,6 @@ func (s *StartedService) StartOrReloadService(profileContent string, options *Ov
|
||||
if err != nil {
|
||||
return s.updateStatusError(err)
|
||||
}
|
||||
s.startedAt = time.Now()
|
||||
s.updateStatus(ServiceStatus_STARTED)
|
||||
s.serviceAccess.Unlock()
|
||||
runtime.GC()
|
||||
@@ -223,7 +164,6 @@ func (s *StartedService) CloseService() error {
|
||||
}
|
||||
}
|
||||
s.instance = nil
|
||||
s.startedAt = time.Time{}
|
||||
s.updateStatus(ServiceStatus_IDLE)
|
||||
s.serviceAccess.Unlock()
|
||||
runtime.GC()
|
||||
@@ -233,11 +173,12 @@ func (s *StartedService) CloseService() error {
|
||||
func (s *StartedService) SetError(err error) {
|
||||
s.serviceAccess.Lock()
|
||||
s.updateStatusError(err)
|
||||
s.serviceAccess.Unlock()
|
||||
s.WriteMessage(log.LevelError, err.Error())
|
||||
}
|
||||
|
||||
func (s *StartedService) StopService(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
err := s.handler.ServiceStop()
|
||||
err := s.platformHandler.ServiceStop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -245,7 +186,7 @@ func (s *StartedService) StopService(ctx context.Context, empty *emptypb.Empty)
|
||||
}
|
||||
|
||||
func (s *StartedService) ReloadService(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
err := s.handler.ServiceReload()
|
||||
err := s.platformHandler.ServiceReload()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -286,8 +227,8 @@ func (s *StartedService) SubscribeLog(empty *emptypb.Empty, server grpc.ServerSt
|
||||
for element := s.logLines.Front(); element != nil; element = element.Next() {
|
||||
savedLines = append(savedLines, element.Value)
|
||||
}
|
||||
subscription, done, err := s.logObserver.Subscribe()
|
||||
s.logAccess.Unlock()
|
||||
subscription, done, err := s.logObserver.Subscribe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -311,33 +252,30 @@ func (s *StartedService) SubscribeLog(empty *emptypb.Empty, server grpc.ServerSt
|
||||
case <-server.Context().Done():
|
||||
return server.Context().Err()
|
||||
case message := <-subscription:
|
||||
var rawMessage Log
|
||||
if message == nil {
|
||||
rawMessage.Reset_ = true
|
||||
} else {
|
||||
rawMessage.Messages = append(rawMessage.Messages, &Log_Message{
|
||||
Level: LogLevel(message.Level),
|
||||
Message: message.Message,
|
||||
})
|
||||
err = server.Send(&Log{Reset_: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
messages := []*Log_Message{{
|
||||
Level: LogLevel(message.Level),
|
||||
Message: message.Message,
|
||||
}}
|
||||
fetch:
|
||||
for {
|
||||
select {
|
||||
case message = <-subscription:
|
||||
if message == nil {
|
||||
rawMessage.Messages = nil
|
||||
rawMessage.Reset_ = true
|
||||
} else {
|
||||
rawMessage.Messages = append(rawMessage.Messages, &Log_Message{
|
||||
Level: LogLevel(message.Level),
|
||||
Message: message.Message,
|
||||
})
|
||||
}
|
||||
messages = append(messages, &Log_Message{
|
||||
Level: LogLevel(message.Level),
|
||||
Message: message.Message,
|
||||
})
|
||||
default:
|
||||
break fetch
|
||||
}
|
||||
}
|
||||
err = server.Send(&rawMessage)
|
||||
err = server.Send(&Log{Messages: messages})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -360,11 +298,6 @@ func (s *StartedService) GetDefaultLogLevel(ctx context.Context, empty *emptypb.
|
||||
return &DefaultLogLevel{Level: LogLevel(logLevel)}, nil
|
||||
}
|
||||
|
||||
func (s *StartedService) ClearLogs(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
s.resetLogs()
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *StartedService) SubscribeStatus(request *SubscribeStatusRequest, server grpc.ServerStreamingServer[Status]) error {
|
||||
interval := time.Duration(request.Interval)
|
||||
if interval <= 0 {
|
||||
@@ -402,9 +335,7 @@ func (s *StartedService) readStatus() *Status {
|
||||
status.Memory = memory.Inuse()
|
||||
status.Goroutines = int32(runtime.NumGoroutine())
|
||||
status.ConnectionsOut = int32(conntrack.Count())
|
||||
s.serviceAccess.RLock()
|
||||
nowService := s.instance
|
||||
s.serviceAccess.RUnlock()
|
||||
if nowService != nil {
|
||||
if clashServer := nowService.clashServer; clashServer != nil {
|
||||
status.TrafficAvailable = true
|
||||
@@ -417,10 +348,6 @@ func (s *StartedService) readStatus() *Status {
|
||||
}
|
||||
|
||||
func (s *StartedService) SubscribeGroups(empty *emptypb.Empty, server grpc.ServerStreamingServer[Groups]) error {
|
||||
err := s.waitForStarted(server.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subscription, done, err := s.urlTestObserver.Subscribe()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -428,16 +355,18 @@ func (s *StartedService) SubscribeGroups(empty *emptypb.Empty, server grpc.Serve
|
||||
defer s.urlTestObserver.UnSubscribe(subscription)
|
||||
for {
|
||||
s.serviceAccess.RLock()
|
||||
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||
switch s.serviceStatus.Status {
|
||||
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||
groups := s.readGroups()
|
||||
s.serviceAccess.RUnlock()
|
||||
err = server.Send(groups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
s.serviceAccess.RUnlock()
|
||||
return os.ErrInvalid
|
||||
}
|
||||
groups := s.readGroups()
|
||||
s.serviceAccess.RUnlock()
|
||||
err = server.Send(groups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-subscription:
|
||||
case <-s.ctx.Done():
|
||||
@@ -514,27 +443,12 @@ func (s *StartedService) GetClashModeStatus(ctx context.Context, empty *emptypb.
|
||||
}
|
||||
|
||||
func (s *StartedService) SubscribeClashMode(empty *emptypb.Empty, server grpc.ServerStreamingServer[ClashMode]) error {
|
||||
err := s.waitForStarted(server.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subscription, done, err := s.clashModeObserver.Subscribe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.clashModeObserver.UnSubscribe(subscription)
|
||||
for {
|
||||
s.serviceAccess.RLock()
|
||||
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||
s.serviceAccess.RUnlock()
|
||||
return os.ErrInvalid
|
||||
}
|
||||
message := &ClashMode{Mode: s.instance.clashServer.Mode()}
|
||||
s.serviceAccess.RUnlock()
|
||||
err = server.Send(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-subscription:
|
||||
case <-s.ctx.Done():
|
||||
@@ -544,6 +458,16 @@ func (s *StartedService) SubscribeClashMode(empty *emptypb.Empty, server grpc.Se
|
||||
case <-done:
|
||||
return nil
|
||||
}
|
||||
s.serviceAccess.RLock()
|
||||
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||
return nil
|
||||
}
|
||||
message := &ClashMode{Mode: s.instance.clashServer.Mode()}
|
||||
s.serviceAccess.RUnlock()
|
||||
err = server.Send(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +504,12 @@ func (s *StartedService) URLTest(ctx context.Context, request *URLTestRequest) (
|
||||
if isURLTest {
|
||||
go urlTest.CheckOutbounds()
|
||||
} else {
|
||||
historyStorage := boxService.urlTestHistoryStorage
|
||||
var historyStorage adapter.URLTestHistoryStorage
|
||||
if s.instance.clashServer != nil {
|
||||
historyStorage = s.instance.clashServer.HistoryStorage()
|
||||
} else {
|
||||
return nil, E.New("Clash API is required for URLTest on non-URLTest group")
|
||||
}
|
||||
|
||||
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||
itOutbound, _ := boxService.instance.Outbound().Outbound(it)
|
||||
@@ -637,7 +566,6 @@ func (s *StartedService) SelectOutbound(ctx context.Context, request *SelectOutb
|
||||
if !selector.SelectOutbound(request.OutboundTag) {
|
||||
return nil, E.New("outbound not found in selector: ", request.OutboundTag)
|
||||
}
|
||||
s.urlTestObserver.Emit(struct{}{})
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
@@ -661,283 +589,91 @@ func (s *StartedService) SetGroupExpand(ctx context.Context, request *SetGroupEx
|
||||
}
|
||||
|
||||
func (s *StartedService) GetSystemProxyStatus(ctx context.Context, empty *emptypb.Empty) (*SystemProxyStatus, error) {
|
||||
return s.handler.SystemProxyStatus()
|
||||
return s.platformHandler.SystemProxyStatus()
|
||||
}
|
||||
|
||||
func (s *StartedService) SetSystemProxyEnabled(ctx context.Context, request *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
||||
err := s.handler.SetSystemProxyEnabled(request.Enabled)
|
||||
err := s.platformHandler.SetSystemProxyEnabled(request.Enabled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *StartedService) SubscribeConnections(request *SubscribeConnectionsRequest, server grpc.ServerStreamingServer[ConnectionEvents]) error {
|
||||
err := s.waitForStarted(server.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (s *StartedService) SubscribeConnections(request *SubscribeConnectionsRequest, server grpc.ServerStreamingServer[Connections]) error {
|
||||
s.serviceAccess.RLock()
|
||||
switch s.serviceStatus.Status {
|
||||
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||
default:
|
||||
s.serviceAccess.RUnlock()
|
||||
return os.ErrInvalid
|
||||
}
|
||||
boxService := s.instance
|
||||
s.serviceAccess.RUnlock()
|
||||
|
||||
if boxService.clashServer == nil {
|
||||
return E.New("clash server not available")
|
||||
}
|
||||
|
||||
trafficManager := boxService.clashServer.(*clashapi.Server).TrafficManager()
|
||||
|
||||
subscription, done, err := s.connectionEventObserver.Subscribe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.connectionEventObserver.UnSubscribe(subscription)
|
||||
|
||||
connectionSnapshots := make(map[uuid.UUID]connectionSnapshot)
|
||||
initialEvents := s.buildInitialConnectionState(trafficManager, connectionSnapshots)
|
||||
err = server.Send(&ConnectionEvents{
|
||||
Events: initialEvents,
|
||||
Reset_: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
interval := time.Duration(request.Interval)
|
||||
if interval <= 0 {
|
||||
interval = time.Second
|
||||
}
|
||||
ticker := time.NewTicker(interval)
|
||||
ticker := time.NewTicker(time.Duration(request.Interval))
|
||||
defer ticker.Stop()
|
||||
|
||||
trafficManager := boxService.clashServer.(*clashapi.Server).TrafficManager()
|
||||
var (
|
||||
connections = make(map[uuid.UUID]*Connection)
|
||||
outConnections []*Connection
|
||||
)
|
||||
for {
|
||||
outConnections = outConnections[:0]
|
||||
for _, connection := range trafficManager.Connections() {
|
||||
outConnections = append(outConnections, newConnection(connections, connection, false))
|
||||
}
|
||||
for _, connection := range trafficManager.ClosedConnections() {
|
||||
outConnections = append(outConnections, newConnection(connections, connection, true))
|
||||
}
|
||||
err := server.Send(&Connections{Connections: outConnections})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return s.ctx.Err()
|
||||
case <-server.Context().Done():
|
||||
return server.Context().Err()
|
||||
case <-done:
|
||||
return nil
|
||||
|
||||
case event := <-subscription:
|
||||
var pendingEvents []*ConnectionEvent
|
||||
if protoEvent := s.applyConnectionEvent(event, connectionSnapshots); protoEvent != nil {
|
||||
pendingEvents = append(pendingEvents, protoEvent)
|
||||
}
|
||||
drain:
|
||||
for {
|
||||
select {
|
||||
case event = <-subscription:
|
||||
if protoEvent := s.applyConnectionEvent(event, connectionSnapshots); protoEvent != nil {
|
||||
pendingEvents = append(pendingEvents, protoEvent)
|
||||
}
|
||||
default:
|
||||
break drain
|
||||
}
|
||||
}
|
||||
if len(pendingEvents) > 0 {
|
||||
err = server.Send(&ConnectionEvents{Events: pendingEvents})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case <-ticker.C:
|
||||
protoEvents := s.buildTrafficUpdates(trafficManager, connectionSnapshots)
|
||||
if len(protoEvents) == 0 {
|
||||
continue
|
||||
}
|
||||
err = server.Send(&ConnectionEvents{Events: protoEvents})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type connectionSnapshot struct {
|
||||
uplink int64
|
||||
downlink int64
|
||||
hadTraffic bool
|
||||
}
|
||||
|
||||
func (s *StartedService) buildInitialConnectionState(manager *trafficontrol.Manager, snapshots map[uuid.UUID]connectionSnapshot) []*ConnectionEvent {
|
||||
var events []*ConnectionEvent
|
||||
|
||||
for _, metadata := range manager.Connections() {
|
||||
events = append(events, &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||
Id: metadata.ID.String(),
|
||||
Connection: buildConnectionProto(metadata),
|
||||
})
|
||||
snapshots[metadata.ID] = connectionSnapshot{
|
||||
uplink: metadata.Upload.Load(),
|
||||
downlink: metadata.Download.Load(),
|
||||
func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol.TrackerMetadata, isClosed bool) *Connection {
|
||||
if oldConnection, loaded := connections[metadata.ID]; loaded {
|
||||
if isClosed {
|
||||
if oldConnection.ClosedAt == 0 {
|
||||
oldConnection.Uplink = 0
|
||||
oldConnection.Downlink = 0
|
||||
oldConnection.ClosedAt = metadata.ClosedAt.UnixMilli()
|
||||
}
|
||||
return oldConnection
|
||||
}
|
||||
lastUplink := oldConnection.UplinkTotal
|
||||
lastDownlink := oldConnection.DownlinkTotal
|
||||
uplinkTotal := metadata.Upload.Load()
|
||||
downlinkTotal := metadata.Download.Load()
|
||||
oldConnection.Uplink = uplinkTotal - lastUplink
|
||||
oldConnection.Downlink = downlinkTotal - lastDownlink
|
||||
oldConnection.UplinkTotal = uplinkTotal
|
||||
oldConnection.DownlinkTotal = downlinkTotal
|
||||
return oldConnection
|
||||
}
|
||||
|
||||
for _, metadata := range manager.ClosedConnections() {
|
||||
conn := buildConnectionProto(metadata)
|
||||
conn.ClosedAt = metadata.ClosedAt.UnixMilli()
|
||||
events = append(events, &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||
Id: metadata.ID.String(),
|
||||
Connection: conn,
|
||||
})
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
func (s *StartedService) applyConnectionEvent(event trafficontrol.ConnectionEvent, snapshots map[uuid.UUID]connectionSnapshot) *ConnectionEvent {
|
||||
switch event.Type {
|
||||
case trafficontrol.ConnectionEventNew:
|
||||
if _, exists := snapshots[event.ID]; exists {
|
||||
return nil
|
||||
}
|
||||
snapshots[event.ID] = connectionSnapshot{
|
||||
uplink: event.Metadata.Upload.Load(),
|
||||
downlink: event.Metadata.Download.Load(),
|
||||
}
|
||||
return &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||
Id: event.ID.String(),
|
||||
Connection: buildConnectionProto(event.Metadata),
|
||||
}
|
||||
case trafficontrol.ConnectionEventClosed:
|
||||
delete(snapshots, event.ID)
|
||||
protoEvent := &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_CLOSED,
|
||||
Id: event.ID.String(),
|
||||
}
|
||||
closedAt := event.ClosedAt
|
||||
if closedAt.IsZero() && !event.Metadata.ClosedAt.IsZero() {
|
||||
closedAt = event.Metadata.ClosedAt
|
||||
}
|
||||
if closedAt.IsZero() {
|
||||
closedAt = time.Now()
|
||||
}
|
||||
protoEvent.ClosedAt = closedAt.UnixMilli()
|
||||
if event.Metadata.ID != uuid.Nil {
|
||||
conn := buildConnectionProto(event.Metadata)
|
||||
conn.ClosedAt = protoEvent.ClosedAt
|
||||
protoEvent.Connection = conn
|
||||
}
|
||||
return protoEvent
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StartedService) buildTrafficUpdates(manager *trafficontrol.Manager, snapshots map[uuid.UUID]connectionSnapshot) []*ConnectionEvent {
|
||||
activeConnections := manager.Connections()
|
||||
activeIndex := make(map[uuid.UUID]trafficontrol.TrackerMetadata, len(activeConnections))
|
||||
var events []*ConnectionEvent
|
||||
|
||||
for _, metadata := range activeConnections {
|
||||
activeIndex[metadata.ID] = metadata
|
||||
currentUpload := metadata.Upload.Load()
|
||||
currentDownload := metadata.Download.Load()
|
||||
snapshot, exists := snapshots[metadata.ID]
|
||||
if !exists {
|
||||
snapshots[metadata.ID] = connectionSnapshot{
|
||||
uplink: currentUpload,
|
||||
downlink: currentDownload,
|
||||
}
|
||||
events = append(events, &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||
Id: metadata.ID.String(),
|
||||
Connection: buildConnectionProto(metadata),
|
||||
})
|
||||
continue
|
||||
}
|
||||
uplinkDelta := currentUpload - snapshot.uplink
|
||||
downlinkDelta := currentDownload - snapshot.downlink
|
||||
if uplinkDelta < 0 || downlinkDelta < 0 {
|
||||
snapshots[metadata.ID] = connectionSnapshot{
|
||||
uplink: currentUpload,
|
||||
downlink: currentDownload,
|
||||
}
|
||||
continue
|
||||
}
|
||||
if uplinkDelta > 0 || downlinkDelta > 0 {
|
||||
snapshots[metadata.ID] = connectionSnapshot{
|
||||
uplink: currentUpload,
|
||||
downlink: currentDownload,
|
||||
hadTraffic: true,
|
||||
}
|
||||
events = append(events, &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_UPDATE,
|
||||
Id: metadata.ID.String(),
|
||||
UplinkDelta: uplinkDelta,
|
||||
DownlinkDelta: downlinkDelta,
|
||||
})
|
||||
continue
|
||||
}
|
||||
if snapshot.hadTraffic {
|
||||
snapshots[metadata.ID] = connectionSnapshot{
|
||||
uplink: currentUpload,
|
||||
downlink: currentDownload,
|
||||
}
|
||||
events = append(events, &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_UPDATE,
|
||||
Id: metadata.ID.String(),
|
||||
UplinkDelta: 0,
|
||||
DownlinkDelta: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var closedIndex map[uuid.UUID]trafficontrol.TrackerMetadata
|
||||
for id := range snapshots {
|
||||
if _, exists := activeIndex[id]; exists {
|
||||
continue
|
||||
}
|
||||
if closedIndex == nil {
|
||||
closedIndex = make(map[uuid.UUID]trafficontrol.TrackerMetadata)
|
||||
for _, metadata := range manager.ClosedConnections() {
|
||||
closedIndex[metadata.ID] = metadata
|
||||
}
|
||||
}
|
||||
closedAt := time.Now()
|
||||
var conn *Connection
|
||||
if metadata, ok := closedIndex[id]; ok {
|
||||
if !metadata.ClosedAt.IsZero() {
|
||||
closedAt = metadata.ClosedAt
|
||||
}
|
||||
conn = buildConnectionProto(metadata)
|
||||
conn.ClosedAt = closedAt.UnixMilli()
|
||||
}
|
||||
events = append(events, &ConnectionEvent{
|
||||
Type: ConnectionEventType_CONNECTION_EVENT_CLOSED,
|
||||
Id: id.String(),
|
||||
ClosedAt: closedAt.UnixMilli(),
|
||||
Connection: conn,
|
||||
})
|
||||
delete(snapshots, id)
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
func buildConnectionProto(metadata trafficontrol.TrackerMetadata) *Connection {
|
||||
var rule string
|
||||
if metadata.Rule != nil {
|
||||
rule = metadata.Rule.String()
|
||||
}
|
||||
uplinkTotal := metadata.Upload.Load()
|
||||
downlinkTotal := metadata.Download.Load()
|
||||
var processInfo *ProcessInfo
|
||||
if metadata.Metadata.ProcessInfo != nil {
|
||||
processInfo = &ProcessInfo{
|
||||
ProcessId: metadata.Metadata.ProcessInfo.ProcessID,
|
||||
UserId: metadata.Metadata.ProcessInfo.UserId,
|
||||
UserName: metadata.Metadata.ProcessInfo.UserName,
|
||||
ProcessPath: metadata.Metadata.ProcessInfo.ProcessPath,
|
||||
PackageName: metadata.Metadata.ProcessInfo.AndroidPackageName,
|
||||
}
|
||||
uplink := uplinkTotal
|
||||
downlink := downlinkTotal
|
||||
var closedAt int64
|
||||
if !metadata.ClosedAt.IsZero() {
|
||||
closedAt = metadata.ClosedAt.UnixMilli()
|
||||
uplink = 0
|
||||
downlink = 0
|
||||
}
|
||||
return &Connection{
|
||||
connection := &Connection{
|
||||
Id: metadata.ID.String(),
|
||||
Inbound: metadata.Metadata.Inbound,
|
||||
InboundType: metadata.Metadata.InboundType,
|
||||
@@ -950,14 +686,18 @@ func buildConnectionProto(metadata trafficontrol.TrackerMetadata) *Connection {
|
||||
User: metadata.Metadata.User,
|
||||
FromOutbound: metadata.Metadata.Outbound,
|
||||
CreatedAt: metadata.CreatedAt.UnixMilli(),
|
||||
ClosedAt: closedAt,
|
||||
Uplink: uplink,
|
||||
Downlink: downlink,
|
||||
UplinkTotal: uplinkTotal,
|
||||
DownlinkTotal: downlinkTotal,
|
||||
Rule: rule,
|
||||
Outbound: metadata.Outbound,
|
||||
OutboundType: metadata.OutboundType,
|
||||
ChainList: metadata.Chain,
|
||||
ProcessInfo: processInfo,
|
||||
}
|
||||
connections[metadata.ID] = connection
|
||||
return connection
|
||||
}
|
||||
|
||||
func (s *StartedService) CloseConnection(ctx context.Context, request *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||
@@ -1002,10 +742,12 @@ func (s *StartedService) GetDeprecatedWarnings(ctx context.Context, empty *empty
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *StartedService) GetStartedAt(ctx context.Context, empty *emptypb.Empty) (*StartedAt, error) {
|
||||
s.serviceAccess.RLock()
|
||||
defer s.serviceAccess.RUnlock()
|
||||
return &StartedAt{StartedAt: s.startedAt.UnixMilli()}, nil
|
||||
func (s *StartedService) SubscribeHelperEvents(empty *emptypb.Empty, server grpc.ServerStreamingServer[HelperRequest]) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *StartedService) SendHelperResponse(ctx context.Context, response *HelperResponse) (*emptypb.Empty, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
|
||||
@@ -1013,15 +755,15 @@ func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
|
||||
|
||||
func (s *StartedService) WriteMessage(level log.Level, message string) {
|
||||
item := &log.Entry{Level: level, Message: message}
|
||||
s.logSubscriber.Emit(item)
|
||||
s.logAccess.Lock()
|
||||
s.logLines.PushBack(item)
|
||||
if s.logLines.Len() > s.logMaxLines {
|
||||
s.logLines.Remove(s.logLines.Front())
|
||||
}
|
||||
s.logAccess.Unlock()
|
||||
s.logSubscriber.Emit(item)
|
||||
if s.debug {
|
||||
s.handler.WriteDebugMessage(message)
|
||||
s.platformHandler.WriteDebugMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,55 +78,104 @@ func (LogLevel) EnumDescriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type ConnectionEventType int32
|
||||
type ConnectionFilter int32
|
||||
|
||||
const (
|
||||
ConnectionEventType_CONNECTION_EVENT_NEW ConnectionEventType = 0
|
||||
ConnectionEventType_CONNECTION_EVENT_UPDATE ConnectionEventType = 1
|
||||
ConnectionEventType_CONNECTION_EVENT_CLOSED ConnectionEventType = 2
|
||||
ConnectionFilter_ALL ConnectionFilter = 0
|
||||
ConnectionFilter_ACTIVE ConnectionFilter = 1
|
||||
ConnectionFilter_CLOSED ConnectionFilter = 2
|
||||
)
|
||||
|
||||
// Enum value maps for ConnectionEventType.
|
||||
// Enum value maps for ConnectionFilter.
|
||||
var (
|
||||
ConnectionEventType_name = map[int32]string{
|
||||
0: "CONNECTION_EVENT_NEW",
|
||||
1: "CONNECTION_EVENT_UPDATE",
|
||||
2: "CONNECTION_EVENT_CLOSED",
|
||||
ConnectionFilter_name = map[int32]string{
|
||||
0: "ALL",
|
||||
1: "ACTIVE",
|
||||
2: "CLOSED",
|
||||
}
|
||||
ConnectionEventType_value = map[string]int32{
|
||||
"CONNECTION_EVENT_NEW": 0,
|
||||
"CONNECTION_EVENT_UPDATE": 1,
|
||||
"CONNECTION_EVENT_CLOSED": 2,
|
||||
ConnectionFilter_value = map[string]int32{
|
||||
"ALL": 0,
|
||||
"ACTIVE": 1,
|
||||
"CLOSED": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x ConnectionEventType) Enum() *ConnectionEventType {
|
||||
p := new(ConnectionEventType)
|
||||
func (x ConnectionFilter) Enum() *ConnectionFilter {
|
||||
p := new(ConnectionFilter)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x ConnectionEventType) String() string {
|
||||
func (x ConnectionFilter) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (ConnectionEventType) Descriptor() protoreflect.EnumDescriptor {
|
||||
func (ConnectionFilter) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_daemon_started_service_proto_enumTypes[1].Descriptor()
|
||||
}
|
||||
|
||||
func (ConnectionEventType) Type() protoreflect.EnumType {
|
||||
func (ConnectionFilter) Type() protoreflect.EnumType {
|
||||
return &file_daemon_started_service_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x ConnectionEventType) Number() protoreflect.EnumNumber {
|
||||
func (x ConnectionFilter) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnectionEventType.Descriptor instead.
|
||||
func (ConnectionEventType) EnumDescriptor() ([]byte, []int) {
|
||||
// Deprecated: Use ConnectionFilter.Descriptor instead.
|
||||
func (ConnectionFilter) EnumDescriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
type ConnectionSortBy int32
|
||||
|
||||
const (
|
||||
ConnectionSortBy_DATE ConnectionSortBy = 0
|
||||
ConnectionSortBy_TRAFFIC ConnectionSortBy = 1
|
||||
ConnectionSortBy_TOTAL_TRAFFIC ConnectionSortBy = 2
|
||||
)
|
||||
|
||||
// Enum value maps for ConnectionSortBy.
|
||||
var (
|
||||
ConnectionSortBy_name = map[int32]string{
|
||||
0: "DATE",
|
||||
1: "TRAFFIC",
|
||||
2: "TOTAL_TRAFFIC",
|
||||
}
|
||||
ConnectionSortBy_value = map[string]int32{
|
||||
"DATE": 0,
|
||||
"TRAFFIC": 1,
|
||||
"TOTAL_TRAFFIC": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x ConnectionSortBy) Enum() *ConnectionSortBy {
|
||||
p := new(ConnectionSortBy)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x ConnectionSortBy) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (ConnectionSortBy) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_daemon_started_service_proto_enumTypes[2].Descriptor()
|
||||
}
|
||||
|
||||
func (ConnectionSortBy) Type() protoreflect.EnumType {
|
||||
return &file_daemon_started_service_proto_enumTypes[2]
|
||||
}
|
||||
|
||||
func (x ConnectionSortBy) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnectionSortBy.Descriptor instead.
|
||||
func (ConnectionSortBy) EnumDescriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
type ServiceStatus_Type int32
|
||||
|
||||
const (
|
||||
@@ -166,11 +215,11 @@ func (x ServiceStatus_Type) String() string {
|
||||
}
|
||||
|
||||
func (ServiceStatus_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_daemon_started_service_proto_enumTypes[2].Descriptor()
|
||||
return file_daemon_started_service_proto_enumTypes[3].Descriptor()
|
||||
}
|
||||
|
||||
func (ServiceStatus_Type) Type() protoreflect.EnumType {
|
||||
return &file_daemon_started_service_proto_enumTypes[2]
|
||||
return &file_daemon_started_service_proto_enumTypes[3]
|
||||
}
|
||||
|
||||
func (x ServiceStatus_Type) Number() protoreflect.EnumNumber {
|
||||
@@ -1065,6 +1114,8 @@ func (x *SetSystemProxyEnabledRequest) GetEnabled() bool {
|
||||
type SubscribeConnectionsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Interval int64 `protobuf:"varint,1,opt,name=interval,proto3" json:"interval,omitempty"`
|
||||
Filter ConnectionFilter `protobuf:"varint,2,opt,name=filter,proto3,enum=daemon.ConnectionFilter" json:"filter,omitempty"`
|
||||
SortBy ConnectionSortBy `protobuf:"varint,3,opt,name=sortBy,proto3,enum=daemon.ConnectionSortBy" json:"sortBy,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -1106,32 +1157,41 @@ func (x *SubscribeConnectionsRequest) GetInterval() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type ConnectionEvent struct {
|
||||
func (x *SubscribeConnectionsRequest) GetFilter() ConnectionFilter {
|
||||
if x != nil {
|
||||
return x.Filter
|
||||
}
|
||||
return ConnectionFilter_ALL
|
||||
}
|
||||
|
||||
func (x *SubscribeConnectionsRequest) GetSortBy() ConnectionSortBy {
|
||||
if x != nil {
|
||||
return x.SortBy
|
||||
}
|
||||
return ConnectionSortBy_DATE
|
||||
}
|
||||
|
||||
type Connections struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Type ConnectionEventType `protobuf:"varint,1,opt,name=type,proto3,enum=daemon.ConnectionEventType" json:"type,omitempty"`
|
||||
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Connection *Connection `protobuf:"bytes,3,opt,name=connection,proto3" json:"connection,omitempty"`
|
||||
UplinkDelta int64 `protobuf:"varint,4,opt,name=uplinkDelta,proto3" json:"uplinkDelta,omitempty"`
|
||||
DownlinkDelta int64 `protobuf:"varint,5,opt,name=downlinkDelta,proto3" json:"downlinkDelta,omitempty"`
|
||||
ClosedAt int64 `protobuf:"varint,6,opt,name=closedAt,proto3" json:"closedAt,omitempty"`
|
||||
Connections []*Connection `protobuf:"bytes,1,rep,name=connections,proto3" json:"connections,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) Reset() {
|
||||
*x = ConnectionEvent{}
|
||||
func (x *Connections) Reset() {
|
||||
*x = Connections{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) String() string {
|
||||
func (x *Connections) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConnectionEvent) ProtoMessage() {}
|
||||
func (*Connections) ProtoMessage() {}
|
||||
|
||||
func (x *ConnectionEvent) ProtoReflect() protoreflect.Message {
|
||||
func (x *Connections) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -1143,105 +1203,18 @@ func (x *ConnectionEvent) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnectionEvent.ProtoReflect.Descriptor instead.
|
||||
func (*ConnectionEvent) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use Connections.ProtoReflect.Descriptor instead.
|
||||
func (*Connections) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) GetType() ConnectionEventType {
|
||||
func (x *Connections) GetConnections() []*Connection {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return ConnectionEventType_CONNECTION_EVENT_NEW
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) GetConnection() *Connection {
|
||||
if x != nil {
|
||||
return x.Connection
|
||||
return x.Connections
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) GetUplinkDelta() int64 {
|
||||
if x != nil {
|
||||
return x.UplinkDelta
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) GetDownlinkDelta() int64 {
|
||||
if x != nil {
|
||||
return x.DownlinkDelta
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ConnectionEvent) GetClosedAt() int64 {
|
||||
if x != nil {
|
||||
return x.ClosedAt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ConnectionEvents struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Events []*ConnectionEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"`
|
||||
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ConnectionEvents) Reset() {
|
||||
*x = ConnectionEvents{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ConnectionEvents) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConnectionEvents) ProtoMessage() {}
|
||||
|
||||
func (x *ConnectionEvents) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnectionEvents.ProtoReflect.Descriptor instead.
|
||||
func (*ConnectionEvents) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *ConnectionEvents) GetEvents() []*ConnectionEvent {
|
||||
if x != nil {
|
||||
return x.Events
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ConnectionEvents) GetReset_() bool {
|
||||
if x != nil {
|
||||
return x.Reset_
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
@@ -1265,14 +1238,13 @@ type Connection struct {
|
||||
Outbound string `protobuf:"bytes,19,opt,name=outbound,proto3" json:"outbound,omitempty"`
|
||||
OutboundType string `protobuf:"bytes,20,opt,name=outboundType,proto3" json:"outboundType,omitempty"`
|
||||
ChainList []string `protobuf:"bytes,21,rep,name=chainList,proto3" json:"chainList,omitempty"`
|
||||
ProcessInfo *ProcessInfo `protobuf:"bytes,22,opt,name=processInfo,proto3" json:"processInfo,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Connection) Reset() {
|
||||
*x = Connection{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[19]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1284,7 +1256,7 @@ func (x *Connection) String() string {
|
||||
func (*Connection) ProtoMessage() {}
|
||||
|
||||
func (x *Connection) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[19]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1297,7 +1269,7 @@ func (x *Connection) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Connection.ProtoReflect.Descriptor instead.
|
||||
func (*Connection) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{19}
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *Connection) GetId() string {
|
||||
@@ -1447,89 +1419,6 @@ func (x *Connection) GetChainList() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Connection) GetProcessInfo() *ProcessInfo {
|
||||
if x != nil {
|
||||
return x.ProcessInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ProcessInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ProcessId uint32 `protobuf:"varint,1,opt,name=processId,proto3" json:"processId,omitempty"`
|
||||
UserId int32 `protobuf:"varint,2,opt,name=userId,proto3" json:"userId,omitempty"`
|
||||
UserName string `protobuf:"bytes,3,opt,name=userName,proto3" json:"userName,omitempty"`
|
||||
ProcessPath string `protobuf:"bytes,4,opt,name=processPath,proto3" json:"processPath,omitempty"`
|
||||
PackageName string `protobuf:"bytes,5,opt,name=packageName,proto3" json:"packageName,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ProcessInfo) Reset() {
|
||||
*x = ProcessInfo{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ProcessInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ProcessInfo) ProtoMessage() {}
|
||||
|
||||
func (x *ProcessInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[20]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ProcessInfo.ProtoReflect.Descriptor instead.
|
||||
func (*ProcessInfo) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{20}
|
||||
}
|
||||
|
||||
func (x *ProcessInfo) GetProcessId() uint32 {
|
||||
if x != nil {
|
||||
return x.ProcessId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ProcessInfo) GetUserId() int32 {
|
||||
if x != nil {
|
||||
return x.UserId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ProcessInfo) GetUserName() string {
|
||||
if x != nil {
|
||||
return x.UserName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProcessInfo) GetProcessPath() string {
|
||||
if x != nil {
|
||||
return x.ProcessPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProcessInfo) GetPackageName() string {
|
||||
if x != nil {
|
||||
return x.PackageName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CloseConnectionRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
@@ -1539,7 +1428,7 @@ type CloseConnectionRequest struct {
|
||||
|
||||
func (x *CloseConnectionRequest) Reset() {
|
||||
*x = CloseConnectionRequest{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[21]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1551,7 +1440,7 @@ func (x *CloseConnectionRequest) String() string {
|
||||
func (*CloseConnectionRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CloseConnectionRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[21]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[19]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1564,7 +1453,7 @@ func (x *CloseConnectionRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CloseConnectionRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CloseConnectionRequest) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{21}
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{19}
|
||||
}
|
||||
|
||||
func (x *CloseConnectionRequest) GetId() string {
|
||||
@@ -1583,7 +1472,7 @@ type DeprecatedWarnings struct {
|
||||
|
||||
func (x *DeprecatedWarnings) Reset() {
|
||||
*x = DeprecatedWarnings{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[22]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1595,7 +1484,7 @@ func (x *DeprecatedWarnings) String() string {
|
||||
func (*DeprecatedWarnings) ProtoMessage() {}
|
||||
|
||||
func (x *DeprecatedWarnings) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[22]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[20]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1608,7 +1497,7 @@ func (x *DeprecatedWarnings) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DeprecatedWarnings.ProtoReflect.Descriptor instead.
|
||||
func (*DeprecatedWarnings) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{22}
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{20}
|
||||
}
|
||||
|
||||
func (x *DeprecatedWarnings) GetWarnings() []*DeprecatedWarning {
|
||||
@@ -1629,7 +1518,7 @@ type DeprecatedWarning struct {
|
||||
|
||||
func (x *DeprecatedWarning) Reset() {
|
||||
*x = DeprecatedWarning{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[23]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1641,7 +1530,7 @@ func (x *DeprecatedWarning) String() string {
|
||||
func (*DeprecatedWarning) ProtoMessage() {}
|
||||
|
||||
func (x *DeprecatedWarning) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[23]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[21]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1654,7 +1543,7 @@ func (x *DeprecatedWarning) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DeprecatedWarning.ProtoReflect.Descriptor instead.
|
||||
func (*DeprecatedWarning) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{23}
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{21}
|
||||
}
|
||||
|
||||
func (x *DeprecatedWarning) GetMessage() string {
|
||||
@@ -1678,50 +1567,6 @@ func (x *DeprecatedWarning) GetMigrationLink() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type StartedAt struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
StartedAt int64 `protobuf:"varint,1,opt,name=startedAt,proto3" json:"startedAt,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *StartedAt) Reset() {
|
||||
*x = StartedAt{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *StartedAt) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StartedAt) ProtoMessage() {}
|
||||
|
||||
func (x *StartedAt) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[24]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StartedAt.ProtoReflect.Descriptor instead.
|
||||
func (*StartedAt) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{24}
|
||||
}
|
||||
|
||||
func (x *StartedAt) GetStartedAt() int64 {
|
||||
if x != nil {
|
||||
return x.StartedAt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Log_Message struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Level LogLevel `protobuf:"varint,1,opt,name=level,proto3,enum=daemon.LogLevel" json:"level,omitempty"`
|
||||
@@ -1732,7 +1577,7 @@ type Log_Message struct {
|
||||
|
||||
func (x *Log_Message) Reset() {
|
||||
*x = Log_Message{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[25]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1744,7 +1589,7 @@ func (x *Log_Message) String() string {
|
||||
func (*Log_Message) ProtoMessage() {}
|
||||
|
||||
func (x *Log_Message) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[25]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[22]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1778,7 +1623,7 @@ var File_daemon_started_service_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x1cdaemon/started_service.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xad\x01\n" +
|
||||
"\x1cdaemon/started_service.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\x1a\x13daemon/helper.proto\"\xad\x01\n" +
|
||||
"\rServiceStatus\x122\n" +
|
||||
"\x06status\x18\x01 \x01(\x0e2\x1a.daemon.ServiceStatus.TypeR\x06status\x12\"\n" +
|
||||
"\ferrorMessage\x18\x02 \x01(\tR\ferrorMessage\"D\n" +
|
||||
@@ -1845,21 +1690,13 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\tavailable\x18\x01 \x01(\bR\tavailable\x12\x18\n" +
|
||||
"\aenabled\x18\x02 \x01(\bR\aenabled\"8\n" +
|
||||
"\x1cSetSystemProxyEnabledRequest\x12\x18\n" +
|
||||
"\aenabled\x18\x01 \x01(\bR\aenabled\"9\n" +
|
||||
"\aenabled\x18\x01 \x01(\bR\aenabled\"\x9d\x01\n" +
|
||||
"\x1bSubscribeConnectionsRequest\x12\x1a\n" +
|
||||
"\binterval\x18\x01 \x01(\x03R\binterval\"\xea\x01\n" +
|
||||
"\x0fConnectionEvent\x12/\n" +
|
||||
"\x04type\x18\x01 \x01(\x0e2\x1b.daemon.ConnectionEventTypeR\x04type\x12\x0e\n" +
|
||||
"\x02id\x18\x02 \x01(\tR\x02id\x122\n" +
|
||||
"\n" +
|
||||
"connection\x18\x03 \x01(\v2\x12.daemon.ConnectionR\n" +
|
||||
"connection\x12 \n" +
|
||||
"\vuplinkDelta\x18\x04 \x01(\x03R\vuplinkDelta\x12$\n" +
|
||||
"\rdownlinkDelta\x18\x05 \x01(\x03R\rdownlinkDelta\x12\x1a\n" +
|
||||
"\bclosedAt\x18\x06 \x01(\x03R\bclosedAt\"Y\n" +
|
||||
"\x10ConnectionEvents\x12/\n" +
|
||||
"\x06events\x18\x01 \x03(\v2\x17.daemon.ConnectionEventR\x06events\x12\x14\n" +
|
||||
"\x05reset\x18\x02 \x01(\bR\x05reset\"\x95\x05\n" +
|
||||
"\binterval\x18\x01 \x01(\x03R\binterval\x120\n" +
|
||||
"\x06filter\x18\x02 \x01(\x0e2\x18.daemon.ConnectionFilterR\x06filter\x120\n" +
|
||||
"\x06sortBy\x18\x03 \x01(\x0e2\x18.daemon.ConnectionSortByR\x06sortBy\"C\n" +
|
||||
"\vConnections\x124\n" +
|
||||
"\vconnections\x18\x01 \x03(\v2\x12.daemon.ConnectionR\vconnections\"\xde\x04\n" +
|
||||
"\n" +
|
||||
"Connection\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n" +
|
||||
@@ -1883,14 +1720,7 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\x04rule\x18\x12 \x01(\tR\x04rule\x12\x1a\n" +
|
||||
"\boutbound\x18\x13 \x01(\tR\boutbound\x12\"\n" +
|
||||
"\foutboundType\x18\x14 \x01(\tR\foutboundType\x12\x1c\n" +
|
||||
"\tchainList\x18\x15 \x03(\tR\tchainList\x125\n" +
|
||||
"\vprocessInfo\x18\x16 \x01(\v2\x13.daemon.ProcessInfoR\vprocessInfo\"\xa3\x01\n" +
|
||||
"\vProcessInfo\x12\x1c\n" +
|
||||
"\tprocessId\x18\x01 \x01(\rR\tprocessId\x12\x16\n" +
|
||||
"\x06userId\x18\x02 \x01(\x05R\x06userId\x12\x1a\n" +
|
||||
"\buserName\x18\x03 \x01(\tR\buserName\x12 \n" +
|
||||
"\vprocessPath\x18\x04 \x01(\tR\vprocessPath\x12 \n" +
|
||||
"\vpackageName\x18\x05 \x01(\tR\vpackageName\"(\n" +
|
||||
"\tchainList\x18\x15 \x03(\tR\tchainList\"(\n" +
|
||||
"\x16CloseConnectionRequest\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\tR\x02id\"K\n" +
|
||||
"\x12DeprecatedWarnings\x125\n" +
|
||||
@@ -1898,9 +1728,7 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\x11DeprecatedWarning\x12\x18\n" +
|
||||
"\amessage\x18\x01 \x01(\tR\amessage\x12\x1c\n" +
|
||||
"\timpending\x18\x02 \x01(\bR\timpending\x12$\n" +
|
||||
"\rmigrationLink\x18\x03 \x01(\tR\rmigrationLink\")\n" +
|
||||
"\tStartedAt\x12\x1c\n" +
|
||||
"\tstartedAt\x18\x01 \x01(\x03R\tstartedAt*U\n" +
|
||||
"\rmigrationLink\x18\x03 \x01(\tR\rmigrationLink*U\n" +
|
||||
"\bLogLevel\x12\t\n" +
|
||||
"\x05PANIC\x10\x00\x12\t\n" +
|
||||
"\x05FATAL\x10\x01\x12\t\n" +
|
||||
@@ -1908,18 +1736,23 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\x04WARN\x10\x03\x12\b\n" +
|
||||
"\x04INFO\x10\x04\x12\t\n" +
|
||||
"\x05DEBUG\x10\x05\x12\t\n" +
|
||||
"\x05TRACE\x10\x06*i\n" +
|
||||
"\x13ConnectionEventType\x12\x18\n" +
|
||||
"\x14CONNECTION_EVENT_NEW\x10\x00\x12\x1b\n" +
|
||||
"\x17CONNECTION_EVENT_UPDATE\x10\x01\x12\x1b\n" +
|
||||
"\x17CONNECTION_EVENT_CLOSED\x10\x022\xe5\v\n" +
|
||||
"\x05TRACE\x10\x06*3\n" +
|
||||
"\x10ConnectionFilter\x12\a\n" +
|
||||
"\x03ALL\x10\x00\x12\n" +
|
||||
"\n" +
|
||||
"\x06ACTIVE\x10\x01\x12\n" +
|
||||
"\n" +
|
||||
"\x06CLOSED\x10\x02*<\n" +
|
||||
"\x10ConnectionSortBy\x12\b\n" +
|
||||
"\x04DATE\x10\x00\x12\v\n" +
|
||||
"\aTRAFFIC\x10\x01\x12\x11\n" +
|
||||
"\rTOTAL_TRAFFIC\x10\x022\xf8\v\n" +
|
||||
"\x0eStartedService\x12=\n" +
|
||||
"\vStopService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12?\n" +
|
||||
"\rReloadService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12K\n" +
|
||||
"\x16SubscribeServiceStatus\x12\x16.google.protobuf.Empty\x1a\x15.daemon.ServiceStatus\"\x000\x01\x127\n" +
|
||||
"\fSubscribeLog\x12\x16.google.protobuf.Empty\x1a\v.daemon.Log\"\x000\x01\x12G\n" +
|
||||
"\x12GetDefaultLogLevel\x12\x16.google.protobuf.Empty\x1a\x17.daemon.DefaultLogLevel\"\x00\x12=\n" +
|
||||
"\tClearLogs\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12E\n" +
|
||||
"\x12GetDefaultLogLevel\x12\x16.google.protobuf.Empty\x1a\x17.daemon.DefaultLogLevel\"\x00\x12E\n" +
|
||||
"\x0fSubscribeStatus\x12\x1e.daemon.SubscribeStatusRequest\x1a\x0e.daemon.Status\"\x000\x01\x12=\n" +
|
||||
"\x0fSubscribeGroups\x12\x16.google.protobuf.Empty\x1a\x0e.daemon.Groups\"\x000\x01\x12G\n" +
|
||||
"\x12GetClashModeStatus\x12\x16.google.protobuf.Empty\x1a\x17.daemon.ClashModeStatus\"\x00\x12C\n" +
|
||||
@@ -1929,12 +1762,13 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\x0eSelectOutbound\x12\x1d.daemon.SelectOutboundRequest\x1a\x16.google.protobuf.Empty\"\x00\x12I\n" +
|
||||
"\x0eSetGroupExpand\x12\x1d.daemon.SetGroupExpandRequest\x1a\x16.google.protobuf.Empty\"\x00\x12K\n" +
|
||||
"\x14GetSystemProxyStatus\x12\x16.google.protobuf.Empty\x1a\x19.daemon.SystemProxyStatus\"\x00\x12W\n" +
|
||||
"\x15SetSystemProxyEnabled\x12$.daemon.SetSystemProxyEnabledRequest\x1a\x16.google.protobuf.Empty\"\x00\x12Y\n" +
|
||||
"\x14SubscribeConnections\x12#.daemon.SubscribeConnectionsRequest\x1a\x18.daemon.ConnectionEvents\"\x000\x01\x12K\n" +
|
||||
"\x15SetSystemProxyEnabled\x12$.daemon.SetSystemProxyEnabledRequest\x1a\x16.google.protobuf.Empty\"\x00\x12T\n" +
|
||||
"\x14SubscribeConnections\x12#.daemon.SubscribeConnectionsRequest\x1a\x13.daemon.Connections\"\x000\x01\x12K\n" +
|
||||
"\x0fCloseConnection\x12\x1e.daemon.CloseConnectionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12G\n" +
|
||||
"\x13CloseAllConnections\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12M\n" +
|
||||
"\x15GetDeprecatedWarnings\x12\x16.google.protobuf.Empty\x1a\x1a.daemon.DeprecatedWarnings\"\x00\x12;\n" +
|
||||
"\fGetStartedAt\x12\x16.google.protobuf.Empty\x1a\x11.daemon.StartedAt\"\x00B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||
"\x15GetDeprecatedWarnings\x12\x16.google.protobuf.Empty\x1a\x1a.daemon.DeprecatedWarnings\"\x00\x12J\n" +
|
||||
"\x15SubscribeHelperEvents\x12\x16.google.protobuf.Empty\x1a\x15.daemon.HelperRequest\"\x000\x01\x12F\n" +
|
||||
"\x12SendHelperResponse\x12\x16.daemon.HelperResponse\x1a\x16.google.protobuf.Empty\"\x00B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||
|
||||
var (
|
||||
file_daemon_started_service_proto_rawDescOnce sync.Once
|
||||
@@ -1949,101 +1783,100 @@ func file_daemon_started_service_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var (
|
||||
file_daemon_started_service_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||
file_daemon_started_service_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
|
||||
file_daemon_started_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
|
||||
file_daemon_started_service_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
|
||||
file_daemon_started_service_proto_goTypes = []any{
|
||||
(LogLevel)(0), // 0: daemon.LogLevel
|
||||
(ConnectionEventType)(0), // 1: daemon.ConnectionEventType
|
||||
(ServiceStatus_Type)(0), // 2: daemon.ServiceStatus.Type
|
||||
(*ServiceStatus)(nil), // 3: daemon.ServiceStatus
|
||||
(*ReloadServiceRequest)(nil), // 4: daemon.ReloadServiceRequest
|
||||
(*SubscribeStatusRequest)(nil), // 5: daemon.SubscribeStatusRequest
|
||||
(*Log)(nil), // 6: daemon.Log
|
||||
(*DefaultLogLevel)(nil), // 7: daemon.DefaultLogLevel
|
||||
(*Status)(nil), // 8: daemon.Status
|
||||
(*Groups)(nil), // 9: daemon.Groups
|
||||
(*Group)(nil), // 10: daemon.Group
|
||||
(*GroupItem)(nil), // 11: daemon.GroupItem
|
||||
(*URLTestRequest)(nil), // 12: daemon.URLTestRequest
|
||||
(*SelectOutboundRequest)(nil), // 13: daemon.SelectOutboundRequest
|
||||
(*SetGroupExpandRequest)(nil), // 14: daemon.SetGroupExpandRequest
|
||||
(*ClashMode)(nil), // 15: daemon.ClashMode
|
||||
(*ClashModeStatus)(nil), // 16: daemon.ClashModeStatus
|
||||
(*SystemProxyStatus)(nil), // 17: daemon.SystemProxyStatus
|
||||
(*SetSystemProxyEnabledRequest)(nil), // 18: daemon.SetSystemProxyEnabledRequest
|
||||
(*SubscribeConnectionsRequest)(nil), // 19: daemon.SubscribeConnectionsRequest
|
||||
(*ConnectionEvent)(nil), // 20: daemon.ConnectionEvent
|
||||
(*ConnectionEvents)(nil), // 21: daemon.ConnectionEvents
|
||||
(ConnectionFilter)(0), // 1: daemon.ConnectionFilter
|
||||
(ConnectionSortBy)(0), // 2: daemon.ConnectionSortBy
|
||||
(ServiceStatus_Type)(0), // 3: daemon.ServiceStatus.Type
|
||||
(*ServiceStatus)(nil), // 4: daemon.ServiceStatus
|
||||
(*ReloadServiceRequest)(nil), // 5: daemon.ReloadServiceRequest
|
||||
(*SubscribeStatusRequest)(nil), // 6: daemon.SubscribeStatusRequest
|
||||
(*Log)(nil), // 7: daemon.Log
|
||||
(*DefaultLogLevel)(nil), // 8: daemon.DefaultLogLevel
|
||||
(*Status)(nil), // 9: daemon.Status
|
||||
(*Groups)(nil), // 10: daemon.Groups
|
||||
(*Group)(nil), // 11: daemon.Group
|
||||
(*GroupItem)(nil), // 12: daemon.GroupItem
|
||||
(*URLTestRequest)(nil), // 13: daemon.URLTestRequest
|
||||
(*SelectOutboundRequest)(nil), // 14: daemon.SelectOutboundRequest
|
||||
(*SetGroupExpandRequest)(nil), // 15: daemon.SetGroupExpandRequest
|
||||
(*ClashMode)(nil), // 16: daemon.ClashMode
|
||||
(*ClashModeStatus)(nil), // 17: daemon.ClashModeStatus
|
||||
(*SystemProxyStatus)(nil), // 18: daemon.SystemProxyStatus
|
||||
(*SetSystemProxyEnabledRequest)(nil), // 19: daemon.SetSystemProxyEnabledRequest
|
||||
(*SubscribeConnectionsRequest)(nil), // 20: daemon.SubscribeConnectionsRequest
|
||||
(*Connections)(nil), // 21: daemon.Connections
|
||||
(*Connection)(nil), // 22: daemon.Connection
|
||||
(*ProcessInfo)(nil), // 23: daemon.ProcessInfo
|
||||
(*CloseConnectionRequest)(nil), // 24: daemon.CloseConnectionRequest
|
||||
(*DeprecatedWarnings)(nil), // 25: daemon.DeprecatedWarnings
|
||||
(*DeprecatedWarning)(nil), // 26: daemon.DeprecatedWarning
|
||||
(*StartedAt)(nil), // 27: daemon.StartedAt
|
||||
(*Log_Message)(nil), // 28: daemon.Log.Message
|
||||
(*emptypb.Empty)(nil), // 29: google.protobuf.Empty
|
||||
(*CloseConnectionRequest)(nil), // 23: daemon.CloseConnectionRequest
|
||||
(*DeprecatedWarnings)(nil), // 24: daemon.DeprecatedWarnings
|
||||
(*DeprecatedWarning)(nil), // 25: daemon.DeprecatedWarning
|
||||
(*Log_Message)(nil), // 26: daemon.Log.Message
|
||||
(*emptypb.Empty)(nil), // 27: google.protobuf.Empty
|
||||
(*HelperResponse)(nil), // 28: daemon.HelperResponse
|
||||
(*HelperRequest)(nil), // 29: daemon.HelperRequest
|
||||
}
|
||||
)
|
||||
|
||||
var file_daemon_started_service_proto_depIdxs = []int32{
|
||||
2, // 0: daemon.ServiceStatus.status:type_name -> daemon.ServiceStatus.Type
|
||||
28, // 1: daemon.Log.messages:type_name -> daemon.Log.Message
|
||||
3, // 0: daemon.ServiceStatus.status:type_name -> daemon.ServiceStatus.Type
|
||||
26, // 1: daemon.Log.messages:type_name -> daemon.Log.Message
|
||||
0, // 2: daemon.DefaultLogLevel.level:type_name -> daemon.LogLevel
|
||||
10, // 3: daemon.Groups.group:type_name -> daemon.Group
|
||||
11, // 4: daemon.Group.items:type_name -> daemon.GroupItem
|
||||
1, // 5: daemon.ConnectionEvent.type:type_name -> daemon.ConnectionEventType
|
||||
22, // 6: daemon.ConnectionEvent.connection:type_name -> daemon.Connection
|
||||
20, // 7: daemon.ConnectionEvents.events:type_name -> daemon.ConnectionEvent
|
||||
23, // 8: daemon.Connection.processInfo:type_name -> daemon.ProcessInfo
|
||||
26, // 9: daemon.DeprecatedWarnings.warnings:type_name -> daemon.DeprecatedWarning
|
||||
0, // 10: daemon.Log.Message.level:type_name -> daemon.LogLevel
|
||||
29, // 11: daemon.StartedService.StopService:input_type -> google.protobuf.Empty
|
||||
29, // 12: daemon.StartedService.ReloadService:input_type -> google.protobuf.Empty
|
||||
29, // 13: daemon.StartedService.SubscribeServiceStatus:input_type -> google.protobuf.Empty
|
||||
29, // 14: daemon.StartedService.SubscribeLog:input_type -> google.protobuf.Empty
|
||||
29, // 15: daemon.StartedService.GetDefaultLogLevel:input_type -> google.protobuf.Empty
|
||||
29, // 16: daemon.StartedService.ClearLogs:input_type -> google.protobuf.Empty
|
||||
5, // 17: daemon.StartedService.SubscribeStatus:input_type -> daemon.SubscribeStatusRequest
|
||||
29, // 18: daemon.StartedService.SubscribeGroups:input_type -> google.protobuf.Empty
|
||||
29, // 19: daemon.StartedService.GetClashModeStatus:input_type -> google.protobuf.Empty
|
||||
29, // 20: daemon.StartedService.SubscribeClashMode:input_type -> google.protobuf.Empty
|
||||
15, // 21: daemon.StartedService.SetClashMode:input_type -> daemon.ClashMode
|
||||
12, // 22: daemon.StartedService.URLTest:input_type -> daemon.URLTestRequest
|
||||
13, // 23: daemon.StartedService.SelectOutbound:input_type -> daemon.SelectOutboundRequest
|
||||
14, // 24: daemon.StartedService.SetGroupExpand:input_type -> daemon.SetGroupExpandRequest
|
||||
29, // 25: daemon.StartedService.GetSystemProxyStatus:input_type -> google.protobuf.Empty
|
||||
18, // 26: daemon.StartedService.SetSystemProxyEnabled:input_type -> daemon.SetSystemProxyEnabledRequest
|
||||
19, // 27: daemon.StartedService.SubscribeConnections:input_type -> daemon.SubscribeConnectionsRequest
|
||||
24, // 28: daemon.StartedService.CloseConnection:input_type -> daemon.CloseConnectionRequest
|
||||
29, // 29: daemon.StartedService.CloseAllConnections:input_type -> google.protobuf.Empty
|
||||
29, // 30: daemon.StartedService.GetDeprecatedWarnings:input_type -> google.protobuf.Empty
|
||||
29, // 31: daemon.StartedService.GetStartedAt:input_type -> google.protobuf.Empty
|
||||
29, // 32: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
|
||||
29, // 33: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
|
||||
3, // 34: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
|
||||
6, // 35: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
|
||||
7, // 36: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
|
||||
29, // 37: daemon.StartedService.ClearLogs:output_type -> google.protobuf.Empty
|
||||
8, // 38: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
|
||||
9, // 39: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
|
||||
16, // 40: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
|
||||
15, // 41: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
|
||||
29, // 42: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
|
||||
29, // 43: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
|
||||
29, // 44: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
|
||||
29, // 45: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
|
||||
17, // 46: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
|
||||
29, // 47: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
|
||||
21, // 48: daemon.StartedService.SubscribeConnections:output_type -> daemon.ConnectionEvents
|
||||
29, // 49: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
|
||||
29, // 50: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
|
||||
25, // 51: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
|
||||
27, // 52: daemon.StartedService.GetStartedAt:output_type -> daemon.StartedAt
|
||||
32, // [32:53] is the sub-list for method output_type
|
||||
11, // [11:32] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
11, // 3: daemon.Groups.group:type_name -> daemon.Group
|
||||
12, // 4: daemon.Group.items:type_name -> daemon.GroupItem
|
||||
1, // 5: daemon.SubscribeConnectionsRequest.filter:type_name -> daemon.ConnectionFilter
|
||||
2, // 6: daemon.SubscribeConnectionsRequest.sortBy:type_name -> daemon.ConnectionSortBy
|
||||
22, // 7: daemon.Connections.connections:type_name -> daemon.Connection
|
||||
25, // 8: daemon.DeprecatedWarnings.warnings:type_name -> daemon.DeprecatedWarning
|
||||
0, // 9: daemon.Log.Message.level:type_name -> daemon.LogLevel
|
||||
27, // 10: daemon.StartedService.StopService:input_type -> google.protobuf.Empty
|
||||
27, // 11: daemon.StartedService.ReloadService:input_type -> google.protobuf.Empty
|
||||
27, // 12: daemon.StartedService.SubscribeServiceStatus:input_type -> google.protobuf.Empty
|
||||
27, // 13: daemon.StartedService.SubscribeLog:input_type -> google.protobuf.Empty
|
||||
27, // 14: daemon.StartedService.GetDefaultLogLevel:input_type -> google.protobuf.Empty
|
||||
6, // 15: daemon.StartedService.SubscribeStatus:input_type -> daemon.SubscribeStatusRequest
|
||||
27, // 16: daemon.StartedService.SubscribeGroups:input_type -> google.protobuf.Empty
|
||||
27, // 17: daemon.StartedService.GetClashModeStatus:input_type -> google.protobuf.Empty
|
||||
27, // 18: daemon.StartedService.SubscribeClashMode:input_type -> google.protobuf.Empty
|
||||
16, // 19: daemon.StartedService.SetClashMode:input_type -> daemon.ClashMode
|
||||
13, // 20: daemon.StartedService.URLTest:input_type -> daemon.URLTestRequest
|
||||
14, // 21: daemon.StartedService.SelectOutbound:input_type -> daemon.SelectOutboundRequest
|
||||
15, // 22: daemon.StartedService.SetGroupExpand:input_type -> daemon.SetGroupExpandRequest
|
||||
27, // 23: daemon.StartedService.GetSystemProxyStatus:input_type -> google.protobuf.Empty
|
||||
19, // 24: daemon.StartedService.SetSystemProxyEnabled:input_type -> daemon.SetSystemProxyEnabledRequest
|
||||
20, // 25: daemon.StartedService.SubscribeConnections:input_type -> daemon.SubscribeConnectionsRequest
|
||||
23, // 26: daemon.StartedService.CloseConnection:input_type -> daemon.CloseConnectionRequest
|
||||
27, // 27: daemon.StartedService.CloseAllConnections:input_type -> google.protobuf.Empty
|
||||
27, // 28: daemon.StartedService.GetDeprecatedWarnings:input_type -> google.protobuf.Empty
|
||||
27, // 29: daemon.StartedService.SubscribeHelperEvents:input_type -> google.protobuf.Empty
|
||||
28, // 30: daemon.StartedService.SendHelperResponse:input_type -> daemon.HelperResponse
|
||||
27, // 31: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
|
||||
27, // 32: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
|
||||
4, // 33: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
|
||||
7, // 34: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
|
||||
8, // 35: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
|
||||
9, // 36: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
|
||||
10, // 37: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
|
||||
17, // 38: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
|
||||
16, // 39: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
|
||||
27, // 40: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
|
||||
27, // 41: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
|
||||
27, // 42: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
|
||||
27, // 43: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
|
||||
18, // 44: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
|
||||
27, // 45: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
|
||||
21, // 46: daemon.StartedService.SubscribeConnections:output_type -> daemon.Connections
|
||||
27, // 47: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
|
||||
27, // 48: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
|
||||
24, // 49: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
|
||||
29, // 50: daemon.StartedService.SubscribeHelperEvents:output_type -> daemon.HelperRequest
|
||||
27, // 51: daemon.StartedService.SendHelperResponse:output_type -> google.protobuf.Empty
|
||||
31, // [31:52] is the sub-list for method output_type
|
||||
10, // [10:31] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_daemon_started_service_proto_init() }
|
||||
@@ -2051,13 +1884,14 @@ func file_daemon_started_service_proto_init() {
|
||||
if File_daemon_started_service_proto != nil {
|
||||
return
|
||||
}
|
||||
file_daemon_helper_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_started_service_proto_rawDesc), len(file_daemon_started_service_proto_rawDesc)),
|
||||
NumEnums: 3,
|
||||
NumMessages: 26,
|
||||
NumEnums: 4,
|
||||
NumMessages: 23,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ package daemon;
|
||||
option go_package = "github.com/sagernet/sing-box/daemon";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "daemon/helper.proto";
|
||||
|
||||
service StartedService {
|
||||
rpc StopService(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||
@@ -12,7 +13,6 @@ service StartedService {
|
||||
rpc SubscribeServiceStatus(google.protobuf.Empty) returns(stream ServiceStatus) {}
|
||||
rpc SubscribeLog(google.protobuf.Empty) returns(stream Log) {}
|
||||
rpc GetDefaultLogLevel(google.protobuf.Empty) returns(DefaultLogLevel) {}
|
||||
rpc ClearLogs(google.protobuf.Empty) returns(google.protobuf.Empty) {}
|
||||
rpc SubscribeStatus(SubscribeStatusRequest) returns(stream Status) {}
|
||||
rpc SubscribeGroups(google.protobuf.Empty) returns(stream Groups) {}
|
||||
|
||||
@@ -27,11 +27,13 @@ service StartedService {
|
||||
rpc GetSystemProxyStatus(google.protobuf.Empty) returns(SystemProxyStatus) {}
|
||||
rpc SetSystemProxyEnabled(SetSystemProxyEnabledRequest) returns(google.protobuf.Empty) {}
|
||||
|
||||
rpc SubscribeConnections(SubscribeConnectionsRequest) returns(stream ConnectionEvents) {}
|
||||
rpc SubscribeConnections(SubscribeConnectionsRequest) returns(stream Connections) {}
|
||||
rpc CloseConnection(CloseConnectionRequest) returns(google.protobuf.Empty) {}
|
||||
rpc CloseAllConnections(google.protobuf.Empty) returns(google.protobuf.Empty) {}
|
||||
rpc GetDeprecatedWarnings(google.protobuf.Empty) returns(DeprecatedWarnings) {}
|
||||
rpc GetStartedAt(google.protobuf.Empty) returns(StartedAt) {}
|
||||
|
||||
rpc SubscribeHelperEvents(google.protobuf.Empty) returns(stream HelperRequest) {}
|
||||
rpc SendHelperResponse(HelperResponse) returns(google.protobuf.Empty) {}
|
||||
}
|
||||
|
||||
message ServiceStatus {
|
||||
@@ -143,26 +145,24 @@ message SetSystemProxyEnabledRequest {
|
||||
|
||||
message SubscribeConnectionsRequest {
|
||||
int64 interval = 1;
|
||||
ConnectionFilter filter = 2;
|
||||
ConnectionSortBy sortBy = 3;
|
||||
}
|
||||
|
||||
enum ConnectionEventType {
|
||||
CONNECTION_EVENT_NEW = 0;
|
||||
CONNECTION_EVENT_UPDATE = 1;
|
||||
CONNECTION_EVENT_CLOSED = 2;
|
||||
enum ConnectionFilter {
|
||||
ALL = 0;
|
||||
ACTIVE = 1;
|
||||
CLOSED = 2;
|
||||
}
|
||||
|
||||
message ConnectionEvent {
|
||||
ConnectionEventType type = 1;
|
||||
string id = 2;
|
||||
Connection connection = 3;
|
||||
int64 uplinkDelta = 4;
|
||||
int64 downlinkDelta = 5;
|
||||
int64 closedAt = 6;
|
||||
enum ConnectionSortBy {
|
||||
DATE = 0;
|
||||
TRAFFIC = 1;
|
||||
TOTAL_TRAFFIC = 2;
|
||||
}
|
||||
|
||||
message ConnectionEvents {
|
||||
repeated ConnectionEvent events = 1;
|
||||
bool reset = 2;
|
||||
message Connections {
|
||||
repeated Connection connections = 1;
|
||||
}
|
||||
|
||||
message Connection {
|
||||
@@ -187,15 +187,6 @@ message Connection {
|
||||
string outbound = 19;
|
||||
string outboundType = 20;
|
||||
repeated string chainList = 21;
|
||||
ProcessInfo processInfo = 22;
|
||||
}
|
||||
|
||||
message ProcessInfo {
|
||||
uint32 processId = 1;
|
||||
int32 userId = 2;
|
||||
string userName = 3;
|
||||
string processPath = 4;
|
||||
string packageName = 5;
|
||||
}
|
||||
|
||||
message CloseConnectionRequest {
|
||||
@@ -210,8 +201,4 @@ message DeprecatedWarning {
|
||||
string message = 1;
|
||||
bool impending = 2;
|
||||
string migrationLink = 3;
|
||||
}
|
||||
|
||||
message StartedAt {
|
||||
int64 startedAt = 1;
|
||||
}
|
||||
@@ -20,7 +20,6 @@ const (
|
||||
StartedService_SubscribeServiceStatus_FullMethodName = "/daemon.StartedService/SubscribeServiceStatus"
|
||||
StartedService_SubscribeLog_FullMethodName = "/daemon.StartedService/SubscribeLog"
|
||||
StartedService_GetDefaultLogLevel_FullMethodName = "/daemon.StartedService/GetDefaultLogLevel"
|
||||
StartedService_ClearLogs_FullMethodName = "/daemon.StartedService/ClearLogs"
|
||||
StartedService_SubscribeStatus_FullMethodName = "/daemon.StartedService/SubscribeStatus"
|
||||
StartedService_SubscribeGroups_FullMethodName = "/daemon.StartedService/SubscribeGroups"
|
||||
StartedService_GetClashModeStatus_FullMethodName = "/daemon.StartedService/GetClashModeStatus"
|
||||
@@ -35,7 +34,8 @@ const (
|
||||
StartedService_CloseConnection_FullMethodName = "/daemon.StartedService/CloseConnection"
|
||||
StartedService_CloseAllConnections_FullMethodName = "/daemon.StartedService/CloseAllConnections"
|
||||
StartedService_GetDeprecatedWarnings_FullMethodName = "/daemon.StartedService/GetDeprecatedWarnings"
|
||||
StartedService_GetStartedAt_FullMethodName = "/daemon.StartedService/GetStartedAt"
|
||||
StartedService_SubscribeHelperEvents_FullMethodName = "/daemon.StartedService/SubscribeHelperEvents"
|
||||
StartedService_SendHelperResponse_FullMethodName = "/daemon.StartedService/SendHelperResponse"
|
||||
)
|
||||
|
||||
// StartedServiceClient is the client API for StartedService service.
|
||||
@@ -47,7 +47,6 @@ type StartedServiceClient interface {
|
||||
SubscribeServiceStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceStatus], error)
|
||||
SubscribeLog(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Log], error)
|
||||
GetDefaultLogLevel(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DefaultLogLevel, error)
|
||||
ClearLogs(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
SubscribeStatus(ctx context.Context, in *SubscribeStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Status], error)
|
||||
SubscribeGroups(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Groups], error)
|
||||
GetClashModeStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ClashModeStatus, error)
|
||||
@@ -58,11 +57,12 @@ type StartedServiceClient interface {
|
||||
SetGroupExpand(ctx context.Context, in *SetGroupExpandRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
GetSystemProxyStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemProxyStatus, error)
|
||||
SetSystemProxyEnabled(ctx context.Context, in *SetSystemProxyEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ConnectionEvents], error)
|
||||
SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Connections], error)
|
||||
CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
CloseAllConnections(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
GetDeprecatedWarnings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DeprecatedWarnings, error)
|
||||
GetStartedAt(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*StartedAt, error)
|
||||
SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error)
|
||||
SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
}
|
||||
|
||||
type startedServiceClient struct {
|
||||
@@ -141,16 +141,6 @@ func (c *startedServiceClient) GetDefaultLogLevel(ctx context.Context, in *empty
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *startedServiceClient) ClearLogs(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, StartedService_ClearLogs_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *startedServiceClient) SubscribeStatus(ctx context.Context, in *SubscribeStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Status], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[2], StartedService_SubscribeStatus_FullMethodName, cOpts...)
|
||||
@@ -278,13 +268,13 @@ func (c *startedServiceClient) SetSystemProxyEnabled(ctx context.Context, in *Se
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ConnectionEvents], error) {
|
||||
func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Connections], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[5], StartedService_SubscribeConnections_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SubscribeConnectionsRequest, ConnectionEvents]{ClientStream: stream}
|
||||
x := &grpc.GenericClientStream[SubscribeConnectionsRequest, Connections]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -295,7 +285,7 @@ func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *Sub
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_SubscribeConnectionsClient = grpc.ServerStreamingClient[ConnectionEvents]
|
||||
type StartedService_SubscribeConnectionsClient = grpc.ServerStreamingClient[Connections]
|
||||
|
||||
func (c *startedServiceClient) CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
@@ -327,10 +317,29 @@ func (c *startedServiceClient) GetDeprecatedWarnings(ctx context.Context, in *em
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *startedServiceClient) GetStartedAt(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*StartedAt, error) {
|
||||
func (c *startedServiceClient) SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StartedAt)
|
||||
err := c.cc.Invoke(ctx, StartedService_GetStartedAt_FullMethodName, in, out, cOpts...)
|
||||
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[6], StartedService_SubscribeHelperEvents_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[emptypb.Empty, HelperRequest]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_SubscribeHelperEventsClient = grpc.ServerStreamingClient[HelperRequest]
|
||||
|
||||
func (c *startedServiceClient) SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, StartedService_SendHelperResponse_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -346,7 +355,6 @@ type StartedServiceServer interface {
|
||||
SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error
|
||||
SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error
|
||||
GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error)
|
||||
ClearLogs(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||
SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error
|
||||
SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error
|
||||
GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error)
|
||||
@@ -357,11 +365,12 @@ type StartedServiceServer interface {
|
||||
SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error)
|
||||
GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error)
|
||||
SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error)
|
||||
SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[ConnectionEvents]) error
|
||||
SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error
|
||||
CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error)
|
||||
CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||
GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error)
|
||||
GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error)
|
||||
SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error
|
||||
SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error)
|
||||
mustEmbedUnimplementedStartedServiceServer()
|
||||
}
|
||||
|
||||
@@ -373,87 +382,87 @@ type StartedServiceServer interface {
|
||||
type UnimplementedStartedServiceServer struct{}
|
||||
|
||||
func (UnimplementedStartedServiceServer) StopService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method StopService not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method StopService not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) ReloadService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ReloadService not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ReloadService not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeServiceStatus not implemented")
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeServiceStatus not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeLog not implemented")
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeLog not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetDefaultLogLevel not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) ClearLogs(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ClearLogs not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetDefaultLogLevel not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeStatus not implemented")
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeStatus not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeGroups not implemented")
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeGroups not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetClashModeStatus not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetClashModeStatus not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SubscribeClashMode(*emptypb.Empty, grpc.ServerStreamingServer[ClashMode]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeClashMode not implemented")
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeClashMode not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SetClashMode(context.Context, *ClashMode) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SetClashMode not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetClashMode not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) URLTest(context.Context, *URLTestRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method URLTest not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method URLTest not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SelectOutbound(context.Context, *SelectOutboundRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SelectOutbound not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SelectOutbound not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SetGroupExpand not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetGroupExpand not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetSystemProxyStatus not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetSystemProxyStatus not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SetSystemProxyEnabled not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetSystemProxyEnabled not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[ConnectionEvents]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeConnections not implemented")
|
||||
func (UnimplementedStartedServiceServer) SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeConnections not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CloseConnection not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CloseConnection not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CloseAllConnections not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CloseAllConnections not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetDeprecatedWarnings not implemented")
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetDeprecatedWarnings not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStartedAt not implemented")
|
||||
func (UnimplementedStartedServiceServer) SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method SubscribeHelperEvents not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SendHelperResponse not implemented")
|
||||
}
|
||||
func (UnimplementedStartedServiceServer) mustEmbedUnimplementedStartedServiceServer() {}
|
||||
func (UnimplementedStartedServiceServer) testEmbeddedByValue() {}
|
||||
@@ -466,7 +475,7 @@ type UnsafeStartedServiceServer interface {
|
||||
}
|
||||
|
||||
func RegisterStartedServiceServer(s grpc.ServiceRegistrar, srv StartedServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedStartedServiceServer was
|
||||
// If the following call pancis, it indicates UnimplementedStartedServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
@@ -552,24 +561,6 @@ func _StartedService_GetDefaultLogLevel_Handler(srv interface{}, ctx context.Con
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StartedService_ClearLogs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(emptypb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StartedServiceServer).ClearLogs(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: StartedService_ClearLogs_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StartedServiceServer).ClearLogs(ctx, req.(*emptypb.Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StartedService_SubscribeStatus_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SubscribeStatusRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
@@ -734,11 +725,11 @@ func _StartedService_SubscribeConnections_Handler(srv interface{}, stream grpc.S
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(StartedServiceServer).SubscribeConnections(m, &grpc.GenericServerStream[SubscribeConnectionsRequest, ConnectionEvents]{ServerStream: stream})
|
||||
return srv.(StartedServiceServer).SubscribeConnections(m, &grpc.GenericServerStream[SubscribeConnectionsRequest, Connections]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_SubscribeConnectionsServer = grpc.ServerStreamingServer[ConnectionEvents]
|
||||
type StartedService_SubscribeConnectionsServer = grpc.ServerStreamingServer[Connections]
|
||||
|
||||
func _StartedService_CloseConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CloseConnectionRequest)
|
||||
@@ -794,20 +785,31 @@ func _StartedService_GetDeprecatedWarnings_Handler(srv interface{}, ctx context.
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _StartedService_GetStartedAt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(emptypb.Empty)
|
||||
func _StartedService_SubscribeHelperEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(emptypb.Empty)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(StartedServiceServer).SubscribeHelperEvents(m, &grpc.GenericServerStream[emptypb.Empty, HelperRequest]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_SubscribeHelperEventsServer = grpc.ServerStreamingServer[HelperRequest]
|
||||
|
||||
func _StartedService_SendHelperResponse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HelperResponse)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StartedServiceServer).GetStartedAt(ctx, in)
|
||||
return srv.(StartedServiceServer).SendHelperResponse(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: StartedService_GetStartedAt_FullMethodName,
|
||||
FullMethod: StartedService_SendHelperResponse_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StartedServiceServer).GetStartedAt(ctx, req.(*emptypb.Empty))
|
||||
return srv.(StartedServiceServer).SendHelperResponse(ctx, req.(*HelperResponse))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
@@ -831,10 +833,6 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "GetDefaultLogLevel",
|
||||
Handler: _StartedService_GetDefaultLogLevel_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ClearLogs",
|
||||
Handler: _StartedService_ClearLogs_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetClashModeStatus",
|
||||
Handler: _StartedService_GetClashModeStatus_Handler,
|
||||
@@ -876,8 +874,8 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _StartedService_GetDeprecatedWarnings_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetStartedAt",
|
||||
Handler: _StartedService_GetStartedAt_Handler,
|
||||
MethodName: "SendHelperResponse",
|
||||
Handler: _StartedService_SendHelperResponse_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
@@ -911,6 +909,11 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _StartedService_SubscribeConnections_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "SubscribeHelperEvents",
|
||||
Handler: _StartedService_SubscribeHelperEvents_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "daemon/started_service.proto",
|
||||
}
|
||||
|
||||
@@ -95,20 +95,6 @@ func (c *Client) Start() {
|
||||
}
|
||||
}
|
||||
|
||||
func extractNegativeTTL(response *dns.Msg) (uint32, bool) {
|
||||
for _, record := range response.Ns {
|
||||
if soa, isSOA := record.(*dns.SOA); isSOA {
|
||||
soaTTL := soa.Header().Ttl
|
||||
soaMinimum := soa.Minttl
|
||||
if soaTTL < soaMinimum {
|
||||
return soaTTL, true
|
||||
}
|
||||
return soaMinimum, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) {
|
||||
if len(message.Question) == 0 {
|
||||
if c.logger != nil {
|
||||
@@ -228,7 +214,7 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
||||
response.Answer = append(response.Answer, validResponse.Answer...)
|
||||
}
|
||||
}*/
|
||||
disableCache = disableCache || (response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError)
|
||||
disableCache = disableCache || response.Rcode != dns.RcodeSuccess || len(response.Answer) == 0
|
||||
if responseChecker != nil {
|
||||
var rejected bool
|
||||
// TODO: add accept_any rule and support to check response instead of addresses
|
||||
@@ -265,17 +251,10 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
||||
}
|
||||
}
|
||||
var timeToLive uint32
|
||||
if len(response.Answer) == 0 {
|
||||
if soaTTL, hasSOA := extractNegativeTTL(response); hasSOA {
|
||||
timeToLive = soaTTL
|
||||
}
|
||||
}
|
||||
if timeToLive == 0 {
|
||||
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||
for _, record := range recordList {
|
||||
if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive {
|
||||
timeToLive = record.Header().Ttl
|
||||
}
|
||||
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||
for _, record := range recordList {
|
||||
if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive {
|
||||
timeToLive = record.Header().Ttl
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,6 +332,64 @@ func (c *Client) ClearCache() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) LookupCache(domain string, strategy C.DomainStrategy) ([]netip.Addr, bool) {
|
||||
if c.disableCache || c.independentCache {
|
||||
return nil, false
|
||||
}
|
||||
if dns.IsFqdn(domain) {
|
||||
domain = domain[:len(domain)-1]
|
||||
}
|
||||
dnsName := dns.Fqdn(domain)
|
||||
if strategy == C.DomainStrategyIPv4Only {
|
||||
addresses, err := c.questionCache(dns.Question{
|
||||
Name: dnsName,
|
||||
Qtype: dns.TypeA,
|
||||
Qclass: dns.ClassINET,
|
||||
}, nil)
|
||||
if err != ErrNotCached {
|
||||
return addresses, true
|
||||
}
|
||||
} else if strategy == C.DomainStrategyIPv6Only {
|
||||
addresses, err := c.questionCache(dns.Question{
|
||||
Name: dnsName,
|
||||
Qtype: dns.TypeAAAA,
|
||||
Qclass: dns.ClassINET,
|
||||
}, nil)
|
||||
if err != ErrNotCached {
|
||||
return addresses, true
|
||||
}
|
||||
} else {
|
||||
response4, _ := c.loadResponse(dns.Question{
|
||||
Name: dnsName,
|
||||
Qtype: dns.TypeA,
|
||||
Qclass: dns.ClassINET,
|
||||
}, nil)
|
||||
response6, _ := c.loadResponse(dns.Question{
|
||||
Name: dnsName,
|
||||
Qtype: dns.TypeAAAA,
|
||||
Qclass: dns.ClassINET,
|
||||
}, nil)
|
||||
if response4 != nil || response6 != nil {
|
||||
return sortAddresses(MessageToAddresses(response4), MessageToAddresses(response6), strategy), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (c *Client) ExchangeCache(ctx context.Context, message *dns.Msg) (*dns.Msg, bool) {
|
||||
if c.disableCache || c.independentCache || len(message.Question) != 1 {
|
||||
return nil, false
|
||||
}
|
||||
question := message.Question[0]
|
||||
response, ttl := c.loadResponse(question, nil)
|
||||
if response == nil {
|
||||
return nil, false
|
||||
}
|
||||
logCachedResponse(c.logger, ctx, response, ttl)
|
||||
response.Id = message.Id
|
||||
return response, true
|
||||
}
|
||||
|
||||
func sortAddresses(response4 []netip.Addr, response6 []netip.Addr, strategy C.DomainStrategy) []netip.Addr {
|
||||
if strategy == C.DomainStrategyPreferIPv6 {
|
||||
return append(response6, response4...)
|
||||
|
||||
175
dns/router.go
175
dns/router.go
@@ -213,94 +213,96 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
}
|
||||
r.logger.DebugContext(ctx, "exchange ", FormatQuestion(message.Question[0].String()))
|
||||
var (
|
||||
response *mDNS.Msg
|
||||
transport adapter.DNSTransport
|
||||
err error
|
||||
)
|
||||
var metadata *adapter.InboundContext
|
||||
ctx, metadata = adapter.ExtendContext(ctx)
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
metadata.Domain = FqdnToDomain(message.Question[0].Name)
|
||||
if options.Transport != nil {
|
||||
transport = options.Transport
|
||||
if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy {
|
||||
response, cached := r.client.ExchangeCache(ctx, message)
|
||||
if !cached {
|
||||
var metadata *adapter.InboundContext
|
||||
ctx, metadata = adapter.ExtendContext(ctx)
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
metadata.Domain = FqdnToDomain(message.Question[0].Name)
|
||||
if options.Transport != nil {
|
||||
transport = options.Transport
|
||||
if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy {
|
||||
if options.Strategy == C.DomainStrategyAsIS {
|
||||
options.Strategy = legacyTransport.LegacyStrategy()
|
||||
}
|
||||
if !options.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = legacyTransport.LegacyClientSubnet()
|
||||
}
|
||||
}
|
||||
if options.Strategy == C.DomainStrategyAsIS {
|
||||
options.Strategy = legacyTransport.LegacyStrategy()
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
if !options.ClientSubnet.IsValid() {
|
||||
options.ClientSubnet = legacyTransport.LegacyClientSubnet()
|
||||
}
|
||||
}
|
||||
if options.Strategy == C.DomainStrategyAsIS {
|
||||
options.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
response, err = r.client.Exchange(ctx, transport, message, options, nil)
|
||||
} else {
|
||||
var (
|
||||
rule adapter.DNSRule
|
||||
ruleIndex int
|
||||
)
|
||||
ruleIndex = -1
|
||||
for {
|
||||
dnsCtx := adapter.OverrideContext(ctx)
|
||||
dnsOptions := options
|
||||
transport, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message), &dnsOptions)
|
||||
if rule != nil {
|
||||
switch action := rule.Action().(type) {
|
||||
case *R.RuleActionReject:
|
||||
switch action.Method {
|
||||
case C.RuleActionRejectMethodDefault:
|
||||
return &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
Id: message.Id,
|
||||
Rcode: mDNS.RcodeRefused,
|
||||
Response: true,
|
||||
},
|
||||
Question: []mDNS.Question{message.Question[0]},
|
||||
}, nil
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return nil, tun.ErrDrop
|
||||
response, err = r.client.Exchange(ctx, transport, message, options, nil)
|
||||
} else {
|
||||
var (
|
||||
rule adapter.DNSRule
|
||||
ruleIndex int
|
||||
)
|
||||
ruleIndex = -1
|
||||
for {
|
||||
dnsCtx := adapter.OverrideContext(ctx)
|
||||
dnsOptions := options
|
||||
transport, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message), &dnsOptions)
|
||||
if rule != nil {
|
||||
switch action := rule.Action().(type) {
|
||||
case *R.RuleActionReject:
|
||||
switch action.Method {
|
||||
case C.RuleActionRejectMethodDefault:
|
||||
return &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
Id: message.Id,
|
||||
Rcode: mDNS.RcodeRefused,
|
||||
Response: true,
|
||||
},
|
||||
Question: []mDNS.Question{message.Question[0]},
|
||||
}, nil
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return nil, tun.ErrDrop
|
||||
}
|
||||
case *R.RuleActionPredefined:
|
||||
return action.Response(message), nil
|
||||
}
|
||||
case *R.RuleActionPredefined:
|
||||
return action.Response(message), nil
|
||||
}
|
||||
}
|
||||
var responseCheck func(responseAddrs []netip.Addr) bool
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
responseCheck = func(responseAddrs []netip.Addr) bool {
|
||||
metadata.DestinationAddresses = responseAddrs
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
var responseCheck func(responseAddrs []netip.Addr) bool
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
responseCheck = func(responseAddrs []netip.Addr) bool {
|
||||
metadata.DestinationAddresses = responseAddrs
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
}
|
||||
}
|
||||
}
|
||||
if dnsOptions.Strategy == C.DomainStrategyAsIS {
|
||||
dnsOptions.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
response, err = r.client.Exchange(dnsCtx, transport, message, dnsOptions, responseCheck)
|
||||
var rejected bool
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrResponseRejectedCached) {
|
||||
rejected = true
|
||||
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())), " (cached)")
|
||||
} else if errors.Is(err, ErrResponseRejected) {
|
||||
rejected = true
|
||||
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
|
||||
} else if len(message.Question) > 0 {
|
||||
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", FormatQuestion(message.Question[0].String())))
|
||||
} else {
|
||||
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for <empty query>"))
|
||||
if dnsOptions.Strategy == C.DomainStrategyAsIS {
|
||||
dnsOptions.Strategy = r.defaultDomainStrategy
|
||||
}
|
||||
response, err = r.client.Exchange(dnsCtx, transport, message, dnsOptions, responseCheck)
|
||||
var rejected bool
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrResponseRejectedCached) {
|
||||
rejected = true
|
||||
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())), " (cached)")
|
||||
} else if errors.Is(err, ErrResponseRejected) {
|
||||
rejected = true
|
||||
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
|
||||
} else if len(message.Question) > 0 {
|
||||
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", FormatQuestion(message.Question[0].String())))
|
||||
} else {
|
||||
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for <empty query>"))
|
||||
}
|
||||
}
|
||||
if responseCheck != nil && rejected {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if responseCheck != nil && rejected {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -324,6 +326,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
||||
func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQueryOptions) ([]netip.Addr, error) {
|
||||
var (
|
||||
responseAddrs []netip.Addr
|
||||
cached bool
|
||||
err error
|
||||
)
|
||||
printResult := func() {
|
||||
@@ -343,6 +346,13 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
||||
err = E.Cause(err, "lookup ", domain)
|
||||
}
|
||||
}
|
||||
responseAddrs, cached = r.client.LookupCache(domain, options.Strategy)
|
||||
if cached {
|
||||
if len(responseAddrs) == 0 {
|
||||
return nil, E.New("lookup ", domain, ": empty result (cached)")
|
||||
}
|
||||
return responseAddrs, nil
|
||||
}
|
||||
r.logger.DebugContext(ctx, "lookup domain ", domain)
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
@@ -375,7 +385,12 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
||||
if rule != nil {
|
||||
switch action := rule.Action().(type) {
|
||||
case *R.RuleActionReject:
|
||||
return nil, &R.RejectedError{Cause: action.Error(ctx)}
|
||||
switch action.Method {
|
||||
case C.RuleActionRejectMethodDefault:
|
||||
return nil, nil
|
||||
case C.RuleActionRejectMethodDrop:
|
||||
return nil, tun.ErrDrop
|
||||
}
|
||||
case *R.RuleActionPredefined:
|
||||
if action.Rcode != mDNS.RcodeSuccess {
|
||||
err = RcodeError(action.Rcode)
|
||||
@@ -444,6 +459,6 @@ func (r *Router) LookupReverseMapping(ip netip.Addr) (string, bool) {
|
||||
func (r *Router) ResetNetwork() {
|
||||
r.ClearCache()
|
||||
for _, transport := range r.transport.Transports() {
|
||||
transport.Reset()
|
||||
transport.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type TransportState int
|
||||
|
||||
const (
|
||||
StateNew TransportState = iota
|
||||
StateStarted
|
||||
StateClosing
|
||||
StateClosed
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTransportClosed = os.ErrClosed
|
||||
ErrConnectionReset = E.New("connection reset")
|
||||
)
|
||||
|
||||
type BaseTransport struct {
|
||||
dns.TransportAdapter
|
||||
Logger logger.ContextLogger
|
||||
|
||||
mutex sync.Mutex
|
||||
state TransportState
|
||||
inFlight int32
|
||||
queriesComplete chan struct{}
|
||||
closeCtx context.Context
|
||||
closeCancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewBaseTransport(adapter dns.TransportAdapter, logger logger.ContextLogger) *BaseTransport {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &BaseTransport{
|
||||
TransportAdapter: adapter,
|
||||
Logger: logger,
|
||||
state: StateNew,
|
||||
closeCtx: ctx,
|
||||
closeCancel: cancel,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BaseTransport) State() TransportState {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
return t.state
|
||||
}
|
||||
|
||||
func (t *BaseTransport) SetStarted() error {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
switch t.state {
|
||||
case StateNew:
|
||||
t.state = StateStarted
|
||||
return nil
|
||||
case StateStarted:
|
||||
return nil
|
||||
default:
|
||||
return ErrTransportClosed
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BaseTransport) BeginQuery() bool {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
if t.state != StateStarted {
|
||||
return false
|
||||
}
|
||||
t.inFlight++
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *BaseTransport) EndQuery() {
|
||||
t.mutex.Lock()
|
||||
if t.inFlight > 0 {
|
||||
t.inFlight--
|
||||
}
|
||||
if t.inFlight == 0 && t.queriesComplete != nil {
|
||||
close(t.queriesComplete)
|
||||
t.queriesComplete = nil
|
||||
}
|
||||
t.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (t *BaseTransport) CloseContext() context.Context {
|
||||
return t.closeCtx
|
||||
}
|
||||
|
||||
func (t *BaseTransport) Shutdown(ctx context.Context) error {
|
||||
t.mutex.Lock()
|
||||
|
||||
if t.state >= StateClosing {
|
||||
t.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
if t.state == StateNew {
|
||||
t.state = StateClosed
|
||||
t.mutex.Unlock()
|
||||
t.closeCancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
t.state = StateClosing
|
||||
|
||||
if t.inFlight == 0 {
|
||||
t.state = StateClosed
|
||||
t.mutex.Unlock()
|
||||
t.closeCancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
t.queriesComplete = make(chan struct{})
|
||||
queriesComplete := t.queriesComplete
|
||||
t.mutex.Unlock()
|
||||
|
||||
t.closeCancel()
|
||||
|
||||
select {
|
||||
case <-queriesComplete:
|
||||
t.mutex.Lock()
|
||||
t.state = StateClosed
|
||||
t.mutex.Unlock()
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
t.mutex.Lock()
|
||||
t.state = StateClosed
|
||||
t.mutex.Unlock()
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BaseTransport) Close() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
|
||||
defer cancel()
|
||||
return t.Shutdown(ctx)
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ConnectorCallbacks[T any] struct {
|
||||
IsClosed func(connection T) bool
|
||||
Close func(connection T)
|
||||
Reset func(connection T)
|
||||
}
|
||||
|
||||
type Connector[T any] struct {
|
||||
dial func(ctx context.Context) (T, error)
|
||||
callbacks ConnectorCallbacks[T]
|
||||
|
||||
access sync.Mutex
|
||||
connection T
|
||||
hasConnection bool
|
||||
connecting chan struct{}
|
||||
|
||||
closeCtx context.Context
|
||||
closed bool
|
||||
}
|
||||
|
||||
func NewConnector[T any](closeCtx context.Context, dial func(context.Context) (T, error), callbacks ConnectorCallbacks[T]) *Connector[T] {
|
||||
return &Connector[T]{
|
||||
dial: dial,
|
||||
callbacks: callbacks,
|
||||
closeCtx: closeCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSingleflightConnector(closeCtx context.Context, dial func(context.Context) (*Connection, error)) *Connector[*Connection] {
|
||||
return NewConnector(closeCtx, dial, ConnectorCallbacks[*Connection]{
|
||||
IsClosed: func(connection *Connection) bool {
|
||||
return connection.IsClosed()
|
||||
},
|
||||
Close: func(connection *Connection) {
|
||||
connection.CloseWithError(ErrTransportClosed)
|
||||
},
|
||||
Reset: func(connection *Connection) {
|
||||
connection.CloseWithError(ErrConnectionReset)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Connector[T]) Get(ctx context.Context) (T, error) {
|
||||
var zero T
|
||||
for {
|
||||
c.access.Lock()
|
||||
|
||||
if c.closed {
|
||||
c.access.Unlock()
|
||||
return zero, ErrTransportClosed
|
||||
}
|
||||
|
||||
if c.hasConnection && !c.callbacks.IsClosed(c.connection) {
|
||||
connection := c.connection
|
||||
c.access.Unlock()
|
||||
return connection, nil
|
||||
}
|
||||
|
||||
c.hasConnection = false
|
||||
|
||||
if c.connecting != nil {
|
||||
connecting := c.connecting
|
||||
c.access.Unlock()
|
||||
|
||||
select {
|
||||
case <-connecting:
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
return zero, ctx.Err()
|
||||
case <-c.closeCtx.Done():
|
||||
return zero, ErrTransportClosed
|
||||
}
|
||||
}
|
||||
|
||||
c.connecting = make(chan struct{})
|
||||
c.access.Unlock()
|
||||
|
||||
connection, err := c.dialWithCancellation(ctx)
|
||||
|
||||
c.access.Lock()
|
||||
close(c.connecting)
|
||||
c.connecting = nil
|
||||
|
||||
if err != nil {
|
||||
c.access.Unlock()
|
||||
return zero, err
|
||||
}
|
||||
|
||||
if c.closed {
|
||||
c.callbacks.Close(connection)
|
||||
c.access.Unlock()
|
||||
return zero, ErrTransportClosed
|
||||
}
|
||||
|
||||
c.connection = connection
|
||||
c.hasConnection = true
|
||||
result := c.connection
|
||||
c.access.Unlock()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connector[T]) dialWithCancellation(ctx context.Context) (T, error) {
|
||||
dialCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-c.closeCtx.Done():
|
||||
cancel()
|
||||
case <-dialCtx.Done():
|
||||
}
|
||||
}()
|
||||
|
||||
return c.dial(dialCtx)
|
||||
}
|
||||
|
||||
func (c *Connector[T]) Close() error {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
c.closed = true
|
||||
|
||||
if c.hasConnection {
|
||||
c.callbacks.Close(c.connection)
|
||||
c.hasConnection = false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Connector[T]) Reset() {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
|
||||
if c.hasConnection {
|
||||
c.callbacks.Reset(c.connection)
|
||||
c.hasConnection = false
|
||||
}
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
net.Conn
|
||||
|
||||
closeOnce sync.Once
|
||||
done chan struct{}
|
||||
closeError error
|
||||
}
|
||||
|
||||
func WrapConnection(conn net.Conn) *Connection {
|
||||
return &Connection{
|
||||
Conn: conn,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) Done() <-chan struct{} {
|
||||
return c.done
|
||||
}
|
||||
|
||||
func (c *Connection) IsClosed() bool {
|
||||
select {
|
||||
case <-c.done:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) CloseError() error {
|
||||
select {
|
||||
case <-c.done:
|
||||
if c.closeError != nil {
|
||||
return c.closeError
|
||||
}
|
||||
return ErrTransportClosed
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) Close() error {
|
||||
return c.CloseWithError(ErrTransportClosed)
|
||||
}
|
||||
|
||||
func (c *Connection) CloseWithError(err error) error {
|
||||
var returnError error
|
||||
c.closeOnce.Do(func() {
|
||||
c.closeError = err
|
||||
returnError = c.Conn.Close()
|
||||
close(c.done)
|
||||
})
|
||||
return returnError
|
||||
}
|
||||
@@ -49,7 +49,6 @@ type Transport struct {
|
||||
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
|
||||
transportLock sync.RWMutex
|
||||
updatedAt time.Time
|
||||
lastError error
|
||||
servers []M.Socksaddr
|
||||
search []string
|
||||
ndots int
|
||||
@@ -93,7 +92,7 @@ func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
t.interfaceCallback = t.networkManager.InterfaceMonitor().RegisterCallback(t.interfaceUpdated)
|
||||
}
|
||||
go func() {
|
||||
_, err := t.fetch()
|
||||
_, err := t.Fetch()
|
||||
if err != nil {
|
||||
t.logger.Error(E.Cause(err, "fetch DNS servers"))
|
||||
}
|
||||
@@ -108,15 +107,8 @@ func (t *Transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
t.transportLock.Lock()
|
||||
t.updatedAt = time.Time{}
|
||||
t.servers = nil
|
||||
t.transportLock.Unlock()
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
servers, err := t.fetch()
|
||||
servers, err := t.Fetch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,20 +128,11 @@ func (t *Transport) Exchange0(ctx context.Context, message *mDNS.Msg, servers []
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) Fetch() []M.Socksaddr {
|
||||
servers, _ := t.fetch()
|
||||
return servers
|
||||
}
|
||||
|
||||
func (t *Transport) fetch() ([]M.Socksaddr, error) {
|
||||
func (t *Transport) Fetch() ([]M.Socksaddr, error) {
|
||||
t.transportLock.RLock()
|
||||
updatedAt := t.updatedAt
|
||||
lastError := t.lastError
|
||||
servers := t.servers
|
||||
t.transportLock.RUnlock()
|
||||
if lastError != nil {
|
||||
return nil, lastError
|
||||
}
|
||||
if time.Since(updatedAt) < C.DHCPTTL {
|
||||
return servers, nil
|
||||
}
|
||||
@@ -160,7 +143,7 @@ func (t *Transport) fetch() ([]M.Socksaddr, error) {
|
||||
}
|
||||
err := t.updateServers()
|
||||
if err != nil {
|
||||
return servers, err
|
||||
return nil, err
|
||||
}
|
||||
return t.servers, nil
|
||||
}
|
||||
@@ -190,15 +173,12 @@ func (t *Transport) updateServers() error {
|
||||
fetchCtx, cancel := context.WithTimeout(t.ctx, C.DHCPTimeout)
|
||||
err = t.fetchServers0(fetchCtx, iface)
|
||||
cancel()
|
||||
t.updatedAt = time.Now()
|
||||
if err != nil {
|
||||
t.lastError = err
|
||||
return err
|
||||
} else if len(t.servers) == 0 {
|
||||
t.lastError = E.New("dhcp: empty DNS servers response")
|
||||
return t.lastError
|
||||
return E.New("dhcp: empty DNS servers response")
|
||||
} else {
|
||||
t.lastError = nil
|
||||
t.updatedAt = time.Now()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -263,7 +243,6 @@ func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn ne
|
||||
defer buffer.Release()
|
||||
|
||||
for {
|
||||
buffer.Reset()
|
||||
_, _, err := buffer.ReadPacketFrom(packetConn)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.ErrShortBuffer) {
|
||||
|
||||
@@ -82,12 +82,8 @@ func (s *MemoryStorage) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr
|
||||
}
|
||||
|
||||
func (s *MemoryStorage) FakeIPReset() error {
|
||||
s.addressAccess.Lock()
|
||||
s.domainAccess.Lock()
|
||||
s.addressCache = make(map[netip.Addr]string)
|
||||
s.domainCache4 = make(map[string]netip.Addr)
|
||||
s.domainCache6 = make(map[string]netip.Addr)
|
||||
s.domainAccess.Unlock()
|
||||
s.addressAccess.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package fakeip
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -14,15 +13,13 @@ import (
|
||||
var _ adapter.FakeIPStore = (*Store)(nil)
|
||||
|
||||
type Store struct {
|
||||
ctx context.Context
|
||||
logger logger.Logger
|
||||
inet4Range netip.Prefix
|
||||
inet6Range netip.Prefix
|
||||
storage adapter.FakeIPStorage
|
||||
|
||||
addressAccess sync.Mutex
|
||||
inet4Current netip.Addr
|
||||
inet6Current netip.Addr
|
||||
ctx context.Context
|
||||
logger logger.Logger
|
||||
inet4Range netip.Prefix
|
||||
inet6Range netip.Prefix
|
||||
storage adapter.FakeIPStorage
|
||||
inet4Current netip.Addr
|
||||
inet6Current netip.Addr
|
||||
}
|
||||
|
||||
func NewStore(ctx context.Context, logger logger.Logger, inet4Range netip.Prefix, inet6Range netip.Prefix) *Store {
|
||||
@@ -68,30 +65,18 @@ func (s *Store) Close() error {
|
||||
if s.storage == nil {
|
||||
return nil
|
||||
}
|
||||
s.addressAccess.Lock()
|
||||
metadata := &adapter.FakeIPMetadata{
|
||||
return s.storage.FakeIPSaveMetadata(&adapter.FakeIPMetadata{
|
||||
Inet4Range: s.inet4Range,
|
||||
Inet6Range: s.inet6Range,
|
||||
Inet4Current: s.inet4Current,
|
||||
Inet6Current: s.inet6Current,
|
||||
}
|
||||
s.addressAccess.Unlock()
|
||||
return s.storage.FakeIPSaveMetadata(metadata)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) Create(domain string, isIPv6 bool) (netip.Addr, error) {
|
||||
if address, loaded := s.storage.FakeIPLoadDomain(domain, isIPv6); loaded {
|
||||
return address, nil
|
||||
}
|
||||
|
||||
s.addressAccess.Lock()
|
||||
defer s.addressAccess.Unlock()
|
||||
|
||||
// Double-check after acquiring lock
|
||||
if address, loaded := s.storage.FakeIPLoadDomain(domain, isIPv6); loaded {
|
||||
return address, nil
|
||||
}
|
||||
|
||||
var address netip.Addr
|
||||
if !isIPv6 {
|
||||
if !s.inet4Current.IsValid() {
|
||||
@@ -114,10 +99,7 @@ func (s *Store) Create(domain string, isIPv6 bool) (netip.Addr, error) {
|
||||
s.inet6Current = nextAddress
|
||||
address = nextAddress
|
||||
}
|
||||
err := s.storage.FakeIPStore(address, domain)
|
||||
if err != nil {
|
||||
s.logger.Warn("save FakeIP cache: ", err)
|
||||
}
|
||||
s.storage.FakeIPStoreAsync(address, domain, s.logger)
|
||||
s.storage.FakeIPSaveMetadataAsync(&adapter.FakeIPMetadata{
|
||||
Inet4Range: s.inet4Range,
|
||||
Inet6Range: s.inet6Range,
|
||||
|
||||
@@ -59,9 +59,6 @@ func (t *Transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
question := message.Question[0]
|
||||
domain := mDNS.CanonicalName(question.Name)
|
||||
|
||||
@@ -145,13 +145,6 @@ func (t *HTTPSTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) Reset() {
|
||||
t.transportAccess.Lock()
|
||||
defer t.transportAccess.Unlock()
|
||||
t.transport.CloseIdleConnections()
|
||||
t.transport = t.transport.Clone()
|
||||
}
|
||||
|
||||
func (t *HTTPSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
startAt := time.Now()
|
||||
response, err := t.exchange(ctx, message)
|
||||
@@ -189,10 +182,7 @@ func (t *HTTPSTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS
|
||||
request.Header = t.headers.Clone()
|
||||
request.Header.Set("Content-Type", MimeType)
|
||||
request.Header.Set("Accept", MimeType)
|
||||
t.transportAccess.Lock()
|
||||
currentTransport := t.transport
|
||||
t.transportAccess.Unlock()
|
||||
response, err := currentTransport.RoundTrip(request)
|
||||
response, err := t.transport.RoundTrip(request)
|
||||
requestBuffer.Release()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -204,12 +194,12 @@ func (t *HTTPSTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS
|
||||
var responseMessage mDNS.Msg
|
||||
if response.ContentLength > 0 {
|
||||
responseBuffer := buf.NewSize(int(response.ContentLength))
|
||||
defer responseBuffer.Release()
|
||||
_, err = responseBuffer.ReadFullFrom(response.Body, int(response.ContentLength))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = responseMessage.Unpack(responseBuffer.Bytes())
|
||||
responseBuffer.Release()
|
||||
} else {
|
||||
rawMessage, err = io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
|
||||
@@ -75,6 +75,5 @@ func (h *HTTPSTransportWrapper) Clone() *HTTPSTransportWrapper {
|
||||
http2Transport: &http2.Transport{
|
||||
DialTLSContext: h.http2Transport.DialTLSContext,
|
||||
},
|
||||
fallback: h.fallback,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +53,13 @@ func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
switch stage {
|
||||
case adapter.StartStateInitialize:
|
||||
if !t.preferGo {
|
||||
if isSystemdResolvedManaged() {
|
||||
resolvedResolver, err := NewResolvedResolver(t.ctx, t.logger)
|
||||
resolvedResolver, err := NewResolvedResolver(t.ctx, t.logger)
|
||||
if err == nil {
|
||||
err = resolvedResolver.Start()
|
||||
if err == nil {
|
||||
err = resolvedResolver.Start()
|
||||
if err == nil {
|
||||
t.resolved = resolvedResolver
|
||||
} else {
|
||||
t.logger.Warn(E.Cause(err, "initialize resolved resolver"))
|
||||
}
|
||||
t.resolved = resolvedResolver
|
||||
} else {
|
||||
t.logger.Warn(E.Cause(err, "initialize resolved resolver"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,9 +74,6 @@ func (t *Transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if t.resolved != nil {
|
||||
resolverObject := t.resolved.Object()
|
||||
@@ -87,11 +82,12 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
}
|
||||
}
|
||||
question := message.Question[0]
|
||||
domain := dns.FqdnToDomain(question.Name)
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name))
|
||||
addresses := t.hosts.Lookup(domain)
|
||||
if len(addresses) > 0 {
|
||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
||||
}
|
||||
}
|
||||
return t.exchange(ctx, message, question.Name)
|
||||
return t.exchange(ctx, message, domain)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ type Transport struct {
|
||||
|
||||
type dhcpTransport interface {
|
||||
adapter.DNSTransport
|
||||
Fetch() []M.Socksaddr
|
||||
Fetch() ([]M.Socksaddr, error)
|
||||
Exchange0(ctx context.Context, message *mDNS.Msg, servers []M.Socksaddr) (*mDNS.Msg, error)
|
||||
}
|
||||
|
||||
@@ -74,12 +74,14 @@ func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
if t.fallback {
|
||||
t.dhcpTransport = newDHCPTransport(t.TransportAdapter, log.ContextWithOverrideLevel(t.ctx, log.LevelDebug), t.dialer, t.logger)
|
||||
if t.dhcpTransport != nil {
|
||||
err := t.dhcpTransport.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
if !C.IsIos {
|
||||
if t.fallback {
|
||||
t.dhcpTransport = newDHCPTransport(t.TransportAdapter, log.ContextWithOverrideLevel(t.ctx, log.LevelDebug), t.dialer, t.logger)
|
||||
if t.dhcpTransport != nil {
|
||||
err := t.dhcpTransport.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,32 +94,29 @@ func (t *Transport) Close() error {
|
||||
)
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
if t.dhcpTransport != nil {
|
||||
t.dhcpTransport.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
question := message.Question[0]
|
||||
domain := dns.FqdnToDomain(question.Name)
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name))
|
||||
addresses := t.hosts.Lookup(domain)
|
||||
if len(addresses) > 0 {
|
||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
||||
}
|
||||
}
|
||||
if !t.fallback {
|
||||
return t.exchange(ctx, message, question.Name)
|
||||
return t.exchange(ctx, message, domain)
|
||||
}
|
||||
if t.dhcpTransport != nil {
|
||||
dhcpTransports := t.dhcpTransport.Fetch()
|
||||
if len(dhcpTransports) > 0 {
|
||||
return t.dhcpTransport.Exchange0(ctx, message, dhcpTransports)
|
||||
if !C.IsIos {
|
||||
if t.dhcpTransport != nil {
|
||||
dhcpTransports, _ := t.dhcpTransport.Fetch()
|
||||
if len(dhcpTransports) > 0 {
|
||||
return t.dhcpTransport.Exchange0(ctx, message, dhcpTransports)
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.preferGo {
|
||||
// Assuming the user knows what they are doing, we still execute the query which will fail.
|
||||
return t.exchange(ctx, message, question.Name)
|
||||
return t.exchange(ctx, message, domain)
|
||||
}
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
var network string
|
||||
@@ -126,7 +125,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
} else {
|
||||
network = "ip6"
|
||||
}
|
||||
addresses, err := t.resolver.LookupNetIP(ctx, network, question.Name)
|
||||
addresses, err := t.resolver.LookupNetIP(ctx, network, domain)
|
||||
if err != nil {
|
||||
var dnsError *net.DNSError
|
||||
if errors.As(err, &dnsError) && dnsError.IsNotFound {
|
||||
@@ -136,5 +135,9 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
}
|
||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
||||
}
|
||||
return nil, E.New("only A and AAAA queries are supported on Apple platforms when using TUN and DHCP unavailable.")
|
||||
if C.IsIos {
|
||||
return nil, E.New("only A and AAAA queries are supported on iOS and tvOS when using NetworkExtension.")
|
||||
} else {
|
||||
return nil, E.New("only A and AAAA queries are supported on macOS when using NetworkExtension and DHCP unavailable.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -24,25 +22,6 @@ import (
|
||||
mDNS "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func isSystemdResolvedManaged() bool {
|
||||
resolvContent, err := os.Open("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer resolvContent.Close()
|
||||
scanner := bufio.NewScanner(resolvContent)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" || line[0] != '#' {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(line, "systemd-resolved") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type DBusResolvedResolver struct {
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
@@ -209,7 +188,7 @@ func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*ResolvedObje
|
||||
int32(defaultInterface.Index),
|
||||
)
|
||||
if call.Err != nil {
|
||||
return nil, call.Err
|
||||
return nil, err
|
||||
}
|
||||
var linkPath dbus.ObjectPath
|
||||
err = call.Store(&linkPath)
|
||||
@@ -235,12 +214,15 @@ func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*ResolvedObje
|
||||
return nil, E.New("No appropriate name servers or networks for name found")
|
||||
}
|
||||
}
|
||||
return nil, E.New("link has no DNS servers configured")
|
||||
return &ResolvedObject{
|
||||
BusObject: dbusObject,
|
||||
}, nil
|
||||
} else {
|
||||
return &ResolvedObject{
|
||||
BusObject: dbusObject,
|
||||
InterfaceIndex: int32(defaultInterface.Index),
|
||||
}, nil
|
||||
}
|
||||
return &ResolvedObject{
|
||||
BusObject: dbusObject,
|
||||
InterfaceIndex: int32(defaultInterface.Index),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) {
|
||||
|
||||
@@ -9,10 +9,6 @@ import (
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
func isSystemdResolvedManaged() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -8,12 +8,10 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
@@ -25,7 +23,6 @@ import (
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
sHTTP "github.com/sagernet/sing/protocol/http"
|
||||
|
||||
@@ -40,14 +37,11 @@ func RegisterHTTP3Transport(registry *dns.TransportRegistry) {
|
||||
|
||||
type HTTP3Transport struct {
|
||||
dns.TransportAdapter
|
||||
logger logger.ContextLogger
|
||||
dialer N.Dialer
|
||||
destination *url.URL
|
||||
headers http.Header
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig *tls.STDConfig
|
||||
transportAccess sync.Mutex
|
||||
transport *http3.Transport
|
||||
logger logger.ContextLogger
|
||||
dialer N.Dialer
|
||||
destination *url.URL
|
||||
headers http.Header
|
||||
transport *http3.Transport
|
||||
}
|
||||
|
||||
func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteHTTPSDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
@@ -101,57 +95,33 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
t := &HTTP3Transport{
|
||||
return &HTTP3Transport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeHTTP3, tag, options.RemoteDNSServerOptions),
|
||||
logger: logger,
|
||||
dialer: transportDialer,
|
||||
destination: &destinationURL,
|
||||
headers: headers,
|
||||
serverAddr: serverAddr,
|
||||
tlsConfig: stdConfig,
|
||||
}
|
||||
t.transport = t.newTransport()
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) newTransport() *http3.Transport {
|
||||
return &http3.Transport{
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (*quic.Conn, error) {
|
||||
conn, dialErr := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
||||
if dialErr != nil {
|
||||
return nil, dialErr
|
||||
}
|
||||
quicConn, dialErr := quic.DialEarly(ctx, bufio.NewUnbindPacketConn(conn), conn.RemoteAddr(), tlsCfg, cfg)
|
||||
if dialErr != nil {
|
||||
conn.Close()
|
||||
return nil, dialErr
|
||||
}
|
||||
return quicConn, nil
|
||||
transport: &http3.Transport{
|
||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (*quic.Conn, error) {
|
||||
conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, serverAddr)
|
||||
if dialErr != nil {
|
||||
return nil, dialErr
|
||||
}
|
||||
return quic.DialEarly(ctx, bufio.NewUnbindPacketConn(conn), conn.RemoteAddr(), tlsCfg, cfg)
|
||||
},
|
||||
TLSClientConfig: stdConfig,
|
||||
},
|
||||
TLSClientConfig: t.tlsConfig,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) Close() error {
|
||||
t.transportAccess.Lock()
|
||||
defer t.transportAccess.Unlock()
|
||||
return t.transport.Close()
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) Reset() {
|
||||
t.transportAccess.Lock()
|
||||
defer t.transportAccess.Unlock()
|
||||
t.transport.Close()
|
||||
t.transport = t.newTransport()
|
||||
}
|
||||
|
||||
func (t *HTTP3Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
exMessage := *message
|
||||
exMessage.Id = 0
|
||||
@@ -170,10 +140,7 @@ func (t *HTTP3Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS
|
||||
request.Header = t.headers.Clone()
|
||||
request.Header.Set("Content-Type", transport.MimeType)
|
||||
request.Header.Set("Accept", transport.MimeType)
|
||||
t.transportAccess.Lock()
|
||||
currentTransport := t.transport
|
||||
t.transportAccess.Unlock()
|
||||
response, err := currentTransport.RoundTrip(request)
|
||||
response, err := t.transport.RoundTrip(request)
|
||||
requestBuffer.Release()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -185,12 +152,12 @@ func (t *HTTP3Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS
|
||||
var responseMessage mDNS.Msg
|
||||
if response.ContentLength > 0 {
|
||||
responseBuffer := buf.NewSize(int(response.ContentLength))
|
||||
defer responseBuffer.Release()
|
||||
_, err = responseBuffer.ReadFullFrom(response.Body, int(response.ContentLength))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = responseMessage.Unpack(responseBuffer.Bytes())
|
||||
responseBuffer.Release()
|
||||
} else {
|
||||
rawMessage, err = io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,11 +3,10 @@ package quic
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
@@ -31,14 +31,14 @@ func RegisterTransport(registry *dns.TransportRegistry) {
|
||||
}
|
||||
|
||||
type Transport struct {
|
||||
*transport.BaseTransport
|
||||
|
||||
dns.TransportAdapter
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
|
||||
connector *transport.Connector[*quic.Conn]
|
||||
access sync.Mutex
|
||||
connection *quic.Conn
|
||||
}
|
||||
|
||||
func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteTLSDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
@@ -62,84 +62,38 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options
|
||||
if !serverAddr.IsValid() {
|
||||
return nil, E.New("invalid server address: ", serverAddr)
|
||||
}
|
||||
|
||||
t := &Transport{
|
||||
BaseTransport: transport.NewBaseTransport(
|
||||
dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeQUIC, tag, options.RemoteDNSServerOptions),
|
||||
logger,
|
||||
),
|
||||
ctx: ctx,
|
||||
dialer: transportDialer,
|
||||
serverAddr: serverAddr,
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
|
||||
t.connector = transport.NewConnector(t.CloseContext(), t.dial, transport.ConnectorCallbacks[*quic.Conn]{
|
||||
IsClosed: func(connection *quic.Conn) bool {
|
||||
return common.Done(connection.Context())
|
||||
},
|
||||
Close: func(connection *quic.Conn) {
|
||||
connection.CloseWithError(0, "")
|
||||
},
|
||||
Reset: func(connection *quic.Conn) {
|
||||
connection.CloseWithError(0, "")
|
||||
},
|
||||
})
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *Transport) dial(ctx context.Context) (*quic.Conn, error) {
|
||||
conn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial UDP connection")
|
||||
}
|
||||
earlyConnection, err := sQUIC.DialEarly(
|
||||
ctx,
|
||||
bufio.NewUnbindPacketConn(conn),
|
||||
t.serverAddr.UDPAddr(),
|
||||
t.tlsConfig,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, E.Cause(err, "establish QUIC connection")
|
||||
}
|
||||
return earlyConnection, nil
|
||||
return &Transport{
|
||||
TransportAdapter: dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeQUIC, tag, options.RemoteDNSServerOptions),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
dialer: transportDialer,
|
||||
serverAddr: serverAddr,
|
||||
tlsConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Transport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
err := t.SetStarted()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Close() error {
|
||||
return E.Errors(t.BaseTransport.Close(), t.connector.Close())
|
||||
}
|
||||
|
||||
func (t *Transport) Reset() {
|
||||
t.connector.Reset()
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
connection := t.connection
|
||||
if connection != nil {
|
||||
connection.CloseWithError(0, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if !t.BeginQuery() {
|
||||
return nil, transport.ErrTransportClosed
|
||||
}
|
||||
defer t.EndQuery()
|
||||
|
||||
var (
|
||||
conn *quic.Conn
|
||||
err error
|
||||
response *mDNS.Msg
|
||||
)
|
||||
for i := 0; i < 2; i++ {
|
||||
conn, err = t.connector.Get(ctx)
|
||||
conn, err = t.openConnection()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -149,38 +103,58 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
} else if !isQUICRetryError(err) {
|
||||
return nil, err
|
||||
} else {
|
||||
t.connector.Reset()
|
||||
conn.CloseWithError(quic.ApplicationErrorCode(0), "")
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (t *Transport) openConnection() (*quic.Conn, error) {
|
||||
connection := t.connection
|
||||
if connection != nil && !common.Done(connection.Context()) {
|
||||
return connection, nil
|
||||
}
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
connection = t.connection
|
||||
if connection != nil && !common.Done(connection.Context()) {
|
||||
return connection, nil
|
||||
}
|
||||
conn, err := t.dialer.DialContext(t.ctx, N.NetworkUDP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
earlyConnection, err := sQUIC.DialEarly(
|
||||
t.ctx,
|
||||
bufio.NewUnbindPacketConn(conn),
|
||||
t.serverAddr.UDPAddr(),
|
||||
t.tlsConfig,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.connection = earlyConnection
|
||||
return earlyConnection, nil
|
||||
}
|
||||
|
||||
func (t *Transport) exchange(ctx context.Context, message *mDNS.Msg, conn *quic.Conn) (*mDNS.Msg, error) {
|
||||
stream, err := conn.OpenStreamSync(ctx)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "open stream")
|
||||
return nil, err
|
||||
}
|
||||
defer stream.CancelRead(0)
|
||||
err = transport.WriteMessage(stream, 0, message)
|
||||
if err != nil {
|
||||
stream.Close()
|
||||
return nil, E.Cause(err, "write request")
|
||||
return nil, err
|
||||
}
|
||||
stream.Close()
|
||||
response, err := transport.ReadMessage(stream)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read response")
|
||||
}
|
||||
return response, nil
|
||||
return transport.ReadMessage(stream)
|
||||
}
|
||||
|
||||
// https://github.com/AdguardTeam/dnsproxy/blob/fd1868577652c639cce3da00e12ca548f421baf1/upstream/upstream_quic.go#L394
|
||||
func isQUICRetryError(err error) (ok bool) {
|
||||
if errors.Is(err, os.ErrClosed) {
|
||||
return true
|
||||
}
|
||||
|
||||
var qAppErr *quic.ApplicationError
|
||||
if errors.As(err, &qAppErr) && qAppErr.ErrorCode == 0 {
|
||||
return true
|
||||
|
||||
@@ -62,24 +62,17 @@ func (t *TCPTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TCPTransport) Reset() {
|
||||
}
|
||||
|
||||
func (t *TCPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
conn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial TCP connection")
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = WriteMessage(conn, 0, message)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "write request")
|
||||
return nil, err
|
||||
}
|
||||
response, err := ReadMessage(conn)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read response")
|
||||
}
|
||||
return response, nil
|
||||
return ReadMessage(conn)
|
||||
}
|
||||
|
||||
func ReadMessage(reader io.Reader) (*mDNS.Msg, error) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package transport
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
@@ -29,8 +28,8 @@ func RegisterTLS(registry *dns.TransportRegistry) {
|
||||
}
|
||||
|
||||
type TLSTransport struct {
|
||||
*BaseTransport
|
||||
|
||||
dns.TransportAdapter
|
||||
logger logger.ContextLogger
|
||||
dialer tls.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
@@ -66,10 +65,11 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
|
||||
func NewTLSRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer N.Dialer, serverAddr M.Socksaddr, tlsConfig tls.Config) *TLSTransport {
|
||||
return &TLSTransport{
|
||||
BaseTransport: NewBaseTransport(adapter, logger),
|
||||
dialer: tls.NewDialer(dialer, tlsConfig),
|
||||
serverAddr: serverAddr,
|
||||
tlsConfig: tlsConfig,
|
||||
TransportAdapter: adapter,
|
||||
logger: logger,
|
||||
dialer: tls.NewDialer(dialer, tlsConfig),
|
||||
serverAddr: serverAddr,
|
||||
tlsConfig: tlsConfig,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,59 +77,37 @@ func (t *TLSTransport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
err := t.SetStarted()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
}
|
||||
|
||||
func (t *TLSTransport) Close() error {
|
||||
t.access.Lock()
|
||||
for connection := t.connections.Front(); connection != nil; connection = connection.Next() {
|
||||
connection.Value.Close()
|
||||
}
|
||||
t.connections.Init()
|
||||
t.access.Unlock()
|
||||
return t.BaseTransport.Close()
|
||||
}
|
||||
|
||||
func (t *TLSTransport) Reset() {
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
for connection := t.connections.Front(); connection != nil; connection = connection.Next() {
|
||||
connection.Value.Close()
|
||||
}
|
||||
t.connections.Init()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TLSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if !t.BeginQuery() {
|
||||
return nil, ErrTransportClosed
|
||||
}
|
||||
defer t.EndQuery()
|
||||
|
||||
t.access.Lock()
|
||||
conn := t.connections.PopFront()
|
||||
t.access.Unlock()
|
||||
if conn != nil {
|
||||
response, err := t.exchange(ctx, message, conn)
|
||||
response, err := t.exchange(message, conn)
|
||||
if err == nil {
|
||||
return response, nil
|
||||
}
|
||||
t.Logger.DebugContext(ctx, "discarded pooled connection: ", err)
|
||||
}
|
||||
tlsConn, err := t.dialer.DialTLSContext(ctx, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial TLS connection")
|
||||
return nil, err
|
||||
}
|
||||
return t.exchange(ctx, message, &tlsDNSConn{Conn: tlsConn})
|
||||
return t.exchange(message, &tlsDNSConn{Conn: tlsConn})
|
||||
}
|
||||
|
||||
func (t *TLSTransport) exchange(ctx context.Context, message *mDNS.Msg, conn *tlsDNSConn) (*mDNS.Msg, error) {
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
conn.SetDeadline(deadline)
|
||||
}
|
||||
func (t *TLSTransport) exchange(message *mDNS.Msg, conn *tlsDNSConn) (*mDNS.Msg, error) {
|
||||
conn.queryId++
|
||||
err := WriteMessage(conn, conn.queryId, message)
|
||||
if err != nil {
|
||||
@@ -142,12 +120,6 @@ func (t *TLSTransport) exchange(ctx context.Context, message *mDNS.Msg, conn *tl
|
||||
return nil, E.Cause(err, "read response")
|
||||
}
|
||||
t.access.Lock()
|
||||
if t.State() >= StateClosing {
|
||||
t.access.Unlock()
|
||||
conn.Close()
|
||||
return response, nil
|
||||
}
|
||||
conn.SetDeadline(time.Time{})
|
||||
t.connections.PushBack(conn)
|
||||
t.access.Unlock()
|
||||
return response, nil
|
||||
|
||||
@@ -2,8 +2,9 @@ package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
@@ -27,23 +28,15 @@ func RegisterUDP(registry *dns.TransportRegistry) {
|
||||
}
|
||||
|
||||
type UDPTransport struct {
|
||||
*BaseTransport
|
||||
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
udpSize atomic.Int32
|
||||
|
||||
connector *Connector[*Connection]
|
||||
|
||||
callbackAccess sync.RWMutex
|
||||
queryId uint16
|
||||
callbacks map[uint16]*udpCallback
|
||||
}
|
||||
|
||||
type udpCallback struct {
|
||||
access sync.Mutex
|
||||
response *mDNS.Msg
|
||||
done chan struct{}
|
||||
dns.TransportAdapter
|
||||
logger logger.ContextLogger
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
udpSize int
|
||||
tcpTransport *TCPTransport
|
||||
access sync.Mutex
|
||||
conn *dnsConnection
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteDNSServerOptions) (adapter.DNSTransport, error) {
|
||||
@@ -61,198 +54,180 @@ func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options o
|
||||
return NewUDPRaw(logger, dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeUDP, tag, options), transportDialer, serverAddr), nil
|
||||
}
|
||||
|
||||
func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialerInstance N.Dialer, serverAddr M.Socksaddr) *UDPTransport {
|
||||
t := &UDPTransport{
|
||||
BaseTransport: NewBaseTransport(adapter, logger),
|
||||
dialer: dialerInstance,
|
||||
serverAddr: serverAddr,
|
||||
callbacks: make(map[uint16]*udpCallback),
|
||||
func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer N.Dialer, serverAddr M.Socksaddr) *UDPTransport {
|
||||
return &UDPTransport{
|
||||
TransportAdapter: adapter,
|
||||
logger: logger,
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
udpSize: 2048,
|
||||
tcpTransport: &TCPTransport{
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
},
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
t.udpSize.Store(2048)
|
||||
t.connector = NewSingleflightConnector(t.CloseContext(), t.dial)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *UDPTransport) dial(ctx context.Context) (*Connection, error) {
|
||||
rawConn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial UDP connection")
|
||||
}
|
||||
conn := WrapConnection(rawConn)
|
||||
go t.recvLoop(conn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (t *UDPTransport) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
err := t.SetStarted()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dialer.InitializeDetour(t.dialer)
|
||||
}
|
||||
|
||||
func (t *UDPTransport) Close() error {
|
||||
return E.Errors(t.BaseTransport.Close(), t.connector.Close())
|
||||
}
|
||||
|
||||
func (t *UDPTransport) Reset() {
|
||||
t.connector.Reset()
|
||||
}
|
||||
|
||||
func (t *UDPTransport) nextAvailableQueryId() (uint16, error) {
|
||||
start := t.queryId
|
||||
for {
|
||||
t.queryId++
|
||||
if _, exists := t.callbacks[t.queryId]; !exists {
|
||||
return t.queryId, nil
|
||||
}
|
||||
if t.queryId == start {
|
||||
return 0, E.New("no available query ID")
|
||||
}
|
||||
}
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
close(t.done)
|
||||
t.done = make(chan struct{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *UDPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
if !t.BeginQuery() {
|
||||
return nil, ErrTransportClosed
|
||||
}
|
||||
defer t.EndQuery()
|
||||
|
||||
response, err := t.exchange(ctx, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.Truncated {
|
||||
t.Logger.InfoContext(ctx, "response truncated, retrying with TCP")
|
||||
return t.exchangeTCP(ctx, message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (t *UDPTransport) exchangeTCP(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
conn, err := t.dialer.DialContext(ctx, N.NetworkTCP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial TCP connection")
|
||||
}
|
||||
defer conn.Close()
|
||||
err = WriteMessage(conn, message.Id, message)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "write request")
|
||||
}
|
||||
response, err := ReadMessage(conn)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read response")
|
||||
t.logger.InfoContext(ctx, "response truncated, retrying with TCP")
|
||||
return t.tcpTransport.Exchange(ctx, message)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
t.access.Lock()
|
||||
if edns0Opt := message.IsEdns0(); edns0Opt != nil {
|
||||
udpSize := int32(edns0Opt.UDPSize())
|
||||
for {
|
||||
current := t.udpSize.Load()
|
||||
if udpSize <= current {
|
||||
break
|
||||
}
|
||||
if t.udpSize.CompareAndSwap(current, udpSize) {
|
||||
t.connector.Reset()
|
||||
break
|
||||
}
|
||||
if udpSize := int(edns0Opt.UDPSize()); udpSize > t.udpSize {
|
||||
t.udpSize = udpSize
|
||||
close(t.done)
|
||||
t.done = make(chan struct{})
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := t.connector.Get(ctx)
|
||||
t.access.Unlock()
|
||||
conn, err := t.open(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
callback := &udpCallback{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
t.callbackAccess.Lock()
|
||||
queryId, err := t.nextAvailableQueryId()
|
||||
if err != nil {
|
||||
t.callbackAccess.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
t.callbacks[queryId] = callback
|
||||
t.callbackAccess.Unlock()
|
||||
|
||||
defer func() {
|
||||
t.callbackAccess.Lock()
|
||||
delete(t.callbacks, queryId)
|
||||
t.callbackAccess.Unlock()
|
||||
}()
|
||||
|
||||
buffer := buf.NewSize(1 + message.Len())
|
||||
defer buffer.Release()
|
||||
|
||||
exMessage := *message
|
||||
exMessage.Compress = true
|
||||
originalId := message.Id
|
||||
exMessage.Id = queryId
|
||||
|
||||
messageId := message.Id
|
||||
callback := &dnsCallback{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
conn.access.Lock()
|
||||
conn.queryId++
|
||||
exMessage.Id = conn.queryId
|
||||
conn.callbacks[exMessage.Id] = callback
|
||||
conn.access.Unlock()
|
||||
defer func() {
|
||||
conn.access.Lock()
|
||||
delete(conn.callbacks, exMessage.Id)
|
||||
conn.access.Unlock()
|
||||
}()
|
||||
rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = conn.Write(rawMessage)
|
||||
if err != nil {
|
||||
conn.CloseWithError(err)
|
||||
return nil, E.Cause(err, "write request")
|
||||
conn.Close(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-callback.done:
|
||||
callback.response.Id = originalId
|
||||
return callback.response, nil
|
||||
case <-conn.Done():
|
||||
return nil, conn.CloseError()
|
||||
case <-t.CloseContext().Done():
|
||||
return nil, ErrTransportClosed
|
||||
callback.message.Id = messageId
|
||||
return callback.message, nil
|
||||
case <-conn.done:
|
||||
return nil, conn.err
|
||||
case <-t.done:
|
||||
return nil, os.ErrClosed
|
||||
case <-ctx.Done():
|
||||
conn.Close(ctx.Err())
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *UDPTransport) recvLoop(conn *Connection) {
|
||||
func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) {
|
||||
t.access.Lock()
|
||||
defer t.access.Unlock()
|
||||
if t.conn != nil {
|
||||
select {
|
||||
case <-t.conn.done:
|
||||
default:
|
||||
return t.conn, nil
|
||||
}
|
||||
}
|
||||
conn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dnsConn := &dnsConnection{
|
||||
Conn: conn,
|
||||
done: make(chan struct{}),
|
||||
callbacks: make(map[uint16]*dnsCallback),
|
||||
}
|
||||
go t.recvLoop(dnsConn)
|
||||
t.conn = dnsConn
|
||||
return dnsConn, nil
|
||||
}
|
||||
|
||||
func (t *UDPTransport) recvLoop(conn *dnsConnection) {
|
||||
for {
|
||||
buffer := buf.NewSize(int(t.udpSize.Load()))
|
||||
buffer := buf.NewSize(t.udpSize)
|
||||
_, err := buffer.ReadOnceFrom(conn)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
conn.CloseWithError(err)
|
||||
conn.Close(err)
|
||||
return
|
||||
}
|
||||
|
||||
var message mDNS.Msg
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
buffer.Release()
|
||||
if err != nil {
|
||||
t.Logger.Debug("discarded malformed UDP response: ", err)
|
||||
continue
|
||||
conn.Close(err)
|
||||
return
|
||||
}
|
||||
|
||||
t.callbackAccess.RLock()
|
||||
callback, loaded := t.callbacks[message.Id]
|
||||
t.callbackAccess.RUnlock()
|
||||
|
||||
conn.access.RLock()
|
||||
callback, loaded := conn.callbacks[message.Id]
|
||||
conn.access.RUnlock()
|
||||
if !loaded {
|
||||
continue
|
||||
}
|
||||
|
||||
callback.access.Lock()
|
||||
select {
|
||||
case <-callback.done:
|
||||
default:
|
||||
callback.response = &message
|
||||
callback.message = &message
|
||||
close(callback.done)
|
||||
}
|
||||
callback.access.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
type dnsConnection struct {
|
||||
net.Conn
|
||||
access sync.RWMutex
|
||||
done chan struct{}
|
||||
closeOnce sync.Once
|
||||
err error
|
||||
queryId uint16
|
||||
callbacks map[uint16]*dnsCallback
|
||||
}
|
||||
|
||||
func (c *dnsConnection) Close(err error) {
|
||||
c.closeOnce.Do(func() {
|
||||
c.err = err
|
||||
close(c.done)
|
||||
})
|
||||
c.Conn.Close()
|
||||
}
|
||||
|
||||
type dnsCallback struct {
|
||||
access sync.Mutex
|
||||
message *mDNS.Msg
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
@@ -2,305 +2,10 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.13.0-beta.6
|
||||
|
||||
* Update uTLS to v1.8.2 **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
This update fixes missing padding extension for Chrome 120+ fingerprints.
|
||||
|
||||
Also, documentation has been updated with a warning about uTLS fingerprinting vulnerabilities.
|
||||
uTLS is not recommended for censorship circumvention due to fundamental architectural limitations;
|
||||
use NaiveProxy instead for TLS fingerprint resistance.
|
||||
|
||||
#### 1.12.17
|
||||
|
||||
* Update uTLS to v1.8.2 **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
This update fixes missing padding extension for Chrome 120+ fingerprints.
|
||||
|
||||
Also, documentation has been updated with a warning about uTLS fingerprinting vulnerabilities.
|
||||
uTLS is not recommended for censorship circumvention due to fundamental architectural limitations;
|
||||
use NaiveProxy instead for TLS fingerprint resistance.
|
||||
|
||||
#### 1.13.0-beta.5
|
||||
#### 1.13.0-alpha.20
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.12.16
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-beta.4
|
||||
|
||||
* Apple/Android: Add support for sharing configurations via [QRS](https://github.com/qifi-dev/qrs)
|
||||
* Android: Add support for resisting VPN detection via Xposed
|
||||
* Update quic-go to v0.59.0
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-beta.2
|
||||
|
||||
* Add `bind_address_no_port` option for dial fields **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Adds the Linux socket option `IP_BIND_ADDRESS_NO_PORT` support when explicitly binding to a source address.
|
||||
|
||||
This allows reusing the same source port for multiple connections, improving scalability for high-concurrency proxy scenarios.
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial/#bind_address_no_port).
|
||||
|
||||
#### 1.13.0-beta.1
|
||||
|
||||
* Add system interface support for Tailscale endpoint **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Tailscale endpoint can now create a system TUN interface to handle traffic directly.
|
||||
|
||||
See [Tailscale endpoint](/configuration/endpoint/tailscale/#system_interface).
|
||||
|
||||
#### 1.12.15
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-alpha.36
|
||||
|
||||
* Downgrade quic-go to v0.57.1
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-alpha.35
|
||||
|
||||
* Add pre-match support for `auto_redirect` **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
`auto_redirect` now allows you to bypass sing-box for connections based on routing rules.
|
||||
|
||||
A new rule action `bypass` is introduced to support this feature. When matched during pre-match, the connection will bypass sing-box and connect directly.
|
||||
|
||||
This feature requires Linux with `auto_redirect` enabled.
|
||||
|
||||
See [Pre-match](/configuration/shared/pre-match/) and [Rule Action](/configuration/route/rule_action/#bypass).
|
||||
|
||||
#### 1.13.0-alpha.34
|
||||
|
||||
* Add Chrome Root Store certificate option **1**
|
||||
* Add new options for ACME DNS-01 challenge providers **2**
|
||||
* Add Wi-Fi state support for Linux and Windows **3**
|
||||
* Update naiveproxy to 143.0.7499.109
|
||||
* Update quic-go to v0.58.0
|
||||
* Update tailscale to v1.92.4
|
||||
* Drop support for go1.23 **4**
|
||||
* Drop support for Android 5.0 **5**
|
||||
|
||||
**1**:
|
||||
|
||||
Adds `chrome` as a new certificate store option alongside `mozilla`.
|
||||
Both stores filter out China-based CA certificates.
|
||||
|
||||
See [Certificate](/configuration/certificate/#store).
|
||||
|
||||
**2**:
|
||||
|
||||
See [DNS-01 Challenge](/configuration/shared/dns01_challenge/).
|
||||
|
||||
**3**:
|
||||
|
||||
sing-box can now monitor Wi-Fi state on Linux and Windows to enable routing rules based on `wifi_ssid` and `wifi_bssid`.
|
||||
|
||||
See [Wi-Fi State](/configuration/shared/wifi-state/).
|
||||
|
||||
**4**:
|
||||
|
||||
Due to maintenance difficulties, sing-box 1.13.0 requires at least Go 1.24 to compile.
|
||||
|
||||
**5**:
|
||||
|
||||
Due to maintenance difficulties, sing-box 1.13.0 will be the last version to support Android 5.0,
|
||||
and only through a separate legacy build (with `-legacy-android-5` suffix).
|
||||
|
||||
For standalone binaries, the minimum Android version has been raised to Android 6.0,
|
||||
since Termux requires Android 7.0 or later.
|
||||
|
||||
#### 1.12.14
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-alpha.33
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-alpha.32
|
||||
|
||||
* Remove `certificate_public_key_sha256` option for NaiveProxy outbound **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Self-signed certificates change traffic behavior significantly, which defeats the purpose of NaiveProxy's design to resist traffic analysis.
|
||||
For this reason, and due to maintenance costs, there is no reason to continue supporting `certificate_public_key_sha256`, which was designed to simplify the use of self-signed certificates.
|
||||
|
||||
#### 1.13.0-alpha.31
|
||||
|
||||
* Add QUIC support for NaiveProxy outbound **1**
|
||||
* Add QUIC congestion control option for NaiveProxy **2**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
NaiveProxy outbound now supports QUIC.
|
||||
|
||||
See [NaiveProxy outbound](/configuration/outbound/naive/#quic).
|
||||
|
||||
**2**:
|
||||
|
||||
NaiveProxy inbound and outbound now supports configurable QUIC congestion control algorithms, including BBR and BBRv2.
|
||||
|
||||
See [NaiveProxy inbound](/configuration/inbound/naive/#quic_congestion_control) and [NaiveProxy outbound](/configuration/outbound/naive/#quic_congestion_control).
|
||||
|
||||
#### 1.13.0-alpha.30
|
||||
|
||||
* Add ECH support for NaiveProxy outbound **1**
|
||||
* Add `tls.ech.query_server_name` option **2**
|
||||
* Fix NaiveProxy outbound on Windows **3**
|
||||
* Add OpenAI Codex Multiplexer service **4**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [NaiveProxy outbound](/configuration/outbound/naive/#tls).
|
||||
|
||||
**2**:
|
||||
|
||||
See [TLS](/configuration/shared/tls/#query_server_name).
|
||||
|
||||
**3**:
|
||||
|
||||
Each Windows release now includes `libcronet.dll`.
|
||||
Ensure this file is in the same directory as `sing-box.exe` or in a directory listed in `PATH`.
|
||||
|
||||
**4**:
|
||||
|
||||
See [OCM](/configuration/service/ocm).
|
||||
|
||||
#### 1.13.0-alpha.29
|
||||
|
||||
* Add UDP over TCP support for naiveproxy outbound **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [NaiveProxy outbound](/configuration/outbound/naive/#udp_over_tcp).
|
||||
|
||||
#### 1.13.0-alpha.28
|
||||
|
||||
* Add naiveproxy outbound **1**
|
||||
* Add `disable_tcp_keep_alive`, `tcp_keep_alive` and `tcp_keep_alive_interval` options for dial fields **2**
|
||||
* Update default TCP keep-alive initial period from 10 minutes to 5 minutes
|
||||
* Update quic-go to v0.57.1
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Only available on Apple platforms, Android, Windows and some Linux architectures.
|
||||
|
||||
See [NaiveProxy outbound](/configuration/outbound/naive/).
|
||||
|
||||
**2**:
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial/#tcp_keep_alive).
|
||||
|
||||
* __Unfortunately, for non-technical reasons, we are currently unable to notarize the standalone version of the macOS client:
|
||||
because system extensions require signatures to function, we have had to temporarily halt its release.__
|
||||
|
||||
__We plan to fix the App Store release issue and launch a new standalone desktop client, but until then,
|
||||
only clients on TestFlight will be available (unless you have an Apple Developer Program and compile from source code).__
|
||||
|
||||
|
||||
#### 1.12.13
|
||||
|
||||
* Fix naive inbound
|
||||
* Fixes and improvements
|
||||
|
||||
__Unfortunately, for non-technical reasons, we are currently unable to notarize the standalone version of the macOS client:
|
||||
because system extensions require signatures to function, we have had to temporarily halt its release.__
|
||||
|
||||
__We plan to fix the App Store release issue and launch a new standalone desktop client, but until then,
|
||||
only clients on TestFlight will be available (unless you have an Apple Developer Program and compile from source code).__
|
||||
|
||||
#### 1.12.12
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-alpha.26
|
||||
|
||||
* Update quic-go to v0.55.0
|
||||
* Fix memory leak in hysteria2
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.12.11
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.13.0-alpha.24
|
||||
|
||||
* Add Claude Code Multiplexer service **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
CCM (Claude Code Multiplexer) service allows you to access your local Claude Code subscription remotely through custom tokens, eliminating the need for OAuth authentication on remote clients.
|
||||
|
||||
See [CCM](/configuration/service/ccm).
|
||||
|
||||
#### 1.13.0-alpha.23
|
||||
|
||||
* Fix compatibility with MPTCP **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
`auto_redirect` now rejects MPTCP connections by default to fix compatibility issues,
|
||||
but you can change it to bypass the sing-box via the new `exclude_mptcp` option.
|
||||
|
||||
See [TUN](/configuration/inbound/tun/#exclude_mptcp).
|
||||
|
||||
#### 1.13.0-alpha.22
|
||||
|
||||
* Update uTLS to v1.8.1 **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
This update fixes an critical issue that could cause simulated Chrome fingerprints to be detected,
|
||||
see https://github.com/refraction-networking/utls/pull/375.
|
||||
|
||||
#### 1.12.10
|
||||
|
||||
* Update uTLS to v1.8.1 **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
This update fixes an critical issue that could cause simulated Chrome fingerprints to be detected,
|
||||
see https://github.com/refraction-networking/utls/pull/375.
|
||||
|
||||
#### 1.13.0-alpha.21
|
||||
|
||||
* Fix missing mTLS support in client options **1**
|
||||
* Fixes and improvements
|
||||
|
||||
See [TLS](/configuration/shared/tls/).
|
||||
|
||||
#### 1.12.9
|
||||
|
||||
* Fixes and improvements
|
||||
@@ -422,8 +127,7 @@ See [Tailscale](/configuration/endpoint/tailscale/).
|
||||
|
||||
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
||||
|
||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches
|
||||
from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||
|
||||
**7**:
|
||||
|
||||
@@ -485,8 +189,7 @@ See [Tun](/configuration/inbound/tun/#loopback_address).
|
||||
|
||||
We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack.
|
||||
|
||||
The following data was tested
|
||||
using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro.
|
||||
The following data was tested using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro.
|
||||
|
||||
| Version | Stack | MTU | Upload | Download |
|
||||
|-------------|--------|-------|--------|----------|
|
||||
@@ -505,8 +208,8 @@ using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/interna
|
||||
|
||||
**18**:
|
||||
|
||||
We continue to experience issues updating our sing-box apps on the App Store and Play Store.
|
||||
Until we rewrite and resubmit the apps, they are considered irrecoverable.
|
||||
We continue to experience issues updating our sing-box apps on the App Store and Play Store.
|
||||
Until we rewrite and resubmit the apps, they are considered irrecoverable.
|
||||
Therefore, after this release, we will not be repeating this notice unless there is new information.
|
||||
|
||||
### 1.11.15
|
||||
@@ -787,8 +490,7 @@ See [AnyTLS Inbound](/configuration/inbound/anytls/) and [AnyTLS Outbound](/conf
|
||||
|
||||
**2**:
|
||||
|
||||
`resolve` route action now accepts `disable_cache` and other options like in DNS route actions,
|
||||
see [Route Action](/configuration/route/rule_action).
|
||||
`resolve` route action now accepts `disable_cache` and other options like in DNS route actions, see [Route Action](/configuration/route/rule_action).
|
||||
|
||||
**3**:
|
||||
|
||||
@@ -819,8 +521,7 @@ See [Tailscale](/configuration/endpoint/tailscale/).
|
||||
|
||||
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
||||
|
||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches
|
||||
from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||
|
||||
### 1.11.3
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ platform-specific function implementation, such as TUN transparent proxy impleme
|
||||
|
||||
!!! failure ""
|
||||
|
||||
Due to non-technical reasons, we are temporarily unable to update the sing-box app on the App Store and release the standalone version of the macOS client (TestFlight users are not affected)
|
||||
We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected).
|
||||
|
||||
## :material-graph: Requirements
|
||||
|
||||
@@ -18,7 +18,7 @@ platform-specific function implementation, such as TUN transparent proxy impleme
|
||||
|
||||
## :material-download: Download
|
||||
|
||||
* ~~[App Store](https://apps.apple.com/app/sing-box-vt/id6673731168)~~
|
||||
* [App Store](https://apps.apple.com/app/sing-box-vt/id6673731168)
|
||||
* TestFlight (Beta)
|
||||
|
||||
TestFlight quota is only available to [sponsors](https://github.com/sponsors/nekohasekai)
|
||||
@@ -26,15 +26,15 @@ TestFlight quota is only available to [sponsors](https://github.com/sponsors/nek
|
||||
Once you donate, you can get an invitation by join our Telegram group for sponsors from [@yet_another_sponsor_bot](https://t.me/yet_another_sponsor_bot)
|
||||
or sending us your Apple ID [via email](mailto:contact@sagernet.org).
|
||||
|
||||
## ~~:material-file-download: Download (macOS standalone version)~~
|
||||
## :material-file-download: Download (macOS standalone version)
|
||||
|
||||
* ~~[Homebrew Cask](https://formulae.brew.sh/cask/sfm)~~
|
||||
* [Homebrew Cask](https://formulae.brew.sh/cask/sfm)
|
||||
|
||||
```bash
|
||||
# brew install sfm
|
||||
brew install sfm
|
||||
```
|
||||
|
||||
* ~~[GitHub Releases](https://github.com/SagerNet/sing-box/releases)~~
|
||||
* [GitHub Releases](https://github.com/SagerNet/sing-box/releases)
|
||||
|
||||
## :material-source-repository: Source code
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@ icon: material/new-box
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [Chrome Root Store](#store)
|
||||
|
||||
# Certificate
|
||||
|
||||
### Structure
|
||||
@@ -31,12 +27,11 @@ icon: material/new-box
|
||||
|
||||
The default X509 trusted CA certificate list.
|
||||
|
||||
| Type | Description |
|
||||
|--------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| `system` (default) | System trusted CA certificates |
|
||||
| Type | Description |
|
||||
|--------------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| `system` (default) | System trusted CA certificates |
|
||||
| `mozilla` | [Mozilla Included List](https://wiki.mozilla.org/CA/Included_Certificates) with China CA certificates removed |
|
||||
| `chrome` | [Chrome Root Store](https://g.co/chrome/root-policy) with China CA certificates removed |
|
||||
| `none` | Empty list |
|
||||
| `none` | Empty list |
|
||||
|
||||
#### certificate
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@ icon: material/new-box
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [Chrome Root Store](#store)
|
||||
|
||||
# 证书
|
||||
|
||||
### 结构
|
||||
@@ -31,12 +27,11 @@ icon: material/new-box
|
||||
|
||||
默认的 X509 受信任 CA 证书列表。
|
||||
|
||||
| 类型 | 描述 |
|
||||
|-------------------|--------------------------------------------------------------------------------------------|
|
||||
| `system`(默认) | 系统受信任的 CA 证书 |
|
||||
| `mozilla` | [Mozilla 包含列表](https://wiki.mozilla.org/CA/Included_Certificates)(已移除中国 CA 证书) |
|
||||
| `chrome` | [Chrome Root Store](https://g.co/chrome/root-policy)(已移除中国 CA 证书) |
|
||||
| `none` | 空列表 |
|
||||
| 类型 | 描述 |
|
||||
|--------------------|--------------------------------------------------------------------------------------------|
|
||||
| `system`(默认) | 系统受信任的 CA 证书 |
|
||||
| `mozilla` | [Mozilla 包含列表](https://wiki.mozilla.org/CA/Included_Certificates)(已移除中国 CA 证书) |
|
||||
| `none` | 空列表 |
|
||||
|
||||
#### certificate
|
||||
|
||||
|
||||
@@ -412,7 +412,7 @@ Match default interface address.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms, or on Linux.
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match WiFi SSID.
|
||||
|
||||
@@ -420,7 +420,7 @@ Match WiFi SSID.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms, or on Linux.
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
Match WiFi BSSID.
|
||||
|
||||
|
||||
@@ -411,7 +411,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端和 Linux 中支持。
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配 WiFi SSID。
|
||||
|
||||
@@ -419,7 +419,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端和 Linux 中支持。
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
|
||||
匹配 WiFi BSSID。
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ DNS 服务器的地址。
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` 或 `dhcp://en0` |
|
||||
| [FakeIP](/zh/configuration/dns/fakeip/) | `fakeip` |
|
||||
| [FakeIP](/configuration/dns/fakeip/) | `fakeip` |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ icon: material/new-box
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [prefer_go](#prefer_go)
|
||||
:material-plus: [prefer_go](#prefer_go)
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [relay_server_port](#relay_server_port)
|
||||
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
|
||||
:material-plus: [system_interface](#system_interface)
|
||||
:material-plus: [system_interface_name](#system_interface_name)
|
||||
:material-plus: [system_interface_mtu](#system_interface_mtu)
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
### Structure
|
||||
@@ -28,13 +20,8 @@ icon: material/new-box
|
||||
"exit_node_allow_lan_access": false,
|
||||
"advertise_routes": [],
|
||||
"advertise_exit_node": false,
|
||||
"relay_server_port": 0,
|
||||
"relay_server_static_endpoints": [],
|
||||
"system_interface": false,
|
||||
"system_interface_name": "",
|
||||
"system_interface_mtu": 0,
|
||||
"udp_timeout": "5m",
|
||||
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
```
|
||||
@@ -102,36 +89,6 @@ Example: `["192.168.1.1/24"]`
|
||||
|
||||
Indicates whether the node should advertise itself as an exit node.
|
||||
|
||||
#### relay_server_port
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
The port to listen on for incoming relay connections from other Tailscale nodes.
|
||||
|
||||
#### relay_server_static_endpoints
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
Static endpoints to advertise for the relay server.
|
||||
|
||||
#### system_interface
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
Create a system TUN interface for Tailscale.
|
||||
|
||||
#### system_interface_name
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
Custom TUN interface name. By default, `tailscale` (or `utun` on macOS) will be used.
|
||||
|
||||
#### system_interface_mtu
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
Override the TUN MTU. By default, Tailscale's own MTU is used.
|
||||
|
||||
#### udp_timeout
|
||||
|
||||
UDP NAT expiration time.
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [relay_server_port](#relay_server_port)
|
||||
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
|
||||
:material-plus: [system_interface](#system_interface)
|
||||
:material-plus: [system_interface_name](#system_interface_name)
|
||||
:material-plus: [system_interface_mtu](#system_interface_mtu)
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
### 结构
|
||||
@@ -28,11 +20,6 @@ icon: material/new-box
|
||||
"exit_node_allow_lan_access": false,
|
||||
"advertise_routes": [],
|
||||
"advertise_exit_node": false,
|
||||
"relay_server_port": 0,
|
||||
"relay_server_static_endpoints": [],
|
||||
"system_interface": false,
|
||||
"system_interface_name": "",
|
||||
"system_interface_mtu": 0,
|
||||
"udp_timeout": "5m",
|
||||
|
||||
... // 拨号字段
|
||||
@@ -101,36 +88,6 @@ icon: material/new-box
|
||||
|
||||
指示节点是否应将自己通告为出口节点。
|
||||
|
||||
#### relay_server_port
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
监听来自其他 Tailscale 节点的中继连接的端口。
|
||||
|
||||
#### relay_server_static_endpoints
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
为中继服务器通告的静态端点。
|
||||
|
||||
#### system_interface
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
为 Tailscale 创建系统 TUN 接口。
|
||||
|
||||
#### system_interface_name
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
自定义 TUN 接口名。默认使用 `tailscale`(macOS 上为 `utun`)。
|
||||
|
||||
#### system_interface_mtu
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
覆盖 TUN 的 MTU。默认使用 Tailscale 自己的 MTU。
|
||||
|
||||
#### udp_timeout
|
||||
|
||||
UDP NAT 过期时间。
|
||||
@@ -143,4 +100,4 @@ UDP NAT 过期时间。
|
||||
|
||||
Tailscale 端点中的拨号字段仅控制它如何连接到控制平面,与实际连接无关。
|
||||
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||
@@ -3,7 +3,7 @@
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [store_rdrc](#store_rdrc)
|
||||
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||
|
||||
### Structure
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [store_rdrc](#store_rdrc)
|
||||
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||
|
||||
### 结构
|
||||
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [quic_congestion_control](#quic_congestion_control)
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "naive",
|
||||
"tag": "naive-in",
|
||||
"network": "udp",
|
||||
...
|
||||
// Listen Fields
|
||||
"type": "naive",
|
||||
"tag": "naive-in",
|
||||
"network": "udp",
|
||||
|
||||
"users": [
|
||||
{
|
||||
"username": "sekai",
|
||||
"password": "password"
|
||||
}
|
||||
],
|
||||
"quic_congestion_control": "",
|
||||
"tls": {}
|
||||
... // Listen Fields
|
||||
|
||||
"users": [
|
||||
{
|
||||
"username": "sekai",
|
||||
"password": "password"
|
||||
}
|
||||
],
|
||||
"tls": {}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -41,23 +36,6 @@ Both if empty.
|
||||
|
||||
Naive users.
|
||||
|
||||
#### quic_congestion_control
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
QUIC congestion control algorithm.
|
||||
|
||||
| Algorithm | Description |
|
||||
|----------------|---------------------------------|
|
||||
| `bbr` | BBR |
|
||||
| `bbr_standard` | BBR (Standard version) |
|
||||
| `bbr2` | BBRv2 |
|
||||
| `bbr2_variant` | BBRv2 (An experimental variant) |
|
||||
| `cubic` | CUBIC |
|
||||
| `reno` | New Reno |
|
||||
|
||||
`bbr` is used by default (the default of QUICHE, used by Chromium which NaiveProxy is based on).
|
||||
|
||||
#### tls
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
@@ -1,25 +1,20 @@
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [quic_congestion_control](#quic_congestion_control)
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "naive",
|
||||
"tag": "naive-in",
|
||||
"network": "udp",
|
||||
"type": "naive",
|
||||
"tag": "naive-in",
|
||||
"network": "udp",
|
||||
|
||||
... // 监听字段
|
||||
... // 监听字段
|
||||
|
||||
"users": [
|
||||
{
|
||||
"username": "sekai",
|
||||
"password": "password"
|
||||
}
|
||||
],
|
||||
"quic_congestion_control": "",
|
||||
"tls": {}
|
||||
"users": [
|
||||
{
|
||||
"username": "sekai",
|
||||
"password": "password"
|
||||
}
|
||||
],
|
||||
"tls": {}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -41,23 +36,6 @@
|
||||
|
||||
Naive 用户。
|
||||
|
||||
#### quic_congestion_control
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
QUIC 拥塞控制算法。
|
||||
|
||||
| 算法 | 描述 |
|
||||
|----------------|--------------------|
|
||||
| `bbr` | BBR |
|
||||
| `bbr_standard` | BBR (标准版) |
|
||||
| `bbr2` | BBRv2 |
|
||||
| `bbr2_variant` | BBRv2 (一种试验变体) |
|
||||
| `cubic` | CUBIC |
|
||||
| `reno` | New Reno |
|
||||
|
||||
默认使用 `bbr`(NaiveProxy 基于的 Chromium 使用的 QUICHE 的默认值)。
|
||||
|
||||
#### tls
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"managed": false,
|
||||
"multiplex": {}
|
||||
}
|
||||
```
|
||||
@@ -87,10 +86,6 @@ Both if empty.
|
||||
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
|
||||
| other methods | any string |
|
||||
|
||||
#### managed
|
||||
|
||||
Defaults to `false`. Enable this when the inbound is managed by the [SSM API](/configuration/service/ssm-api) for dynamic user.
|
||||
|
||||
#### multiplex
|
||||
|
||||
See [Multiplex](/configuration/shared/multiplex#inbound) for details.
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"managed": false,
|
||||
"multiplex": {}
|
||||
}
|
||||
```
|
||||
@@ -49,9 +48,9 @@
|
||||
}
|
||||
```
|
||||
|
||||
### 监听字段
|
||||
### Listen Fields
|
||||
|
||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||
See [Listen Fields](/configuration/shared/listen/) for details.
|
||||
|
||||
### 字段
|
||||
|
||||
@@ -87,10 +86,6 @@
|
||||
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
|
||||
| other methods | 任意字符串 |
|
||||
|
||||
#### managed
|
||||
|
||||
默认为 `false`。当该入站需要由 [SSM API](/zh/configuration/service/ssm-api) 管理用户时必须启用此字段。
|
||||
|
||||
#### multiplex
|
||||
|
||||
参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.13.0"
|
||||
|
||||
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
|
||||
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
|
||||
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
||||
|
||||
!!! quote "Changes in sing-box 1.12.0"
|
||||
|
||||
:material-plus: [loopback_address](#loopback_address)
|
||||
@@ -40,7 +34,7 @@ icon: material/new-box
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [platform.http_proxy.bypass_domain](#platformhttp_proxybypass_domain)
|
||||
:material-plus: [platform.http_proxy.match_domain](#platformhttp_proxymatch_domain)
|
||||
:material-plus: [platform.http_proxy.match_domain](#platformhttp_proxymatch_domain)
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
@@ -69,9 +63,6 @@ icon: material/new-box
|
||||
"auto_redirect": true,
|
||||
"auto_redirect_input_mark": "0x2023",
|
||||
"auto_redirect_output_mark": "0x2024",
|
||||
"auto_redirect_reset_mark": "0x2025",
|
||||
"auto_redirect_nfqueue": 100,
|
||||
"exclude_mptcp": false,
|
||||
"loopback_address": [
|
||||
"10.7.0.1"
|
||||
],
|
||||
@@ -287,36 +278,6 @@ Connection output mark used by `auto_redirect`.
|
||||
|
||||
`0x2024` is used by default.
|
||||
|
||||
#### auto_redirect_reset_mark
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
Connection reset mark used by `auto_redirect` pre-matching.
|
||||
|
||||
`0x2025` is used by default.
|
||||
|
||||
#### auto_redirect_nfqueue
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
NFQueue number used by `auto_redirect` pre-matching.
|
||||
|
||||
`100` is used by default.
|
||||
|
||||
#### exclude_mptcp
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux with nftables and requires `auto_route` and `auto_redirect` enabled.
|
||||
|
||||
MPTCP cannot be transparently proxied due to protocol limitations.
|
||||
|
||||
Such traffic is usually created by Apple systems.
|
||||
|
||||
When enabled, MPTCP connections will bypass sing-box and connect directly, otherwise, will be rejected to avoid errors by default.
|
||||
|
||||
#### loopback_address
|
||||
|
||||
!!! question "Since sing-box 1.12.0"
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.13.0 中的更改"
|
||||
|
||||
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
|
||||
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
|
||||
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
||||
|
||||
!!! quote "sing-box 1.12.0 中的更改"
|
||||
|
||||
:material-plus: [loopback_address](#loopback_address)
|
||||
@@ -28,7 +22,7 @@ icon: material/new-box
|
||||
:material-delete-clock: [inet6_route_address](#inet6_route_address)
|
||||
:material-plus: [route_exclude_address](#route_address)
|
||||
:material-delete-clock: [inet4_route_exclude_address](#inet4_route_exclude_address)
|
||||
:material-delete-clock: [inet6_route_exclude_address](#inet6_route_exclude_address)
|
||||
:material-delete-clock: [inet6_route_exclude_address](#inet6_route_exclude_address)
|
||||
:material-plus: [iproute2_table_index](#iproute2_table_index)
|
||||
:material-plus: [iproute2_rule_index](#iproute2_table_index)
|
||||
:material-plus: [auto_redirect](#auto_redirect)
|
||||
@@ -40,7 +34,7 @@ icon: material/new-box
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [platform.http_proxy.bypass_domain](#platformhttp_proxybypass_domain)
|
||||
:material-plus: [platform.http_proxy.match_domain](#platformhttp_proxymatch_domain)
|
||||
:material-plus: [platform.http_proxy.match_domain](#platformhttp_proxymatch_domain)
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
@@ -69,9 +63,6 @@ icon: material/new-box
|
||||
"auto_redirect": true,
|
||||
"auto_redirect_input_mark": "0x2023",
|
||||
"auto_redirect_output_mark": "0x2024",
|
||||
"auto_redirect_reset_mark": "0x2025",
|
||||
"auto_redirect_nfqueue": 100,
|
||||
"exclude_mptcp": false,
|
||||
"loopback_address": [
|
||||
"10.7.0.1"
|
||||
],
|
||||
@@ -286,36 +277,6 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
默认使用 `0x2024`。
|
||||
|
||||
#### auto_redirect_reset_mark
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
`auto_redirect` 预匹配使用的连接重置标记。
|
||||
|
||||
默认使用 `0x2025`。
|
||||
|
||||
#### auto_redirect_nfqueue
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
`auto_redirect` 预匹配使用的 NFQueue 编号。
|
||||
|
||||
默认使用 `100`。
|
||||
|
||||
#### exclude_mptcp
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux,且需要 nftables,`auto_route` 和 `auto_redirect` 已启用。
|
||||
|
||||
由于协议限制,MPTCP 无法被透明代理。
|
||||
|
||||
此类流量通常由 Apple 系统创建。
|
||||
|
||||
启用时,MPTCP 连接将绕过 sing-box 直接连接,否则,将被拒绝以避免错误。
|
||||
|
||||
#### loopback_address
|
||||
|
||||
!!! question "自 sing-box 1.12.0 起"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user