mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 02:27:19 +10:00
Compare commits
30 Commits
dev-ts-rel
...
v1.13.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d663278d66 | ||
|
|
2b207bb2ae | ||
|
|
97a02c9e70 | ||
|
|
15c295994d | ||
|
|
a06a0b5253 | ||
|
|
b5efc1d533 | ||
|
|
d2fe7ec481 | ||
|
|
d689419001 | ||
|
|
831f242ddb | ||
|
|
71ed614dc4 | ||
|
|
9eee3a04e1 | ||
|
|
6937c753e9 | ||
|
|
ae5b82df69 | ||
|
|
2df4812c31 | ||
|
|
182a15c3af | ||
|
|
fb8b1f8824 | ||
|
|
918d4ff3ca | ||
|
|
454fe61aa0 | ||
|
|
60c464fc13 | ||
|
|
11720b048a | ||
|
|
bde25018a4 | ||
|
|
44bad22f39 | ||
|
|
2305225cc6 | ||
|
|
8be89b54e5 | ||
|
|
a256159461 | ||
|
|
6c395e3ce7 | ||
|
|
93cd0e5df1 | ||
|
|
98c00bb120 | ||
|
|
8f90d500bc | ||
|
|
c113a27ecc |
1
.github/CRONET_GO_VERSION
vendored
1
.github/CRONET_GO_VERSION
vendored
@@ -1 +0,0 @@
|
||||
1cc61ad20399081362ccbc18d650432d1a6d42ec
|
||||
2
.github/setup_go_for_windows7.sh
vendored
2
.github/setup_go_for_windows7.sh
vendored
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION="1.25.5"
|
||||
VERSION="1.25.1"
|
||||
|
||||
mkdir -p $HOME/go
|
||||
cd $HOME/go
|
||||
|
||||
13
.github/update_cronet.sh
vendored
13
.github/update_cronet.sh
vendored
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
PROJECTS=$SCRIPT_DIR/../..
|
||||
|
||||
git -C $PROJECTS/cronet-go fetch origin main
|
||||
git -C $PROJECTS/cronet-go fetch origin go
|
||||
go get -x github.com/sagernet/cronet-go/all@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
||||
go get -x github.com/sagernet/cronet-go@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
||||
go mod tidy
|
||||
git -C $PROJECTS/cronet-go rev-parse origin/HEAD > "$SCRIPT_DIR/CRONET_GO_VERSION"
|
||||
357
.github/workflows/build.yml
vendored
357
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.5
|
||||
go-version: ^1.25.1
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@@ -69,25 +69,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { os: linux, arch: amd64, variant: purego, naive: true, openwrt: "x86_64" }
|
||||
- { os: linux, arch: amd64, variant: glibc, naive: true }
|
||||
- { os: linux, arch: amd64, variant: musl, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
||||
|
||||
- { os: linux, arch: arm64, variant: purego, naive: true, 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: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
||||
- { os: linux, arch: "386", go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
||||
- { os: linux, arch: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
|
||||
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
||||
- { os: linux, arch: arm, goarm: "5", openwrt: "arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" }
|
||||
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl, openwrt: "arm_arm1176jzf-s_vfp" }
|
||||
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
||||
- { os: linux, arch: mips, gomips: softfloat, openwrt: "mips_24kc mips_4kec mips_mips32" }
|
||||
- { os: linux, arch: mipsle, gomips: hardfloat, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc_24kf" }
|
||||
- { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
|
||||
@@ -99,28 +87,35 @@ jobs:
|
||||
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
|
||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||
|
||||
- { os: windows, arch: amd64 }
|
||||
- { os: windows, arch: amd64, legacy_win7: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: "386" }
|
||||
- { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
|
||||
- { os: windows, arch: arm64 }
|
||||
|
||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android23" }
|
||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi23" }
|
||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android23" }
|
||||
- { os: android, arch: "386", ndk: "i686-linux-android23" }
|
||||
- { os: darwin, arch: amd64 }
|
||||
- { os: darwin, arch: arm64 }
|
||||
- { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||
|
||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
|
||||
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
|
||||
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.5
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Go 1.24
|
||||
if: matrix.legacy_go124
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.24.10
|
||||
go-version: ~1.24.6
|
||||
- name: Cache Go for Windows 7
|
||||
if: matrix.legacy_win7
|
||||
id: cache-go-for-windows7
|
||||
@@ -128,7 +123,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
~/go/go_win7
|
||||
key: go_win7_1255
|
||||
key: go_win7_1251
|
||||
- name: Setup Go for Windows 7
|
||||
if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
|
||||
run: |-
|
||||
@@ -144,45 +139,6 @@ jobs:
|
||||
with:
|
||||
ndk-version: r28
|
||||
local-cache: true
|
||||
- name: Clone cronet-go
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
|
||||
git init ~/cronet-go
|
||||
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
|
||||
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION"
|
||||
git -C ~/cronet-go checkout FETCH_HEAD
|
||||
git -C ~/cronet-go submodule update --init --recursive --depth=1
|
||||
- name: Cache Chromium toolchain
|
||||
if: matrix.naive
|
||||
id: cache-chromium-toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/cronet-go/naiveproxy/src/third_party/llvm-build/Release+Asserts
|
||||
~/cronet-go/naiveproxy/src/out/sysroot-build
|
||||
key: chromium-toolchain-${{ matrix.arch }}-${{ matrix.variant }}-${{ hashFiles('.github/CRONET_GO_VERSION') }}
|
||||
- name: Download Chromium toolchain
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
if [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl download-toolchain
|
||||
else
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} download-toolchain
|
||||
fi
|
||||
- name: Set Chromium toolchain environment
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
if [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl env >> $GITHUB_ENV
|
||||
else
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} env >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||
@@ -190,70 +146,10 @@ jobs:
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.naive }}" == "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound"
|
||||
fi
|
||||
if [[ "${{ matrix.variant }}" == "purego" ]]; then
|
||||
TAGS="${TAGS},with_purego"
|
||||
elif [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
TAGS="${TAGS},with_musl"
|
||||
fi
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build (purego)
|
||||
if: matrix.variant == 'purego'
|
||||
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 == ''
|
||||
- name: Build
|
||||
if: matrix.os != 'android'
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
@@ -297,11 +193,6 @@ jobs:
|
||||
elif [[ -n "${{ matrix.legacy_name }}" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
||||
fi
|
||||
if [[ "${{ matrix.variant }}" == "glibc" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-glibc"
|
||||
elif [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-musl"
|
||||
fi
|
||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
||||
PKG_VERSION="${PKG_VERSION//-/\~}"
|
||||
@@ -369,12 +260,8 @@ jobs:
|
||||
-p "dist/openwrt.deb" \
|
||||
--architecture all \
|
||||
dist/sing-box=/usr/bin/sing-box
|
||||
SUFFIX=""
|
||||
if [[ "${{ matrix.variant }}" == "musl" ]]; then
|
||||
SUFFIX="_musl"
|
||||
fi
|
||||
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
|
||||
rm "dist/openwrt.deb"
|
||||
- name: Archive
|
||||
@@ -388,177 +275,15 @@ jobs:
|
||||
zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
|
||||
else
|
||||
cp sing-box "${DIR_NAME}"
|
||||
if [ -f libcronet.so ]; then
|
||||
cp libcronet.so "${DIR_NAME}"
|
||||
fi
|
||||
tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
|
||||
fi
|
||||
rm -r "${DIR_NAME}"
|
||||
- name: Cleanup
|
||||
run: rm -f dist/sing-box dist/libcronet.so
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}${{ matrix.variant && format('-{0}', matrix.variant) }}
|
||||
path: "dist"
|
||||
build_darwin:
|
||||
name: Build Darwin binaries
|
||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
||||
runs-on: macos-latest
|
||||
needs:
|
||||
- calculate_version
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { arch: amd64 }
|
||||
- { arch: arm64 }
|
||||
- { arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
if: ${{ ! matrix.legacy_go124 }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.3
|
||||
- name: Setup Go 1.24
|
||||
if: matrix.legacy_go124
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.24.6
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.legacy_go124 }}" != "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound"
|
||||
fi
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
||||
./cmd/sing-box
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
GOOS: darwin
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set name
|
||||
run: |-
|
||||
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-darwin-${{ matrix.arch }}"
|
||||
if [[ -n "${{ matrix.legacy_name }}" ]]; then
|
||||
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}"
|
||||
fi
|
||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||
- name: Archive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd dist
|
||||
mkdir -p "${DIR_NAME}"
|
||||
cp ../LICENSE "${DIR_NAME}"
|
||||
cp sing-box "${DIR_NAME}"
|
||||
tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
|
||||
rm -r "${DIR_NAME}"
|
||||
- name: Cleanup
|
||||
run: rm dist/sing-box
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: binary-darwin_${{ matrix.arch }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
||||
path: "dist"
|
||||
build_windows:
|
||||
name: Build Windows binaries
|
||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
||||
runs-on: windows-latest
|
||||
needs:
|
||||
- calculate_version
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { arch: amd64, naive: true }
|
||||
- { arch: "386" }
|
||||
- { arch: arm64, naive: true }
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.4
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$env:GITHUB_ENV"
|
||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||
- name: Build
|
||||
if: matrix.naive
|
||||
run: |
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_naive_outbound,with_purego,badlinkname,tfogo_checklinkname0" `
|
||||
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -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 }}
|
||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
||||
path: "dist"
|
||||
build_android:
|
||||
name: Build Android
|
||||
@@ -575,7 +300,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.5
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@@ -623,9 +348,9 @@ jobs:
|
||||
- name: Build
|
||||
run: |-
|
||||
mkdir clients/android/app/libs
|
||||
cp *.aar clients/android/app/libs
|
||||
cp libbox.aar clients/android/app/libs
|
||||
cd clients/android
|
||||
./gradlew :app:assembleOtherRelease :app:assembleOtherLegacyRelease
|
||||
./gradlew :app:assemblePlayRelease :app:assembleOtherRelease
|
||||
env:
|
||||
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
@@ -633,18 +358,8 @@ jobs:
|
||||
- name: Prepare upload
|
||||
run: |-
|
||||
mkdir -p dist
|
||||
#cp clients/android/app/build/outputs/apk/play/release/*.apk dist
|
||||
cp clients/android/app/build/outputs/apk/other/release/*.apk dist
|
||||
cp clients/android/app/build/outputs/apk/otherLegacy/release/*.apk dist
|
||||
VERSION_CODE=$(grep VERSION_CODE clients/android/version.properties | cut -d= -f2)
|
||||
VERSION_NAME=$(grep VERSION_NAME clients/android/version.properties | cut -d= -f2)
|
||||
cat > dist/SFA-version-metadata.json << EOF
|
||||
{
|
||||
"version_code": ${VERSION_CODE},
|
||||
"version_name": "${VERSION_NAME}"
|
||||
}
|
||||
EOF
|
||||
cat dist/SFA-version-metadata.json
|
||||
cp clients/android/app/build/outputs/apk/play/release/*.apk dist
|
||||
cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -665,7 +380,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.5
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Android NDK
|
||||
id: setup-ndk
|
||||
uses: nttld/setup-ndk@v1
|
||||
@@ -706,7 +421,7 @@ jobs:
|
||||
run: |-
|
||||
go run -v ./cmd/internal/update_android_version --ci
|
||||
mkdir clients/android/app/libs
|
||||
cp *.aar clients/android/app/libs
|
||||
cp libbox.aar clients/android/app/libs
|
||||
cd clients/android
|
||||
echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
|
||||
./gradlew :app:publishPlayReleaseBundle
|
||||
@@ -718,7 +433,7 @@ jobs:
|
||||
build_apple:
|
||||
name: Build Apple clients
|
||||
runs-on: macos-26
|
||||
if: false # github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Apple' || inputs.build == 'app-store' || inputs.build == 'iOS' || inputs.build == 'macOS' || inputs.build == 'tvOS' || inputs.build == 'macOS-standalone'
|
||||
if: false
|
||||
needs:
|
||||
- calculate_version
|
||||
strategy:
|
||||
@@ -764,7 +479,7 @@ jobs:
|
||||
if: matrix.if
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.5
|
||||
go-version: ^1.25.1
|
||||
- name: Set tag
|
||||
if: matrix.if
|
||||
run: |-
|
||||
@@ -883,7 +598,7 @@ jobs:
|
||||
--app-drop-link 0 0 \
|
||||
--skip-jenkins \
|
||||
SFM.dmg "${{ matrix.export_path }}/SFM.app"
|
||||
xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
|
||||
xcrun notarytool submit "SFM.dmg" --wait --keychain-profile "notarytool-password"
|
||||
cd "${{ matrix.archive }}"
|
||||
zip -r SFM.dSYMs.zip dSYMs
|
||||
popd
|
||||
@@ -904,8 +619,6 @@ jobs:
|
||||
needs:
|
||||
- calculate_version
|
||||
- build
|
||||
- build_darwin
|
||||
- build_windows
|
||||
- build_android
|
||||
- build_apple
|
||||
steps:
|
||||
|
||||
149
.github/workflows/docker.yml
vendored
149
.github/workflows/docker.yml
vendored
@@ -1,10 +1,6 @@
|
||||
name: Publish Docker Images
|
||||
|
||||
on:
|
||||
#push:
|
||||
# branches:
|
||||
# - main-next
|
||||
# - dev-next
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
@@ -17,134 +13,8 @@ env:
|
||||
REGISTRY_IMAGE: ghcr.io/sagernet/sing-box
|
||||
|
||||
jobs:
|
||||
build_binary:
|
||||
name: Build binary
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
# Naive-enabled builds (musl)
|
||||
- { arch: amd64, naive: true, docker_platform: "linux/amd64" }
|
||||
- { arch: arm64, naive: true, docker_platform: "linux/arm64" }
|
||||
- { arch: "386", naive: true, docker_platform: "linux/386" }
|
||||
- { arch: arm, goarm: "7", naive: true, docker_platform: "linux/arm/v7" }
|
||||
# Non-naive builds
|
||||
- { arch: arm, goarm: "6", docker_platform: "linux/arm/v6" }
|
||||
- { arch: ppc64le, docker_platform: "linux/ppc64le" }
|
||||
- { arch: riscv64, docker_platform: "linux/riscv64" }
|
||||
- { arch: s390x, docker_platform: "linux/s390x" }
|
||||
steps:
|
||||
- name: Get commit to build
|
||||
id: ref
|
||||
run: |-
|
||||
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
|
||||
ref="${{ github.ref_name }}"
|
||||
else
|
||||
ref="${{ github.event.inputs.tag }}"
|
||||
fi
|
||||
echo "ref=$ref"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
ref: ${{ steps.ref.outputs.ref }}
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.4
|
||||
- name: Clone cronet-go
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
|
||||
git init ~/cronet-go
|
||||
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
|
||||
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION"
|
||||
git -C ~/cronet-go checkout FETCH_HEAD
|
||||
git -C ~/cronet-go submodule update --init --recursive --depth=1
|
||||
- name: Cache Chromium toolchain
|
||||
if: matrix.naive
|
||||
id: cache-chromium-toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/cronet-go/naiveproxy/src/third_party/llvm-build/Release+Asserts
|
||||
~/cronet-go/naiveproxy/src/out/sysroot-build
|
||||
key: chromium-toolchain-${{ matrix.arch }}-musl-${{ hashFiles('.github/CRONET_GO_VERSION') }}
|
||||
- name: Download Chromium toolchain
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl download-toolchain
|
||||
- name: Set version
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
VERSION=$(go run ./cmd/internal/read_tag)
|
||||
echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"
|
||||
- name: Set Chromium toolchain environment
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl env >> $GITHUB_ENV
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.naive }}" == "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound,with_musl"
|
||||
fi
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build (naive)
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -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:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
@@ -177,16 +47,6 @@ jobs:
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
- name: Download binary
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: binary-${{ env.PLATFORM_PAIR }}
|
||||
path: .
|
||||
- name: Prepare binary
|
||||
run: |
|
||||
# Find and make the binary executable
|
||||
chmod +x sing-box-*
|
||||
ls -la sing-box-*
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Setup Docker Buildx
|
||||
@@ -208,7 +68,8 @@ jobs:
|
||||
with:
|
||||
platforms: ${{ matrix.platform }}
|
||||
context: .
|
||||
file: Dockerfile.binary
|
||||
build-args: |
|
||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
- name: Export digest
|
||||
@@ -226,7 +87,7 @@ jobs:
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build_docker
|
||||
- build
|
||||
steps:
|
||||
- name: Get commit to build
|
||||
id: ref
|
||||
@@ -260,7 +121,6 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Create manifest list and push
|
||||
if: github.event_name != 'push'
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
@@ -268,7 +128,6 @@ jobs:
|
||||
-t "${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}" \
|
||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
||||
- name: Inspect image
|
||||
if: github.event_name != 'push'
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.latest }}
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
version: v2.4.0
|
||||
version: latest
|
||||
args: --timeout=30m
|
||||
install-mode: binary
|
||||
verify: false
|
||||
|
||||
76
.github/workflows/linux.yml
vendored
76
.github/workflows/linux.yml
vendored
@@ -1,10 +1,6 @@
|
||||
name: Build Linux Packages
|
||||
|
||||
on:
|
||||
#push:
|
||||
# branches:
|
||||
# - main-next
|
||||
# - dev-next
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
@@ -34,7 +30,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.5
|
||||
go-version: ^1.25.1
|
||||
- name: Check input version
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |-
|
||||
@@ -56,13 +52,11 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Naive-enabled builds (musl)
|
||||
- { os: linux, arch: amd64, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
||||
- { os: linux, arch: arm64, naive: true, debian: arm64, rpm: aarch64, pacman: aarch64 }
|
||||
- { os: linux, arch: "386", naive: true, debian: i386, rpm: i386 }
|
||||
- { os: linux, arch: arm, goarm: "7", naive: true, debian: armhf, rpm: armv7hl, pacman: armv7hl }
|
||||
# Non-naive builds (unsupported architectures)
|
||||
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
||||
- { os: linux, arch: "386", debian: i386, rpm: i386 }
|
||||
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl }
|
||||
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl }
|
||||
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64 }
|
||||
- { os: linux, arch: mips64le, debian: mips64el, rpm: mips64el }
|
||||
- { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel }
|
||||
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
||||
@@ -77,38 +71,13 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.25.5
|
||||
- name: Clone cronet-go
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
|
||||
git init ~/cronet-go
|
||||
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
|
||||
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION"
|
||||
git -C ~/cronet-go checkout FETCH_HEAD
|
||||
git -C ~/cronet-go submodule update --init --recursive --depth=1
|
||||
- name: Cache Chromium toolchain
|
||||
if: matrix.naive
|
||||
id: cache-chromium-toolchain
|
||||
uses: actions/cache@v4
|
||||
go-version: ^1.25.1
|
||||
- name: Setup Android NDK
|
||||
if: matrix.os == 'android'
|
||||
uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
path: |
|
||||
~/cronet-go/naiveproxy/src/third_party/llvm-build/Release+Asserts
|
||||
~/cronet-go/naiveproxy/src/out/sysroot-build
|
||||
key: chromium-toolchain-${{ matrix.arch }}-musl-${{ hashFiles('.github/CRONET_GO_VERSION') }}
|
||||
- name: Download Chromium toolchain
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl download-toolchain
|
||||
- name: Set Chromium toolchain environment
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
cd ~/cronet-go
|
||||
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} --libc=musl env >> $GITHUB_ENV
|
||||
ndk-version: r28
|
||||
local-cache: true
|
||||
- name: Set tag
|
||||
run: |-
|
||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||
@@ -116,27 +85,9 @@ jobs:
|
||||
- name: Set build tags
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0'
|
||||
if [[ "${{ matrix.naive }}" == "true" ]]; then
|
||||
TAGS="${TAGS},with_naive_outbound,with_musl"
|
||||
fi
|
||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||
- name: Build (naive)
|
||||
if: matrix.naive
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -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 }}
|
||||
- name: Build
|
||||
run: |
|
||||
set -xeuo pipefail
|
||||
mkdir -p dist
|
||||
@@ -234,6 +185,5 @@ jobs:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
- name: Publish packages
|
||||
if: github.event_name != 'push'
|
||||
run: |-
|
||||
ls dist | xargs -I {} curl -F "package=@dist/{}" https://${{ secrets.FURY_TOKEN }}@push.fury.io/sagernet/
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,6 +15,4 @@
|
||||
.DS_Store
|
||||
/config.d/
|
||||
/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 VERSION=$(go run ./cmd/internal/read_tag) \
|
||||
&& go build -v -trimpath -tags \
|
||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" \
|
||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale" \
|
||||
-o /go/bin/sing-box \
|
||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
||||
./cmd/sing-box
|
||||
FROM --platform=$TARGETPLATFORM alpine AS dist
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache --upgrade bash tzdata ca-certificates nftables
|
||||
&& apk upgrade \
|
||||
&& apk add bash tzdata ca-certificates nftables \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
COPY --from=builder /go/bin/sing-box /usr/local/bin/sing-box
|
||||
ENTRYPOINT ["sing-box"]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache --upgrade bash tzdata ca-certificates nftables
|
||||
COPY sing-box-${TARGETARCH}${TARGETVARIANT} /usr/local/bin/sing-box
|
||||
ENTRYPOINT ["sing-box"]
|
||||
13
Makefile
13
Makefile
@@ -1,6 +1,6 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0
|
||||
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale
|
||||
|
||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||
@@ -37,11 +37,8 @@ fmt:
|
||||
@gofmt -s -w .
|
||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||
|
||||
fmt_docs:
|
||||
go run ./cmd/internal/format_docs
|
||||
|
||||
fmt_install:
|
||||
go install -v mvdan.cc/gofumpt@v0.8.0
|
||||
go install -v mvdan.cc/gofumpt@latest
|
||||
go install -v github.com/daixiang0/gci@latest
|
||||
|
||||
lint:
|
||||
@@ -52,7 +49,7 @@ lint:
|
||||
GOOS=freebsd golangci-lint run ./...
|
||||
|
||||
lint_install:
|
||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||
|
||||
proto:
|
||||
@go run ./cmd/internal/protogen
|
||||
@@ -252,8 +249,8 @@ lib:
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib_install:
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.10
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@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.8
|
||||
|
||||
docs:
|
||||
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
|
||||
|
||||
The universal proxy platform.
|
||||
|
||||
@@ -27,6 +27,8 @@ type DNSClient interface {
|
||||
Start()
|
||||
Exchange(ctx context.Context, transport DNSTransport, message *dns.Msg, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error)
|
||||
Lookup(ctx context.Context, transport DNSTransport, domain string, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error)
|
||||
LookupCache(domain string, strategy C.DomainStrategy) ([]netip.Addr, bool)
|
||||
ExchangeCache(ctx context.Context, message *dns.Msg) (*dns.Msg, bool)
|
||||
ClearCache()
|
||||
}
|
||||
|
||||
@@ -68,7 +70,6 @@ type DNSTransport interface {
|
||||
Type() string
|
||||
Tag() string
|
||||
Dependencies() []string
|
||||
Reset()
|
||||
Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ adapter.EndpointManager = (*Manager)(nil)
|
||||
@@ -48,14 +46,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
for _, endpoint := range m.endpoints {
|
||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(endpoint, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -72,15 +66,11 @@ func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
var err error
|
||||
for _, endpoint := range endpoints {
|
||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
err = E.Append(err, endpoint.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -129,15 +119,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
name := "endpoint/" + endpoint.Type() + "[" + endpoint.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(endpoint, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if existsEndpoint, loaded := m.endpointByTag[tag]; loaded {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
)
|
||||
|
||||
@@ -15,7 +14,6 @@ type ClashServer interface {
|
||||
ConnectionTracker
|
||||
Mode() string
|
||||
ModeList() []string
|
||||
SetModeUpdateHook(hook *observable.Subscriber[struct{}])
|
||||
HistoryStorage() URLTestHistoryStorage
|
||||
}
|
||||
|
||||
@@ -25,7 +23,7 @@ type URLTestHistory struct {
|
||||
}
|
||||
|
||||
type URLTestHistoryStorage interface {
|
||||
SetHook(hook *observable.Subscriber[struct{}])
|
||||
SetHook(hook chan<- struct{})
|
||||
LoadURLTestHistory(tag string) *URLTestHistory
|
||||
DeleteURLTestHistory(tag string)
|
||||
StoreURLTestHistory(tag string, history *URLTestHistory)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -84,7 +85,7 @@ type InboundContext struct {
|
||||
DestinationAddresses []netip.Addr
|
||||
SourceGeoIPCode string
|
||||
GeoIPCode string
|
||||
ProcessInfo *ConnectionOwner
|
||||
ProcessInfo *process.Info
|
||||
QueryType uint16
|
||||
FakeIP bool
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ adapter.InboundManager = (*Manager)(nil)
|
||||
@@ -47,14 +45,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
inbounds := m.inbounds
|
||||
m.access.Unlock()
|
||||
for _, inbound := range inbounds {
|
||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -71,15 +65,11 @@ func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
var err error
|
||||
for _, inbound := range inbounds {
|
||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
err = E.Append(err, inbound.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -131,15 +121,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
name := "inbound/" + inbound.Type() + "[" + inbound.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if existsInbound, loaded := m.inboundByTag[tag]; loaded {
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
type SimpleLifecycle interface {
|
||||
Start() error
|
||||
@@ -56,47 +48,22 @@ type LifecycleService interface {
|
||||
Lifecycle
|
||||
}
|
||||
|
||||
func getServiceName(service any) string {
|
||||
if named, ok := service.(interface {
|
||||
Type() string
|
||||
Tag() string
|
||||
}); ok {
|
||||
tag := named.Tag()
|
||||
if tag != "" {
|
||||
return named.Type() + "[" + tag + "]"
|
||||
}
|
||||
return named.Type()
|
||||
}
|
||||
t := reflect.TypeOf(service)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
return strings.ToLower(t.Name())
|
||||
}
|
||||
|
||||
func Start(logger log.ContextLogger, stage StartStage, services ...Lifecycle) error {
|
||||
func Start(stage StartStage, services ...Lifecycle) error {
|
||||
for _, service := range services {
|
||||
name := getServiceName(service)
|
||||
logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := service.Start(stage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartNamed(logger log.ContextLogger, stage StartStage, services []LifecycleService) error {
|
||||
func StartNamed(stage StartStage, services []LifecycleService) error {
|
||||
for _, service := range services {
|
||||
logger.Trace(stage, " ", service.Name())
|
||||
startTime := time.Now()
|
||||
err := service.Start(stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.String(), " ", service.Name())
|
||||
}
|
||||
logger.Trace(stage, " ", service.Name(), " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
type NetworkManager interface {
|
||||
Lifecycle
|
||||
Initialize(ruleSets []RuleSet)
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
UpdateInterfaces() error
|
||||
DefaultNetworkInterface() *NetworkInterface
|
||||
@@ -25,10 +24,9 @@ type NetworkManager interface {
|
||||
NetworkMonitor() tun.NetworkUpdateMonitor
|
||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||
PackageManager() tun.PackageManager
|
||||
NeedWIFIState() bool
|
||||
WIFIState() WIFIState
|
||||
UpdateWIFIState()
|
||||
ResetNetwork()
|
||||
UpdateWIFIState()
|
||||
}
|
||||
|
||||
type NetworkOptions struct {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
@@ -83,14 +81,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
outbounds := m.outbounds
|
||||
m.access.Unlock()
|
||||
for _, outbound := range outbounds {
|
||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -115,29 +109,22 @@ func (m *Manager) startOutbounds(outbounds []adapter.Outbound) error {
|
||||
}
|
||||
started[outboundTag] = true
|
||||
canContinue = true
|
||||
name := "outbound/" + outboundToStart.Type() + "[" + outboundTag + "]"
|
||||
if starter, isStarter := outboundToStart.(adapter.Lifecycle); isStarter {
|
||||
m.logger.Trace("start ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("start ", name)
|
||||
monitor.Start("start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
err := starter.Start(adapter.StartStateStart)
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start ", name)
|
||||
return E.Cause(err, "start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
}
|
||||
m.logger.Trace("start ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
} else if starter, isStarter := outboundToStart.(interface {
|
||||
Start() error
|
||||
}); isStarter {
|
||||
m.logger.Trace("start ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("start ", name)
|
||||
monitor.Start("start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
err := starter.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start ", name)
|
||||
return E.Cause(err, "start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
}
|
||||
m.logger.Trace("start ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if len(started) == len(outbounds) {
|
||||
@@ -184,15 +171,11 @@ func (m *Manager) Close() error {
|
||||
var err error
|
||||
for _, outbound := range outbounds {
|
||||
if closer, isCloser := outbound.(io.Closer); isCloser {
|
||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
err = E.Append(err, closer.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -273,15 +256,11 @@ func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.
|
||||
return err
|
||||
}
|
||||
if m.started {
|
||||
name := "outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
m.access.Lock()
|
||||
|
||||
@@ -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 {
|
||||
Lifecycle
|
||||
ConnectionRouter
|
||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration, supportBypass bool) (tun.DirectRouteDestination, error)
|
||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
||||
ConnectionRouterEx
|
||||
RuleSet(tag string) (RuleSet, bool)
|
||||
NeedWIFIState() bool
|
||||
Rules() []Rule
|
||||
AppendTracker(tracker ConnectionTracker)
|
||||
ResetNetwork()
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ adapter.ServiceManager = (*Manager)(nil)
|
||||
@@ -45,14 +43,10 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
services := m.services
|
||||
m.access.Unlock()
|
||||
for _, service := range services {
|
||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err := adapter.LegacyStart(service, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " service/", service.Type(), "[", service.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -69,15 +63,11 @@ func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
var err error
|
||||
for _, service := range services {
|
||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
||||
m.logger.Trace("close ", name)
|
||||
startTime := time.Now()
|
||||
monitor.Start("close ", name)
|
||||
monitor.Start("close service/", service.Type(), "[", service.Tag(), "]")
|
||||
err = E.Append(err, service.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", name)
|
||||
return E.Cause(err, "close service/", service.Type(), "[", service.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
m.logger.Trace("close ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -126,15 +116,11 @@ func (m *Manager) Create(ctx context.Context, logger log.ContextLogger, tag stri
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
name := "service/" + service.Type() + "[" + service.Tag() + "]"
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
m.logger.Trace(stage, " ", name)
|
||||
startTime := time.Now()
|
||||
err = adapter.LegacyStart(service, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage, " ", name)
|
||||
return E.Cause(err, stage, " service/", service.Type(), "[", service.Tag(), "]")
|
||||
}
|
||||
m.logger.Trace(stage, " ", name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
}
|
||||
if existsService, loaded := m.serviceByTag[tag]; loaded {
|
||||
|
||||
@@ -73,7 +73,7 @@ func NewUpstreamContextHandlerEx(
|
||||
}
|
||||
|
||||
func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, myMetadata := ExtendContext(ctx)
|
||||
myMetadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context,
|
||||
}
|
||||
|
||||
func (w *myUpstreamContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, myMetadata := ExtendContext(ctx)
|
||||
myMetadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
myMetadata.Source = source
|
||||
}
|
||||
@@ -146,7 +146,7 @@ type routeContextHandlerWrapperEx struct {
|
||||
}
|
||||
|
||||
func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, metadata := ExtendContext(ctx)
|
||||
metadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
metadata.Source = source
|
||||
}
|
||||
@@ -157,7 +157,7 @@ func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn
|
||||
}
|
||||
|
||||
func (r *routeContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||
_, metadata := ExtendContext(ctx)
|
||||
metadata := ContextFrom(ctx)
|
||||
if source.IsValid() {
|
||||
metadata.Source = source
|
||||
}
|
||||
|
||||
58
box.go
58
box.go
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/sagernet/sing-box/dns/transport/local"
|
||||
"github.com/sagernet/sing-box/experimental"
|
||||
"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/option"
|
||||
"github.com/sagernet/sing-box/protocol/direct"
|
||||
@@ -138,7 +139,7 @@ func New(options Options) (*Box, error) {
|
||||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||
needV2RayAPI = true
|
||||
}
|
||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
var defaultLogWriter io.Writer
|
||||
if platformInterface != nil {
|
||||
defaultLogWriter = io.Discard
|
||||
@@ -183,7 +184,7 @@ func New(options Options) (*Box, error) {
|
||||
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
|
||||
dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions)
|
||||
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 {
|
||||
return nil, E.Cause(err, "initialize network manager")
|
||||
}
|
||||
@@ -443,15 +444,15 @@ func (s *Box) preStart() error {
|
||||
if err != nil {
|
||||
return E.Cause(err, "start logger")
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStateInitialize, s.internalService) // cache-file clash-api v2ray-api
|
||||
err = adapter.StartNamed(adapter.StartStateInitialize, s.internalService) // cache-file clash-api v2ray-api
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -463,27 +464,27 @@ func (s *Box) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStateStart, s.internalService)
|
||||
err = adapter.StartNamed(adapter.StartStateStart, s.internalService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStart, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStateStart, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStatePostStart, s.internalService)
|
||||
err = adapter.StartNamed(adapter.StartStatePostStart, s.internalService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.Start(s.logger, adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adapter.StartNamed(s.logger, adapter.StartStateStarted, s.internalService)
|
||||
err = adapter.StartNamed(adapter.StartStateStarted, s.internalService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -497,42 +498,17 @@ func (s *Box) Close() error {
|
||||
default:
|
||||
close(s.done)
|
||||
}
|
||||
var err error
|
||||
for _, closeItem := range []struct {
|
||||
name string
|
||||
service adapter.Lifecycle
|
||||
}{
|
||||
{"service", s.service},
|
||||
{"endpoint", s.endpoint},
|
||||
{"inbound", s.inbound},
|
||||
{"outbound", s.outbound},
|
||||
{"router", s.router},
|
||||
{"connection", s.connection},
|
||||
{"dns-router", s.dnsRouter},
|
||||
{"dns-transport", s.dnsTransport},
|
||||
{"network", s.network},
|
||||
} {
|
||||
s.logger.Trace("close ", closeItem.name)
|
||||
startTime := time.Now()
|
||||
err = E.Append(err, closeItem.service.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", closeItem.name)
|
||||
})
|
||||
s.logger.Trace("close ", closeItem.name, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
err := common.Close(
|
||||
s.service, s.endpoint, s.inbound, s.outbound, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
|
||||
)
|
||||
for _, lifecycleService := range s.internalService {
|
||||
s.logger.Trace("close ", lifecycleService.Name())
|
||||
startTime := time.Now()
|
||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ", lifecycleService.Name())
|
||||
})
|
||||
s.logger.Trace("close ", lifecycleService.Name(), " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
}
|
||||
s.logger.Trace("close logger")
|
||||
startTime := time.Now()
|
||||
err = E.Append(err, s.logFactory.Close(), func(err error) error {
|
||||
return E.Cause(err, "close logger")
|
||||
})
|
||||
s.logger.Trace("close logger completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -551,7 +527,3 @@ func (s *Box) Inbound() adapter.InboundManager {
|
||||
func (s *Box) Outbound() adapter.OutboundManager {
|
||||
return s.outbound
|
||||
}
|
||||
|
||||
func (s *Box) LogFactory() log.Factory {
|
||||
return s.logFactory
|
||||
}
|
||||
|
||||
Submodule clients/android updated: fe128a6cd7...cd8ac376f6
Submodule clients/apple updated: 532c140f05...96bee16bf0
@@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/sagernet/gomobile"
|
||||
@@ -47,7 +46,7 @@ var (
|
||||
sharedFlags []string
|
||||
debugFlags []string
|
||||
sharedTags []string
|
||||
darwinTags []string
|
||||
macOSTags []string
|
||||
memcTags []string
|
||||
notMemcTags []string
|
||||
debugTags []string
|
||||
@@ -63,34 +62,16 @@ func init() {
|
||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
||||
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
||||
darwinTags = append(darwinTags, "with_dhcp")
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack")
|
||||
macOSTags = append(macOSTags, "with_dhcp")
|
||||
memcTags = append(memcTags, "with_tailscale")
|
||||
notMemcTags = append(notMemcTags, "with_low_memory")
|
||||
debugTags = append(debugTags, "debug")
|
||||
}
|
||||
|
||||
type AndroidBuildConfig struct {
|
||||
AndroidAPI int
|
||||
OutputName string
|
||||
Tags []string
|
||||
}
|
||||
func buildAndroid() {
|
||||
build_shared.FindSDK()
|
||||
|
||||
func filterTags(tags []string, exclude ...string) []string {
|
||||
excludeMap := make(map[string]bool)
|
||||
for _, tag := range exclude {
|
||||
excludeMap[tag] = true
|
||||
}
|
||||
var result []string
|
||||
for _, tag := range tags {
|
||||
if !excludeMap[tag] {
|
||||
result = append(result, tag)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func checkJavaVersion() {
|
||||
var javaPath string
|
||||
javaHome := os.Getenv("JAVA_HOME")
|
||||
if javaHome == "" {
|
||||
@@ -106,87 +87,61 @@ func checkJavaVersion() {
|
||||
if !strings.Contains(javaVersion, "openjdk 17") {
|
||||
log.Fatal("java version should be openjdk 17")
|
||||
}
|
||||
}
|
||||
|
||||
func getAndroidBindTarget() string {
|
||||
var bindTarget string
|
||||
if platform != "" {
|
||||
return platform
|
||||
bindTarget = platform
|
||||
} else if debugEnabled {
|
||||
return "android/arm64"
|
||||
bindTarget = "android/arm64"
|
||||
} else {
|
||||
bindTarget = "android"
|
||||
}
|
||||
return "android"
|
||||
}
|
||||
|
||||
func buildAndroidVariant(config AndroidBuildConfig, bindTarget string) {
|
||||
args := []string{
|
||||
"bind",
|
||||
"-v",
|
||||
"-o", config.OutputName,
|
||||
"-target", bindTarget,
|
||||
"-androidapi", strconv.Itoa(config.AndroidAPI),
|
||||
"-androidapi", "21",
|
||||
"-javapkg=io.nekohasekai",
|
||||
"-libname=box",
|
||||
}
|
||||
|
||||
if !debugEnabled {
|
||||
// sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
|
||||
args = append(args, sharedFlags...)
|
||||
} else {
|
||||
// debugFlags[1] = debugFlags[1] + " -checklinkname=0"
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
args = append(args, "-tags", strings.Join(config.Tags, ","))
|
||||
tags := append(sharedTags, memcTags...)
|
||||
if debugEnabled {
|
||||
tags = append(tags, debugTags...)
|
||||
}
|
||||
|
||||
args = append(args, "-tags", strings.Join(tags, ","))
|
||||
args = append(args, "./experimental/libbox")
|
||||
|
||||
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||
command.Stdout = os.Stdout
|
||||
command.Stderr = os.Stderr
|
||||
err := command.Run()
|
||||
err = command.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
const name = "libbox.aar"
|
||||
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
||||
if rw.IsDir(copyPath) {
|
||||
copyPath, _ = filepath.Abs(copyPath)
|
||||
err = rw.CopyFile(config.OutputName, filepath.Join(copyPath, config.OutputName))
|
||||
err = rw.CopyFile(name, filepath.Join(copyPath, name))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Info("copied ", config.OutputName, " to ", copyPath)
|
||||
log.Info("copied to ", copyPath)
|
||||
}
|
||||
}
|
||||
|
||||
func buildAndroid() {
|
||||
build_shared.FindSDK()
|
||||
checkJavaVersion()
|
||||
|
||||
bindTarget := getAndroidBindTarget()
|
||||
|
||||
// Build main variant (SDK 23)
|
||||
mainTags := append([]string{}, sharedTags...)
|
||||
mainTags = append(mainTags, memcTags...)
|
||||
if debugEnabled {
|
||||
mainTags = append(mainTags, debugTags...)
|
||||
}
|
||||
buildAndroidVariant(AndroidBuildConfig{
|
||||
AndroidAPI: 23,
|
||||
OutputName: "libbox.aar",
|
||||
Tags: mainTags,
|
||||
}, bindTarget)
|
||||
|
||||
// Build legacy variant (SDK 21, no naive outbound)
|
||||
legacyTags := filterTags(sharedTags, "with_naive_outbound")
|
||||
legacyTags = append(legacyTags, memcTags...)
|
||||
if debugEnabled {
|
||||
legacyTags = append(legacyTags, debugTags...)
|
||||
}
|
||||
buildAndroidVariant(AndroidBuildConfig{
|
||||
AndroidAPI: 21,
|
||||
OutputName: "libbox-legacy.aar",
|
||||
Tags: legacyTags,
|
||||
}, bindTarget)
|
||||
}
|
||||
|
||||
func buildApple() {
|
||||
var bindTarget string
|
||||
if platform != "" {
|
||||
@@ -205,7 +160,9 @@ func buildApple() {
|
||||
"-tags-not-macos=with_low_memory",
|
||||
}
|
||||
if !withTailscale {
|
||||
args = append(args, "-tags-macos="+strings.Join(memcTags, ","))
|
||||
args = append(args, "-tags-macos="+strings.Join(append(macOSTags, memcTags...), ","))
|
||||
} else {
|
||||
args = append(args, "-tags-macos="+strings.Join(macOSTags, ","))
|
||||
}
|
||||
|
||||
if !debugEnabled {
|
||||
@@ -214,7 +171,7 @@ func buildApple() {
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
tags := append(sharedTags, darwinTags...)
|
||||
tags := sharedTags
|
||||
if withTailscale {
|
||||
tags = append(tags, memcTags...)
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := filepath.Walk("docs", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(path, ".md") {
|
||||
return nil
|
||||
}
|
||||
return processFile(path)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processFile(path string) error {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
modified := false
|
||||
result := make([]string, 0, len(lines))
|
||||
|
||||
inQuoteBlock := false
|
||||
materialLines := []int{} // indices of :material- lines in the block
|
||||
|
||||
for _, line := range lines {
|
||||
// Check for quote block start
|
||||
if strings.HasPrefix(line, "!!! quote \"") && strings.Contains(line, "sing-box") {
|
||||
inQuoteBlock = true
|
||||
materialLines = nil
|
||||
result = append(result, line)
|
||||
continue
|
||||
}
|
||||
|
||||
// Inside a quote block
|
||||
if inQuoteBlock {
|
||||
trimmed := strings.TrimPrefix(line, " ")
|
||||
isMaterialLine := strings.HasPrefix(trimmed, ":material-")
|
||||
isEmpty := strings.TrimSpace(line) == ""
|
||||
isIndented := strings.HasPrefix(line, " ")
|
||||
|
||||
if isMaterialLine {
|
||||
materialLines = append(materialLines, len(result))
|
||||
result = append(result, line)
|
||||
continue
|
||||
}
|
||||
|
||||
// Block ends when:
|
||||
// - Empty line AFTER we've seen material lines, OR
|
||||
// - Non-indented, non-empty line
|
||||
blockEnds := (isEmpty && len(materialLines) > 0) || (!isEmpty && !isIndented)
|
||||
if blockEnds {
|
||||
// Process collected material lines
|
||||
if len(materialLines) > 0 {
|
||||
for j, idx := range materialLines {
|
||||
isLast := j == len(materialLines)-1
|
||||
resultLine := strings.TrimRight(result[idx], " ")
|
||||
if !isLast {
|
||||
// Add trailing two spaces for non-last lines
|
||||
resultLine += " "
|
||||
}
|
||||
if result[idx] != resultLine {
|
||||
modified = true
|
||||
result[idx] = resultLine
|
||||
}
|
||||
}
|
||||
}
|
||||
inQuoteBlock = false
|
||||
materialLines = nil
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, line)
|
||||
}
|
||||
|
||||
// Handle case where file ends while still in a block
|
||||
if inQuoteBlock && len(materialLines) > 0 {
|
||||
for j, idx := range materialLines {
|
||||
isLast := j == len(materialLines)-1
|
||||
resultLine := strings.TrimRight(result[idx], " ")
|
||||
if !isLast {
|
||||
resultLine += " "
|
||||
}
|
||||
if result[idx] != resultLine {
|
||||
modified = true
|
||||
result[idx] = resultLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if modified {
|
||||
newContent := strings.Join(result, "\n")
|
||||
if !bytes.Equal(content, []byte(newContent)) {
|
||||
log.Info("formatted: ", path)
|
||||
return os.WriteFile(path, []byte(newContent), 0o644)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -17,10 +17,6 @@ func main() {
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
err = updateChromeIncludedRootCAs()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func updateMozillaIncludedRootCAs() error {
|
||||
@@ -73,94 +69,3 @@ func init() {
|
||||
generated.WriteString("}\n")
|
||||
return os.WriteFile("common/certificate/mozilla.go", []byte(generated.String()), 0o644)
|
||||
}
|
||||
|
||||
func fetchChinaFingerprints() (map[string]bool, error) {
|
||||
response, err := http.Get("https://ccadb.my.salesforce-sites.com/ccadb/AllCertificateRecordsCSVFormatv4")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
reader := csv.NewReader(response.Body)
|
||||
header, err := reader.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
countryIndex := slices.Index(header, "Country")
|
||||
fingerprintIndex := slices.Index(header, "SHA-256 Fingerprint")
|
||||
|
||||
chinaFingerprints := make(map[string]bool)
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if record[countryIndex] == "China" {
|
||||
chinaFingerprints[record[fingerprintIndex]] = true
|
||||
}
|
||||
}
|
||||
return chinaFingerprints, nil
|
||||
}
|
||||
|
||||
func updateChromeIncludedRootCAs() error {
|
||||
chinaFingerprints, err := fetchChinaFingerprints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := http.Get("https://ccadb.my.salesforce-sites.com/ccadb/RootCACertificatesIncludedByRSReportCSV")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
reader := csv.NewReader(response.Body)
|
||||
header, err := reader.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subjectIndex := slices.Index(header, "Subject")
|
||||
statusIndex := slices.Index(header, "Google Chrome Status")
|
||||
certIndex := slices.Index(header, "X.509 Certificate (PEM)")
|
||||
fingerprintIndex := slices.Index(header, "SHA-256 Fingerprint")
|
||||
|
||||
generated := strings.Builder{}
|
||||
generated.WriteString(`// Code generated by 'make update_certificates'. DO NOT EDIT.
|
||||
|
||||
package certificate
|
||||
|
||||
import "crypto/x509"
|
||||
|
||||
var chromeIncluded *x509.CertPool
|
||||
|
||||
func init() {
|
||||
chromeIncluded = x509.NewCertPool()
|
||||
`)
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if record[statusIndex] != "Included" {
|
||||
continue
|
||||
}
|
||||
if chinaFingerprints[record[fingerprintIndex]] {
|
||||
continue
|
||||
}
|
||||
generated.WriteString("\n // ")
|
||||
generated.WriteString(record[subjectIndex])
|
||||
generated.WriteString("\n")
|
||||
generated.WriteString(" chromeIncluded.AppendCertsFromPEM([]byte(`")
|
||||
cert := record[certIndex]
|
||||
// Remove single quotes if present
|
||||
if len(cert) > 0 && cert[0] == '\'' {
|
||||
cert = cert[1 : len(cert)-1]
|
||||
}
|
||||
generated.WriteString(cert)
|
||||
generated.WriteString("`))\n")
|
||||
}
|
||||
generated.WriteString("}\n")
|
||||
return os.WriteFile("common/certificate/chrome.go", []byte(generated.String()), 0o644)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build go1.25 && badlinkname
|
||||
//go:build go1.25 && !without_badtls
|
||||
|
||||
package badtls
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build go1.25 && badlinkname
|
||||
//go:build go1.25 && !without_badtls
|
||||
|
||||
package badtls
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build go1.25 && badlinkname
|
||||
//go:build go1.25 && !without_badtls
|
||||
|
||||
package badtls
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !go1.25 || !badlinkname
|
||||
//go:build !go1.25 || without_badtls
|
||||
|
||||
package badtls
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build go1.25 && badlinkname
|
||||
//go:build go1.25 && !without_badtls
|
||||
|
||||
package badtls
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build go1.25 && badlinkname
|
||||
//go:build go1.25 && !without_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/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
@@ -35,7 +36,7 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
||||
switch options.Store {
|
||||
case C.CertificateStoreSystem, "":
|
||||
systemPool = x509.NewCertPool()
|
||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
var systemValid bool
|
||||
if platformInterface != nil {
|
||||
for _, cert := range platformInterface.SystemCertificates() {
|
||||
@@ -53,8 +54,6 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
||||
}
|
||||
case C.CertificateStoreMozilla:
|
||||
systemPool = mozillaIncluded
|
||||
case C.CertificateStoreChrome:
|
||||
systemPool = chromeIncluded
|
||||
case C.CertificateStoreNone:
|
||||
systemPool = nil
|
||||
default:
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
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/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
@@ -19,8 +20,6 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"github.com/database64128/tfo-go/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -29,8 +28,8 @@ var (
|
||||
)
|
||||
|
||||
type DefaultDialer struct {
|
||||
dialer4 tfo.Dialer
|
||||
dialer6 tfo.Dialer
|
||||
dialer4 tcpDialer
|
||||
dialer6 tcpDialer
|
||||
udpDialer4 net.Dialer
|
||||
udpDialer6 net.Dialer
|
||||
udpListener net.ListenConfig
|
||||
@@ -48,7 +47,7 @@ type DefaultDialer struct {
|
||||
|
||||
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||
|
||||
var (
|
||||
dialer net.Dialer
|
||||
@@ -142,18 +141,9 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
} else {
|
||||
dialer.Timeout = C.TCPConnectTimeout
|
||||
}
|
||||
if !options.DisableTCPKeepAlive {
|
||||
keepIdle := time.Duration(options.TCPKeepAlive)
|
||||
if keepIdle == 0 {
|
||||
keepIdle = C.TCPKeepAliveInitial
|
||||
}
|
||||
keepInterval := time.Duration(options.TCPKeepAliveInterval)
|
||||
if keepInterval == 0 {
|
||||
keepInterval = C.TCPKeepAliveInterval
|
||||
}
|
||||
dialer.KeepAlive = keepIdle
|
||||
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(keepIdle, keepInterval))
|
||||
}
|
||||
// TODO: Add an option to customize the keep alive period
|
||||
dialer.KeepAlive = C.TCPKeepAliveInitial
|
||||
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(C.TCPKeepAliveInitial, C.TCPKeepAliveInterval))
|
||||
var udpFragment bool
|
||||
if options.UDPFragment != nil {
|
||||
udpFragment = *options.UDPFragment
|
||||
@@ -187,10 +177,19 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
||||
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||
}
|
||||
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{
|
||||
dialer4: tcpDialer4,
|
||||
dialer6: tcpDialer6,
|
||||
@@ -270,7 +269,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||
}
|
||||
var dialer net.Dialer
|
||||
if N.NetworkName(network) == N.NetworkTCP {
|
||||
dialer = d.dialer4.Dialer
|
||||
dialer = dialerFromTCPDialer(d.dialer4)
|
||||
} else {
|
||||
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 {
|
||||
if !destination.Is6() {
|
||||
return d.dialer6.Dialer
|
||||
return dialerFromTCPDialer(d.dialer6)
|
||||
} else {
|
||||
return d.dialer4.Dialer
|
||||
return dialerFromTCPDialer(d.dialer4)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
@@ -14,7 +16,7 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/database64128/tfo-go/v2"
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
type slowOpenConn struct {
|
||||
@@ -30,7 +32,7 @@ type slowOpenConn struct {
|
||||
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 {
|
||||
switch N.NetworkName(network) {
|
||||
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,4 +1,4 @@
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//go:build !linux
|
||||
//go:build !linux || !go1.25 || without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
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 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
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && go1.25 && badlinkname
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
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"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
"github.com/database64128/tfo-go/v2"
|
||||
"github.com/metacubex/tfo-go"
|
||||
)
|
||||
|
||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
@@ -37,7 +37,7 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
if l.listenOptions.ReuseAddr {
|
||||
listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
|
||||
}
|
||||
if !l.listenOptions.DisableTCPKeepAlive {
|
||||
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
||||
if keepIdle == 0 {
|
||||
keepIdle = C.TCPKeepAliveInitial
|
||||
@@ -46,14 +46,13 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||
if keepInterval == 0 {
|
||||
keepInterval = C.TCPKeepAliveInterval
|
||||
}
|
||||
listenConfig.KeepAliveConfig = net.KeepAliveConfig{
|
||||
Enable: true,
|
||||
Idle: keepIdle,
|
||||
Interval: keepInterval,
|
||||
}
|
||||
setKeepAliveConfig(&listenConfig, keepIdle, keepInterval)
|
||||
}
|
||||
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 {
|
||||
listenConfig.Control = control.Append(listenConfig.Control, func(network, address string, conn syscall.RawConn) error {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/netip"
|
||||
"os/user"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-tun"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
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")
|
||||
@@ -23,7 +22,15 @@ type Config struct {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -31,7 +38,7 @@ func FindProcessInfo(searcher Searcher, ctx context.Context, network string, sou
|
||||
if info.UserId != -1 {
|
||||
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
||||
if osUser != nil {
|
||||
info.UserName = osUser.Username
|
||||
info.User = osUser.Username
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-tun"
|
||||
)
|
||||
|
||||
@@ -18,22 +17,22 @@ func NewSearcher(config Config) (Searcher, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded {
|
||||
return &adapter.ConnectionOwner{
|
||||
UserId: int32(uid),
|
||||
AndroidPackageName: sharedPackage,
|
||||
return &Info{
|
||||
UserId: int32(uid),
|
||||
PackageName: sharedPackage,
|
||||
}, nil
|
||||
}
|
||||
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
||||
return &adapter.ConnectionOwner{
|
||||
UserId: int32(uid),
|
||||
AndroidPackageName: packageName,
|
||||
return &Info{
|
||||
UserId: int32(uid),
|
||||
PackageName: packageName,
|
||||
}, nil
|
||||
}
|
||||
return &adapter.ConnectionOwner{UserId: int32(uid)}, nil
|
||||
return &Info{UserId: int32(uid)}, nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -24,12 +23,12 @@ func NewSearcher(_ Config) (Searcher, error) {
|
||||
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()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &adapter.ConnectionOwner{ProcessPath: processName, UserId: -1}, nil
|
||||
return &Info{ProcessPath: processName, UserId: -1}, nil
|
||||
}
|
||||
|
||||
var structSize = func() int {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
@@ -20,7 +19,7 @@ func NewSearcher(config Config) (Searcher, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -29,7 +28,7 @@ func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, sou
|
||||
if err != nil {
|
||||
s.logger.DebugContext(ctx, "find process path: ", err)
|
||||
}
|
||||
return &adapter.ConnectionOwner{
|
||||
return &Info{
|
||||
UserId: int32(uid),
|
||||
ProcessPath: processPath,
|
||||
}, nil
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/netip"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/winiphlpapi"
|
||||
|
||||
@@ -28,16 +27,16 @@ func initWin32API() error {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := getProcessPath(pid)
|
||||
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) {
|
||||
|
||||
@@ -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 {
|
||||
case C.DNSProviderAliDNS:
|
||||
solver.DNSProvider = &alidns.Provider{
|
||||
CredentialInfo: alidns.CredentialInfo{
|
||||
AccessKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
||||
AccessKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
||||
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
||||
SecurityToken: dnsOptions.AliDNSOptions.SecurityToken,
|
||||
},
|
||||
AccKeyID: dnsOptions.AliDNSOptions.AccessKeyID,
|
||||
AccKeySecret: dnsOptions.AliDNSOptions.AccessKeySecret,
|
||||
RegionID: dnsOptions.AliDNSOptions.RegionID,
|
||||
}
|
||||
case C.DNSProviderCloudflare:
|
||||
solver.DNSProvider = &cloudflare.Provider{
|
||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||
}
|
||||
default:
|
||||
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
|
||||
|
||||
@@ -119,19 +119,21 @@ func (d *defaultDialer) dialContext(ctx context.Context, destination M.Socksaddr
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConn, err := aTLS.ClientHandshake(ctx, conn, d.config)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
tlsConn, err := ClientHandshake(ctx, conn, d.config)
|
||||
if err == nil {
|
||||
return tlsConn, nil
|
||||
}
|
||||
conn.Close()
|
||||
if echRetry {
|
||||
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 {
|
||||
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 {
|
||||
|
||||
@@ -51,7 +51,6 @@ func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, op
|
||||
return &ECHClientConfig{
|
||||
ECHCapableConfig: clientConfig,
|
||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
||||
queryServerName: options.ECH.QueryServerName,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -109,11 +108,10 @@ func parseECHKeys(echKey []byte) ([]tls.EncryptedClientHelloKey, error) {
|
||||
|
||||
type ECHClientConfig struct {
|
||||
ECHCapableConfig
|
||||
access sync.Mutex
|
||||
dnsRouter adapter.DNSRouter
|
||||
queryServerName string
|
||||
lastTTL time.Duration
|
||||
lastUpdate time.Time
|
||||
access sync.Mutex
|
||||
dnsRouter adapter.DNSRouter
|
||||
lastTTL time.Duration
|
||||
lastUpdate time.Time
|
||||
}
|
||||
|
||||
func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
||||
@@ -132,17 +130,13 @@ func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn)
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL {
|
||||
queryServerName := s.queryServerName
|
||||
if queryServerName == "" {
|
||||
queryServerName = s.ServerName()
|
||||
}
|
||||
message := &mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
RecursionDesired: true,
|
||||
},
|
||||
Question: []mDNS.Question{
|
||||
{
|
||||
Name: mDNS.Fqdn(queryServerName),
|
||||
Name: mDNS.Fqdn(s.ServerName()),
|
||||
Qtype: mDNS.TypeHTTPS,
|
||||
Qclass: mDNS.ClassINET,
|
||||
},
|
||||
@@ -181,12 +175,7 @@ func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn)
|
||||
}
|
||||
|
||||
func (s *ECHClientConfig) Clone() Config {
|
||||
return &ECHClientConfig{
|
||||
ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig),
|
||||
dnsRouter: s.dnsRouter,
|
||||
queryServerName: s.queryServerName,
|
||||
lastUpdate: s.lastUpdate,
|
||||
}
|
||||
return &ECHClientConfig{ECHCapableConfig: s.ECHCapableConfig.Clone().(ECHCapableConfig), dnsRouter: s.dnsRouter, lastUpdate: s.lastUpdate}
|
||||
}
|
||||
|
||||
func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
if len(options.CurvePreferences) > 0 {
|
||||
return nil, E.New("curve preferences is unavailable in reality")
|
||||
}
|
||||
if len(options.Certificate) > 0 || options.CertificatePath != "" || len(options.ClientCertificatePublicKeySHA256) > 0 {
|
||||
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
||||
return nil, E.New("certificate is unavailable in reality")
|
||||
}
|
||||
if len(options.Key) > 0 || options.KeyPath != "" {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -111,15 +108,6 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
for _, curve := range options.CurvePreferences {
|
||||
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curve))
|
||||
}
|
||||
var certificate []byte
|
||||
if len(options.Certificate) > 0 {
|
||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||
@@ -169,35 +154,6 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
var clientCertificate []byte
|
||||
if len(options.ClientCertificate) > 0 {
|
||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
||||
} else if options.ClientCertificatePath != "" {
|
||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client certificate")
|
||||
}
|
||||
clientCertificate = content
|
||||
}
|
||||
var clientKey []byte
|
||||
if len(options.ClientKey) > 0 {
|
||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
||||
} else if options.ClientKeyPath != "" {
|
||||
content, err := os.ReadFile(options.ClientKeyPath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client key")
|
||||
}
|
||||
clientKey = content
|
||||
}
|
||||
if len(clientCertificate) > 0 && len(clientKey) > 0 {
|
||||
keyPair, err := tls.X509KeyPair(clientCertificate, clientKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse client x509 key pair")
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||
} else if len(clientCertificate) > 0 || len(clientKey) > 0 {
|
||||
return nil, E.New("client certificate and client key must be provided together")
|
||||
}
|
||||
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||
if options.ECH != nil && options.ECH.Enabled {
|
||||
var err error
|
||||
@@ -219,22 +175,3 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
||||
}
|
||||
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 (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -23,17 +22,16 @@ import (
|
||||
var errInsecureUnused = E.New("tls: insecure unused")
|
||||
|
||||
type STDServerConfig struct {
|
||||
access sync.RWMutex
|
||||
config *tls.Config
|
||||
logger log.Logger
|
||||
acmeService adapter.SimpleLifecycle
|
||||
certificate []byte
|
||||
key []byte
|
||||
certificatePath string
|
||||
keyPath string
|
||||
clientCertificatePath []string
|
||||
echKeyPath string
|
||||
watcher *fswatch.Watcher
|
||||
access sync.RWMutex
|
||||
config *tls.Config
|
||||
logger log.Logger
|
||||
acmeService adapter.SimpleLifecycle
|
||||
certificate []byte
|
||||
key []byte
|
||||
certificatePath string
|
||||
keyPath string
|
||||
echKeyPath string
|
||||
watcher *fswatch.Watcher
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) ServerName() string {
|
||||
@@ -113,9 +111,6 @@ func (c *STDServerConfig) startWatcher() error {
|
||||
if c.echKeyPath != "" {
|
||||
watchPath = append(watchPath, c.echKeyPath)
|
||||
}
|
||||
if len(c.clientCertificatePath) > 0 {
|
||||
watchPath = append(watchPath, c.clientCertificatePath...)
|
||||
}
|
||||
if len(watchPath) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -164,30 +159,6 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
||||
c.config = config
|
||||
c.access.Unlock()
|
||||
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 {
|
||||
echKey, err := os.ReadFile(c.echKeyPath)
|
||||
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)
|
||||
}
|
||||
}
|
||||
for _, curveID := range options.CurvePreferences {
|
||||
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curveID))
|
||||
}
|
||||
tlsConfig.ClientAuth = tls.ClientAuthType(options.ClientAuthentication)
|
||||
var (
|
||||
certificate []byte
|
||||
key []byte
|
||||
)
|
||||
var certificate []byte
|
||||
var key []byte
|
||||
if acmeService == nil {
|
||||
if len(options.Certificate) > 0 {
|
||||
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}
|
||||
}
|
||||
}
|
||||
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
|
||||
if options.ECH != nil && options.ECH.Enabled {
|
||||
err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath)
|
||||
@@ -358,15 +286,14 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
||||
}
|
||||
}
|
||||
serverConfig := &STDServerConfig{
|
||||
config: tlsConfig,
|
||||
logger: logger,
|
||||
acmeService: acmeService,
|
||||
certificate: certificate,
|
||||
key: key,
|
||||
certificatePath: options.CertificatePath,
|
||||
clientCertificatePath: options.ClientCertificatePath,
|
||||
keyPath: options.KeyPath,
|
||||
echKeyPath: echKeyPath,
|
||||
config: tlsConfig,
|
||||
logger: logger,
|
||||
acmeService: acmeService,
|
||||
certificate: certificate,
|
||||
key: key,
|
||||
certificatePath: options.CertificatePath,
|
||||
keyPath: options.KeyPath,
|
||||
echKeyPath: echKeyPath,
|
||||
}
|
||||
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
serverConfig.access.Lock()
|
||||
|
||||
@@ -167,15 +167,6 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
||||
}
|
||||
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 {
|
||||
tlsConfig.NextProtos = options.ALPN
|
||||
}
|
||||
@@ -222,35 +213,6 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
var clientCertificate []byte
|
||||
if len(options.ClientCertificate) > 0 {
|
||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
||||
} else if options.ClientCertificatePath != "" {
|
||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client certificate")
|
||||
}
|
||||
clientCertificate = content
|
||||
}
|
||||
var clientKey []byte
|
||||
if len(options.ClientKey) > 0 {
|
||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
||||
} else if options.ClientKeyPath != "" {
|
||||
content, err := os.ReadFile(options.ClientKeyPath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read client key")
|
||||
}
|
||||
clientKey = content
|
||||
}
|
||||
if len(clientCertificate) > 0 && len(clientKey) > 0 {
|
||||
keyPair, err := utls.X509KeyPair(clientCertificate, clientKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse client x509 key pair")
|
||||
}
|
||||
tlsConfig.Certificates = []utls.Certificate{keyPair}
|
||||
} else if len(clientCertificate) > 0 || len(clientKey) > 0 {
|
||||
return nil, E.New("client certificate and client key must be provided together")
|
||||
}
|
||||
id, err := uTLSClientHelloID(options.UTLS.Fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
)
|
||||
|
||||
var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil)
|
||||
@@ -22,7 +21,7 @@ var _ adapter.URLTestHistoryStorage = (*HistoryStorage)(nil)
|
||||
type HistoryStorage struct {
|
||||
access sync.RWMutex
|
||||
delayHistory map[string]*adapter.URLTestHistory
|
||||
updateHook *observable.Subscriber[struct{}]
|
||||
updateHook chan<- struct{}
|
||||
}
|
||||
|
||||
func NewHistoryStorage() *HistoryStorage {
|
||||
@@ -31,7 +30,7 @@ func NewHistoryStorage() *HistoryStorage {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HistoryStorage) SetHook(hook *observable.Subscriber[struct{}]) {
|
||||
func (s *HistoryStorage) SetHook(hook chan<- struct{}) {
|
||||
s.updateHook = hook
|
||||
}
|
||||
|
||||
@@ -61,7 +60,10 @@ func (s *HistoryStorage) StoreURLTestHistory(tag string, history *adapter.URLTes
|
||||
func (s *HistoryStorage) notifyUpdated() {
|
||||
updateHook := s.updateHook
|
||||
if updateHook != nil {
|
||||
updateHook.Emit(struct{}{})
|
||||
select {
|
||||
case updateHook <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,5 @@ package constant
|
||||
const (
|
||||
CertificateStoreSystem = "system"
|
||||
CertificateStoreMozilla = "mozilla"
|
||||
CertificateStoreChrome = "chrome"
|
||||
CertificateStoreNone = "none"
|
||||
)
|
||||
|
||||
@@ -4,5 +4,5 @@ import "time"
|
||||
|
||||
const (
|
||||
DHCPTTL = time.Hour
|
||||
DHCPTimeout = 5 * time.Second
|
||||
DHCPTimeout = time.Minute
|
||||
)
|
||||
|
||||
@@ -28,8 +28,6 @@ const (
|
||||
TypeDERP = "derp"
|
||||
TypeResolved = "resolved"
|
||||
TypeSSMAPI = "ssm-api"
|
||||
TypeCCM = "ccm"
|
||||
TypeOCM = "ocm"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -30,7 +30,6 @@ const (
|
||||
RuleActionTypeRoute = "route"
|
||||
RuleActionTypeRouteOptions = "route-options"
|
||||
RuleActionTypeDirect = "direct"
|
||||
RuleActionTypeBypass = "bypass"
|
||||
RuleActionTypeReject = "reject"
|
||||
RuleActionTypeHijackDNS = "hijack-dns"
|
||||
RuleActionTypeSniff = "sniff"
|
||||
|
||||
@@ -3,7 +3,7 @@ package constant
|
||||
import "time"
|
||||
|
||||
const (
|
||||
TCPKeepAliveInitial = 5 * time.Minute
|
||||
TCPKeepAliveInitial = 10 * time.Minute
|
||||
TCPKeepAliveInterval = 75 * time.Second
|
||||
TCPConnectTimeout = 5 * time.Second
|
||||
TCPTimeout = 15 * time.Second
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
)
|
||||
|
||||
type Instance struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
instance *box.Box
|
||||
clashServer adapter.ClashServer
|
||||
cacheFile adapter.CacheFile
|
||||
pauseManager pause.Manager
|
||||
urlTestHistoryStorage *urltest.HistoryStorage
|
||||
}
|
||||
|
||||
func (s *StartedService) CheckConfig(configContent string) error {
|
||||
options, err := parseConfig(s.ctx, configContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(s.ctx)
|
||||
defer cancel()
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
})
|
||||
if err == nil {
|
||||
instance.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StartedService) FormatConfig(configContent string) (string, error) {
|
||||
options, err := parseConfig(s.ctx, configContent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
encoder := json.NewEncoder(&buffer)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
type OverrideOptions struct {
|
||||
AutoRedirect bool
|
||||
IncludePackage []string
|
||||
ExcludePackage []string
|
||||
}
|
||||
|
||||
func (s *StartedService) newInstance(profileContent string, overrideOptions *OverrideOptions) (*Instance, error) {
|
||||
ctx := s.ctx
|
||||
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
|
||||
ctx, cancel := context.WithCancel(include.Context(ctx))
|
||||
options, err := parseConfig(ctx, profileContent)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
if overrideOptions != nil {
|
||||
for _, inbound := range options.Inbounds {
|
||||
if tunInboundOptions, isTUN := inbound.Options.(*option.TunInboundOptions); isTUN {
|
||||
tunInboundOptions.AutoRedirect = overrideOptions.AutoRedirect
|
||||
tunInboundOptions.IncludePackage = append(tunInboundOptions.IncludePackage, overrideOptions.IncludePackage...)
|
||||
tunInboundOptions.ExcludePackage = append(tunInboundOptions.ExcludePackage, overrideOptions.ExcludePackage...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
||||
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
||||
i := &Instance{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
urlTestHistoryStorage: urlTestHistoryStorage,
|
||||
}
|
||||
boxInstance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
PlatformLogWriter: s,
|
||||
})
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
i.instance = boxInstance
|
||||
i.clashServer = service.FromContext[adapter.ClashServer](ctx)
|
||||
i.pauseManager = service.FromContext[pause.Manager](ctx)
|
||||
i.cacheFile = service.FromContext[adapter.CacheFile](ctx)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (i *Instance) Start() error {
|
||||
return i.instance.Start()
|
||||
}
|
||||
|
||||
func (i *Instance) Close() error {
|
||||
i.cancel()
|
||||
i.urlTestHistoryStorage.Close()
|
||||
return i.instance.Close()
|
||||
}
|
||||
|
||||
func (i *Instance) Box() *box.Box {
|
||||
return i.instance
|
||||
}
|
||||
|
||||
func (i *Instance) PauseManager() pause.Manager {
|
||||
return i.pauseManager
|
||||
}
|
||||
|
||||
func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
|
||||
options, err := json.UnmarshalExtendedContext[option.Options](ctx, []byte(configContent))
|
||||
if err != nil {
|
||||
return option.Options{}, E.Cause(err, "decode config")
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package daemon
|
||||
|
||||
type PlatformHandler interface {
|
||||
ServiceStop() error
|
||||
ServiceReload() error
|
||||
SystemProxyStatus() (*SystemProxyStatus, error)
|
||||
SetSystemProxyEnabled(enabled bool) error
|
||||
WriteDebugMessage(message string)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user