Compare commits
27 Commits
dev-ts-rel
...
v1.13.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bb28ff4f1 | ||
|
|
35fc76f2c9 | ||
|
|
dcbf54e1ec | ||
|
|
a28ab43498 | ||
|
|
6e5b9922de | ||
|
|
e366c7e2fd | ||
|
|
ff420c86fa | ||
|
|
6cad62af81 | ||
|
|
4a9dbe540a | ||
|
|
fc9d5bb519 | ||
|
|
ba9c4bb1b7 | ||
|
|
facf0d5eae | ||
|
|
a463d1250e | ||
|
|
dabf180add | ||
|
|
4e4117cd69 | ||
|
|
042ca947c0 | ||
|
|
973e016605 | ||
|
|
14939be4bf | ||
|
|
3478f08ca5 | ||
|
|
f50259934c | ||
|
|
f1a8ac9a62 | ||
|
|
685c83c923 | ||
|
|
c8990aae7f | ||
|
|
289705d8a3 | ||
|
|
f845099c8d | ||
|
|
9a774ae8d6 | ||
|
|
864ad66e88 |
1
.github/CRONET_GO_VERSION
vendored
1
.github/CRONET_GO_VERSION
vendored
@@ -1 +0,0 @@
|
|||||||
1cc61ad20399081362ccbc18d650432d1a6d42ec
|
|
||||||
@@ -1,27 +1,25 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="1.25.5"
|
VERSION="1.23.12"
|
||||||
|
|
||||||
mkdir -p $HOME/go
|
mkdir -p $HOME/go
|
||||||
cd $HOME/go
|
cd $HOME/go
|
||||||
wget "https://dl.google.com/go/go${VERSION}.linux-amd64.tar.gz"
|
wget "https://dl.google.com/go/go${VERSION}.linux-amd64.tar.gz"
|
||||||
tar -xzf "go${VERSION}.linux-amd64.tar.gz"
|
tar -xzf "go${VERSION}.linux-amd64.tar.gz"
|
||||||
mv go go_win7
|
mv go go_legacy
|
||||||
cd go_win7
|
cd go_legacy
|
||||||
|
|
||||||
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
|
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
|
||||||
# this patch file only works on golang1.25.x
|
# this patch file only works on golang1.23.x
|
||||||
# that means after golang1.26 release it must be changed
|
# that means after golang1.24 release it must be changed
|
||||||
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.25/
|
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/
|
||||||
# revert:
|
# revert:
|
||||||
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
|
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
|
||||||
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
|
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
|
||||||
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
|
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
|
||||||
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
|
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
|
||||||
|
|
||||||
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
|
curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1
|
||||||
|
curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1
|
||||||
curl https://github.com/MetaCubeX/go/commit/8cb5472d94c34b88733a81091bd328e70ee565a4.diff | patch --verbose -p 1
|
curl https://github.com/MetaCubeX/go/commit/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1
|
||||||
curl https://github.com/MetaCubeX/go/commit/6788c4c6f9fafb56729bad6b660f7ee2272d699f.diff | patch --verbose -p 1
|
curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1
|
||||||
curl https://github.com/MetaCubeX/go/commit/a5b2168bb836ed9d6601c626f95e56c07923f906.diff | patch --verbose -p 1
|
|
||||||
curl https://github.com/MetaCubeX/go/commit/f56f1e23507e646c85243a71bde7b9629b2f970c.diff | patch --verbose -p 1
|
|
||||||
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"
|
|
||||||
383
.github/workflows/build.yml
vendored
383
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.1
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -69,25 +69,13 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- { os: linux, arch: amd64, variant: purego, naive: true, openwrt: "x86_64" }
|
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
||||||
- { os: linux, arch: amd64, variant: glibc, naive: true }
|
- { os: linux, arch: "386", go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
||||||
- { 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, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
|
||||||
- { 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, openwrt: "i386_pentium4" }
|
|
||||||
- { 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", 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: 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: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
|
- { 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: "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: "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: 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: hardfloat, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc_24kf" }
|
||||||
- { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
|
- { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
|
||||||
@@ -99,90 +87,58 @@ jobs:
|
|||||||
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
|
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
|
||||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||||
|
|
||||||
- { os: windows, arch: amd64, legacy_win7: true, legacy_name: "windows-7" }
|
- { os: windows, arch: amd64 }
|
||||||
- { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
|
- { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" }
|
||||||
|
- { os: windows, arch: "386" }
|
||||||
|
- { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" }
|
||||||
|
- { os: windows, arch: arm64 }
|
||||||
|
|
||||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android23" }
|
- { os: darwin, arch: amd64 }
|
||||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi23" }
|
- { os: darwin, arch: arm64 }
|
||||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android23" }
|
- { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||||
- { os: android, arch: "386", ndk: "i686-linux-android23" }
|
|
||||||
|
- { 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:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
|
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.1
|
||||||
- name: Setup Go 1.24
|
- name: Setup Go 1.24
|
||||||
if: matrix.legacy_go124
|
if: matrix.legacy_go124
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.24.10
|
go-version: ~1.24.6
|
||||||
- name: Cache Go for Windows 7
|
- name: Cache Go 1.23
|
||||||
if: matrix.legacy_win7
|
if: matrix.legacy_go123
|
||||||
id: cache-go-for-windows7
|
id: cache-legacy-go
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/go_win7
|
~/go/go_legacy
|
||||||
key: go_win7_1255
|
key: go_legacy_12312
|
||||||
- name: Setup Go for Windows 7
|
- name: Setup Go 1.23
|
||||||
if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
|
if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||||
run: |-
|
run: |-
|
||||||
.github/setup_go_for_windows7.sh
|
.github/setup_legacy_go.sh
|
||||||
- name: Setup Go for Windows 7
|
- name: Setup Go 1.23
|
||||||
if: matrix.legacy_win7
|
if: matrix.legacy_go123
|
||||||
run: |-
|
run: |-
|
||||||
echo "PATH=$HOME/go/go_win7/bin:$PATH" >> $GITHUB_ENV
|
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
|
||||||
echo "GOROOT=$HOME/go/go_win7" >> $GITHUB_ENV
|
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
if: matrix.os == 'android'
|
if: matrix.os == 'android'
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r28
|
ndk-version: r28
|
||||||
local-cache: true
|
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
|
- name: Set tag
|
||||||
run: |-
|
run: |-
|
||||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||||
@@ -190,70 +146,10 @@ jobs:
|
|||||||
- name: Set build tags
|
- name: Set build tags
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
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'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
||||||
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
|
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build (purego)
|
- name: Build
|
||||||
if: matrix.variant == 'purego'
|
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 }} -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 }} -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 }} -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: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
@@ -297,11 +193,6 @@ jobs:
|
|||||||
elif [[ -n "${{ matrix.legacy_name }}" ]]; then
|
elif [[ -n "${{ matrix.legacy_name }}" ]]; then
|
||||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
||||||
fi
|
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}"
|
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
||||||
PKG_VERSION="${PKG_VERSION//-/\~}"
|
PKG_VERSION="${PKG_VERSION//-/\~}"
|
||||||
@@ -369,12 +260,8 @@ jobs:
|
|||||||
-p "dist/openwrt.deb" \
|
-p "dist/openwrt.deb" \
|
||||||
--architecture all \
|
--architecture all \
|
||||||
dist/sing-box=/usr/bin/sing-box
|
dist/sing-box=/usr/bin/sing-box
|
||||||
SUFFIX=""
|
|
||||||
if [[ "${{ matrix.variant }}" == "musl" ]]; then
|
|
||||||
SUFFIX="_musl"
|
|
||||||
fi
|
|
||||||
for architecture in ${{ matrix.openwrt }}; do
|
for architecture in ${{ matrix.openwrt }}; do
|
||||||
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}${SUFFIX}.ipk"
|
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
|
||||||
done
|
done
|
||||||
rm "dist/openwrt.deb"
|
rm "dist/openwrt.deb"
|
||||||
- name: Archive
|
- name: Archive
|
||||||
@@ -388,177 +275,15 @@ jobs:
|
|||||||
zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
|
zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
|
||||||
else
|
else
|
||||||
cp sing-box "${DIR_NAME}"
|
cp sing-box "${DIR_NAME}"
|
||||||
if [ -f libcronet.so ]; then
|
|
||||||
cp libcronet.so "${DIR_NAME}"
|
|
||||||
fi
|
|
||||||
tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
|
tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
|
||||||
fi
|
fi
|
||||||
rm -r "${DIR_NAME}"
|
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 }} -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
|
- name: Cleanup
|
||||||
run: rm dist/sing-box
|
run: rm dist/sing-box
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: binary-darwin_${{ matrix.arch }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_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 }} -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 }} -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 }}
|
|
||||||
path: "dist"
|
path: "dist"
|
||||||
build_android:
|
build_android:
|
||||||
name: Build Android
|
name: Build Android
|
||||||
@@ -575,7 +300,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.1
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -623,9 +348,9 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: |-
|
run: |-
|
||||||
mkdir clients/android/app/libs
|
mkdir clients/android/app/libs
|
||||||
cp *.aar clients/android/app/libs
|
cp libbox.aar clients/android/app/libs
|
||||||
cd clients/android
|
cd clients/android
|
||||||
./gradlew :app:assembleOtherRelease :app:assembleOtherLegacyRelease
|
./gradlew :app:assemblePlayRelease :app:assembleOtherRelease
|
||||||
env:
|
env:
|
||||||
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
@@ -633,18 +358,8 @@ jobs:
|
|||||||
- name: Prepare upload
|
- name: Prepare upload
|
||||||
run: |-
|
run: |-
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
#cp clients/android/app/build/outputs/apk/play/release/*.apk 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/other/release/*-universal.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
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -665,7 +380,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.1
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -706,7 +421,7 @@ jobs:
|
|||||||
run: |-
|
run: |-
|
||||||
go run -v ./cmd/internal/update_android_version --ci
|
go run -v ./cmd/internal/update_android_version --ci
|
||||||
mkdir clients/android/app/libs
|
mkdir clients/android/app/libs
|
||||||
cp *.aar clients/android/app/libs
|
cp libbox.aar clients/android/app/libs
|
||||||
cd clients/android
|
cd clients/android
|
||||||
echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
|
echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
|
||||||
./gradlew :app:publishPlayReleaseBundle
|
./gradlew :app:publishPlayReleaseBundle
|
||||||
@@ -718,7 +433,7 @@ jobs:
|
|||||||
build_apple:
|
build_apple:
|
||||||
name: Build Apple clients
|
name: Build Apple clients
|
||||||
runs-on: macos-26
|
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:
|
needs:
|
||||||
- calculate_version
|
- calculate_version
|
||||||
strategy:
|
strategy:
|
||||||
@@ -764,7 +479,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.1
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
if: matrix.if
|
if: matrix.if
|
||||||
run: |-
|
run: |-
|
||||||
@@ -883,7 +598,7 @@ jobs:
|
|||||||
--app-drop-link 0 0 \
|
--app-drop-link 0 0 \
|
||||||
--skip-jenkins \
|
--skip-jenkins \
|
||||||
SFM.dmg "${{ matrix.export_path }}/SFM.app"
|
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 }}"
|
cd "${{ matrix.archive }}"
|
||||||
zip -r SFM.dSYMs.zip dSYMs
|
zip -r SFM.dSYMs.zip dSYMs
|
||||||
popd
|
popd
|
||||||
@@ -904,8 +619,6 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- calculate_version
|
- calculate_version
|
||||||
- build
|
- build
|
||||||
- build_darwin
|
|
||||||
- build_windows
|
|
||||||
- build_android
|
- build_android
|
||||||
- build_apple
|
- build_apple
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
149
.github/workflows/docker.yml
vendored
149
.github/workflows/docker.yml
vendored
@@ -1,10 +1,6 @@
|
|||||||
name: Publish Docker Images
|
name: Publish Docker Images
|
||||||
|
|
||||||
on:
|
on:
|
||||||
#push:
|
|
||||||
# branches:
|
|
||||||
# - main-next
|
|
||||||
# - dev-next
|
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
- published
|
- published
|
||||||
@@ -17,134 +13,8 @@ env:
|
|||||||
REGISTRY_IMAGE: ghcr.io/sagernet/sing-box
|
REGISTRY_IMAGE: ghcr.io/sagernet/sing-box
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_binary:
|
build:
|
||||||
name: Build binary
|
|
||||||
runs-on: ubuntu-latest
|
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}\" -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}\" -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:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
@@ -177,16 +47,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
platform=${{ matrix.platform }}
|
platform=${{ matrix.platform }}
|
||||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
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
|
- name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Setup Docker Buildx
|
- name: Setup Docker Buildx
|
||||||
@@ -208,7 +68,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
context: .
|
context: .
|
||||||
file: Dockerfile.binary
|
build-args: |
|
||||||
|
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||||
- name: Export digest
|
- name: Export digest
|
||||||
@@ -226,7 +87,7 @@ jobs:
|
|||||||
merge:
|
merge:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- build_docker
|
- build
|
||||||
steps:
|
steps:
|
||||||
- name: Get commit to build
|
- name: Get commit to build
|
||||||
id: ref
|
id: ref
|
||||||
@@ -260,7 +121,6 @@ jobs:
|
|||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Create manifest list and push
|
- name: Create manifest list and push
|
||||||
if: github.event_name != 'push'
|
|
||||||
working-directory: /tmp/digests
|
working-directory: /tmp/digests
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create \
|
docker buildx imagetools create \
|
||||||
@@ -268,7 +128,6 @@ jobs:
|
|||||||
-t "${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}" \
|
-t "${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}" \
|
||||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
||||||
- name: Inspect image
|
- name: Inspect image
|
||||||
if: github.event_name != 'push'
|
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.latest }}
|
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.latest }}
|
||||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}
|
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
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
with:
|
with:
|
||||||
version: v2.4.0
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
install-mode: binary
|
install-mode: binary
|
||||||
verify: false
|
verify: false
|
||||||
|
|||||||
76
.github/workflows/linux.yml
vendored
76
.github/workflows/linux.yml
vendored
@@ -1,10 +1,6 @@
|
|||||||
name: Build Linux Packages
|
name: Build Linux Packages
|
||||||
|
|
||||||
on:
|
on:
|
||||||
#push:
|
|
||||||
# branches:
|
|
||||||
# - main-next
|
|
||||||
# - dev-next
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
@@ -34,7 +30,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.1
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -56,13 +52,11 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Naive-enabled builds (musl)
|
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
||||||
- { os: linux, arch: amd64, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
- { os: linux, arch: "386", debian: i386, rpm: i386 }
|
||||||
- { 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: arm, goarm: "6", debian: armel, rpm: armv6hl }
|
- { 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: mips64le, debian: mips64el, rpm: mips64el }
|
||||||
- { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel }
|
- { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel }
|
||||||
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
||||||
@@ -77,38 +71,13 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.1
|
||||||
- name: Clone cronet-go
|
- name: Setup Android NDK
|
||||||
if: matrix.naive
|
if: matrix.os == 'android'
|
||||||
run: |
|
uses: nttld/setup-ndk@v1
|
||||||
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:
|
with:
|
||||||
path: |
|
ndk-version: r28
|
||||||
~/cronet-go/naiveproxy/src/third_party/llvm-build/Release+Asserts
|
local-cache: true
|
||||||
~/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
|
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
run: |-
|
run: |-
|
||||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||||
@@ -116,27 +85,9 @@ jobs:
|
|||||||
- name: Set build tags
|
- name: Set build tags
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
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'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
||||||
if [[ "${{ matrix.naive }}" == "true" ]]; then
|
|
||||||
TAGS="${TAGS},with_naive_outbound,with_musl"
|
|
||||||
fi
|
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build (naive)
|
- name: Build
|
||||||
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 }} -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: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
@@ -234,6 +185,5 @@ jobs:
|
|||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
- name: Publish packages
|
- name: Publish packages
|
||||||
if: github.event_name != 'push'
|
|
||||||
run: |-
|
run: |-
|
||||||
ls dist | xargs -I {} curl -F "package=@dist/{}" https://${{ secrets.FURY_TOKEN }}@push.fury.io/sagernet/
|
ls dist | xargs -I {} curl -F "package=@dist/{}" https://${{ secrets.FURY_TOKEN }}@push.fury.io/sagernet/
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,6 +15,4 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
/config.d/
|
/config.d/
|
||||||
/venv/
|
/venv/
|
||||||
CLAUDE.md
|
|
||||||
AGENTS.md
|
|
||||||
/.claude/
|
|
||||||
|
|||||||
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 COMMIT=$(git rev-parse --short HEAD) \
|
||||||
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
||||||
&& go build -v -trimpath -tags \
|
&& go build -v -trimpath -tags \
|
||||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" \
|
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale" \
|
||||||
-o /go/bin/sing-box \
|
-o /go/bin/sing-box \
|
||||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
FROM --platform=$TARGETPLATFORM alpine AS dist
|
FROM --platform=$TARGETPLATFORM alpine AS dist
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
RUN set -ex \
|
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
|
COPY --from=builder /go/bin/sing-box /usr/local/bin/sing-box
|
||||||
ENTRYPOINT ["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"]
|
|
||||||
13
Makefile
13
Makefile
@@ -1,6 +1,6 @@
|
|||||||
NAME = sing-box
|
NAME = sing-box
|
||||||
COMMIT = $(shell git rev-parse --short HEAD)
|
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
|
||||||
|
|
||||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||||
@@ -37,11 +37,8 @@ fmt:
|
|||||||
@gofmt -s -w .
|
@gofmt -s -w .
|
||||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||||
|
|
||||||
fmt_docs:
|
|
||||||
go run ./cmd/internal/format_docs
|
|
||||||
|
|
||||||
fmt_install:
|
fmt_install:
|
||||||
go install -v mvdan.cc/gofumpt@v0.8.0
|
go install -v mvdan.cc/gofumpt@latest
|
||||||
go install -v github.com/daixiang0/gci@latest
|
go install -v github.com/daixiang0/gci@latest
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@@ -52,7 +49,7 @@ lint:
|
|||||||
GOOS=freebsd golangci-lint run ./...
|
GOOS=freebsd golangci-lint run ./...
|
||||||
|
|
||||||
lint_install:
|
lint_install:
|
||||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
@go run ./cmd/internal/protogen
|
@go run ./cmd/internal/protogen
|
||||||
@@ -252,8 +249,8 @@ lib:
|
|||||||
go run ./cmd/internal/build_libbox -target ios
|
go run ./cmd/internal/build_libbox -target ios
|
||||||
|
|
||||||
lib_install:
|
lib_install:
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.10
|
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.8
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.10
|
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
venv/bin/mkdocs serve
|
venv/bin/mkdocs serve
|
||||||
|
|||||||
@@ -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
|
# sing-box
|
||||||
|
|
||||||
The universal proxy platform.
|
The universal proxy platform.
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ type DNSClient interface {
|
|||||||
Start()
|
Start()
|
||||||
Exchange(ctx context.Context, transport DNSTransport, message *dns.Msg, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error)
|
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)
|
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()
|
ClearCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +70,6 @@ type DNSTransport interface {
|
|||||||
Type() string
|
Type() string
|
||||||
Tag() string
|
Tag() string
|
||||||
Dependencies() []string
|
Dependencies() []string
|
||||||
Reset()
|
|
||||||
Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error)
|
Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||||
@@ -12,7 +11,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.EndpointManager = (*Manager)(nil)
|
var _ adapter.EndpointManager = (*Manager)(nil)
|
||||||
@@ -48,14 +46,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, endpoint := range m.endpoints {
|
for _, endpoint := range m.endpoints {
|
||||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err := adapter.LegacyStart(endpoint, stage)
|
err := adapter.LegacyStart(endpoint, stage)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@@ -72,15 +66,11 @@ func (m *Manager) Close() error {
|
|||||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||||
var err error
|
var err error
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
monitor.Start("close endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||||
m.logger.Trace("close ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
monitor.Start("close ", name)
|
|
||||||
err = E.Append(err, endpoint.Close(), func(err error) error {
|
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()
|
monitor.Finish()
|
||||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -129,15 +119,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
|||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
if m.started {
|
if m.started {
|
||||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
|
||||||
for _, stage := range adapter.ListStartStages {
|
for _, stage := range adapter.ListStartStages {
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err = adapter.LegacyStart(endpoint, stage)
|
err = adapter.LegacyStart(endpoint, stage)
|
||||||
if err != nil {
|
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 {
|
if existsEndpoint, loaded := m.endpointByTag[tag]; loaded {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/observable"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,7 +14,6 @@ type ClashServer interface {
|
|||||||
ConnectionTracker
|
ConnectionTracker
|
||||||
Mode() string
|
Mode() string
|
||||||
ModeList() []string
|
ModeList() []string
|
||||||
SetModeUpdateHook(hook *observable.Subscriber[struct{}])
|
|
||||||
HistoryStorage() URLTestHistoryStorage
|
HistoryStorage() URLTestHistoryStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +23,7 @@ type URLTestHistory struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type URLTestHistoryStorage interface {
|
type URLTestHistoryStorage interface {
|
||||||
SetHook(hook *observable.Subscriber[struct{}])
|
SetHook(hook chan<- struct{})
|
||||||
LoadURLTestHistory(tag string) *URLTestHistory
|
LoadURLTestHistory(tag string) *URLTestHistory
|
||||||
DeleteURLTestHistory(tag string)
|
DeleteURLTestHistory(tag string)
|
||||||
StoreURLTestHistory(tag string, history *URLTestHistory)
|
StoreURLTestHistory(tag string, history *URLTestHistory)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/process"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
@@ -84,7 +85,7 @@ type InboundContext struct {
|
|||||||
DestinationAddresses []netip.Addr
|
DestinationAddresses []netip.Addr
|
||||||
SourceGeoIPCode string
|
SourceGeoIPCode string
|
||||||
GeoIPCode string
|
GeoIPCode string
|
||||||
ProcessInfo *ConnectionOwner
|
ProcessInfo *process.Info
|
||||||
QueryType uint16
|
QueryType uint16
|
||||||
FakeIP bool
|
FakeIP bool
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||||
@@ -12,7 +11,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.InboundManager = (*Manager)(nil)
|
var _ adapter.InboundManager = (*Manager)(nil)
|
||||||
@@ -47,14 +45,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
|||||||
inbounds := m.inbounds
|
inbounds := m.inbounds
|
||||||
m.access.Unlock()
|
m.access.Unlock()
|
||||||
for _, inbound := range inbounds {
|
for _, inbound := range inbounds {
|
||||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err := adapter.LegacyStart(inbound, stage)
|
err := adapter.LegacyStart(inbound, stage)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@@ -71,15 +65,11 @@ func (m *Manager) Close() error {
|
|||||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||||
var err error
|
var err error
|
||||||
for _, inbound := range inbounds {
|
for _, inbound := range inbounds {
|
||||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
monitor.Start("close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||||
m.logger.Trace("close ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
monitor.Start("close ", name)
|
|
||||||
err = E.Append(err, inbound.Close(), func(err error) error {
|
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()
|
monitor.Finish()
|
||||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -131,15 +121,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
|||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
if m.started {
|
if m.started {
|
||||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
|
||||||
for _, stage := range adapter.ListStartStages {
|
for _, stage := range adapter.ListStartStages {
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err = adapter.LegacyStart(inbound, stage)
|
err = adapter.LegacyStart(inbound, stage)
|
||||||
if err != nil {
|
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 {
|
if existsInbound, loaded := m.inboundByTag[tag]; loaded {
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import (
|
import E "github.com/sagernet/sing/common/exceptions"
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SimpleLifecycle interface {
|
type SimpleLifecycle interface {
|
||||||
Start() error
|
Start() error
|
||||||
@@ -56,47 +48,22 @@ type LifecycleService interface {
|
|||||||
Lifecycle
|
Lifecycle
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceName(service any) string {
|
func Start(stage StartStage, services ...Lifecycle) error {
|
||||||
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 {
|
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
name := getServiceName(service)
|
|
||||||
logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err := service.Start(stage)
|
err := service.Start(stage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartNamed(logger log.ContextLogger, stage StartStage, services []LifecycleService) error {
|
func StartNamed(stage StartStage, services []LifecycleService) error {
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
logger.Trace(stage, " ", service.Name())
|
|
||||||
startTime := time.Now()
|
|
||||||
err := service.Start(stage)
|
err := service.Start(stage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, stage.String(), " ", service.Name())
|
return E.Cause(err, stage.String(), " ", service.Name())
|
||||||
}
|
}
|
||||||
logger.Trace(stage, " ", service.Name(), " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
|
|
||||||
type NetworkManager interface {
|
type NetworkManager interface {
|
||||||
Lifecycle
|
Lifecycle
|
||||||
Initialize(ruleSets []RuleSet)
|
|
||||||
InterfaceFinder() control.InterfaceFinder
|
InterfaceFinder() control.InterfaceFinder
|
||||||
UpdateInterfaces() error
|
UpdateInterfaces() error
|
||||||
DefaultNetworkInterface() *NetworkInterface
|
DefaultNetworkInterface() *NetworkInterface
|
||||||
@@ -25,10 +24,9 @@ type NetworkManager interface {
|
|||||||
NetworkMonitor() tun.NetworkUpdateMonitor
|
NetworkMonitor() tun.NetworkUpdateMonitor
|
||||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||||
PackageManager() tun.PackageManager
|
PackageManager() tun.PackageManager
|
||||||
NeedWIFIState() bool
|
|
||||||
WIFIState() WIFIState
|
WIFIState() WIFIState
|
||||||
UpdateWIFIState()
|
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
|
UpdateWIFIState()
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkOptions struct {
|
type NetworkOptions struct {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||||
@@ -14,7 +13,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -83,14 +81,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
|||||||
outbounds := m.outbounds
|
outbounds := m.outbounds
|
||||||
m.access.Unlock()
|
m.access.Unlock()
|
||||||
for _, outbound := range outbounds {
|
for _, outbound := range outbounds {
|
||||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err := adapter.LegacyStart(outbound, stage)
|
err := adapter.LegacyStart(outbound, stage)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
@@ -115,29 +109,22 @@ func (m *Manager) startOutbounds(outbounds []adapter.Outbound) error {
|
|||||||
}
|
}
|
||||||
started[outboundTag] = true
|
started[outboundTag] = true
|
||||||
canContinue = true
|
canContinue = true
|
||||||
name := "outbound/" + outboundToStart.Type() + "[" + outboundTag + "]"
|
|
||||||
if starter, isStarter := outboundToStart.(adapter.Lifecycle); isStarter {
|
if starter, isStarter := outboundToStart.(adapter.Lifecycle); isStarter {
|
||||||
m.logger.Trace("start ", name)
|
monitor.Start("start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||||
startTime := time.Now()
|
|
||||||
monitor.Start("start ", name)
|
|
||||||
err := starter.Start(adapter.StartStateStart)
|
err := starter.Start(adapter.StartStateStart)
|
||||||
monitor.Finish()
|
monitor.Finish()
|
||||||
if err != nil {
|
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 {
|
} else if starter, isStarter := outboundToStart.(interface {
|
||||||
Start() error
|
Start() error
|
||||||
}); isStarter {
|
}); isStarter {
|
||||||
m.logger.Trace("start ", name)
|
monitor.Start("start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||||
startTime := time.Now()
|
|
||||||
monitor.Start("start ", name)
|
|
||||||
err := starter.Start()
|
err := starter.Start()
|
||||||
monitor.Finish()
|
monitor.Finish()
|
||||||
if err != nil {
|
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) {
|
if len(started) == len(outbounds) {
|
||||||
@@ -184,15 +171,11 @@ func (m *Manager) Close() error {
|
|||||||
var err error
|
var err error
|
||||||
for _, outbound := range outbounds {
|
for _, outbound := range outbounds {
|
||||||
if closer, isCloser := outbound.(io.Closer); isCloser {
|
if closer, isCloser := outbound.(io.Closer); isCloser {
|
||||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
monitor.Start("close outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||||
m.logger.Trace("close ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
monitor.Start("close ", name)
|
|
||||||
err = E.Append(err, closer.Close(), func(err error) error {
|
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()
|
monitor.Finish()
|
||||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -273,15 +256,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if m.started {
|
if m.started {
|
||||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
|
||||||
for _, stage := range adapter.ListStartStages {
|
for _, stage := range adapter.ListStartStages {
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err = adapter.LegacyStart(outbound, stage)
|
err = adapter.LegacyStart(outbound, stage)
|
||||||
if err != nil {
|
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()
|
m.access.Lock()
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package adapter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
"github.com/sagernet/sing-tun"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PlatformInterface interface {
|
|
||||||
Initialize(networkManager NetworkManager) error
|
|
||||||
|
|
||||||
UsePlatformAutoDetectInterfaceControl() bool
|
|
||||||
AutoDetectInterfaceControl(fd int) error
|
|
||||||
|
|
||||||
UsePlatformInterface() bool
|
|
||||||
OpenInterface(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
|
||||||
|
|
||||||
UsePlatformDefaultInterfaceMonitor() bool
|
|
||||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
|
||||||
|
|
||||||
UsePlatformNetworkInterfaces() bool
|
|
||||||
NetworkInterfaces() ([]NetworkInterface, error)
|
|
||||||
|
|
||||||
UnderNetworkExtension() bool
|
|
||||||
NetworkExtensionIncludeAllNetworks() bool
|
|
||||||
|
|
||||||
ClearDNSCache()
|
|
||||||
RequestPermissionForWIFIState() error
|
|
||||||
ReadWIFIState() WIFIState
|
|
||||||
SystemCertificates() []string
|
|
||||||
|
|
||||||
UsePlatformConnectionOwnerFinder() bool
|
|
||||||
FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error)
|
|
||||||
|
|
||||||
UsePlatformWIFIMonitor() bool
|
|
||||||
|
|
||||||
UsePlatformNotification() bool
|
|
||||||
SendNotification(notification *Notification) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type FindConnectionOwnerRequest struct {
|
|
||||||
IpProtocol int32
|
|
||||||
SourceAddress string
|
|
||||||
SourcePort int32
|
|
||||||
DestinationAddress string
|
|
||||||
DestinationPort int32
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConnectionOwner struct {
|
|
||||||
ProcessID uint32
|
|
||||||
UserId int32
|
|
||||||
UserName string
|
|
||||||
ProcessPath string
|
|
||||||
AndroidPackageName string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Notification struct {
|
|
||||||
Identifier string
|
|
||||||
TypeName string
|
|
||||||
TypeID int32
|
|
||||||
Title string
|
|
||||||
Subtitle string
|
|
||||||
Body string
|
|
||||||
OpenURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
type SystemProxyStatus struct {
|
|
||||||
Available bool
|
|
||||||
Enabled bool
|
|
||||||
}
|
|
||||||
@@ -21,9 +21,10 @@ import (
|
|||||||
type Router interface {
|
type Router interface {
|
||||||
Lifecycle
|
Lifecycle
|
||||||
ConnectionRouter
|
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
|
ConnectionRouterEx
|
||||||
RuleSet(tag string) (RuleSet, bool)
|
RuleSet(tag string) (RuleSet, bool)
|
||||||
|
NeedWIFIState() bool
|
||||||
Rules() []Rule
|
Rules() []Rule
|
||||||
AppendTracker(tracker ConnectionTracker)
|
AppendTracker(tracker ConnectionTracker)
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||||
@@ -12,7 +11,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.ServiceManager = (*Manager)(nil)
|
var _ adapter.ServiceManager = (*Manager)(nil)
|
||||||
@@ -45,14 +43,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
|||||||
services := m.services
|
services := m.services
|
||||||
m.access.Unlock()
|
m.access.Unlock()
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err := adapter.LegacyStart(service, stage)
|
err := adapter.LegacyStart(service, stage)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@@ -69,15 +63,11 @@ func (m *Manager) Close() error {
|
|||||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||||
var err error
|
var err error
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
monitor.Start("close service/", service.Type(), "[", service.Tag(), "]")
|
||||||
m.logger.Trace("close ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
monitor.Start("close ", name)
|
|
||||||
err = E.Append(err, service.Close(), func(err error) error {
|
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()
|
monitor.Finish()
|
||||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -126,15 +116,11 @@ func (m *Manager) Create(ctx context.Context, logger log.ContextLogger, tag stri
|
|||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
if m.started {
|
if m.started {
|
||||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
|
||||||
for _, stage := range adapter.ListStartStages {
|
for _, stage := range adapter.ListStartStages {
|
||||||
m.logger.Trace(stage, " ", name)
|
|
||||||
startTime := time.Now()
|
|
||||||
err = adapter.LegacyStart(service, stage)
|
err = adapter.LegacyStart(service, stage)
|
||||||
if err != nil {
|
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 {
|
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) {
|
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() {
|
if source.IsValid() {
|
||||||
myMetadata.Source = source
|
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) {
|
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() {
|
if source.IsValid() {
|
||||||
myMetadata.Source = source
|
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) {
|
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() {
|
if source.IsValid() {
|
||||||
metadata.Source = source
|
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) {
|
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() {
|
if source.IsValid() {
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
}
|
}
|
||||||
|
|||||||
58
box.go
58
box.go
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/dns/transport/local"
|
"github.com/sagernet/sing-box/dns/transport/local"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
"github.com/sagernet/sing-box/experimental/cachefile"
|
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/protocol/direct"
|
"github.com/sagernet/sing-box/protocol/direct"
|
||||||
@@ -138,7 +139,7 @@ func New(options Options) (*Box, error) {
|
|||||||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||||
needV2RayAPI = true
|
needV2RayAPI = true
|
||||||
}
|
}
|
||||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
var defaultLogWriter io.Writer
|
var defaultLogWriter io.Writer
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
defaultLogWriter = io.Discard
|
defaultLogWriter = io.Discard
|
||||||
@@ -183,7 +184,7 @@ func New(options Options) (*Box, error) {
|
|||||||
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
|
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
|
||||||
dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions)
|
dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions)
|
||||||
service.MustRegister[adapter.DNSRouter](ctx, dnsRouter)
|
service.MustRegister[adapter.DNSRouter](ctx, dnsRouter)
|
||||||
networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions, dnsOptions)
|
networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize network manager")
|
return nil, E.Cause(err, "initialize network manager")
|
||||||
}
|
}
|
||||||
@@ -443,15 +444,15 @@ func (s *Box) preStart() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "start logger")
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -463,27 +464,27 @@ func (s *Box) start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.StartNamed(s.logger, adapter.StartStateStart, s.internalService)
|
err = adapter.StartNamed(adapter.StartStateStart, s.internalService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.StartNamed(s.logger, adapter.StartStatePostStart, s.internalService)
|
err = adapter.StartNamed(adapter.StartStatePostStart, s.internalService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.StartNamed(s.logger, adapter.StartStateStarted, s.internalService)
|
err = adapter.StartNamed(adapter.StartStateStarted, s.internalService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -497,42 +498,17 @@ func (s *Box) Close() error {
|
|||||||
default:
|
default:
|
||||||
close(s.done)
|
close(s.done)
|
||||||
}
|
}
|
||||||
var err error
|
err := common.Close(
|
||||||
for _, closeItem := range []struct {
|
s.service, s.endpoint, s.inbound, s.outbound, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
|
||||||
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)")
|
|
||||||
}
|
|
||||||
for _, lifecycleService := range s.internalService {
|
for _, lifecycleService := range s.internalService {
|
||||||
s.logger.Trace("close ", lifecycleService.Name())
|
|
||||||
startTime := time.Now()
|
|
||||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||||
return E.Cause(err, "close ", lifecycleService.Name())
|
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 {
|
err = E.Append(err, s.logFactory.Close(), func(err error) error {
|
||||||
return E.Cause(err, "close logger")
|
return E.Cause(err, "close logger")
|
||||||
})
|
})
|
||||||
s.logger.Trace("close logger completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,7 +527,3 @@ func (s *Box) Inbound() adapter.InboundManager {
|
|||||||
func (s *Box) Outbound() adapter.OutboundManager {
|
func (s *Box) Outbound() adapter.OutboundManager {
|
||||||
return s.outbound
|
return s.outbound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Box) LogFactory() log.Factory {
|
|
||||||
return s.logFactory
|
|
||||||
}
|
|
||||||
|
|||||||
Submodule clients/android updated: fe128a6cd7...9fe53a952e
Submodule clients/apple updated: 532c140f05...e0e928b8d9
@@ -5,7 +5,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
_ "github.com/sagernet/gomobile"
|
_ "github.com/sagernet/gomobile"
|
||||||
@@ -47,7 +46,7 @@ var (
|
|||||||
sharedFlags []string
|
sharedFlags []string
|
||||||
debugFlags []string
|
debugFlags []string
|
||||||
sharedTags []string
|
sharedTags []string
|
||||||
darwinTags []string
|
macOSTags []string
|
||||||
memcTags []string
|
memcTags []string
|
||||||
notMemcTags []string
|
notMemcTags []string
|
||||||
debugTags []string
|
debugTags []string
|
||||||
@@ -63,34 +62,16 @@ func init() {
|
|||||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
||||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
||||||
|
|
||||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack")
|
||||||
darwinTags = append(darwinTags, "with_dhcp")
|
macOSTags = append(macOSTags, "with_dhcp")
|
||||||
memcTags = append(memcTags, "with_tailscale")
|
memcTags = append(memcTags, "with_tailscale")
|
||||||
notMemcTags = append(notMemcTags, "with_low_memory")
|
notMemcTags = append(notMemcTags, "with_low_memory")
|
||||||
debugTags = append(debugTags, "debug")
|
debugTags = append(debugTags, "debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
type AndroidBuildConfig struct {
|
func buildAndroid() {
|
||||||
AndroidAPI int
|
build_shared.FindSDK()
|
||||||
OutputName string
|
|
||||||
Tags []string
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
var javaPath string
|
||||||
javaHome := os.Getenv("JAVA_HOME")
|
javaHome := os.Getenv("JAVA_HOME")
|
||||||
if javaHome == "" {
|
if javaHome == "" {
|
||||||
@@ -106,87 +87,61 @@ func checkJavaVersion() {
|
|||||||
if !strings.Contains(javaVersion, "openjdk 17") {
|
if !strings.Contains(javaVersion, "openjdk 17") {
|
||||||
log.Fatal("java version should be openjdk 17")
|
log.Fatal("java version should be openjdk 17")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func getAndroidBindTarget() string {
|
var bindTarget string
|
||||||
if platform != "" {
|
if platform != "" {
|
||||||
return platform
|
bindTarget = platform
|
||||||
} else if debugEnabled {
|
} else if debugEnabled {
|
||||||
return "android/arm64"
|
bindTarget = "android/arm64"
|
||||||
|
} else {
|
||||||
|
bindTarget = "android"
|
||||||
}
|
}
|
||||||
return "android"
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildAndroidVariant(config AndroidBuildConfig, bindTarget string) {
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"bind",
|
"bind",
|
||||||
"-v",
|
"-v",
|
||||||
"-o", config.OutputName,
|
|
||||||
"-target", bindTarget,
|
"-target", bindTarget,
|
||||||
"-androidapi", strconv.Itoa(config.AndroidAPI),
|
"-androidapi", "21",
|
||||||
"-javapkg=io.nekohasekai",
|
"-javapkg=io.nekohasekai",
|
||||||
"-libname=box",
|
"-libname=box",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
|
// sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
|
||||||
args = append(args, sharedFlags...)
|
args = append(args, sharedFlags...)
|
||||||
} else {
|
} else {
|
||||||
|
// debugFlags[1] = debugFlags[1] + " -checklinkname=0"
|
||||||
args = append(args, debugFlags...)
|
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")
|
args = append(args, "./experimental/libbox")
|
||||||
|
|
||||||
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||||
command.Stdout = os.Stdout
|
command.Stdout = os.Stdout
|
||||||
command.Stderr = os.Stderr
|
command.Stderr = os.Stderr
|
||||||
err := command.Run()
|
err = command.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const name = "libbox.aar"
|
||||||
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
||||||
if rw.IsDir(copyPath) {
|
if rw.IsDir(copyPath) {
|
||||||
copyPath, _ = filepath.Abs(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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
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() {
|
func buildApple() {
|
||||||
var bindTarget string
|
var bindTarget string
|
||||||
if platform != "" {
|
if platform != "" {
|
||||||
@@ -205,7 +160,9 @@ func buildApple() {
|
|||||||
"-tags-not-macos=with_low_memory",
|
"-tags-not-macos=with_low_memory",
|
||||||
}
|
}
|
||||||
if !withTailscale {
|
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 {
|
if !debugEnabled {
|
||||||
@@ -214,7 +171,7 @@ func buildApple() {
|
|||||||
args = append(args, debugFlags...)
|
args = append(args, debugFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := append(sharedTags, darwinTags...)
|
tags := sharedTags
|
||||||
if withTailscale {
|
if withTailscale {
|
||||||
tags = append(tags, memcTags...)
|
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 {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
err = updateChromeIncludedRootCAs()
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMozillaIncludedRootCAs() error {
|
func updateMozillaIncludedRootCAs() error {
|
||||||
@@ -73,94 +69,3 @@ func init() {
|
|||||||
generated.WriteString("}\n")
|
generated.WriteString("}\n")
|
||||||
return os.WriteFile("common/certificate/mozilla.go", []byte(generated.String()), 0o644)
|
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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func initializeHTTP3Client(instance *box.Box) error {
|
|||||||
}
|
}
|
||||||
http3Client = &http.Client{
|
http3Client = &http.Client{
|
||||||
Transport: &http3.Transport{
|
Transport: &http3.Transport{
|
||||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {
|
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||||
destination := M.ParseSocksaddr(addr)
|
destination := M.ParseSocksaddr(addr)
|
||||||
udpConn, dErr := dialer.DialContext(ctx, N.NetworkUDP, destination)
|
udpConn, dErr := dialer.DialContext(ctx, N.NetworkUDP, destination)
|
||||||
if dErr != nil {
|
if dErr != nil {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && badlinkname
|
//go:build go1.25 && !without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && badlinkname
|
//go:build go1.25 && !without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && badlinkname
|
//go:build go1.25 && !without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !go1.25 || !badlinkname
|
//go:build !go1.25 || without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && badlinkname
|
//go:build go1.25 && !without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && badlinkname
|
//go:build go1.25 && !without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/sagernet/fswatch"
|
"github.com/sagernet/fswatch"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
@@ -35,7 +36,7 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
|||||||
switch options.Store {
|
switch options.Store {
|
||||||
case C.CertificateStoreSystem, "":
|
case C.CertificateStoreSystem, "":
|
||||||
systemPool = x509.NewCertPool()
|
systemPool = x509.NewCertPool()
|
||||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
var systemValid bool
|
var systemValid bool
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
for _, cert := range platformInterface.SystemCertificates() {
|
for _, cert := range platformInterface.SystemCertificates() {
|
||||||
@@ -53,8 +54,6 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
|||||||
}
|
}
|
||||||
case C.CertificateStoreMozilla:
|
case C.CertificateStoreMozilla:
|
||||||
systemPool = mozillaIncluded
|
systemPool = mozillaIncluded
|
||||||
case C.CertificateStoreChrome:
|
|
||||||
systemPool = chromeIncluded
|
|
||||||
case C.CertificateStoreNone:
|
case C.CertificateStoreNone:
|
||||||
systemPool = nil
|
systemPool = nil
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/conntrack"
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
"github.com/sagernet/sing-box/common/listener"
|
"github.com/sagernet/sing-box/common/listener"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
@@ -19,8 +20,6 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
"github.com/database64128/tfo-go/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -29,8 +28,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DefaultDialer struct {
|
type DefaultDialer struct {
|
||||||
dialer4 tfo.Dialer
|
dialer4 tcpDialer
|
||||||
dialer6 tfo.Dialer
|
dialer6 tcpDialer
|
||||||
udpDialer4 net.Dialer
|
udpDialer4 net.Dialer
|
||||||
udpDialer6 net.Dialer
|
udpDialer6 net.Dialer
|
||||||
udpListener net.ListenConfig
|
udpListener net.ListenConfig
|
||||||
@@ -48,7 +47,7 @@ type DefaultDialer struct {
|
|||||||
|
|
||||||
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
||||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dialer net.Dialer
|
dialer net.Dialer
|
||||||
@@ -142,18 +141,9 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
} else {
|
} else {
|
||||||
dialer.Timeout = C.TCPConnectTimeout
|
dialer.Timeout = C.TCPConnectTimeout
|
||||||
}
|
}
|
||||||
if !options.DisableTCPKeepAlive {
|
// TODO: Add an option to customize the keep alive period
|
||||||
keepIdle := time.Duration(options.TCPKeepAlive)
|
dialer.KeepAlive = C.TCPKeepAliveInitial
|
||||||
if keepIdle == 0 {
|
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(C.TCPKeepAliveInitial, C.TCPKeepAliveInterval))
|
||||||
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))
|
|
||||||
}
|
|
||||||
var udpFragment bool
|
var udpFragment bool
|
||||||
if options.UDPFragment != nil {
|
if options.UDPFragment != nil {
|
||||||
udpFragment = *options.UDPFragment
|
udpFragment = *options.UDPFragment
|
||||||
@@ -187,10 +177,19 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||||
}
|
}
|
||||||
if options.TCPMultiPath {
|
if options.TCPMultiPath {
|
||||||
dialer4.SetMultipathTCP(true)
|
if !go121Available {
|
||||||
|
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
||||||
|
}
|
||||||
|
setMultiPathTCP(&dialer4)
|
||||||
|
}
|
||||||
|
tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tcpDialer6, err := newTCPDialer(dialer6, options.TCPFastOpen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
tcpDialer4 := tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen}
|
|
||||||
tcpDialer6 := tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen}
|
|
||||||
return &DefaultDialer{
|
return &DefaultDialer{
|
||||||
dialer4: tcpDialer4,
|
dialer4: tcpDialer4,
|
||||||
dialer6: tcpDialer6,
|
dialer6: tcpDialer6,
|
||||||
@@ -270,7 +269,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
|||||||
}
|
}
|
||||||
var dialer net.Dialer
|
var dialer net.Dialer
|
||||||
if N.NetworkName(network) == N.NetworkTCP {
|
if N.NetworkName(network) == N.NetworkTCP {
|
||||||
dialer = d.dialer4.Dialer
|
dialer = dialerFromTCPDialer(d.dialer4)
|
||||||
} else {
|
} else {
|
||||||
dialer = d.udpDialer4
|
dialer = d.udpDialer4
|
||||||
}
|
}
|
||||||
@@ -318,9 +317,9 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
|||||||
|
|
||||||
func (d *DefaultDialer) DialerForICMPDestination(destination netip.Addr) net.Dialer {
|
func (d *DefaultDialer) DialerForICMPDestination(destination netip.Addr) net.Dialer {
|
||||||
if !destination.Is6() {
|
if !destination.Is6() {
|
||||||
return d.dialer6.Dialer
|
return dialerFromTCPDialer(d.dialer6)
|
||||||
} else {
|
} else {
|
||||||
return d.dialer4.Dialer
|
return dialerFromTCPDialer(d.dialer4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,8 +356,18 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
|||||||
return trackPacketConn(packetConn, nil)
|
return trackPacketConn(packetConn, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) WireGuardControl() control.Func {
|
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
||||||
return d.udpListener.Control
|
udpListener := d.udpListener
|
||||||
|
udpListener.Control = control.Append(udpListener.Control, func(network, address string, conn syscall.RawConn) error {
|
||||||
|
for _, wgControlFn := range WgControlFns {
|
||||||
|
err := wgControlFn(network, address, conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return udpListener.ListenPacket(context.Background(), network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||||
|
|||||||
19
common/dialer/default_go1.20.go
Normal file
19
common/dialer/default_go1.20.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//go:build go1.20
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/metacubex/tfo-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tcpDialer = tfo.Dialer
|
||||||
|
|
||||||
|
func newTCPDialer(dialer net.Dialer, tfoEnabled bool) (tcpDialer, error) {
|
||||||
|
return tfo.Dialer{Dialer: dialer, DisableTFO: !tfoEnabled}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialerFromTCPDialer(dialer tcpDialer) net.Dialer {
|
||||||
|
return dialer.Dialer
|
||||||
|
}
|
||||||
11
common/dialer/default_go1.21.go
Normal file
11
common/dialer/default_go1.21.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build go1.21
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
const go121Available = true
|
||||||
|
|
||||||
|
func setMultiPathTCP(dialer *net.Dialer) {
|
||||||
|
dialer.SetMultipathTCP(true)
|
||||||
|
}
|
||||||
22
common/dialer/default_nongo1.20.go
Normal file
22
common/dialer/default_nongo1.20.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tcpDialer = net.Dialer
|
||||||
|
|
||||||
|
func newTCPDialer(dialer net.Dialer, tfoEnabled bool) (tcpDialer, error) {
|
||||||
|
if tfoEnabled {
|
||||||
|
return dialer, E.New("TCP Fast Open requires go1.20, please recompile your binary.")
|
||||||
|
}
|
||||||
|
return dialer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dialerFromTCPDialer(dialer tcpDialer) net.Dialer {
|
||||||
|
return dialer
|
||||||
|
}
|
||||||
12
common/dialer/default_nongo1.21.go
Normal file
12
common/dialer/default_nongo1.21.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build !go1.21
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const go121Available = false
|
||||||
|
|
||||||
|
func setMultiPathTCP(dialer *net.Dialer) {
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build go1.20
|
||||||
|
|
||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -14,7 +16,7 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/database64128/tfo-go/v2"
|
"github.com/metacubex/tfo-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type slowOpenConn struct {
|
type slowOpenConn struct {
|
||||||
@@ -30,7 +32,7 @@ type slowOpenConn struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func DialSlowContext(dialer *tfo.Dialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
if dialer.DisableTFO || N.NetworkName(network) != N.NetworkTCP {
|
if dialer.DisableTFO || N.NetworkName(network) != N.NetworkTCP {
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP, N.NetworkUDP:
|
case N.NetworkTCP, N.NetworkUDP:
|
||||||
|
|||||||
20
common/dialer/tfo_stub.go
Normal file
20
common/dialer/tfo_stub.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
|
switch N.NetworkName(network) {
|
||||||
|
case N.NetworkTCP, N.NetworkUDP:
|
||||||
|
return dialer.DialContext(ctx, network, destination.String())
|
||||||
|
default:
|
||||||
|
return dialer.DialContext(ctx, network, destination.AddrString())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WireGuardListener interface {
|
type WireGuardListener interface {
|
||||||
WireGuardControl() control.Func
|
ListenPacketCompat(network, address string) (net.PacketConn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var WgControlFns []control.Func
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
//go:build !linux
|
//go:build !linux || !go1.25 || without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
||||||
return nil, E.New("kTLS is only supported on Linux")
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//go:build linux && go1.25 && !badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
|
||||||
return nil, E.New("kTLS requires build flags `badlinkname` and `-ldflags=-checklinkname=0`, please recompile your binary")
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//go:build linux && !go1.25
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
|
||||||
return nil, E.New("kTLS requires Go 1.25 or later, please recompile your binary")
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
//go:build linux && go1.25 && !without_badtls
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
11
common/listener/listener_go121.go
Normal file
11
common/listener/listener_go121.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build go1.21
|
||||||
|
|
||||||
|
package listener
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
const go121Available = true
|
||||||
|
|
||||||
|
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||||
|
listenConfig.SetMultipathTCP(true)
|
||||||
|
}
|
||||||
16
common/listener/listener_go123.go
Normal file
16
common/listener/listener_go123.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build go1.23
|
||||||
|
|
||||||
|
package listener
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
||||||
|
listener.KeepAliveConfig = net.KeepAliveConfig{
|
||||||
|
Enable: true,
|
||||||
|
Idle: idle,
|
||||||
|
Interval: interval,
|
||||||
|
}
|
||||||
|
}
|
||||||
10
common/listener/listener_nongo121.go
Normal file
10
common/listener/listener_nongo121.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
//go:build !go1.21
|
||||||
|
|
||||||
|
package listener
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
const go121Available = false
|
||||||
|
|
||||||
|
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||||
|
}
|
||||||
15
common/listener/listener_nongo123.go
Normal file
15
common/listener/listener_nongo123.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build !go1.23
|
||||||
|
|
||||||
|
package listener
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
||||||
|
listener.KeepAlive = idle
|
||||||
|
listener.Control = control.Append(listener.Control, control.SetKeepAlivePeriod(idle, interval))
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
"github.com/database64128/tfo-go/v2"
|
"github.com/metacubex/tfo-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||||
@@ -37,7 +37,7 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
|||||||
if l.listenOptions.ReuseAddr {
|
if l.listenOptions.ReuseAddr {
|
||||||
listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
|
listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
|
||||||
}
|
}
|
||||||
if !l.listenOptions.DisableTCPKeepAlive {
|
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||||
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
||||||
if keepIdle == 0 {
|
if keepIdle == 0 {
|
||||||
keepIdle = C.TCPKeepAliveInitial
|
keepIdle = C.TCPKeepAliveInitial
|
||||||
@@ -46,14 +46,13 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
|||||||
if keepInterval == 0 {
|
if keepInterval == 0 {
|
||||||
keepInterval = C.TCPKeepAliveInterval
|
keepInterval = C.TCPKeepAliveInterval
|
||||||
}
|
}
|
||||||
listenConfig.KeepAliveConfig = net.KeepAliveConfig{
|
setKeepAliveConfig(&listenConfig, keepIdle, keepInterval)
|
||||||
Enable: true,
|
|
||||||
Idle: keepIdle,
|
|
||||||
Interval: keepInterval,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if l.listenOptions.TCPMultiPath {
|
if l.listenOptions.TCPMultiPath {
|
||||||
listenConfig.SetMultipathTCP(true)
|
if !go121Available {
|
||||||
|
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
||||||
|
}
|
||||||
|
setMultiPathTCP(&listenConfig)
|
||||||
}
|
}
|
||||||
if l.tproxy {
|
if l.tproxy {
|
||||||
listenConfig.Control = control.Append(listenConfig.Control, func(network, address string, conn syscall.RawConn) error {
|
listenConfig.Control = control.Append(listenConfig.Control, func(network, address string, conn syscall.RawConn) error {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -13,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Searcher interface {
|
type Searcher interface {
|
||||||
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error)
|
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNotFound = E.New("process not found")
|
var ErrNotFound = E.New("process not found")
|
||||||
@@ -23,7 +22,15 @@ type Config struct {
|
|||||||
PackageManager tun.PackageManager
|
PackageManager tun.PackageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
type Info struct {
|
||||||
|
ProcessID uint32
|
||||||
|
ProcessPath string
|
||||||
|
PackageName string
|
||||||
|
User string
|
||||||
|
UserId int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||||
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -31,7 +38,7 @@ func FindProcessInfo(searcher Searcher, ctx context.Context, network string, sou
|
|||||||
if info.UserId != -1 {
|
if info.UserId != -1 {
|
||||||
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
||||||
if osUser != nil {
|
if osUser != nil {
|
||||||
info.UserName = osUser.Username
|
info.User = osUser.Username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info, nil
|
return info, nil
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,22 +17,22 @@ func NewSearcher(config Config) (Searcher, error) {
|
|||||||
return &androidSearcher{config.PackageManager}, nil
|
return &androidSearcher{config.PackageManager}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||||
_, uid, err := resolveSocketByNetlink(network, source, destination)
|
_, uid, err := resolveSocketByNetlink(network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded {
|
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded {
|
||||||
return &adapter.ConnectionOwner{
|
return &Info{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
AndroidPackageName: sharedPackage,
|
PackageName: sharedPackage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
||||||
return &adapter.ConnectionOwner{
|
return &Info{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
AndroidPackageName: packageName,
|
PackageName: packageName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return &adapter.ConnectionOwner{UserId: int32(uid)}, nil
|
return &Info{UserId: int32(uid)}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@@ -24,12 +23,12 @@ func NewSearcher(_ Config) (Searcher, error) {
|
|||||||
return &darwinSearcher{}, nil
|
return &darwinSearcher{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||||
processName, err := findProcessName(network, source.Addr(), int(source.Port()))
|
processName, err := findProcessName(network, source.Addr(), int(source.Port()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &adapter.ConnectionOwner{ProcessPath: processName, UserId: -1}, nil
|
return &Info{ProcessPath: processName, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var structSize = func() int {
|
var structSize = func() int {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ func NewSearcher(config Config) (Searcher, error) {
|
|||||||
return &linuxSearcher{config.Logger}, nil
|
return &linuxSearcher{config.Logger}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||||
inode, uid, err := resolveSocketByNetlink(network, source, destination)
|
inode, uid, err := resolveSocketByNetlink(network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -29,7 +28,7 @@ func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, sou
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.DebugContext(ctx, "find process path: ", err)
|
s.logger.DebugContext(ctx, "find process path: ", err)
|
||||||
}
|
}
|
||||||
return &adapter.ConnectionOwner{
|
return &Info{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
ProcessPath: processPath,
|
ProcessPath: processPath,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/winiphlpapi"
|
"github.com/sagernet/sing/common/winiphlpapi"
|
||||||
|
|
||||||
@@ -28,16 +27,16 @@ func initWin32API() error {
|
|||||||
return winiphlpapi.LoadExtendedTable()
|
return winiphlpapi.LoadExtendedTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||||
pid, err := winiphlpapi.FindPid(network, source)
|
pid, err := winiphlpapi.FindPid(network, source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
path, err := getProcessPath(pid)
|
path, err := getProcessPath(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &adapter.ConnectionOwner{ProcessID: pid, UserId: -1}, err
|
return &Info{ProcessID: pid, UserId: -1}, err
|
||||||
}
|
}
|
||||||
return &adapter.ConnectionOwner{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
return &Info{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProcessPath(pid uint32) (string, error) {
|
func getProcessPath(pid uint32) (string, error) {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package settings
|
|
||||||
|
|
||||||
import "github.com/sagernet/sing-box/adapter"
|
|
||||||
|
|
||||||
type WIFIMonitor interface {
|
|
||||||
ReadWIFIState() adapter.WIFIState
|
|
||||||
Start() error
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LinuxWIFIMonitor struct {
|
|
||||||
monitor WIFIMonitor
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
||||||
monitors := []func(func(adapter.WIFIState)) (WIFIMonitor, error){
|
|
||||||
newNetworkManagerMonitor,
|
|
||||||
newIWDMonitor,
|
|
||||||
newWpaSupplicantMonitor,
|
|
||||||
newConnManMonitor,
|
|
||||||
}
|
|
||||||
var errors []error
|
|
||||||
for _, factory := range monitors {
|
|
||||||
monitor, err := factory(callback)
|
|
||||||
if err == nil {
|
|
||||||
return &LinuxWIFIMonitor{monitor: monitor}, nil
|
|
||||||
}
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
return nil, E.Cause(E.Errors(errors...), "no supported WIFI manager found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *LinuxWIFIMonitor) ReadWIFIState() adapter.WIFIState {
|
|
||||||
return m.monitor.ReadWIFIState()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *LinuxWIFIMonitor) Start() error {
|
|
||||||
if m.monitor != nil {
|
|
||||||
return m.monitor.Start()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *LinuxWIFIMonitor) Close() error {
|
|
||||||
if m.monitor != nil {
|
|
||||||
return m.monitor.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type connmanMonitor struct {
|
|
||||||
conn *dbus.Conn
|
|
||||||
callback func(adapter.WIFIState)
|
|
||||||
cancel context.CancelFunc
|
|
||||||
signalChan chan *dbus.Signal
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConnManMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
||||||
conn, err := dbus.ConnectSystemBus()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cmObj := conn.Object("net.connman", "/")
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
call := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0)
|
|
||||||
if call.Err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, call.Err
|
|
||||||
}
|
|
||||||
return &connmanMonitor{conn: conn, callback: callback}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *connmanMonitor) ReadWIFIState() adapter.WIFIState {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cmObj := m.conn.Object("net.connman", "/")
|
|
||||||
var services []interface{}
|
|
||||||
err := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0).Store(&services)
|
|
||||||
if err != nil {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, service := range services {
|
|
||||||
servicePair, ok := service.([]interface{})
|
|
||||||
if !ok || len(servicePair) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceProps, ok := servicePair[1].(map[string]dbus.Variant)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
typeVariant, hasType := serviceProps["Type"]
|
|
||||||
if !hasType {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
serviceType, ok := typeVariant.Value().(string)
|
|
||||||
if !ok || serviceType != "wifi" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
stateVariant, hasState := serviceProps["State"]
|
|
||||||
if !hasState {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
state, ok := stateVariant.Value().(string)
|
|
||||||
if !ok || (state != "online" && state != "ready") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
nameVariant, hasName := serviceProps["Name"]
|
|
||||||
if !hasName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ssid, ok := nameVariant.Value().(string)
|
|
||||||
if !ok || ssid == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
bssidVariant, hasBSSID := serviceProps["BSSID"]
|
|
||||||
if !hasBSSID {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
bssid, ok := bssidVariant.Value().(string)
|
|
||||||
if !ok {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter.WIFIState{
|
|
||||||
SSID: ssid,
|
|
||||||
BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *connmanMonitor) Start() error {
|
|
||||||
if m.callback == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
m.cancel = cancel
|
|
||||||
|
|
||||||
m.signalChan = make(chan *dbus.Signal, 10)
|
|
||||||
m.conn.Signal(m.signalChan)
|
|
||||||
|
|
||||||
err := m.conn.AddMatchSignal(
|
|
||||||
dbus.WithMatchInterface("net.connman.Service"),
|
|
||||||
dbus.WithMatchSender("net.connman"),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
go m.monitorSignals(ctx, m.signalChan, state)
|
|
||||||
m.callback(state)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *connmanMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case signal, ok := <-signalChan:
|
|
||||||
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" {
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
if state != lastState {
|
|
||||||
lastState = state
|
|
||||||
m.callback(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *connmanMonitor) Close() error {
|
|
||||||
if m.cancel != nil {
|
|
||||||
m.cancel()
|
|
||||||
}
|
|
||||||
if m.signalChan != nil {
|
|
||||||
m.conn.RemoveSignal(m.signalChan)
|
|
||||||
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,190 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type iwdMonitor struct {
|
|
||||||
conn *dbus.Conn
|
|
||||||
callback func(adapter.WIFIState)
|
|
||||||
cancel context.CancelFunc
|
|
||||||
signalChan chan *dbus.Signal
|
|
||||||
}
|
|
||||||
|
|
||||||
func newIWDMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
||||||
conn, err := dbus.ConnectSystemBus()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
iwdObj := conn.Object("net.connman.iwd", "/")
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
call := iwdObj.CallWithContext(ctx, "org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0)
|
|
||||||
if call.Err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, call.Err
|
|
||||||
}
|
|
||||||
return &iwdMonitor{conn: conn, callback: callback}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *iwdMonitor) ReadWIFIState() adapter.WIFIState {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
iwdObj := m.conn.Object("net.connman.iwd", "/")
|
|
||||||
var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
|
|
||||||
err := iwdObj.CallWithContext(ctx, "org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0).Store(&objects)
|
|
||||||
if err != nil {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, interfaces := range objects {
|
|
||||||
stationProps, hasStation := interfaces["net.connman.iwd.Station"]
|
|
||||||
if !hasStation {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
stateVariant, hasState := stationProps["State"]
|
|
||||||
if !hasState {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
state, ok := stateVariant.Value().(string)
|
|
||||||
if !ok || state != "connected" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedNetworkVariant, hasNetwork := stationProps["ConnectedNetwork"]
|
|
||||||
if !hasNetwork {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
networkPath, ok := connectedNetworkVariant.Value().(dbus.ObjectPath)
|
|
||||||
if !ok || networkPath == "/" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
networkInterfaces, hasNetworkPath := objects[networkPath]
|
|
||||||
if !hasNetworkPath {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
networkProps, hasNetworkInterface := networkInterfaces["net.connman.iwd.Network"]
|
|
||||||
if !hasNetworkInterface {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
nameVariant, hasName := networkProps["Name"]
|
|
||||||
if !hasName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ssid, ok := nameVariant.Value().(string)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedBSSVariant, hasBSS := stationProps["ConnectedAccessPoint"]
|
|
||||||
if !hasBSS {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
bssPath, ok := connectedBSSVariant.Value().(dbus.ObjectPath)
|
|
||||||
if !ok || bssPath == "/" {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
|
|
||||||
bssInterfaces, hasBSSPath := objects[bssPath]
|
|
||||||
if !hasBSSPath {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
|
|
||||||
bssProps, hasBSSInterface := bssInterfaces["net.connman.iwd.BasicServiceSet"]
|
|
||||||
if !hasBSSInterface {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
|
|
||||||
addressVariant, hasAddress := bssProps["Address"]
|
|
||||||
if !hasAddress {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
bssid, ok := addressVariant.Value().(string)
|
|
||||||
if !ok {
|
|
||||||
return adapter.WIFIState{SSID: ssid}
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter.WIFIState{
|
|
||||||
SSID: ssid,
|
|
||||||
BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *iwdMonitor) Start() error {
|
|
||||||
if m.callback == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
m.cancel = cancel
|
|
||||||
|
|
||||||
m.signalChan = make(chan *dbus.Signal, 10)
|
|
||||||
m.conn.Signal(m.signalChan)
|
|
||||||
|
|
||||||
err := m.conn.AddMatchSignal(
|
|
||||||
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
|
||||||
dbus.WithMatchSender("net.connman.iwd"),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
go m.monitorSignals(ctx, m.signalChan, state)
|
|
||||||
m.callback(state)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *iwdMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case signal, ok := <-signalChan:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
if state != lastState {
|
|
||||||
lastState = state
|
|
||||||
m.callback(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *iwdMonitor) Close() error {
|
|
||||||
if m.cancel != nil {
|
|
||||||
m.cancel()
|
|
||||||
}
|
|
||||||
if m.signalChan != nil {
|
|
||||||
m.conn.RemoveSignal(m.signalChan)
|
|
||||||
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,165 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type networkManagerMonitor struct {
|
|
||||||
conn *dbus.Conn
|
|
||||||
callback func(adapter.WIFIState)
|
|
||||||
cancel context.CancelFunc
|
|
||||||
signalChan chan *dbus.Signal
|
|
||||||
}
|
|
||||||
|
|
||||||
func newNetworkManagerMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
||||||
conn, err := dbus.ConnectSystemBus()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nmObj := conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
var state uint32
|
|
||||||
err = nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "State").Store(&state)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &networkManagerMonitor{conn: conn, callback: callback}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, connectionPath := range activeConnectionPaths {
|
|
||||||
connObj := m.conn.Object("org.freedesktop.NetworkManager", connectionPath)
|
|
||||||
|
|
||||||
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 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
apObj := m.conn.Object("org.freedesktop.NetworkManager", accessPointPath)
|
|
||||||
|
|
||||||
var ssidBytes []byte
|
|
||||||
err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "Ssid").Store(&ssidBytes)
|
|
||||||
if err != nil {
|
|
||||||
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{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *networkManagerMonitor) Start() error {
|
|
||||||
if m.callback == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
m.cancel = cancel
|
|
||||||
|
|
||||||
m.signalChan = make(chan *dbus.Signal, 10)
|
|
||||||
m.conn.Signal(m.signalChan)
|
|
||||||
|
|
||||||
err := m.conn.AddMatchSignal(
|
|
||||||
dbus.WithMatchSender("org.freedesktop.NetworkManager"),
|
|
||||||
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
go m.monitorSignals(ctx, m.signalChan, state)
|
|
||||||
m.callback(state)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *networkManagerMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case signal, ok := <-signalChan:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
if state != lastState {
|
|
||||||
lastState = state
|
|
||||||
m.callback(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *networkManagerMonitor) Close() error {
|
|
||||||
if m.cancel != nil {
|
|
||||||
m.cancel()
|
|
||||||
}
|
|
||||||
if m.signalChan != nil {
|
|
||||||
m.conn.RemoveSignal(m.signalChan)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
||||||
socketDirs := []string{"/var/run/wpa_supplicant", "/run/wpa_supplicant"}
|
|
||||||
for _, socketDir := range socketDirs {
|
|
||||||
entries, err := os.ReadDir(socketDir)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.IsDir() || entry.Name() == "." || entry.Name() == ".." {
|
|
||||||
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"}
|
|
||||||
remoteAddr := &net.UnixAddr{Name: socketPath, Net: "unixgram"}
|
|
||||||
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
return &wpaSupplicantMonitor{socketPath: socketPath, callback: callback}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, os.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
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"}
|
|
||||||
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
|
||||||
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
conn.SetDeadline(time.Now().Add(3 * time.Second))
|
|
||||||
|
|
||||||
status, err := m.sendCommand(conn, "STATUS")
|
|
||||||
if err != nil {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ssid, bssid string
|
|
||||||
var connected bool
|
|
||||||
scanner := bufio.NewScanner(strings.NewReader(status))
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if strings.HasPrefix(line, "wpa_state=") {
|
|
||||||
state := strings.TrimPrefix(line, "wpa_state=")
|
|
||||||
connected = state == "COMPLETED"
|
|
||||||
} else if strings.HasPrefix(line, "ssid=") {
|
|
||||||
ssid = strings.TrimPrefix(line, "ssid=")
|
|
||||||
} else if strings.HasPrefix(line, "bssid=") {
|
|
||||||
bssid = strings.TrimPrefix(line, "bssid=")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !connected || ssid == "" {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter.WIFIState{
|
|
||||||
SSID: ssid,
|
|
||||||
BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 4096)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
response := string(buf[:n])
|
|
||||||
if strings.HasPrefix(response, "FAIL") {
|
|
||||||
return "", os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(response), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *wpaSupplicantMonitor) Start() error {
|
|
||||||
if m.callback == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
m.cancel = cancel
|
|
||||||
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
go m.monitorEvents(ctx, state)
|
|
||||||
m.callback(state)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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"}
|
|
||||||
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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"))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 4096)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
if err != nil || !strings.HasPrefix(string(buf[:n]), "OK") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
debounceMutex.Lock()
|
|
||||||
if debounceTimer != nil {
|
|
||||||
debounceTimer.Stop()
|
|
||||||
}
|
|
||||||
debounceMutex.Unlock()
|
|
||||||
conn.Write([]byte("DETACH"))
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
consecutiveErrors = 0
|
|
||||||
|
|
||||||
msg := string(buf[:n])
|
|
||||||
if strings.Contains(msg, "CTRL-EVENT-CONNECTED") || strings.Contains(msg, "CTRL-EVENT-DISCONNECTED") {
|
|
||||||
debounceMutex.Lock()
|
|
||||||
if debounceTimer != nil {
|
|
||||||
debounceTimer.Stop()
|
|
||||||
}
|
|
||||||
debounceTimer = time.AfterFunc(500*time.Millisecond, func() {
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
if state != lastState {
|
|
||||||
lastState = state
|
|
||||||
m.callback(state)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
debounceMutex.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,27 +0,0 @@
|
|||||||
//go:build !linux && !windows
|
|
||||||
|
|
||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stubWIFIMonitor struct{}
|
|
||||||
|
|
||||||
func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *stubWIFIMonitor) ReadWIFIState() adapter.WIFIState {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *stubWIFIMonitor) Start() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *stubWIFIMonitor) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -114,17 +114,13 @@ func startACME(ctx context.Context, logger logger.Logger, options option.Inbound
|
|||||||
switch dnsOptions.Provider {
|
switch dnsOptions.Provider {
|
||||||
case C.DNSProviderAliDNS:
|
case C.DNSProviderAliDNS:
|
||||||
solver.DNSProvider = &alidns.Provider{
|
solver.DNSProvider = &alidns.Provider{
|
||||||
CredentialInfo: alidns.CredentialInfo{
|
AccKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
||||||
AccessKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
AccKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
||||||
AccessKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
||||||
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
|
||||||
SecurityToken: dnsOptions.AliDNSOptions.SecurityToken,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
case C.DNSProviderCloudflare:
|
case C.DNSProviderCloudflare:
|
||||||
solver.DNSProvider = &cloudflare.Provider{
|
solver.DNSProvider = &cloudflare.Provider{
|
||||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||||
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
|
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
|
||||||
|
|||||||
@@ -119,19 +119,21 @@ func (d *defaultDialer) dialContext(ctx context.Context, destination M.Socksaddr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tlsConn, err := aTLS.ClientHandshake(ctx, conn, d.config)
|
tlsConn, err := ClientHandshake(ctx, conn, d.config)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
conn.Close()
|
return tlsConn, nil
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
if echRetry {
|
||||||
var echErr *tls.ECHRejectionError
|
var echErr *tls.ECHRejectionError
|
||||||
if echRetry && errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 {
|
if errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 {
|
||||||
if echConfig, isECH := d.config.(ECHCapableConfig); isECH {
|
if echConfig, isECH := d.config.(ECHCapableConfig); isECH {
|
||||||
echConfig.SetECHConfigList(echErr.RetryConfigList)
|
echConfig.SetECHConfigList(echErr.RetryConfigList)
|
||||||
return d.dialContext(ctx, destination, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, err
|
return d.dialContext(ctx, destination, false)
|
||||||
}
|
}
|
||||||
return tlsConn, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *defaultDialer) Upstream() any {
|
func (d *defaultDialer) Upstream() any {
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, op
|
|||||||
return &ECHClientConfig{
|
return &ECHClientConfig{
|
||||||
ECHCapableConfig: clientConfig,
|
ECHCapableConfig: clientConfig,
|
||||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
||||||
queryServerName: options.ECH.QueryServerName,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,11 +108,10 @@ func parseECHKeys(echKey []byte) ([]tls.EncryptedClientHelloKey, error) {
|
|||||||
|
|
||||||
type ECHClientConfig struct {
|
type ECHClientConfig struct {
|
||||||
ECHCapableConfig
|
ECHCapableConfig
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
dnsRouter adapter.DNSRouter
|
dnsRouter adapter.DNSRouter
|
||||||
queryServerName string
|
lastTTL time.Duration
|
||||||
lastTTL time.Duration
|
lastUpdate time.Time
|
||||||
lastUpdate time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
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()
|
s.access.Lock()
|
||||||
defer s.access.Unlock()
|
defer s.access.Unlock()
|
||||||
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
|
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
|
||||||
queryServerName := s.queryServerName
|
|
||||||
if queryServerName == "" {
|
|
||||||
queryServerName = s.ServerName()
|
|
||||||
}
|
|
||||||
message := &mDNS.Msg{
|
message := &mDNS.Msg{
|
||||||
MsgHdr: mDNS.MsgHdr{
|
MsgHdr: mDNS.MsgHdr{
|
||||||
RecursionDesired: true,
|
RecursionDesired: true,
|
||||||
},
|
},
|
||||||
Question: []mDNS.Question{
|
Question: []mDNS.Question{
|
||||||
{
|
{
|
||||||
Name: mDNS.Fqdn(queryServerName),
|
Name: mDNS.Fqdn(s.ServerName()),
|
||||||
Qtype: mDNS.TypeHTTPS,
|
Qtype: mDNS.TypeHTTPS,
|
||||||
Qclass: mDNS.ClassINET,
|
Qclass: mDNS.ClassINET,
|
||||||
},
|
},
|
||||||
@@ -181,12 +175,7 @@ func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ECHClientConfig) Clone() Config {
|
func (s *ECHClientConfig) Clone() Config {
|
||||||
return &ECHClientConfig{
|
return &ECHClientConfig{ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig), dnsRouter: s.dnsRouter, lastUpdate: s.lastUpdate}
|
||||||
ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig),
|
|
||||||
dnsRouter: s.dnsRouter,
|
|
||||||
queryServerName: s.queryServerName,
|
|
||||||
lastUpdate: s.lastUpdate,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
|
func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
|
||||||
|
|||||||
23
common/tls/ech_stub.go
Normal file
23
common/tls/ech_stub.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//go:build !go1.24
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, options option.OutboundTLSOptions) (Config, error) {
|
||||||
|
return nil, E.New("ECH requires go1.24, please recompile your binary.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions, tlsConfig *tls.Config, echKeyPath *string) error {
|
||||||
|
return E.New("ECH requires go1.24, please recompile your binary.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *STDServerConfig) setECHServerConfig(echKey []byte) error {
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
@@ -68,10 +68,7 @@ func NewRealityServer(ctx context.Context, logger log.ContextLogger, options opt
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(options.CurvePreferences) > 0 {
|
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
||||||
return nil, E.New("curve preferences is unavailable in reality")
|
|
||||||
}
|
|
||||||
if len(options.Certificate) > 0 || options.CertificatePath != "" || len(options.ClientCertificatePublicKeySHA256) > 0 {
|
|
||||||
return nil, E.New("certificate is unavailable in reality")
|
return nil, E.New("certificate is unavailable in reality")
|
||||||
}
|
}
|
||||||
if len(options.Key) > 0 || options.KeyPath != "" {
|
if len(options.Key) > 0 || options.KeyPath != "" {
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -111,15 +108,6 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(options.CertificatePublicKeySHA256) > 0 {
|
|
||||||
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
|
||||||
return nil, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
|
|
||||||
}
|
|
||||||
tlsConfig.InsecureSkipVerify = true
|
|
||||||
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
|
||||||
return verifyPublicKeySHA256(options.CertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(options.ALPN) > 0 {
|
if len(options.ALPN) > 0 {
|
||||||
tlsConfig.NextProtos = options.ALPN
|
tlsConfig.NextProtos = options.ALPN
|
||||||
}
|
}
|
||||||
@@ -149,9 +137,6 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, curve := range options.CurvePreferences {
|
|
||||||
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curve))
|
|
||||||
}
|
|
||||||
var certificate []byte
|
var certificate []byte
|
||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
@@ -169,35 +154,6 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
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}
|
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
var err error
|
var err error
|
||||||
@@ -219,22 +175,3 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyPublicKeySHA256(knownHashValues [][]byte, rawCerts [][]byte, timeFunc func() time.Time) error {
|
|
||||||
leafCertificate, err := x509.ParseCertificate(rawCerts[0])
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "failed to parse leaf certificate")
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKeyBytes, err := x509.MarshalPKIXPublicKey(leafCertificate.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "failed to marshal public key")
|
|
||||||
}
|
|
||||||
hashValue := sha256.Sum256(pubKeyBytes)
|
|
||||||
for _, value := range knownHashValues {
|
|
||||||
if bytes.Equal(value, hashValue[:]) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return E.New("unrecognized remote public key: ", base64.StdEncoding.EncodeToString(hashValue[:]))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -23,17 +22,16 @@ import (
|
|||||||
var errInsecureUnused = E.New("tls: insecure unused")
|
var errInsecureUnused = E.New("tls: insecure unused")
|
||||||
|
|
||||||
type STDServerConfig struct {
|
type STDServerConfig struct {
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
config *tls.Config
|
config *tls.Config
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
acmeService adapter.SimpleLifecycle
|
acmeService adapter.SimpleLifecycle
|
||||||
certificate []byte
|
certificate []byte
|
||||||
key []byte
|
key []byte
|
||||||
certificatePath string
|
certificatePath string
|
||||||
keyPath string
|
keyPath string
|
||||||
clientCertificatePath []string
|
echKeyPath string
|
||||||
echKeyPath string
|
watcher *fswatch.Watcher
|
||||||
watcher *fswatch.Watcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) ServerName() string {
|
func (c *STDServerConfig) ServerName() string {
|
||||||
@@ -113,9 +111,6 @@ func (c *STDServerConfig) startWatcher() error {
|
|||||||
if c.echKeyPath != "" {
|
if c.echKeyPath != "" {
|
||||||
watchPath = append(watchPath, c.echKeyPath)
|
watchPath = append(watchPath, c.echKeyPath)
|
||||||
}
|
}
|
||||||
if len(c.clientCertificatePath) > 0 {
|
|
||||||
watchPath = append(watchPath, c.clientCertificatePath...)
|
|
||||||
}
|
|
||||||
if len(watchPath) == 0 {
|
if len(watchPath) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -164,30 +159,6 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
c.config = config
|
c.config = config
|
||||||
c.access.Unlock()
|
c.access.Unlock()
|
||||||
c.logger.Info("reloaded TLS certificate")
|
c.logger.Info("reloaded TLS certificate")
|
||||||
} else if common.Contains(c.clientCertificatePath, path) {
|
|
||||||
clientCertificateCA := x509.NewCertPool()
|
|
||||||
var reloaded bool
|
|
||||||
for _, certPath := range c.clientCertificatePath {
|
|
||||||
content, err := os.ReadFile(certPath)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error(E.Cause(err, "reload certificate from ", c.clientCertificatePath))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
|
||||||
c.logger.Error(E.New("invalid client certificate file: ", certPath))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
reloaded = true
|
|
||||||
}
|
|
||||||
if !reloaded {
|
|
||||||
return E.New("client certificates is empty")
|
|
||||||
}
|
|
||||||
c.access.Lock()
|
|
||||||
config := c.config.Clone()
|
|
||||||
config.ClientCAs = clientCertificateCA
|
|
||||||
c.config = config
|
|
||||||
c.access.Unlock()
|
|
||||||
c.logger.Info("reloaded client certificates")
|
|
||||||
} else if path == c.echKeyPath {
|
} else if path == c.echKeyPath {
|
||||||
echKey, err := os.ReadFile(c.echKeyPath)
|
echKey, err := os.ReadFile(c.echKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -264,14 +235,8 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, curveID := range options.CurvePreferences {
|
var certificate []byte
|
||||||
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curveID))
|
var key []byte
|
||||||
}
|
|
||||||
tlsConfig.ClientAuth = tls.ClientAuthType(options.ClientAuthentication)
|
|
||||||
var (
|
|
||||||
certificate []byte
|
|
||||||
key []byte
|
|
||||||
)
|
|
||||||
if acmeService == nil {
|
if acmeService == nil {
|
||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
@@ -313,43 +278,6 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(options.ClientCertificate) > 0 || len(options.ClientCertificatePath) > 0 {
|
|
||||||
if tlsConfig.ClientAuth == tls.NoClientCert {
|
|
||||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {
|
|
||||||
if len(options.ClientCertificate) > 0 {
|
|
||||||
clientCertificateCA := x509.NewCertPool()
|
|
||||||
if !clientCertificateCA.AppendCertsFromPEM([]byte(strings.Join(options.ClientCertificate, "\n"))) {
|
|
||||||
return nil, E.New("invalid client certificate strings")
|
|
||||||
}
|
|
||||||
tlsConfig.ClientCAs = clientCertificateCA
|
|
||||||
} else if len(options.ClientCertificatePath) > 0 {
|
|
||||||
clientCertificateCA := x509.NewCertPool()
|
|
||||||
for _, path := range options.ClientCertificatePath {
|
|
||||||
content, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "read client certificate from ", path)
|
|
||||||
}
|
|
||||||
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
|
||||||
return nil, E.New("invalid client certificate file: ", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tlsConfig.ClientCAs = clientCertificateCA
|
|
||||||
} else if len(options.ClientCertificatePublicKeySHA256) > 0 {
|
|
||||||
if tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {
|
|
||||||
tlsConfig.ClientAuth = tls.RequireAnyClientCert
|
|
||||||
} else if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven {
|
|
||||||
tlsConfig.ClientAuth = tls.RequestClientCert
|
|
||||||
}
|
|
||||||
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
|
||||||
return verifyPublicKeySHA256(options.ClientCertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, E.New("missing client_certificate, client_certificate_path or client_certificate_public_key_sha256 for client authentication")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var echKeyPath string
|
var echKeyPath string
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath)
|
err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath)
|
||||||
@@ -358,15 +286,14 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
serverConfig := &STDServerConfig{
|
serverConfig := &STDServerConfig{
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
acmeService: acmeService,
|
acmeService: acmeService,
|
||||||
certificate: certificate,
|
certificate: certificate,
|
||||||
key: key,
|
key: key,
|
||||||
certificatePath: options.CertificatePath,
|
certificatePath: options.CertificatePath,
|
||||||
clientCertificatePath: options.ClientCertificatePath,
|
keyPath: options.KeyPath,
|
||||||
keyPath: options.KeyPath,
|
echKeyPath: echKeyPath,
|
||||||
echKeyPath: echKeyPath,
|
|
||||||
}
|
}
|
||||||
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||||
serverConfig.access.Lock()
|
serverConfig.access.Lock()
|
||||||
|
|||||||
@@ -167,15 +167,6 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
}
|
}
|
||||||
tlsConfig.InsecureServerNameToVerify = serverName
|
tlsConfig.InsecureServerNameToVerify = serverName
|
||||||
}
|
}
|
||||||
if len(options.CertificatePublicKeySHA256) > 0 {
|
|
||||||
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
|
||||||
return nil, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
|
|
||||||
}
|
|
||||||
tlsConfig.InsecureSkipVerify = true
|
|
||||||
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
|
||||||
return verifyPublicKeySHA256(options.CertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(options.ALPN) > 0 {
|
if len(options.ALPN) > 0 {
|
||||||
tlsConfig.NextProtos = options.ALPN
|
tlsConfig.NextProtos = options.ALPN
|
||||||
}
|
}
|
||||||
@@ -222,35 +213,6 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
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)
|
id, err := uTLSClientHelloID(options.UTLS.Fingerprint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
"github.com/sagernet/sing/common/observable"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil)
|
var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil)
|
||||||
@@ -22,7 +21,7 @@ var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil)
|
|||||||
type HistoryStorage struct {
|
type HistoryStorage struct {
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
delayHistory map[string]*adapter.URLTestHistory
|
delayHistory map[string]*adapter.URLTestHistory
|
||||||
updateHook *observable.Subscriber[struct{}]
|
updateHook chan<- struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHistoryStorage() *HistoryStorage {
|
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
|
s.updateHook = hook
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +60,10 @@ func (s *HistoryStorage) StoreURLTestHistory(tag string, history *adapter.URLTes
|
|||||||
func (s *HistoryStorage) notifyUpdated() {
|
func (s *HistoryStorage) notifyUpdated() {
|
||||||
updateHook := s.updateHook
|
updateHook := s.updateHook
|
||||||
if updateHook != nil {
|
if updateHook != nil {
|
||||||
updateHook.Emit(struct{}{})
|
select {
|
||||||
|
case updateHook <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ package constant
|
|||||||
const (
|
const (
|
||||||
CertificateStoreSystem = "system"
|
CertificateStoreSystem = "system"
|
||||||
CertificateStoreMozilla = "mozilla"
|
CertificateStoreMozilla = "mozilla"
|
||||||
CertificateStoreChrome = "chrome"
|
|
||||||
CertificateStoreNone = "none"
|
CertificateStoreNone = "none"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ import "time"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
DHCPTTL = time.Hour
|
DHCPTTL = time.Hour
|
||||||
DHCPTimeout = 5 * time.Second
|
DHCPTimeout = time.Minute
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ const (
|
|||||||
TypeDERP = "derp"
|
TypeDERP = "derp"
|
||||||
TypeResolved = "resolved"
|
TypeResolved = "resolved"
|
||||||
TypeSSMAPI = "ssm-api"
|
TypeSSMAPI = "ssm-api"
|
||||||
TypeCCM = "ccm"
|
|
||||||
TypeOCM = "ocm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ const (
|
|||||||
RuleActionTypeRoute = "route"
|
RuleActionTypeRoute = "route"
|
||||||
RuleActionTypeRouteOptions = "route-options"
|
RuleActionTypeRouteOptions = "route-options"
|
||||||
RuleActionTypeDirect = "direct"
|
RuleActionTypeDirect = "direct"
|
||||||
RuleActionTypeBypass = "bypass"
|
|
||||||
RuleActionTypeReject = "reject"
|
RuleActionTypeReject = "reject"
|
||||||
RuleActionTypeHijackDNS = "hijack-dns"
|
RuleActionTypeHijackDNS = "hijack-dns"
|
||||||
RuleActionTypeSniff = "sniff"
|
RuleActionTypeSniff = "sniff"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package constant
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TCPKeepAliveInitial = 5 * time.Minute
|
TCPKeepAliveInitial = 10 * time.Minute
|
||||||
TCPKeepAliveInterval = 75 * time.Second
|
TCPKeepAliveInterval = 75 * time.Second
|
||||||
TCPConnectTimeout = 5 * time.Second
|
TCPConnectTimeout = 5 * time.Second
|
||||||
TCPTimeout = 15 * time.Second
|
TCPTimeout = 15 * time.Second
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package daemon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ deprecated.Manager = (*deprecatedManager)(nil)
|
|
||||||
|
|
||||||
type deprecatedManager struct {
|
|
||||||
access sync.Mutex
|
|
||||||
notes []deprecated.Note
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) {
|
|
||||||
m.access.Lock()
|
|
||||||
defer m.access.Unlock()
|
|
||||||
m.notes = common.Uniq(append(m.notes, feature))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *deprecatedManager) Get() []deprecated.Note {
|
|
||||||
m.access.Lock()
|
|
||||||
defer m.access.Unlock()
|
|
||||||
notes := m.notes
|
|
||||||
m.notes = nil
|
|
||||||
return notes
|
|
||||||
}
|
|
||||||
@@ -1,702 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user