mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 02:27:19 +10:00
Compare commits
39 Commits
copilot/im
...
dev-wifi-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
766972ce52 | ||
|
|
f1e48a1cad | ||
|
|
ba496ae300 | ||
|
|
4488148322 | ||
|
|
f086454f81 | ||
|
|
f9b6a068ee | ||
|
|
0ad2a441d9 | ||
|
|
c714b59c87 | ||
|
|
bd6b125707 | ||
|
|
d1109cee90 | ||
|
|
b48002b4db | ||
|
|
67cedfd927 | ||
|
|
853b576d12 | ||
|
|
05cd7f6192 | ||
|
|
e4cf55a86d | ||
|
|
2bf605fad4 | ||
|
|
b913899d43 | ||
|
|
e1a22c0cc2 | ||
|
|
d890c8e8d7 | ||
|
|
1b4ffff67c | ||
|
|
6d012c04cd | ||
|
|
c1309b63c9 | ||
|
|
6674b252bf | ||
|
|
8c7b2e4ac3 | ||
|
|
f400c927c4 | ||
|
|
587d330b58 | ||
|
|
719a28920a | ||
|
|
2e0d344a3d | ||
|
|
387084b7c7 | ||
|
|
e15c02ee33 | ||
|
|
2648b80c6b | ||
|
|
391f7a73d2 | ||
|
|
c1565e2bb0 | ||
|
|
605d8b41f9 | ||
|
|
03d8243d41 | ||
|
|
30f1beb071 | ||
|
|
3841d9cb5a | ||
|
|
dbb7345ec9 | ||
|
|
88f000412b |
2
.github/setup_go_for_windows7.sh
vendored
2
.github/setup_go_for_windows7.sh
vendored
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="1.25.4"
|
VERSION="1.25.1"
|
||||||
|
|
||||||
mkdir -p $HOME/go
|
mkdir -p $HOME/go
|
||||||
cd $HOME/go
|
cd $HOME/go
|
||||||
|
|||||||
11
.github/setup_musl_cross.sh
vendored
11
.github/setup_musl_cross.sh
vendored
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -xeuo pipefail
|
|
||||||
|
|
||||||
TARGET="$1"
|
|
||||||
|
|
||||||
# Download musl-cross toolchain from musl.cc
|
|
||||||
cd "$HOME"
|
|
||||||
wget -q "https://musl.cc/${TARGET}-cross.tgz"
|
|
||||||
mkdir -p musl-cross
|
|
||||||
tar -xf "${TARGET}-cross.tgz" -C musl-cross --strip-components=1
|
|
||||||
rm "${TARGET}-cross.tgz"
|
|
||||||
7
.github/update_cronet.sh
vendored
7
.github/update_cronet.sh
vendored
@@ -1,7 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
PROJECTS=$(dirname "$0")/../..
|
|
||||||
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 mod tidy
|
|
||||||
220
.github/workflows/build.yml
vendored
220
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.4
|
go-version: ^1.25.1
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -93,6 +93,10 @@ jobs:
|
|||||||
- { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
|
- { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
|
||||||
- { os: windows, arch: arm64 }
|
- { os: windows, arch: arm64 }
|
||||||
|
|
||||||
|
- { os: darwin, arch: amd64 }
|
||||||
|
- { os: darwin, arch: arm64 }
|
||||||
|
- { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
|
||||||
|
|
||||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
||||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
||||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
|
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
|
||||||
@@ -103,15 +107,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
|
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.4
|
go-version: ^1.25.1
|
||||||
- name: Setup Go 1.24
|
- name: Setup Go 1.24
|
||||||
if: matrix.legacy_go124
|
if: matrix.legacy_go124
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.24.10
|
go-version: ~1.24.6
|
||||||
- name: Cache Go for Windows 7
|
- name: Cache Go for Windows 7
|
||||||
if: matrix.legacy_win7
|
if: matrix.legacy_win7
|
||||||
id: cache-go-for-windows7
|
id: cache-go-for-windows7
|
||||||
@@ -119,7 +123,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/go_win7
|
~/go/go_win7
|
||||||
key: go_win7_1254
|
key: go_win7_1251
|
||||||
- name: Setup Go for Windows 7
|
- name: Setup Go for Windows 7
|
||||||
if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
|
if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -142,10 +146,7 @@ jobs:
|
|||||||
- name: Set build tags
|
- name: Set build tags
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,badlinkname,tfogo_checklinkname0'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0'
|
||||||
if [[ "${{ matrix.os }}" == "android" ]]; then
|
|
||||||
TAGS="${TAGS},with_naive_outbound"
|
|
||||||
fi
|
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build
|
- name: Build
|
||||||
if: matrix.os != 'android'
|
if: matrix.os != 'android'
|
||||||
@@ -284,199 +285,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }}
|
||||||
path: "dist"
|
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_naive_outbound,badlinkname,tfogo_checklinkname0'
|
|
||||||
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_naive_linux:
|
|
||||||
name: Build Linux with naive outbound
|
|
||||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- calculate_version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Linux glibc (dynamic linking with Debian Bullseye sysroot)
|
|
||||||
- { arch: amd64, sysroot_arch: amd64, sysroot_sha: "36a164623d03f525e3dfb783a5e9b8a00e98e1ddd2b5cff4e449bd016dd27e50", cc_target: "x86_64-linux-gnu", suffix: "-naive" }
|
|
||||||
- { arch: arm64, sysroot_arch: arm64, sysroot_sha: "2f915d821eec27515c0c6d21b69898e23762908d8d7ccc1aa2a8f5f25e8b7e18", cc_target: "aarch64-linux-gnu", suffix: "-naive" }
|
|
||||||
- { arch: "386", sysroot_arch: i386, sysroot_sha: "63f0e5128b84f7b0421956a4a40affa472be8da0e58caf27e9acbc84072daee7", cc_target: "i686-linux-gnu", suffix: "-naive" }
|
|
||||||
- { arch: arm, goarm: "7", sysroot_arch: armhf, sysroot_sha: "47b3a0b161ca011b2b33d4fc1ef6ef269b8208a0b7e4c900700c345acdfd1814", cc_target: "arm-linux-gnueabihf", suffix: "-naive" }
|
|
||||||
# Linux musl (static linking)
|
|
||||||
- { arch: amd64, musl: true, cc_target: "x86_64-linux-musl", suffix: "-naive-musl" }
|
|
||||||
- { arch: arm64, musl: true, cc_target: "aarch64-linux-musl", suffix: "-naive-musl" }
|
|
||||||
- { arch: "386", musl: true, cc_target: "i686-linux-musl", suffix: "-naive-musl" }
|
|
||||||
- { arch: arm, goarm: "7", musl: true, cc_target: "arm-linux-musleabihf", suffix: "-naive-musl" }
|
|
||||||
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" >> "$GITHUB_ENV"
|
|
||||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
|
||||||
- name: Download sysroot (glibc)
|
|
||||||
if: ${{ ! matrix.musl }}
|
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
wget -q "https://commondatastorage.googleapis.com/chrome-linux-sysroot/${{ matrix.sysroot_sha }}" -O sysroot.tar.xz
|
|
||||||
mkdir -p /tmp/sysroot
|
|
||||||
tar -xf sysroot.tar.xz -C /tmp/sysroot
|
|
||||||
- name: Install cross compiler (glibc)
|
|
||||||
if: ${{ ! matrix.musl }}
|
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y clang lld
|
|
||||||
if [[ "${{ matrix.arch }}" == "arm64" ]]; then
|
|
||||||
sudo apt-get install -y libc6-dev-arm64-cross
|
|
||||||
elif [[ "${{ matrix.arch }}" == "386" ]]; then
|
|
||||||
sudo apt-get install -y libc6-dev-i386-cross
|
|
||||||
elif [[ "${{ matrix.arch }}" == "arm" ]]; then
|
|
||||||
sudo apt-get install -y libc6-dev-armhf-cross
|
|
||||||
fi
|
|
||||||
- name: Install musl cross compiler
|
|
||||||
if: matrix.musl
|
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
.github/setup_musl_cross.sh "${{ matrix.cc_target }}"
|
|
||||||
echo "PATH=$HOME/musl-cross/bin:$PATH" >> $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_naive_outbound,badlinkname,tfogo_checklinkname0'
|
|
||||||
if [[ "${{ matrix.musl }}" == "true" ]]; then
|
|
||||||
TAGS="${TAGS},with_musl"
|
|
||||||
fi
|
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
|
||||||
- name: Build (glibc)
|
|
||||||
if: ${{ ! matrix.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 -linkmode=external -extldflags "-fuse-ld=lld --sysroot=/tmp/sysroot"' \
|
|
||||||
./cmd/sing-box
|
|
||||||
env:
|
|
||||||
CGO_ENABLED: "1"
|
|
||||||
GOOS: linux
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
GOARM: ${{ matrix.goarm }}
|
|
||||||
CC: "clang --target=${{ matrix.cc_target }} --sysroot=/tmp/sysroot"
|
|
||||||
CXX: "clang++ --target=${{ matrix.cc_target }} --sysroot=/tmp/sysroot"
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Build (musl)
|
|
||||||
if: matrix.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 -linkmode=external -extldflags "-static"' \
|
|
||||||
./cmd/sing-box
|
|
||||||
env:
|
|
||||||
CGO_ENABLED: "1"
|
|
||||||
GOOS: linux
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
GOARM: ${{ matrix.goarm }}
|
|
||||||
CC: "${{ matrix.cc_target }}-gcc"
|
|
||||||
CXX: "${{ matrix.cc_target }}-g++"
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Set name
|
|
||||||
run: |-
|
|
||||||
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-linux-${{ matrix.arch }}"
|
|
||||||
if [[ -n "${{ matrix.goarm }}" ]]; then
|
|
||||||
DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
|
|
||||||
fi
|
|
||||||
DIR_NAME="${DIR_NAME}${{ matrix.suffix }}"
|
|
||||||
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-linux_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.suffix }}
|
|
||||||
path: "dist"
|
|
||||||
build_android:
|
build_android:
|
||||||
name: Build Android
|
name: Build Android
|
||||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
|
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
|
||||||
@@ -492,7 +300,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.4
|
go-version: ^1.25.1
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -572,7 +380,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.4
|
go-version: ^1.25.1
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -671,7 +479,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.4
|
go-version: ^1.25.1
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
if: matrix.if
|
if: matrix.if
|
||||||
run: |-
|
run: |-
|
||||||
@@ -811,8 +619,6 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- calculate_version
|
- calculate_version
|
||||||
- build
|
- build
|
||||||
- build_darwin
|
|
||||||
- build_naive_linux
|
|
||||||
- build_android
|
- build_android
|
||||||
- build_apple
|
- build_apple
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
6
.github/workflows/linux.yml
vendored
6
.github/workflows/linux.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.4
|
go-version: ^1.25.1
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.4
|
go-version: ^1.25.1
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
if: matrix.os == 'android'
|
if: matrix.os == 'android'
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
- name: Set build tags
|
- name: Set build tags
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,badlinkname,tfogo_checklinkname0'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0'
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
103
.goreleaser.fury.yaml
Normal file
103
.goreleaser.fury.yaml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
project_name: sing-box
|
||||||
|
builds:
|
||||||
|
- id: main
|
||||||
|
main: ./cmd/sing-box
|
||||||
|
flags:
|
||||||
|
- -v
|
||||||
|
- -trimpath
|
||||||
|
ldflags:
|
||||||
|
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
|
||||||
|
- -s
|
||||||
|
- -buildid=
|
||||||
|
tags:
|
||||||
|
- with_gvisor
|
||||||
|
- with_quic
|
||||||
|
- with_dhcp
|
||||||
|
- with_wireguard
|
||||||
|
- with_utls
|
||||||
|
- with_acme
|
||||||
|
- with_clash_api
|
||||||
|
- with_tailscale
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
targets:
|
||||||
|
- linux_386
|
||||||
|
- linux_amd64_v1
|
||||||
|
- linux_arm64
|
||||||
|
- linux_arm_7
|
||||||
|
- linux_s390x
|
||||||
|
- linux_riscv64
|
||||||
|
- linux_mips64le
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ .Version }}.{{ .ShortCommit }}"
|
||||||
|
nfpms:
|
||||||
|
- &template
|
||||||
|
id: package
|
||||||
|
package_name: sing-box
|
||||||
|
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||||
|
builds:
|
||||||
|
- main
|
||||||
|
homepage: https://sing-box.sagernet.org/
|
||||||
|
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||||
|
description: The universal proxy platform.
|
||||||
|
license: GPLv3 or later
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
priority: extra
|
||||||
|
contents:
|
||||||
|
- src: release/config/config.json
|
||||||
|
dst: /etc/sing-box/config.json
|
||||||
|
type: "config|noreplace"
|
||||||
|
|
||||||
|
- src: release/config/sing-box.service
|
||||||
|
dst: /usr/lib/systemd/system/sing-box.service
|
||||||
|
- src: release/config/sing-box@.service
|
||||||
|
dst: /usr/lib/systemd/system/sing-box@.service
|
||||||
|
- src: release/config/sing-box.sysusers
|
||||||
|
dst: /usr/lib/sysusers.d/sing-box.conf
|
||||||
|
- src: release/config/sing-box.rules
|
||||||
|
dst: /usr/share/polkit-1/rules.d/sing-box.rules
|
||||||
|
- src: release/config/sing-box-split-dns.xml
|
||||||
|
dst: /usr/share/dbus-1/system.d/sing-box-split-dns.conf
|
||||||
|
|
||||||
|
- src: release/completions/sing-box.bash
|
||||||
|
dst: /usr/share/bash-completion/completions/sing-box.bash
|
||||||
|
- src: release/completions/sing-box.fish
|
||||||
|
dst: /usr/share/fish/vendor_completions.d/sing-box.fish
|
||||||
|
- src: release/completions/sing-box.zsh
|
||||||
|
dst: /usr/share/zsh/site-functions/_sing-box
|
||||||
|
|
||||||
|
- src: LICENSE
|
||||||
|
dst: /usr/share/licenses/sing-box/LICENSE
|
||||||
|
deb:
|
||||||
|
signature:
|
||||||
|
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||||
|
fields:
|
||||||
|
Bugs: https://github.com/SagerNet/sing-box/issues
|
||||||
|
rpm:
|
||||||
|
signature:
|
||||||
|
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||||
|
conflicts:
|
||||||
|
- sing-box-beta
|
||||||
|
- id: package_beta
|
||||||
|
<<: *template
|
||||||
|
package_name: sing-box-beta
|
||||||
|
file_name_template: '{{ .ProjectName }}-beta_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
conflicts:
|
||||||
|
- sing-box
|
||||||
|
release:
|
||||||
|
disable: true
|
||||||
|
furies:
|
||||||
|
- account: sagernet
|
||||||
|
ids:
|
||||||
|
- package
|
||||||
|
disable: "{{ not (not .Prerelease) }}"
|
||||||
|
- account: sagernet
|
||||||
|
ids:
|
||||||
|
- package_beta
|
||||||
|
disable: "{{ not .Prerelease }}"
|
||||||
213
.goreleaser.yaml
Normal file
213
.goreleaser.yaml
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
version: 2
|
||||||
|
project_name: sing-box
|
||||||
|
builds:
|
||||||
|
- &template
|
||||||
|
id: main
|
||||||
|
main: ./cmd/sing-box
|
||||||
|
flags:
|
||||||
|
- -v
|
||||||
|
- -trimpath
|
||||||
|
ldflags:
|
||||||
|
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }}
|
||||||
|
- -s
|
||||||
|
- -buildid=
|
||||||
|
tags:
|
||||||
|
- with_gvisor
|
||||||
|
- with_quic
|
||||||
|
- with_dhcp
|
||||||
|
- with_wireguard
|
||||||
|
- with_utls
|
||||||
|
- with_acme
|
||||||
|
- with_clash_api
|
||||||
|
- with_tailscale
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
- GOTOOLCHAIN=local
|
||||||
|
targets:
|
||||||
|
- linux_386
|
||||||
|
- linux_amd64_v1
|
||||||
|
- linux_arm64
|
||||||
|
- linux_arm_6
|
||||||
|
- linux_arm_7
|
||||||
|
- linux_s390x
|
||||||
|
- linux_riscv64
|
||||||
|
- linux_mips64le
|
||||||
|
- windows_amd64_v1
|
||||||
|
- windows_386
|
||||||
|
- windows_arm64
|
||||||
|
- darwin_amd64_v1
|
||||||
|
- darwin_arm64
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
- id: legacy
|
||||||
|
<<: *template
|
||||||
|
tags:
|
||||||
|
- with_gvisor
|
||||||
|
- with_quic
|
||||||
|
- with_dhcp
|
||||||
|
- with_wireguard
|
||||||
|
- with_utls
|
||||||
|
- with_acme
|
||||||
|
- with_clash_api
|
||||||
|
- with_tailscale
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
- GOROOT={{ .Env.GOPATH }}/go_legacy
|
||||||
|
tool: "{{ .Env.GOPATH }}/go_legacy/bin/go"
|
||||||
|
targets:
|
||||||
|
- windows_amd64_v1
|
||||||
|
- windows_386
|
||||||
|
- id: android
|
||||||
|
<<: *template
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=1
|
||||||
|
- GOTOOLCHAIN=local
|
||||||
|
overrides:
|
||||||
|
- goos: android
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
env:
|
||||||
|
- CC=armv7a-linux-androideabi21-clang
|
||||||
|
- CXX=armv7a-linux-androideabi21-clang++
|
||||||
|
- goos: android
|
||||||
|
goarch: arm64
|
||||||
|
env:
|
||||||
|
- CC=aarch64-linux-android21-clang
|
||||||
|
- CXX=aarch64-linux-android21-clang++
|
||||||
|
- goos: android
|
||||||
|
goarch: 386
|
||||||
|
env:
|
||||||
|
- CC=i686-linux-android21-clang
|
||||||
|
- CXX=i686-linux-android21-clang++
|
||||||
|
- goos: android
|
||||||
|
goarch: amd64
|
||||||
|
goamd64: v1
|
||||||
|
env:
|
||||||
|
- CC=x86_64-linux-android21-clang
|
||||||
|
- CXX=x86_64-linux-android21-clang++
|
||||||
|
targets:
|
||||||
|
- android_arm_7
|
||||||
|
- android_arm64
|
||||||
|
- android_386
|
||||||
|
- android_amd64
|
||||||
|
archives:
|
||||||
|
- &template
|
||||||
|
id: archive
|
||||||
|
builds:
|
||||||
|
- main
|
||||||
|
- android
|
||||||
|
formats:
|
||||||
|
- tar.gz
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
formats:
|
||||||
|
- zip
|
||||||
|
wrap_in_directory: true
|
||||||
|
files:
|
||||||
|
- LICENSE
|
||||||
|
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}_{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||||
|
- id: archive-legacy
|
||||||
|
<<: *template
|
||||||
|
builds:
|
||||||
|
- legacy
|
||||||
|
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-legacy'
|
||||||
|
nfpms:
|
||||||
|
- id: package
|
||||||
|
package_name: sing-box
|
||||||
|
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ if and .Mips (not (eq .Mips "hardfloat")) }}_{{ .Mips }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||||
|
builds:
|
||||||
|
- main
|
||||||
|
homepage: https://sing-box.sagernet.org/
|
||||||
|
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||||
|
description: The universal proxy platform.
|
||||||
|
license: GPLv3 or later
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
- archlinux
|
||||||
|
# - apk
|
||||||
|
# - ipk
|
||||||
|
priority: extra
|
||||||
|
contents:
|
||||||
|
- src: release/config/config.json
|
||||||
|
dst: /etc/sing-box/config.json
|
||||||
|
type: "config|noreplace"
|
||||||
|
|
||||||
|
- src: release/config/sing-box.service
|
||||||
|
dst: /usr/lib/systemd/system/sing-box.service
|
||||||
|
- src: release/config/sing-box@.service
|
||||||
|
dst: /usr/lib/systemd/system/sing-box@.service
|
||||||
|
- src: release/config/sing-box.sysusers
|
||||||
|
dst: /usr/lib/sysusers.d/sing-box.conf
|
||||||
|
- src: release/config/sing-box.rules
|
||||||
|
dst: /usr/share/polkit-1/rules.d/sing-box.rules
|
||||||
|
- src: release/config/sing-box-split-dns.xml
|
||||||
|
dst: /usr/share/dbus-1/system.d/sing-box-split-dns.conf
|
||||||
|
|
||||||
|
- src: release/completions/sing-box.bash
|
||||||
|
dst: /usr/share/bash-completion/completions/sing-box.bash
|
||||||
|
- src: release/completions/sing-box.fish
|
||||||
|
dst: /usr/share/fish/vendor_completions.d/sing-box.fish
|
||||||
|
- src: release/completions/sing-box.zsh
|
||||||
|
dst: /usr/share/zsh/site-functions/_sing-box
|
||||||
|
|
||||||
|
- src: LICENSE
|
||||||
|
dst: /usr/share/licenses/sing-box/LICENSE
|
||||||
|
deb:
|
||||||
|
signature:
|
||||||
|
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||||
|
fields:
|
||||||
|
Bugs: https://github.com/SagerNet/sing-box/issues
|
||||||
|
rpm:
|
||||||
|
signature:
|
||||||
|
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||||
|
overrides:
|
||||||
|
apk:
|
||||||
|
contents:
|
||||||
|
- src: release/config/config.json
|
||||||
|
dst: /etc/sing-box/config.json
|
||||||
|
type: config
|
||||||
|
|
||||||
|
- src: release/config/sing-box.initd
|
||||||
|
dst: /etc/init.d/sing-box
|
||||||
|
|
||||||
|
- src: release/completions/sing-box.bash
|
||||||
|
dst: /usr/share/bash-completion/completions/sing-box.bash
|
||||||
|
- src: release/completions/sing-box.fish
|
||||||
|
dst: /usr/share/fish/vendor_completions.d/sing-box.fish
|
||||||
|
- src: release/completions/sing-box.zsh
|
||||||
|
dst: /usr/share/zsh/site-functions/_sing-box
|
||||||
|
|
||||||
|
- src: LICENSE
|
||||||
|
dst: /usr/share/licenses/sing-box/LICENSE
|
||||||
|
ipk:
|
||||||
|
contents:
|
||||||
|
- src: release/config/config.json
|
||||||
|
dst: /etc/sing-box/config.json
|
||||||
|
type: config
|
||||||
|
|
||||||
|
- src: release/config/openwrt.init
|
||||||
|
dst: /etc/init.d/sing-box
|
||||||
|
- src: release/config/openwrt.conf
|
||||||
|
dst: /etc/config/sing-box
|
||||||
|
source:
|
||||||
|
enabled: false
|
||||||
|
name_template: '{{ .ProjectName }}-{{ .Version }}.source'
|
||||||
|
prefix_template: '{{ .ProjectName }}-{{ .Version }}/'
|
||||||
|
checksum:
|
||||||
|
disable: true
|
||||||
|
name_template: '{{ .ProjectName }}-{{ .Version }}.checksum'
|
||||||
|
signs:
|
||||||
|
- artifacts: checksum
|
||||||
|
release:
|
||||||
|
github:
|
||||||
|
owner: SagerNet
|
||||||
|
name: sing-box
|
||||||
|
draft: true
|
||||||
|
prerelease: auto
|
||||||
|
mode: replace
|
||||||
|
ids:
|
||||||
|
- archive
|
||||||
|
- package
|
||||||
|
skip_upload: true
|
||||||
|
partial:
|
||||||
|
by: target
|
||||||
@@ -13,13 +13,15 @@ RUN set -ex \
|
|||||||
&& export COMMIT=$(git rev-parse --short HEAD) \
|
&& export COMMIT=$(git rev-parse --short HEAD) \
|
||||||
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
||||||
&& go build -v -trimpath -tags \
|
&& go build -v -trimpath -tags \
|
||||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,badlinkname,tfogo_checklinkname0" \
|
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0" \
|
||||||
-o /go/bin/sing-box \
|
-o /go/bin/sing-box \
|
||||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
FROM --platform=$TARGETPLATFORM alpine AS dist
|
FROM --platform=$TARGETPLATFORM alpine AS dist
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
RUN set -ex \
|
RUN set -ex \
|
||||||
&& apk add --no-cache --upgrade bash tzdata ca-certificates nftables
|
&& apk upgrade \
|
||||||
|
&& apk add bash tzdata ca-certificates nftables \
|
||||||
|
&& rm -rf /var/cache/apk/*
|
||||||
COPY --from=builder /go/bin/sing-box /usr/local/bin/sing-box
|
COPY --from=builder /go/bin/sing-box /usr/local/bin/sing-box
|
||||||
ENTRYPOINT ["sing-box"]
|
ENTRYPOINT ["sing-box"]
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
|||||||
NAME = sing-box
|
NAME = sing-box
|
||||||
COMMIT = $(shell git rev-parse --short HEAD)
|
COMMIT = $(shell git rev-parse --short HEAD)
|
||||||
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,badlinkname,tfogo_checklinkname0
|
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0
|
||||||
|
|
||||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||||
|
|||||||
@@ -1,11 +1,3 @@
|
|||||||
> Sponsored by [Warp](https://go.warp.dev/sing-box), built for coding with multiple AI agents
|
|
||||||
|
|
||||||
<a href="https://go.warp.dev/sing-box">
|
|
||||||
<img alt="Warp sponsorship" width="400" src="https://github.com/warpdotdev/brand-assets/raw/refs/heads/main/Github/Sponsor/Warp-Github-LG-02.png">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# sing-box
|
# sing-box
|
||||||
|
|
||||||
The universal proxy platform.
|
The universal proxy platform.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/process"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
@@ -85,7 +84,7 @@ type InboundContext struct {
|
|||||||
DestinationAddresses []netip.Addr
|
DestinationAddresses []netip.Addr
|
||||||
SourceGeoIPCode string
|
SourceGeoIPCode string
|
||||||
GeoIPCode string
|
GeoIPCode string
|
||||||
ProcessInfo *process.Info
|
ProcessInfo *ConnectionOwner
|
||||||
QueryType uint16
|
QueryType uint16
|
||||||
FakeIP bool
|
FakeIP bool
|
||||||
|
|
||||||
|
|||||||
70
adapter/platform.go
Normal file
70
adapter/platform.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -73,7 +73,7 @@ func NewUpstreamContextHandlerEx(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
_, myMetadata := ExtendContext(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if source.IsValid() {
|
if source.IsValid() {
|
||||||
myMetadata.Source = source
|
myMetadata.Source = source
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *myUpstreamContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (w *myUpstreamContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
_, myMetadata := ExtendContext(ctx)
|
myMetadata := ContextFrom(ctx)
|
||||||
if source.IsValid() {
|
if source.IsValid() {
|
||||||
myMetadata.Source = source
|
myMetadata.Source = source
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ type routeContextHandlerWrapperEx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
_, metadata := ExtendContext(ctx)
|
metadata := ContextFrom(ctx)
|
||||||
if source.IsValid() {
|
if source.IsValid() {
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *routeContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (r *routeContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
_, metadata := ExtendContext(ctx)
|
metadata := ContextFrom(ctx)
|
||||||
if source.IsValid() {
|
if source.IsValid() {
|
||||||
metadata.Source = source
|
metadata.Source = source
|
||||||
}
|
}
|
||||||
|
|||||||
7
box.go
7
box.go
@@ -22,7 +22,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/dns/transport/local"
|
"github.com/sagernet/sing-box/dns/transport/local"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
"github.com/sagernet/sing-box/experimental/cachefile"
|
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/protocol/direct"
|
"github.com/sagernet/sing-box/protocol/direct"
|
||||||
@@ -139,7 +138,7 @@ func New(options Options) (*Box, error) {
|
|||||||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||||
needV2RayAPI = true
|
needV2RayAPI = true
|
||||||
}
|
}
|
||||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||||
var defaultLogWriter io.Writer
|
var defaultLogWriter io.Writer
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
defaultLogWriter = io.Discard
|
defaultLogWriter = io.Discard
|
||||||
@@ -527,3 +526,7 @@ func (s *Box) Inbound() adapter.InboundManager {
|
|||||||
func (s *Box) Outbound() adapter.OutboundManager {
|
func (s *Box) Outbound() adapter.OutboundManager {
|
||||||
return s.outbound
|
return s.outbound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Box) LogFactory() log.Factory {
|
||||||
|
return s.logFactory
|
||||||
|
}
|
||||||
|
|||||||
Submodule clients/android updated: 3b2c371905...e08fbfcfea
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/sagernet/fswatch"
|
"github.com/sagernet/fswatch"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
@@ -36,7 +35,7 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
|||||||
switch options.Store {
|
switch options.Store {
|
||||||
case C.CertificateStoreSystem, "":
|
case C.CertificateStoreSystem, "":
|
||||||
systemPool = x509.NewCertPool()
|
systemPool = x509.NewCertPool()
|
||||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||||
var systemValid bool
|
var systemValid bool
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
for _, cert := range platformInterface.SystemCertificates() {
|
for _, cert := range platformInterface.SystemCertificates() {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/conntrack"
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
"github.com/sagernet/sing-box/common/listener"
|
"github.com/sagernet/sing-box/common/listener"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
@@ -49,7 +48,7 @@ type DefaultDialer struct {
|
|||||||
|
|
||||||
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
||||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dialer net.Dialer
|
dialer net.Dialer
|
||||||
@@ -143,18 +142,9 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
} else {
|
} else {
|
||||||
dialer.Timeout = C.TCPConnectTimeout
|
dialer.Timeout = C.TCPConnectTimeout
|
||||||
}
|
}
|
||||||
if !options.DisableTCPKeepAlive {
|
// TODO: Add an option to customize the keep alive period
|
||||||
keepIdle := time.Duration(options.TCPKeepAlive)
|
dialer.KeepAlive = C.TCPKeepAliveInitial
|
||||||
if keepIdle == 0 {
|
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(C.TCPKeepAliveInitial, C.TCPKeepAliveInterval))
|
||||||
keepIdle = C.TCPKeepAliveInitial
|
|
||||||
}
|
|
||||||
keepInterval := time.Duration(options.TCPKeepAliveInterval)
|
|
||||||
if keepInterval == 0 {
|
|
||||||
keepInterval = C.TCPKeepAliveInterval
|
|
||||||
}
|
|
||||||
dialer.KeepAlive = keepIdle
|
|
||||||
dialer.Control = control.Append(dialer.Control, control.SetKeepAlivePeriod(keepIdle, keepInterval))
|
|
||||||
}
|
|
||||||
var udpFragment bool
|
var udpFragment bool
|
||||||
if options.UDPFragment != nil {
|
if options.UDPFragment != nil {
|
||||||
udpFragment = *options.UDPFragment
|
udpFragment = *options.UDPFragment
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
|||||||
if l.listenOptions.ReuseAddr {
|
if l.listenOptions.ReuseAddr {
|
||||||
listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
|
listenConfig.Control = control.Append(listenConfig.Control, control.ReuseAddr())
|
||||||
}
|
}
|
||||||
if !l.listenOptions.DisableTCPKeepAlive {
|
if l.listenOptions.TCPKeepAlive >= 0 {
|
||||||
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
|
||||||
if keepIdle == 0 {
|
if keepIdle == 0 {
|
||||||
keepIdle = C.TCPKeepAliveInitial
|
keepIdle = C.TCPKeepAliveInitial
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -12,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Searcher interface {
|
type Searcher interface {
|
||||||
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error)
|
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNotFound = E.New("process not found")
|
var ErrNotFound = E.New("process not found")
|
||||||
@@ -22,15 +23,7 @@ type Config struct {
|
|||||||
PackageManager tun.PackageManager
|
PackageManager tun.PackageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
type Info struct {
|
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
ProcessID uint32
|
|
||||||
ProcessPath string
|
|
||||||
PackageName string
|
|
||||||
User string
|
|
||||||
UserId int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
|
||||||
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -38,7 +31,7 @@ func FindProcessInfo(searcher Searcher, ctx context.Context, network string, sou
|
|||||||
if info.UserId != -1 {
|
if info.UserId != -1 {
|
||||||
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
||||||
if osUser != nil {
|
if osUser != nil {
|
||||||
info.User = osUser.Username
|
info.UserName = osUser.Username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info, nil
|
return info, nil
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,22 +18,22 @@ func NewSearcher(config Config) (Searcher, error) {
|
|||||||
return &androidSearcher{config.PackageManager}, nil
|
return &androidSearcher{config.PackageManager}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
_, uid, err := resolveSocketByNetlink(network, source, destination)
|
_, uid, err := resolveSocketByNetlink(network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded {
|
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded {
|
||||||
return &Info{
|
return &adapter.ConnectionOwner{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
PackageName: sharedPackage,
|
AndroidPackageName: sharedPackage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
||||||
return &Info{
|
return &adapter.ConnectionOwner{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
PackageName: packageName,
|
AndroidPackageName: packageName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return &Info{UserId: int32(uid)}, nil
|
return &adapter.ConnectionOwner{UserId: int32(uid)}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@@ -23,12 +24,12 @@ func NewSearcher(_ Config) (Searcher, error) {
|
|||||||
return &darwinSearcher{}, nil
|
return &darwinSearcher{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
processName, err := findProcessName(network, source.Addr(), int(source.Port()))
|
processName, err := findProcessName(network, source.Addr(), int(source.Port()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Info{ProcessPath: processName, UserId: -1}, nil
|
return &adapter.ConnectionOwner{ProcessPath: processName, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var structSize = func() int {
|
var structSize = func() int {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ func NewSearcher(config Config) (Searcher, error) {
|
|||||||
return &linuxSearcher{config.Logger}, nil
|
return &linuxSearcher{config.Logger}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
inode, uid, err := resolveSocketByNetlink(network, source, destination)
|
inode, uid, err := resolveSocketByNetlink(network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -28,7 +29,7 @@ func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, sou
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.DebugContext(ctx, "find process path: ", err)
|
s.logger.DebugContext(ctx, "find process path: ", err)
|
||||||
}
|
}
|
||||||
return &Info{
|
return &adapter.ConnectionOwner{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
ProcessPath: processPath,
|
ProcessPath: processPath,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/winiphlpapi"
|
"github.com/sagernet/sing/common/winiphlpapi"
|
||||||
|
|
||||||
@@ -27,16 +28,16 @@ func initWin32API() error {
|
|||||||
return winiphlpapi.LoadExtendedTable()
|
return winiphlpapi.LoadExtendedTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
pid, err := winiphlpapi.FindPid(network, source)
|
pid, err := winiphlpapi.FindPid(network, source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
path, err := getProcessPath(pid)
|
path, err := getProcessPath(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &Info{ProcessID: pid, UserId: -1}, err
|
return &adapter.ConnectionOwner{ProcessID: pid, UserId: -1}, err
|
||||||
}
|
}
|
||||||
return &Info{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
return &adapter.ConnectionOwner{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProcessPath(pid uint32) (string, error) {
|
func getProcessPath(pid uint32) (string, error) {
|
||||||
|
|||||||
@@ -134,9 +134,7 @@ func (m *connmanMonitor) monitorSignals(ctx context.Context, signalChan chan *db
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// godbus Signal.Name uses "interface.member" format (e.g. "net.connman.Service.PropertyChanged"),
|
if signal.Name == "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()
|
state := m.ReadWIFIState()
|
||||||
if state != lastState {
|
if state != lastState {
|
||||||
lastState = state
|
lastState = state
|
||||||
@@ -156,10 +154,6 @@ func (m *connmanMonitor) Close() error {
|
|||||||
close(m.signalChan)
|
close(m.signalChan)
|
||||||
}
|
}
|
||||||
if m.conn != nil {
|
if m.conn != nil {
|
||||||
m.conn.RemoveMatchSignal(
|
|
||||||
dbus.WithMatchInterface("net.connman.Service"),
|
|
||||||
dbus.WithMatchSender("net.connman"),
|
|
||||||
)
|
|
||||||
return m.conn.Close()
|
return m.conn.Close()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -178,10 +178,6 @@ func (m *iwdMonitor) Close() error {
|
|||||||
close(m.signalChan)
|
close(m.signalChan)
|
||||||
}
|
}
|
||||||
if m.conn != nil {
|
if m.conn != nil {
|
||||||
m.conn.RemoveMatchSignal(
|
|
||||||
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
|
||||||
dbus.WithMatchSender("net.connman.iwd"),
|
|
||||||
)
|
|
||||||
return m.conn.Close()
|
return m.conn.Close()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -40,19 +40,18 @@ func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState {
|
|||||||
|
|
||||||
nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
|
nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
|
||||||
|
|
||||||
var activeConnectionPaths []dbus.ObjectPath
|
var primaryConnectionPath dbus.ObjectPath
|
||||||
err := nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "ActiveConnections").Store(&activeConnectionPaths)
|
err := nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "PrimaryConnection").Store(&primaryConnectionPath)
|
||||||
if err != nil || len(activeConnectionPaths) == 0 {
|
if err != nil || primaryConnectionPath == "/" {
|
||||||
return adapter.WIFIState{}
|
return adapter.WIFIState{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, connectionPath := range activeConnectionPaths {
|
connObj := m.conn.Object("org.freedesktop.NetworkManager", primaryConnectionPath)
|
||||||
connObj := m.conn.Object("org.freedesktop.NetworkManager", connectionPath)
|
|
||||||
|
|
||||||
var devicePaths []dbus.ObjectPath
|
var devicePaths []dbus.ObjectPath
|
||||||
err = connObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Connection.Active", "Devices").Store(&devicePaths)
|
err = connObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Connection.Active", "Devices").Store(&devicePaths)
|
||||||
if err != nil || len(devicePaths) == 0 {
|
if err != nil || len(devicePaths) == 0 {
|
||||||
continue
|
return adapter.WIFIState{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, devicePath := range devicePaths {
|
for _, devicePath := range devicePaths {
|
||||||
@@ -94,7 +93,6 @@ func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState {
|
|||||||
BSSID: strings.ToUpper(strings.ReplaceAll(hwAddress, ":", "")),
|
BSSID: strings.ToUpper(strings.ReplaceAll(hwAddress, ":", "")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return adapter.WIFIState{}
|
return adapter.WIFIState{}
|
||||||
}
|
}
|
||||||
@@ -153,10 +151,6 @@ func (m *networkManagerMonitor) Close() error {
|
|||||||
close(m.signalChan)
|
close(m.signalChan)
|
||||||
}
|
}
|
||||||
if m.conn != nil {
|
if m.conn != nil {
|
||||||
m.conn.RemoveMatchSignal(
|
|
||||||
dbus.WithMatchSender("org.freedesktop.NetworkManager"),
|
|
||||||
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
|
||||||
)
|
|
||||||
return m.conn.Close()
|
return m.conn.Close()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -8,21 +8,15 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
)
|
)
|
||||||
|
|
||||||
var wpaSocketCounter atomic.Uint64
|
|
||||||
|
|
||||||
type wpaSupplicantMonitor struct {
|
type wpaSupplicantMonitor struct {
|
||||||
socketPath string
|
socketPath string
|
||||||
callback func(adapter.WIFIState)
|
callback func(adapter.WIFIState)
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
monitorConn *net.UnixConn
|
|
||||||
connMutex sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||||
@@ -37,8 +31,7 @@ func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, err
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
socketPath := filepath.Join(socketDir, entry.Name())
|
socketPath := filepath.Join(socketDir, entry.Name())
|
||||||
id := wpaSocketCounter.Add(1)
|
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
|
||||||
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d-%d", os.Getpid(), id), Net: "unixgram"}
|
|
||||||
remoteAddr := &net.UnixAddr{Name: socketPath, Net: "unixgram"}
|
remoteAddr := &net.UnixAddr{Name: socketPath, Net: "unixgram"}
|
||||||
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -52,8 +45,7 @@ func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
|
func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
|
||||||
id := wpaSocketCounter.Add(1)
|
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
|
||||||
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d-%d", os.Getpid(), id), Net: "unixgram"}
|
|
||||||
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
||||||
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,11 +85,8 @@ func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendCommand sends a command to wpa_supplicant and returns the response.
|
|
||||||
// Commands are sent without trailing newlines per the wpa_supplicant control
|
|
||||||
// interface protocol - the official wpa_ctrl.c sends raw command strings.
|
|
||||||
func (m *wpaSupplicantMonitor) sendCommand(conn *net.UnixConn, command string) (string, error) {
|
func (m *wpaSupplicantMonitor) sendCommand(conn *net.UnixConn, command string) (string, error) {
|
||||||
_, err := conn.Write([]byte(command))
|
_, err := conn.Write([]byte(command + "\n"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -132,8 +121,6 @@ func (m *wpaSupplicantMonitor) Start() error {
|
|||||||
|
|
||||||
func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adapter.WIFIState) {
|
func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adapter.WIFIState) {
|
||||||
var consecutiveErrors int
|
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"}
|
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-mon-%d", os.Getpid()), Net: "unixgram"}
|
||||||
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
||||||
@@ -143,14 +130,7 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
m.connMutex.Lock()
|
_, err = conn.Write([]byte("ATTACH\n"))
|
||||||
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -164,12 +144,6 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
debounceMutex.Lock()
|
|
||||||
if debounceTimer != nil {
|
|
||||||
debounceTimer.Stop()
|
|
||||||
}
|
|
||||||
debounceMutex.Unlock()
|
|
||||||
conn.Write([]byte("DETACH"))
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -177,14 +151,6 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
|||||||
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
|
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
|
||||||
n, err := conn.Read(buf)
|
n, err := conn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
consecutiveErrors++
|
consecutiveErrors++
|
||||||
if consecutiveErrors > 10 {
|
if consecutiveErrors > 10 {
|
||||||
return
|
return
|
||||||
@@ -196,18 +162,11 @@ func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adap
|
|||||||
|
|
||||||
msg := string(buf[:n])
|
msg := string(buf[:n])
|
||||||
if strings.Contains(msg, "CTRL-EVENT-CONNECTED") || strings.Contains(msg, "CTRL-EVENT-DISCONNECTED") {
|
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()
|
state := m.ReadWIFIState()
|
||||||
if state != lastState {
|
if state != lastState {
|
||||||
lastState = state
|
lastState = state
|
||||||
m.callback(state)
|
m.callback(state)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
debounceMutex.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,10 +175,5 @@ func (m *wpaSupplicantMonitor) Close() error {
|
|||||||
if m.cancel != nil {
|
if m.cancel != nil {
|
||||||
m.cancel()
|
m.cancel()
|
||||||
}
|
}
|
||||||
m.connMutex.Lock()
|
|
||||||
if m.monitorConn != nil {
|
|
||||||
m.monitorConn.Close()
|
|
||||||
}
|
|
||||||
m.connMutex.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !linux && !windows
|
//go:build !linux
|
||||||
|
|
||||||
package settings
|
package settings
|
||||||
|
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
package settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing/common/winwlanapi"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
type windowsWIFIMonitor struct {
|
|
||||||
handle windows.Handle
|
|
||||||
callback func(adapter.WIFIState)
|
|
||||||
cancel context.CancelFunc
|
|
||||||
lastState adapter.WIFIState
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
|
||||||
handle, err := winwlanapi.OpenHandle()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
interfaces, err := winwlanapi.EnumInterfaces(handle)
|
|
||||||
if err != nil {
|
|
||||||
winwlanapi.CloseHandle(handle)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(interfaces) == 0 {
|
|
||||||
winwlanapi.CloseHandle(handle)
|
|
||||||
return nil, fmt.Errorf("no wireless interfaces found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &windowsWIFIMonitor{
|
|
||||||
handle: handle,
|
|
||||||
callback: callback,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *windowsWIFIMonitor) ReadWIFIState() adapter.WIFIState {
|
|
||||||
interfaces, err := winwlanapi.EnumInterfaces(m.handle)
|
|
||||||
if err != nil || len(interfaces) == 0 {
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, iface := range interfaces {
|
|
||||||
if iface.InterfaceState != winwlanapi.InterfaceStateConnected {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
guid := iface.InterfaceGUID
|
|
||||||
attrs, err := winwlanapi.QueryCurrentConnection(m.handle, &guid)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ssidLength := attrs.AssociationAttributes.SSID.Length
|
|
||||||
if ssidLength == 0 || ssidLength > winwlanapi.Dot11SSIDMaxLength {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ssid := string(attrs.AssociationAttributes.SSID.SSID[:ssidLength])
|
|
||||||
bssid := formatBSSID(attrs.AssociationAttributes.BSSID)
|
|
||||||
|
|
||||||
return adapter.WIFIState{
|
|
||||||
SSID: strings.TrimSpace(ssid),
|
|
||||||
BSSID: bssid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapter.WIFIState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatBSSID(mac winwlanapi.Dot11MacAddress) string {
|
|
||||||
return fmt.Sprintf("%02X%02X%02X%02X%02X%02X",
|
|
||||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *windowsWIFIMonitor) Start() error {
|
|
||||||
if m.callback == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
m.cancel = cancel
|
|
||||||
|
|
||||||
m.lastState = m.ReadWIFIState()
|
|
||||||
|
|
||||||
callbackFunc := func(data *winwlanapi.NotificationData, callbackContext uintptr) uintptr {
|
|
||||||
if data.NotificationSource != winwlanapi.NotificationSourceACM {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
switch data.NotificationCode {
|
|
||||||
case winwlanapi.NotificationACMConnectionComplete,
|
|
||||||
winwlanapi.NotificationACMDisconnected:
|
|
||||||
m.checkAndNotify()
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
callbackPointer := syscall.NewCallback(callbackFunc)
|
|
||||||
|
|
||||||
err := winwlanapi.RegisterNotification(m.handle, winwlanapi.NotificationSourceACM, callbackPointer, 0)
|
|
||||||
if err != nil {
|
|
||||||
cancel()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
}()
|
|
||||||
|
|
||||||
m.callback(m.lastState)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *windowsWIFIMonitor) checkAndNotify() {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
state := m.ReadWIFIState()
|
|
||||||
if state != m.lastState {
|
|
||||||
m.lastState = state
|
|
||||||
if m.callback != nil {
|
|
||||||
m.callback(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *windowsWIFIMonitor) Close() error {
|
|
||||||
if m.cancel != nil {
|
|
||||||
m.cancel()
|
|
||||||
}
|
|
||||||
winwlanapi.UnregisterNotification(m.handle)
|
|
||||||
return winwlanapi.CloseHandle(m.handle)
|
|
||||||
}
|
|
||||||
@@ -169,35 +169,6 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
var clientCertificate []byte
|
|
||||||
if len(options.ClientCertificate) > 0 {
|
|
||||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
|
||||||
} else if options.ClientCertificatePath != "" {
|
|
||||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "read client certificate")
|
|
||||||
}
|
|
||||||
clientCertificate = content
|
|
||||||
}
|
|
||||||
var clientKey []byte
|
|
||||||
if len(options.ClientKey) > 0 {
|
|
||||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
|
||||||
} else if options.ClientKeyPath != "" {
|
|
||||||
content, err := os.ReadFile(options.ClientKeyPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "read client key")
|
|
||||||
}
|
|
||||||
clientKey = content
|
|
||||||
}
|
|
||||||
if len(clientCertificate) > 0 && len(clientKey) > 0 {
|
|
||||||
keyPair, err := tls.X509KeyPair(clientCertificate, clientKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "parse client x509 key pair")
|
|
||||||
}
|
|
||||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
|
||||||
} else if len(clientCertificate) > 0 || len(clientKey) > 0 {
|
|
||||||
return nil, E.New("client certificate and client key must be provided together")
|
|
||||||
}
|
|
||||||
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -222,35 +222,6 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
var clientCertificate []byte
|
|
||||||
if len(options.ClientCertificate) > 0 {
|
|
||||||
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
|
|
||||||
} else if options.ClientCertificatePath != "" {
|
|
||||||
content, err := os.ReadFile(options.ClientCertificatePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "read client certificate")
|
|
||||||
}
|
|
||||||
clientCertificate = content
|
|
||||||
}
|
|
||||||
var clientKey []byte
|
|
||||||
if len(options.ClientKey) > 0 {
|
|
||||||
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
|
|
||||||
} else if options.ClientKeyPath != "" {
|
|
||||||
content, err := os.ReadFile(options.ClientKeyPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "read client key")
|
|
||||||
}
|
|
||||||
clientKey = content
|
|
||||||
}
|
|
||||||
if len(clientCertificate) > 0 && len(clientKey) > 0 {
|
|
||||||
keyPair, err := utls.X509KeyPair(clientCertificate, clientKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "parse client x509 key pair")
|
|
||||||
}
|
|
||||||
tlsConfig.Certificates = []utls.Certificate{keyPair}
|
|
||||||
} else if len(clientCertificate) > 0 || len(clientKey) > 0 {
|
|
||||||
return nil, E.New("client certificate and client key must be provided together")
|
|
||||||
}
|
|
||||||
id, err := uTLSClientHelloID(options.UTLS.Fingerprint)
|
id, err := uTLSClientHelloID(options.UTLS.Fingerprint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ const (
|
|||||||
TypeDERP = "derp"
|
TypeDERP = "derp"
|
||||||
TypeResolved = "resolved"
|
TypeResolved = "resolved"
|
||||||
TypeSSMAPI = "ssm-api"
|
TypeSSMAPI = "ssm-api"
|
||||||
TypeCCM = "ccm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package constant
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TCPKeepAliveInitial = 5 * time.Minute
|
TCPKeepAliveInitial = 10 * time.Minute
|
||||||
TCPKeepAliveInterval = 75 * time.Second
|
TCPKeepAliveInterval = 75 * time.Second
|
||||||
TCPConnectTimeout = 5 * time.Second
|
TCPConnectTimeout = 5 * time.Second
|
||||||
TCPTimeout = 15 * time.Second
|
TCPTimeout = 15 * time.Second
|
||||||
|
|||||||
29
daemon/deprecated.go
Normal file
29
daemon/deprecated.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
702
daemon/helper.pb.go
Normal file
702
daemon/helper.pb.go
Normal file
@@ -0,0 +1,702 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubscribeHelperRequestRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
AcceptGetWIFIStateRequests bool `protobuf:"varint,1,opt,name=acceptGetWIFIStateRequests,proto3" json:"acceptGetWIFIStateRequests,omitempty"`
|
||||||
|
AcceptFindConnectionOwnerRequests bool `protobuf:"varint,2,opt,name=acceptFindConnectionOwnerRequests,proto3" json:"acceptFindConnectionOwnerRequests,omitempty"`
|
||||||
|
AcceptSendNotificationRequests bool `protobuf:"varint,3,opt,name=acceptSendNotificationRequests,proto3" json:"acceptSendNotificationRequests,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) Reset() {
|
||||||
|
*x = SubscribeHelperRequestRequest{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SubscribeHelperRequestRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use SubscribeHelperRequestRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SubscribeHelperRequestRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) GetAcceptGetWIFIStateRequests() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.AcceptGetWIFIStateRequests
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) GetAcceptFindConnectionOwnerRequests() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.AcceptFindConnectionOwnerRequests
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) GetAcceptSendNotificationRequests() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.AcceptSendNotificationRequests
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
// Types that are valid to be assigned to Request:
|
||||||
|
//
|
||||||
|
// *HelperRequest_GetWIFIState
|
||||||
|
// *HelperRequest_FindConnectionOwner
|
||||||
|
// *HelperRequest_SendNotification
|
||||||
|
Request isHelperRequest_Request `protobuf_oneof:"request"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) Reset() {
|
||||||
|
*x = HelperRequest{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HelperRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[1]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HelperRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HelperRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetRequest() isHelperRequest_Request {
|
||||||
|
if x != nil {
|
||||||
|
return x.Request
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetGetWIFIState() *emptypb.Empty {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Request.(*HelperRequest_GetWIFIState); ok {
|
||||||
|
return x.GetWIFIState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetFindConnectionOwner() *FindConnectionOwnerRequest {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Request.(*HelperRequest_FindConnectionOwner); ok {
|
||||||
|
return x.FindConnectionOwner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetSendNotification() *Notification {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Request.(*HelperRequest_SendNotification); ok {
|
||||||
|
return x.SendNotification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type isHelperRequest_Request interface {
|
||||||
|
isHelperRequest_Request()
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest_GetWIFIState struct {
|
||||||
|
GetWIFIState *emptypb.Empty `protobuf:"bytes,2,opt,name=getWIFIState,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest_FindConnectionOwner struct {
|
||||||
|
FindConnectionOwner *FindConnectionOwnerRequest `protobuf:"bytes,3,opt,name=findConnectionOwner,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest_SendNotification struct {
|
||||||
|
SendNotification *Notification `protobuf:"bytes,4,opt,name=sendNotification,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperRequest_GetWIFIState) isHelperRequest_Request() {}
|
||||||
|
|
||||||
|
func (*HelperRequest_FindConnectionOwner) isHelperRequest_Request() {}
|
||||||
|
|
||||||
|
func (*HelperRequest_SendNotification) isHelperRequest_Request() {}
|
||||||
|
|
||||||
|
type FindConnectionOwnerRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
IpProtocol int32 `protobuf:"varint,1,opt,name=ipProtocol,proto3" json:"ipProtocol,omitempty"`
|
||||||
|
SourceAddress string `protobuf:"bytes,2,opt,name=sourceAddress,proto3" json:"sourceAddress,omitempty"`
|
||||||
|
SourcePort int32 `protobuf:"varint,3,opt,name=sourcePort,proto3" json:"sourcePort,omitempty"`
|
||||||
|
DestinationAddress string `protobuf:"bytes,4,opt,name=destinationAddress,proto3" json:"destinationAddress,omitempty"`
|
||||||
|
DestinationPort int32 `protobuf:"varint,5,opt,name=destinationPort,proto3" json:"destinationPort,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) Reset() {
|
||||||
|
*x = FindConnectionOwnerRequest{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FindConnectionOwnerRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[2]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use FindConnectionOwnerRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*FindConnectionOwnerRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetIpProtocol() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.IpProtocol
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetSourceAddress() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.SourceAddress
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetSourcePort() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.SourcePort
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetDestinationAddress() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DestinationAddress
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetDestinationPort() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.DestinationPort
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Notification struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
|
||||||
|
TypeName string `protobuf:"bytes,2,opt,name=typeName,proto3" json:"typeName,omitempty"`
|
||||||
|
TypeId int32 `protobuf:"varint,3,opt,name=typeId,proto3" json:"typeId,omitempty"`
|
||||||
|
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
|
||||||
|
Subtitle string `protobuf:"bytes,5,opt,name=subtitle,proto3" json:"subtitle,omitempty"`
|
||||||
|
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
|
||||||
|
OpenURL string `protobuf:"bytes,7,opt,name=openURL,proto3" json:"openURL,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) Reset() {
|
||||||
|
*x = Notification{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Notification) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Notification) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[3]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Notification) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetIdentifier() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Identifier
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetTypeName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.TypeName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetTypeId() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.TypeId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetTitle() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Title
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetSubtitle() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Subtitle
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetBody() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Body
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetOpenURL() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.OpenURL
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
// Types that are valid to be assigned to Response:
|
||||||
|
//
|
||||||
|
// *HelperResponse_WifiState
|
||||||
|
// *HelperResponse_Error
|
||||||
|
// *HelperResponse_ConnectionOwner
|
||||||
|
Response isHelperResponse_Response `protobuf_oneof:"response"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) Reset() {
|
||||||
|
*x = HelperResponse{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HelperResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[4]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HelperResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HelperResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetResponse() isHelperResponse_Response {
|
||||||
|
if x != nil {
|
||||||
|
return x.Response
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetWifiState() *WIFIState {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Response.(*HelperResponse_WifiState); ok {
|
||||||
|
return x.WifiState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetError() string {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Response.(*HelperResponse_Error); ok {
|
||||||
|
return x.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetConnectionOwner() *ConnectionOwner {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Response.(*HelperResponse_ConnectionOwner); ok {
|
||||||
|
return x.ConnectionOwner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type isHelperResponse_Response interface {
|
||||||
|
isHelperResponse_Response()
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse_WifiState struct {
|
||||||
|
WifiState *WIFIState `protobuf:"bytes,2,opt,name=wifiState,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse_Error struct {
|
||||||
|
Error string `protobuf:"bytes,3,opt,name=error,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse_ConnectionOwner struct {
|
||||||
|
ConnectionOwner *ConnectionOwner `protobuf:"bytes,4,opt,name=connectionOwner,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperResponse_WifiState) isHelperResponse_Response() {}
|
||||||
|
|
||||||
|
func (*HelperResponse_Error) isHelperResponse_Response() {}
|
||||||
|
|
||||||
|
func (*HelperResponse_ConnectionOwner) isHelperResponse_Response() {}
|
||||||
|
|
||||||
|
type ConnectionOwner struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
UserId int32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"`
|
||||||
|
UserName string `protobuf:"bytes,2,opt,name=userName,proto3" json:"userName,omitempty"`
|
||||||
|
ProcessPath string `protobuf:"bytes,3,opt,name=processPath,proto3" json:"processPath,omitempty"`
|
||||||
|
AndroidPackageName string `protobuf:"bytes,4,opt,name=androidPackageName,proto3" json:"androidPackageName,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) Reset() {
|
||||||
|
*x = ConnectionOwner{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ConnectionOwner) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[5]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionOwner.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ConnectionOwner) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetUserId() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetUserName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetProcessPath() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ProcessPath
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetAndroidPackageName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AndroidPackageName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type WIFIState struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Ssid string `protobuf:"bytes,1,opt,name=ssid,proto3" json:"ssid,omitempty"`
|
||||||
|
Bssid string `protobuf:"bytes,2,opt,name=bssid,proto3" json:"bssid,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) Reset() {
|
||||||
|
*x = WIFIState{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*WIFIState) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *WIFIState) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[6]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use WIFIState.ProtoReflect.Descriptor instead.
|
||||||
|
func (*WIFIState) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) GetSsid() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Ssid
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) GetBssid() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Bssid
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_daemon_helper_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_daemon_helper_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"\x13daemon/helper.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xf5\x01\n" +
|
||||||
|
"\x1dSubscribeHelperRequestRequest\x12>\n" +
|
||||||
|
"\x1aacceptGetWIFIStateRequests\x18\x01 \x01(\bR\x1aacceptGetWIFIStateRequests\x12L\n" +
|
||||||
|
"!acceptFindConnectionOwnerRequests\x18\x02 \x01(\bR!acceptFindConnectionOwnerRequests\x12F\n" +
|
||||||
|
"\x1eacceptSendNotificationRequests\x18\x03 \x01(\bR\x1eacceptSendNotificationRequests\"\x84\x02\n" +
|
||||||
|
"\rHelperRequest\x12\x0e\n" +
|
||||||
|
"\x02id\x18\x01 \x01(\x03R\x02id\x12<\n" +
|
||||||
|
"\fgetWIFIState\x18\x02 \x01(\v2\x16.google.protobuf.EmptyH\x00R\fgetWIFIState\x12V\n" +
|
||||||
|
"\x13findConnectionOwner\x18\x03 \x01(\v2\".daemon.FindConnectionOwnerRequestH\x00R\x13findConnectionOwner\x12B\n" +
|
||||||
|
"\x10sendNotification\x18\x04 \x01(\v2\x14.daemon.NotificationH\x00R\x10sendNotificationB\t\n" +
|
||||||
|
"\arequest\"\xdc\x01\n" +
|
||||||
|
"\x1aFindConnectionOwnerRequest\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"ipProtocol\x18\x01 \x01(\x05R\n" +
|
||||||
|
"ipProtocol\x12$\n" +
|
||||||
|
"\rsourceAddress\x18\x02 \x01(\tR\rsourceAddress\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"sourcePort\x18\x03 \x01(\x05R\n" +
|
||||||
|
"sourcePort\x12.\n" +
|
||||||
|
"\x12destinationAddress\x18\x04 \x01(\tR\x12destinationAddress\x12(\n" +
|
||||||
|
"\x0fdestinationPort\x18\x05 \x01(\x05R\x0fdestinationPort\"\xc2\x01\n" +
|
||||||
|
"\fNotification\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"identifier\x18\x01 \x01(\tR\n" +
|
||||||
|
"identifier\x12\x1a\n" +
|
||||||
|
"\btypeName\x18\x02 \x01(\tR\btypeName\x12\x16\n" +
|
||||||
|
"\x06typeId\x18\x03 \x01(\x05R\x06typeId\x12\x14\n" +
|
||||||
|
"\x05title\x18\x04 \x01(\tR\x05title\x12\x1a\n" +
|
||||||
|
"\bsubtitle\x18\x05 \x01(\tR\bsubtitle\x12\x12\n" +
|
||||||
|
"\x04body\x18\x06 \x01(\tR\x04body\x12\x18\n" +
|
||||||
|
"\aopenURL\x18\a \x01(\tR\aopenURL\"\xbc\x01\n" +
|
||||||
|
"\x0eHelperResponse\x12\x0e\n" +
|
||||||
|
"\x02id\x18\x01 \x01(\x03R\x02id\x121\n" +
|
||||||
|
"\twifiState\x18\x02 \x01(\v2\x11.daemon.WIFIStateH\x00R\twifiState\x12\x16\n" +
|
||||||
|
"\x05error\x18\x03 \x01(\tH\x00R\x05error\x12C\n" +
|
||||||
|
"\x0fconnectionOwner\x18\x04 \x01(\v2\x17.daemon.ConnectionOwnerH\x00R\x0fconnectionOwnerB\n" +
|
||||||
|
"\n" +
|
||||||
|
"\bresponse\"\x97\x01\n" +
|
||||||
|
"\x0fConnectionOwner\x12\x16\n" +
|
||||||
|
"\x06userId\x18\x01 \x01(\x05R\x06userId\x12\x1a\n" +
|
||||||
|
"\buserName\x18\x02 \x01(\tR\buserName\x12 \n" +
|
||||||
|
"\vprocessPath\x18\x03 \x01(\tR\vprocessPath\x12.\n" +
|
||||||
|
"\x12androidPackageName\x18\x04 \x01(\tR\x12androidPackageName\"5\n" +
|
||||||
|
"\tWIFIState\x12\x12\n" +
|
||||||
|
"\x04ssid\x18\x01 \x01(\tR\x04ssid\x12\x14\n" +
|
||||||
|
"\x05bssid\x18\x02 \x01(\tR\x05bssidB%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_daemon_helper_proto_rawDescOnce sync.Once
|
||||||
|
file_daemon_helper_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_daemon_helper_proto_rawDescGZIP() []byte {
|
||||||
|
file_daemon_helper_proto_rawDescOnce.Do(func() {
|
||||||
|
file_daemon_helper_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_daemon_helper_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_daemon_helper_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
|
file_daemon_helper_proto_goTypes = []any{
|
||||||
|
(*SubscribeHelperRequestRequest)(nil), // 0: daemon.SubscribeHelperRequestRequest
|
||||||
|
(*HelperRequest)(nil), // 1: daemon.HelperRequest
|
||||||
|
(*FindConnectionOwnerRequest)(nil), // 2: daemon.FindConnectionOwnerRequest
|
||||||
|
(*Notification)(nil), // 3: daemon.Notification
|
||||||
|
(*HelperResponse)(nil), // 4: daemon.HelperResponse
|
||||||
|
(*ConnectionOwner)(nil), // 5: daemon.ConnectionOwner
|
||||||
|
(*WIFIState)(nil), // 6: daemon.WIFIState
|
||||||
|
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var file_daemon_helper_proto_depIdxs = []int32{
|
||||||
|
7, // 0: daemon.HelperRequest.getWIFIState:type_name -> google.protobuf.Empty
|
||||||
|
2, // 1: daemon.HelperRequest.findConnectionOwner:type_name -> daemon.FindConnectionOwnerRequest
|
||||||
|
3, // 2: daemon.HelperRequest.sendNotification:type_name -> daemon.Notification
|
||||||
|
6, // 3: daemon.HelperResponse.wifiState:type_name -> daemon.WIFIState
|
||||||
|
5, // 4: daemon.HelperResponse.connectionOwner:type_name -> daemon.ConnectionOwner
|
||||||
|
5, // [5:5] is the sub-list for method output_type
|
||||||
|
5, // [5:5] is the sub-list for method input_type
|
||||||
|
5, // [5:5] is the sub-list for extension type_name
|
||||||
|
5, // [5:5] is the sub-list for extension extendee
|
||||||
|
0, // [0:5] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_daemon_helper_proto_init() }
|
||||||
|
func file_daemon_helper_proto_init() {
|
||||||
|
if File_daemon_helper_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file_daemon_helper_proto_msgTypes[1].OneofWrappers = []any{
|
||||||
|
(*HelperRequest_GetWIFIState)(nil),
|
||||||
|
(*HelperRequest_FindConnectionOwner)(nil),
|
||||||
|
(*HelperRequest_SendNotification)(nil),
|
||||||
|
}
|
||||||
|
file_daemon_helper_proto_msgTypes[4].OneofWrappers = []any{
|
||||||
|
(*HelperResponse_WifiState)(nil),
|
||||||
|
(*HelperResponse_Error)(nil),
|
||||||
|
(*HelperResponse_ConnectionOwner)(nil),
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 7,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_daemon_helper_proto_goTypes,
|
||||||
|
DependencyIndexes: file_daemon_helper_proto_depIdxs,
|
||||||
|
MessageInfos: file_daemon_helper_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_daemon_helper_proto = out.File
|
||||||
|
file_daemon_helper_proto_goTypes = nil
|
||||||
|
file_daemon_helper_proto_depIdxs = nil
|
||||||
|
}
|
||||||
61
daemon/helper.proto
Normal file
61
daemon/helper.proto
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package daemon;
|
||||||
|
option go_package = "github.com/sagernet/sing-box/daemon";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
message SubscribeHelperRequestRequest {
|
||||||
|
bool acceptGetWIFIStateRequests = 1;
|
||||||
|
bool acceptFindConnectionOwnerRequests = 2;
|
||||||
|
bool acceptSendNotificationRequests = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelperRequest {
|
||||||
|
int64 id = 1;
|
||||||
|
oneof request {
|
||||||
|
google.protobuf.Empty getWIFIState = 2;
|
||||||
|
FindConnectionOwnerRequest findConnectionOwner = 3;
|
||||||
|
Notification sendNotification = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message FindConnectionOwnerRequest {
|
||||||
|
int32 ipProtocol = 1;
|
||||||
|
string sourceAddress = 2;
|
||||||
|
int32 sourcePort = 3;
|
||||||
|
string destinationAddress = 4;
|
||||||
|
int32 destinationPort = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Notification {
|
||||||
|
string identifier = 1;
|
||||||
|
string typeName = 2;
|
||||||
|
int32 typeId = 3;
|
||||||
|
string title = 4;
|
||||||
|
string subtitle = 5;
|
||||||
|
string body = 6;
|
||||||
|
string openURL = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelperResponse {
|
||||||
|
int64 id = 1;
|
||||||
|
oneof response {
|
||||||
|
WIFIState wifiState = 2;
|
||||||
|
string error = 3;
|
||||||
|
ConnectionOwner connectionOwner = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConnectionOwner {
|
||||||
|
int32 userId = 1;
|
||||||
|
string userName = 2;
|
||||||
|
string processPath = 3;
|
||||||
|
string androidPackageName = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WIFIState {
|
||||||
|
string ssid = 1;
|
||||||
|
string bssid = 2;
|
||||||
|
}
|
||||||
|
|
||||||
147
daemon/instance.go
Normal file
147
daemon/instance.go
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box"
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/dns"
|
||||||
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
|
"github.com/sagernet/sing-box/include"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
"github.com/sagernet/sing/service/pause"
|
||||||
|
)
|
||||||
|
|
||||||
|
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) baseContext() context.Context {
|
||||||
|
dnsRegistry := include.DNSTransportRegistry()
|
||||||
|
if s.platform != nil && s.platform.UsePlatformLocalDNSTransport() {
|
||||||
|
dns.RegisterTransport[option.LocalDNSServerOptions](dnsRegistry, C.DNSTypeLocal, s.platform.LocalDNSTransport())
|
||||||
|
}
|
||||||
|
ctx := box.Context(s.ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||||
|
ctx = filemanager.WithDefault(ctx, s.workingDirectory, s.tempDirectory, s.userID, s.groupID)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CheckConfig(configContent string) error {
|
||||||
|
ctx := s.baseContext()
|
||||||
|
options, err := parseConfig(ctx, configContent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(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.baseContext(), 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.baseContext()
|
||||||
|
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
|
||||||
|
}
|
||||||
22
daemon/platform.go
Normal file
22
daemon/platform.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/dns"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlatformHandler interface {
|
||||||
|
ServiceStop() error
|
||||||
|
ServiceReload() error
|
||||||
|
SystemProxyStatus() (*SystemProxyStatus, error)
|
||||||
|
SetSystemProxyEnabled(enabled bool) error
|
||||||
|
WriteDebugMessage(message string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlatformInterface interface {
|
||||||
|
adapter.PlatformInterface
|
||||||
|
|
||||||
|
UsePlatformLocalDNSTransport() bool
|
||||||
|
LocalDNSTransport() dns.TransportConstructorFunc[option.LocalDNSServerOptions]
|
||||||
|
}
|
||||||
774
daemon/started_service.go
Normal file
774
daemon/started_service.go
Normal file
@@ -0,0 +1,774 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
|
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||||
|
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||||
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/protocol/group"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/batch"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/memory"
|
||||||
|
"github.com/sagernet/sing/common/observable"
|
||||||
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
|
"github.com/gofrs/uuid/v5"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ StartedServiceServer = (*StartedService)(nil)
|
||||||
|
|
||||||
|
type StartedService struct {
|
||||||
|
ctx context.Context
|
||||||
|
platform PlatformInterface
|
||||||
|
platformHandler PlatformHandler
|
||||||
|
debug bool
|
||||||
|
logMaxLines int
|
||||||
|
workingDirectory string
|
||||||
|
tempDirectory string
|
||||||
|
userID int
|
||||||
|
groupID int
|
||||||
|
systemProxyEnabled bool
|
||||||
|
serviceAccess sync.RWMutex
|
||||||
|
serviceStatus *ServiceStatus
|
||||||
|
serviceStatusSubscriber *observable.Subscriber[*ServiceStatus]
|
||||||
|
serviceStatusObserver *observable.Observer[*ServiceStatus]
|
||||||
|
logAccess sync.RWMutex
|
||||||
|
logLines list.List[*log.Entry]
|
||||||
|
logSubscriber *observable.Subscriber[*log.Entry]
|
||||||
|
logObserver *observable.Observer[*log.Entry]
|
||||||
|
instance *Instance
|
||||||
|
urlTestSubscriber *observable.Subscriber[struct{}]
|
||||||
|
urlTestObserver *observable.Observer[struct{}]
|
||||||
|
urlTestHistoryStorage *urltest.HistoryStorage
|
||||||
|
clashModeSubscriber *observable.Subscriber[struct{}]
|
||||||
|
clashModeObserver *observable.Observer[struct{}]
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceOptions struct {
|
||||||
|
Context context.Context
|
||||||
|
Platform PlatformInterface
|
||||||
|
PlatformHandler PlatformHandler
|
||||||
|
Debug bool
|
||||||
|
LogMaxLines int
|
||||||
|
WorkingDirectory string
|
||||||
|
TempDirectory string
|
||||||
|
UserID int
|
||||||
|
GroupID int
|
||||||
|
SystemProxyEnabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStartedService(options ServiceOptions) *StartedService {
|
||||||
|
s := &StartedService{
|
||||||
|
ctx: options.Context,
|
||||||
|
platform: options.Platform,
|
||||||
|
platformHandler: options.PlatformHandler,
|
||||||
|
debug: options.Debug,
|
||||||
|
logMaxLines: options.LogMaxLines,
|
||||||
|
workingDirectory: options.WorkingDirectory,
|
||||||
|
tempDirectory: options.TempDirectory,
|
||||||
|
userID: options.UserID,
|
||||||
|
groupID: options.GroupID,
|
||||||
|
systemProxyEnabled: options.SystemProxyEnabled,
|
||||||
|
serviceStatus: &ServiceStatus{Status: ServiceStatus_IDLE},
|
||||||
|
serviceStatusSubscriber: observable.NewSubscriber[*ServiceStatus](4),
|
||||||
|
logSubscriber: observable.NewSubscriber[*log.Entry](128),
|
||||||
|
urlTestSubscriber: observable.NewSubscriber[struct{}](1),
|
||||||
|
urlTestHistoryStorage: urltest.NewHistoryStorage(),
|
||||||
|
clashModeSubscriber: observable.NewSubscriber[struct{}](1),
|
||||||
|
}
|
||||||
|
s.serviceStatusObserver = observable.NewObserver(s.serviceStatusSubscriber, 2)
|
||||||
|
s.logObserver = observable.NewObserver(s.logSubscriber, 64)
|
||||||
|
s.urlTestObserver = observable.NewObserver(s.urlTestSubscriber, 1)
|
||||||
|
s.clashModeObserver = observable.NewObserver(s.clashModeSubscriber, 1)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) resetLogs() {
|
||||||
|
s.logAccess.Lock()
|
||||||
|
s.logLines = list.List[*log.Entry]{}
|
||||||
|
s.logAccess.Unlock()
|
||||||
|
s.logSubscriber.Emit(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) updateStatus(newStatus ServiceStatus_Type) {
|
||||||
|
statusObject := &ServiceStatus{Status: newStatus}
|
||||||
|
s.serviceStatusSubscriber.Emit(statusObject)
|
||||||
|
s.serviceStatus = statusObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) updateStatusError(err error) error {
|
||||||
|
statusObject := &ServiceStatus{Status: ServiceStatus_FATAL, ErrorMessage: err.Error()}
|
||||||
|
s.serviceStatusSubscriber.Emit(statusObject)
|
||||||
|
s.serviceStatus = statusObject
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) StartOrReloadService(profileContent string, options *OverrideOptions) error {
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_IDLE, ServiceStatus_STARTED, ServiceStatus_STARTING:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
s.updateStatus(ServiceStatus_STARTING)
|
||||||
|
s.resetLogs()
|
||||||
|
instance, err := s.newInstance(profileContent, options)
|
||||||
|
if err != nil {
|
||||||
|
return s.updateStatusError(err)
|
||||||
|
}
|
||||||
|
s.instance = instance
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
err = instance.Start()
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTING {
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return s.updateStatusError(err)
|
||||||
|
}
|
||||||
|
s.updateStatus(ServiceStatus_STARTED)
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
runtime.GC()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CloseService() error {
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
s.updateStatus(ServiceStatus_STOPPING)
|
||||||
|
if s.instance != nil {
|
||||||
|
err := s.instance.Close()
|
||||||
|
if err != nil {
|
||||||
|
return s.updateStatusError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.instance = nil
|
||||||
|
s.updateStatus(ServiceStatus_IDLE)
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
runtime.GC()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetError(err error) {
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
s.updateStatusError(err)
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
s.WriteMessage(log.LevelError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) StopService(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
err := s.platformHandler.ServiceStop()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) ReloadService(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
err := s.platformHandler.ServiceReload()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeServiceStatus(empty *emptypb.Empty, server grpc.ServerStreamingServer[ServiceStatus]) error {
|
||||||
|
subscription, done, err := s.serviceStatusObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.serviceStatusObserver.UnSubscribe(subscription)
|
||||||
|
err = server.Send(s.serviceStatus)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case newStatus := <-subscription:
|
||||||
|
err = server.Send(newStatus)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeLog(empty *emptypb.Empty, server grpc.ServerStreamingServer[Log]) error {
|
||||||
|
var savedLines []*log.Entry
|
||||||
|
s.logAccess.Lock()
|
||||||
|
savedLines = make([]*log.Entry, 0, s.logLines.Len())
|
||||||
|
for element := s.logLines.Front(); element != nil; element = element.Next() {
|
||||||
|
savedLines = append(savedLines, element.Value)
|
||||||
|
}
|
||||||
|
s.logAccess.Unlock()
|
||||||
|
subscription, done, err := s.logObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.logObserver.UnSubscribe(subscription)
|
||||||
|
err = server.Send(&Log{
|
||||||
|
Messages: common.Map(savedLines, func(it *log.Entry) *Log_Message {
|
||||||
|
return &Log_Message{
|
||||||
|
Level: LogLevel(it.Level),
|
||||||
|
Message: it.Message,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Reset_: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case message := <-subscription:
|
||||||
|
if message == nil {
|
||||||
|
err = server.Send(&Log{Reset_: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
messages := []*Log_Message{{
|
||||||
|
Level: LogLevel(message.Level),
|
||||||
|
Message: message.Message,
|
||||||
|
}}
|
||||||
|
fetch:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message = <-subscription:
|
||||||
|
messages = append(messages, &Log_Message{
|
||||||
|
Level: LogLevel(message.Level),
|
||||||
|
Message: message.Message,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
break fetch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = server.Send(&Log{Messages: messages})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetDefaultLogLevel(ctx context.Context, empty *emptypb.Empty) (*DefaultLogLevel, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
logLevel := s.instance.instance.LogFactory().Level()
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return &DefaultLogLevel{Level: LogLevel(logLevel)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeStatus(request *SubscribeStatusRequest, server grpc.ServerStreamingServer[Status]) error {
|
||||||
|
interval := time.Duration(request.Interval)
|
||||||
|
if interval <= 0 {
|
||||||
|
interval = time.Second // Default to 1 second
|
||||||
|
}
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
status := s.readStatus()
|
||||||
|
uploadTotal := status.UplinkTotal
|
||||||
|
downloadTotal := status.DownlinkTotal
|
||||||
|
for {
|
||||||
|
err := server.Send(status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-ticker.C:
|
||||||
|
}
|
||||||
|
status = s.readStatus()
|
||||||
|
upload := status.UplinkTotal - uploadTotal
|
||||||
|
download := status.DownlinkTotal - downloadTotal
|
||||||
|
uploadTotal = status.UplinkTotal
|
||||||
|
downloadTotal = status.DownlinkTotal
|
||||||
|
status.Uplink = upload
|
||||||
|
status.Downlink = download
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) readStatus() *Status {
|
||||||
|
var status Status
|
||||||
|
status.Memory = memory.Inuse()
|
||||||
|
status.Goroutines = int32(runtime.NumGoroutine())
|
||||||
|
status.ConnectionsOut = int32(conntrack.Count())
|
||||||
|
nowService := s.instance
|
||||||
|
if nowService != nil {
|
||||||
|
if clashServer := nowService.clashServer; clashServer != nil {
|
||||||
|
status.TrafficAvailable = true
|
||||||
|
trafficManager := clashServer.(*clashapi.Server).TrafficManager()
|
||||||
|
status.UplinkTotal, status.DownlinkTotal = trafficManager.Total()
|
||||||
|
status.ConnectionsIn = int32(trafficManager.ConnectionsLen())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeGroups(empty *emptypb.Empty, server grpc.ServerStreamingServer[Groups]) error {
|
||||||
|
subscription, done, err := s.urlTestObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.urlTestObserver.UnSubscribe(subscription)
|
||||||
|
for {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
groups := s.readGroups()
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
err = server.Send(groups)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-subscription:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) readGroups() *Groups {
|
||||||
|
historyStorage := s.instance.urlTestHistoryStorage
|
||||||
|
boxService := s.instance
|
||||||
|
outbounds := boxService.instance.Outbound().Outbounds()
|
||||||
|
var iGroups []adapter.OutboundGroup
|
||||||
|
for _, it := range outbounds {
|
||||||
|
if group, isGroup := it.(adapter.OutboundGroup); isGroup {
|
||||||
|
iGroups = append(iGroups, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var gs Groups
|
||||||
|
for _, iGroup := range iGroups {
|
||||||
|
var g Group
|
||||||
|
g.Tag = iGroup.Tag()
|
||||||
|
g.Type = iGroup.Type()
|
||||||
|
_, g.Selectable = iGroup.(*group.Selector)
|
||||||
|
g.Selected = iGroup.Now()
|
||||||
|
if boxService.cacheFile != nil {
|
||||||
|
if isExpand, loaded := boxService.cacheFile.LoadGroupExpand(g.Tag); loaded {
|
||||||
|
g.IsExpand = isExpand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, itemTag := range iGroup.All() {
|
||||||
|
itemOutbound, isLoaded := boxService.instance.Outbound().Outbound(itemTag)
|
||||||
|
if !isLoaded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var item GroupItem
|
||||||
|
item.Tag = itemTag
|
||||||
|
item.Type = itemOutbound.Type()
|
||||||
|
if history := historyStorage.LoadURLTestHistory(adapter.OutboundTag(itemOutbound)); history != nil {
|
||||||
|
item.UrlTestTime = history.Time.Unix()
|
||||||
|
item.UrlTestDelay = int32(history.Delay)
|
||||||
|
}
|
||||||
|
g.Items = append(g.Items, &item)
|
||||||
|
}
|
||||||
|
if len(g.Items) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
gs.Group = append(gs.Group, &g)
|
||||||
|
}
|
||||||
|
return &gs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetClashModeStatus(ctx context.Context, empty *emptypb.Empty) (*ClashModeStatus, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
clashServer := s.instance.clashServer
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
if clashServer == nil {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
return &ClashModeStatus{
|
||||||
|
ModeList: clashServer.ModeList(),
|
||||||
|
CurrentMode: clashServer.Mode(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeClashMode(empty *emptypb.Empty, server grpc.ServerStreamingServer[ClashMode]) error {
|
||||||
|
subscription, done, err := s.clashModeObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.clashModeObserver.UnSubscribe(subscription)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-subscription:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
message := &ClashMode{Mode: s.instance.clashServer.Mode()}
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
err = server.Send(message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetClashMode(ctx context.Context, request *ClashMode) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
clashServer := s.instance.clashServer
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
clashServer.(*clashapi.Server).SetMode(request.Mode)
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) URLTest(ctx context.Context, request *URLTestRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
groupTag := request.OutboundTag
|
||||||
|
abstractOutboundGroup, isLoaded := boxService.instance.Outbound().Outbound(groupTag)
|
||||||
|
if !isLoaded {
|
||||||
|
return nil, E.New("outbound group not found: ", groupTag)
|
||||||
|
}
|
||||||
|
outboundGroup, isOutboundGroup := abstractOutboundGroup.(adapter.OutboundGroup)
|
||||||
|
if !isOutboundGroup {
|
||||||
|
return nil, E.New("outbound is not a group: ", groupTag)
|
||||||
|
}
|
||||||
|
urlTest, isURLTest := abstractOutboundGroup.(*group.URLTest)
|
||||||
|
if isURLTest {
|
||||||
|
go urlTest.CheckOutbounds()
|
||||||
|
} else {
|
||||||
|
var historyStorage adapter.URLTestHistoryStorage
|
||||||
|
if s.instance.clashServer != nil {
|
||||||
|
historyStorage = s.instance.clashServer.HistoryStorage()
|
||||||
|
} else {
|
||||||
|
return nil, E.New("Clash API is required for URLTest on non-URLTest group")
|
||||||
|
}
|
||||||
|
|
||||||
|
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||||
|
itOutbound, _ := boxService.instance.Outbound().Outbound(it)
|
||||||
|
return itOutbound
|
||||||
|
}), func(it adapter.Outbound) bool {
|
||||||
|
if it == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, isGroup := it.(adapter.OutboundGroup)
|
||||||
|
if isGroup {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
b, _ := batch.New(boxService.ctx, batch.WithConcurrencyNum[any](10))
|
||||||
|
for _, detour := range outbounds {
|
||||||
|
outboundToTest := detour
|
||||||
|
outboundTag := outboundToTest.Tag()
|
||||||
|
b.Go(outboundTag, func() (any, error) {
|
||||||
|
t, err := urltest.URLTest(boxService.ctx, "", outboundToTest)
|
||||||
|
if err != nil {
|
||||||
|
historyStorage.DeleteURLTestHistory(outboundTag)
|
||||||
|
} else {
|
||||||
|
historyStorage.StoreURLTestHistory(outboundTag, &adapter.URLTestHistory{
|
||||||
|
Time: time.Now(),
|
||||||
|
Delay: t,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SelectOutbound(ctx context.Context, request *SelectOutboundRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
outboundGroup, isLoaded := boxService.Outbound().Outbound(request.GroupTag)
|
||||||
|
if !isLoaded {
|
||||||
|
return nil, E.New("selector not found: ", request.GroupTag)
|
||||||
|
}
|
||||||
|
selector, isSelector := outboundGroup.(*group.Selector)
|
||||||
|
if !isSelector {
|
||||||
|
return nil, E.New("outbound is not a selector: ", request.GroupTag)
|
||||||
|
}
|
||||||
|
if !selector.SelectOutbound(request.OutboundTag) {
|
||||||
|
return nil, E.New("outbound not found in selector: ", request.OutboundTag)
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetGroupExpand(ctx context.Context, request *SetGroupExpandRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
if boxService.cacheFile != nil {
|
||||||
|
err := boxService.cacheFile.StoreGroupExpand(request.GroupTag, request.IsExpand)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetSystemProxyStatus(ctx context.Context, empty *emptypb.Empty) (*SystemProxyStatus, error) {
|
||||||
|
return s.platformHandler.SystemProxyStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetSystemProxyEnabled(ctx context.Context, request *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
||||||
|
err := s.platformHandler.SetSystemProxyEnabled(request.Enabled)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeConnections(request *SubscribeConnectionsRequest, server grpc.ServerStreamingServer[Connections]) error {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
ticker := time.NewTicker(time.Duration(request.Interval))
|
||||||
|
defer ticker.Stop()
|
||||||
|
trafficManager := boxService.clashServer.(*clashapi.Server).TrafficManager()
|
||||||
|
var (
|
||||||
|
connections = make(map[uuid.UUID]*Connection)
|
||||||
|
outConnections []*Connection
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
outConnections = outConnections[:0]
|
||||||
|
for _, connection := range trafficManager.Connections() {
|
||||||
|
outConnections = append(outConnections, newConnection(connections, connection, false))
|
||||||
|
}
|
||||||
|
for _, connection := range trafficManager.ClosedConnections() {
|
||||||
|
outConnections = append(outConnections, newConnection(connections, connection, true))
|
||||||
|
}
|
||||||
|
err := server.Send(&Connections{Connections: outConnections})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-ticker.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol.TrackerMetadata, isClosed bool) *Connection {
|
||||||
|
if oldConnection, loaded := connections[metadata.ID]; loaded {
|
||||||
|
if isClosed {
|
||||||
|
if oldConnection.ClosedAt == 0 {
|
||||||
|
oldConnection.Uplink = 0
|
||||||
|
oldConnection.Downlink = 0
|
||||||
|
oldConnection.ClosedAt = metadata.ClosedAt.UnixMilli()
|
||||||
|
}
|
||||||
|
return oldConnection
|
||||||
|
}
|
||||||
|
lastUplink := oldConnection.UplinkTotal
|
||||||
|
lastDownlink := oldConnection.DownlinkTotal
|
||||||
|
uplinkTotal := metadata.Upload.Load()
|
||||||
|
downlinkTotal := metadata.Download.Load()
|
||||||
|
oldConnection.Uplink = uplinkTotal - lastUplink
|
||||||
|
oldConnection.Downlink = downlinkTotal - lastDownlink
|
||||||
|
oldConnection.UplinkTotal = uplinkTotal
|
||||||
|
oldConnection.DownlinkTotal = downlinkTotal
|
||||||
|
return oldConnection
|
||||||
|
}
|
||||||
|
var rule string
|
||||||
|
if metadata.Rule != nil {
|
||||||
|
rule = metadata.Rule.String()
|
||||||
|
}
|
||||||
|
uplinkTotal := metadata.Upload.Load()
|
||||||
|
downlinkTotal := metadata.Download.Load()
|
||||||
|
uplink := uplinkTotal
|
||||||
|
downlink := downlinkTotal
|
||||||
|
var closedAt int64
|
||||||
|
if !metadata.ClosedAt.IsZero() {
|
||||||
|
closedAt = metadata.ClosedAt.UnixMilli()
|
||||||
|
uplink = 0
|
||||||
|
downlink = 0
|
||||||
|
}
|
||||||
|
connection := &Connection{
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
Inbound: metadata.Metadata.Inbound,
|
||||||
|
InboundType: metadata.Metadata.InboundType,
|
||||||
|
IpVersion: int32(metadata.Metadata.IPVersion),
|
||||||
|
Network: metadata.Metadata.Network,
|
||||||
|
Source: metadata.Metadata.Source.String(),
|
||||||
|
Destination: metadata.Metadata.Destination.String(),
|
||||||
|
Domain: metadata.Metadata.Domain,
|
||||||
|
Protocol: metadata.Metadata.Protocol,
|
||||||
|
User: metadata.Metadata.User,
|
||||||
|
FromOutbound: metadata.Metadata.Outbound,
|
||||||
|
CreatedAt: metadata.CreatedAt.UnixMilli(),
|
||||||
|
ClosedAt: closedAt,
|
||||||
|
Uplink: uplink,
|
||||||
|
Downlink: downlink,
|
||||||
|
UplinkTotal: uplinkTotal,
|
||||||
|
DownlinkTotal: downlinkTotal,
|
||||||
|
Rule: rule,
|
||||||
|
Outbound: metadata.Outbound,
|
||||||
|
OutboundType: metadata.OutboundType,
|
||||||
|
ChainList: metadata.Chain,
|
||||||
|
}
|
||||||
|
connections[metadata.ID] = connection
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CloseConnection(ctx context.Context, request *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
targetConn := boxService.clashServer.(*clashapi.Server).TrafficManager().Connection(uuid.FromStringOrNil(request.Id))
|
||||||
|
if targetConn != nil {
|
||||||
|
targetConn.Close()
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CloseAllConnections(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
conntrack.Close()
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetDeprecatedWarnings(ctx context.Context, empty *emptypb.Empty) (*DeprecatedWarnings, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
notes := service.FromContext[deprecated.Manager](boxService.ctx).(*deprecatedManager).Get()
|
||||||
|
return &DeprecatedWarnings{
|
||||||
|
Warnings: common.Map(notes, func(it deprecated.Note) *DeprecatedWarning {
|
||||||
|
return &DeprecatedWarning{
|
||||||
|
Message: it.Message(),
|
||||||
|
Impending: it.Impending(),
|
||||||
|
MigrationLink: it.MigrationLink,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeHelperEvents(empty *emptypb.Empty, server grpc.ServerStreamingServer[HelperRequest]) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SendHelperResponse(ctx context.Context, response *HelperResponse) (*emptypb.Empty, error) {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) WriteMessage(level log.Level, message string) {
|
||||||
|
item := &log.Entry{Level: level, Message: message}
|
||||||
|
s.logSubscriber.Emit(item)
|
||||||
|
s.logAccess.Lock()
|
||||||
|
s.logLines.PushBack(item)
|
||||||
|
if s.logLines.Len() > s.logMaxLines {
|
||||||
|
s.logLines.Remove(s.logLines.Front())
|
||||||
|
}
|
||||||
|
s.logAccess.Unlock()
|
||||||
|
if s.debug {
|
||||||
|
s.platformHandler.WriteDebugMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) Instance() *Instance {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
defer s.serviceAccess.RUnlock()
|
||||||
|
return s.instance
|
||||||
|
}
|
||||||
1906
daemon/started_service.pb.go
Normal file
1906
daemon/started_service.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
204
daemon/started_service.proto
Normal file
204
daemon/started_service.proto
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package daemon;
|
||||||
|
option go_package = "github.com/sagernet/sing-box/daemon";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "daemon/helper.proto";
|
||||||
|
|
||||||
|
service StartedService {
|
||||||
|
rpc StopService(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
rpc ReloadService(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc SubscribeServiceStatus(google.protobuf.Empty) returns(stream ServiceStatus) {}
|
||||||
|
rpc SubscribeLog(google.protobuf.Empty) returns(stream Log) {}
|
||||||
|
rpc GetDefaultLogLevel(google.protobuf.Empty) returns(DefaultLogLevel) {}
|
||||||
|
rpc SubscribeStatus(SubscribeStatusRequest) returns(stream Status) {}
|
||||||
|
rpc SubscribeGroups(google.protobuf.Empty) returns(stream Groups) {}
|
||||||
|
|
||||||
|
rpc GetClashModeStatus(google.protobuf.Empty) returns(ClashModeStatus) {}
|
||||||
|
rpc SubscribeClashMode(google.protobuf.Empty) returns(stream ClashMode) {}
|
||||||
|
rpc SetClashMode(ClashMode) returns(google.protobuf.Empty) {}
|
||||||
|
|
||||||
|
rpc URLTest(URLTestRequest) returns(google.protobuf.Empty) {}
|
||||||
|
rpc SelectOutbound(SelectOutboundRequest) returns (google.protobuf.Empty) {}
|
||||||
|
rpc SetGroupExpand(SetGroupExpandRequest) returns (google.protobuf.Empty) {}
|
||||||
|
|
||||||
|
rpc GetSystemProxyStatus(google.protobuf.Empty) returns(SystemProxyStatus) {}
|
||||||
|
rpc SetSystemProxyEnabled(SetSystemProxyEnabledRequest) returns(google.protobuf.Empty) {}
|
||||||
|
|
||||||
|
rpc SubscribeConnections(SubscribeConnectionsRequest) returns(stream Connections) {}
|
||||||
|
rpc CloseConnection(CloseConnectionRequest) returns(google.protobuf.Empty) {}
|
||||||
|
rpc CloseAllConnections(google.protobuf.Empty) returns(google.protobuf.Empty) {}
|
||||||
|
rpc GetDeprecatedWarnings(google.protobuf.Empty) returns(DeprecatedWarnings) {}
|
||||||
|
|
||||||
|
rpc SubscribeHelperEvents(google.protobuf.Empty) returns(stream HelperRequest) {}
|
||||||
|
rpc SendHelperResponse(HelperResponse) returns(google.protobuf.Empty) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServiceStatus {
|
||||||
|
enum Type {
|
||||||
|
IDLE = 0;
|
||||||
|
STARTING = 1;
|
||||||
|
STARTED = 2;
|
||||||
|
STOPPING = 3;
|
||||||
|
FATAL = 4;
|
||||||
|
}
|
||||||
|
Type status = 1;
|
||||||
|
string errorMessage = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReloadServiceRequest {
|
||||||
|
string newProfileContent = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeStatusRequest {
|
||||||
|
int64 interval = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LogLevel {
|
||||||
|
PANIC = 0;
|
||||||
|
FATAL = 1;
|
||||||
|
ERROR = 2;
|
||||||
|
WARN = 3;
|
||||||
|
INFO = 4;
|
||||||
|
DEBUG = 5;
|
||||||
|
TRACE = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Log {
|
||||||
|
repeated Message messages = 1;
|
||||||
|
bool reset = 2;
|
||||||
|
message Message {
|
||||||
|
LogLevel level = 1;
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message DefaultLogLevel {
|
||||||
|
LogLevel level = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Status {
|
||||||
|
uint64 memory = 1;
|
||||||
|
int32 goroutines = 2;
|
||||||
|
int32 connectionsIn = 3;
|
||||||
|
int32 connectionsOut = 4;
|
||||||
|
bool trafficAvailable = 5;
|
||||||
|
int64 uplink = 6;
|
||||||
|
int64 downlink = 7;
|
||||||
|
int64 uplinkTotal = 8;
|
||||||
|
int64 downlinkTotal = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Groups {
|
||||||
|
repeated Group group = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Group {
|
||||||
|
string tag = 1;
|
||||||
|
string type = 2;
|
||||||
|
bool selectable = 3;
|
||||||
|
string selected = 4;
|
||||||
|
bool isExpand = 5;
|
||||||
|
repeated GroupItem items = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GroupItem {
|
||||||
|
string tag = 1;
|
||||||
|
string type = 2;
|
||||||
|
int64 urlTestTime = 3;
|
||||||
|
int32 urlTestDelay = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message URLTestRequest {
|
||||||
|
string outboundTag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SelectOutboundRequest {
|
||||||
|
string groupTag = 1;
|
||||||
|
string outboundTag = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetGroupExpandRequest {
|
||||||
|
string groupTag = 1;
|
||||||
|
bool isExpand = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClashMode {
|
||||||
|
string mode = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClashModeStatus {
|
||||||
|
repeated string modeList = 1;
|
||||||
|
string currentMode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SystemProxyStatus {
|
||||||
|
bool available = 1;
|
||||||
|
bool enabled = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetSystemProxyEnabledRequest {
|
||||||
|
bool enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeConnectionsRequest {
|
||||||
|
int64 interval = 1;
|
||||||
|
ConnectionFilter filter = 2;
|
||||||
|
ConnectionSortBy sortBy = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectionFilter {
|
||||||
|
ALL = 0;
|
||||||
|
ACTIVE = 1;
|
||||||
|
CLOSED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectionSortBy {
|
||||||
|
DATE = 0;
|
||||||
|
TRAFFIC = 1;
|
||||||
|
TOTAL_TRAFFIC = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Connections {
|
||||||
|
repeated Connection connections = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Connection {
|
||||||
|
string id = 1;
|
||||||
|
string inbound = 2;
|
||||||
|
string inboundType = 3;
|
||||||
|
int32 ipVersion = 4;
|
||||||
|
string network = 5;
|
||||||
|
string source = 6;
|
||||||
|
string destination = 7;
|
||||||
|
string domain = 8;
|
||||||
|
string protocol = 9;
|
||||||
|
string user = 10;
|
||||||
|
string fromOutbound = 11;
|
||||||
|
int64 createdAt = 12;
|
||||||
|
int64 closedAt = 13;
|
||||||
|
int64 uplink = 14;
|
||||||
|
int64 downlink = 15;
|
||||||
|
int64 uplinkTotal = 16;
|
||||||
|
int64 downlinkTotal = 17;
|
||||||
|
string rule = 18;
|
||||||
|
string outbound = 19;
|
||||||
|
string outboundType = 20;
|
||||||
|
repeated string chainList = 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CloseConnectionRequest {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeprecatedWarnings {
|
||||||
|
repeated DeprecatedWarning warnings = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeprecatedWarning {
|
||||||
|
string message = 1;
|
||||||
|
bool impending = 2;
|
||||||
|
string migrationLink = 3;
|
||||||
|
}
|
||||||
919
daemon/started_service_grpc.pb.go
Normal file
919
daemon/started_service_grpc.pb.go
Normal file
@@ -0,0 +1,919 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
StartedService_StopService_FullMethodName = "/daemon.StartedService/StopService"
|
||||||
|
StartedService_ReloadService_FullMethodName = "/daemon.StartedService/ReloadService"
|
||||||
|
StartedService_SubscribeServiceStatus_FullMethodName = "/daemon.StartedService/SubscribeServiceStatus"
|
||||||
|
StartedService_SubscribeLog_FullMethodName = "/daemon.StartedService/SubscribeLog"
|
||||||
|
StartedService_GetDefaultLogLevel_FullMethodName = "/daemon.StartedService/GetDefaultLogLevel"
|
||||||
|
StartedService_SubscribeStatus_FullMethodName = "/daemon.StartedService/SubscribeStatus"
|
||||||
|
StartedService_SubscribeGroups_FullMethodName = "/daemon.StartedService/SubscribeGroups"
|
||||||
|
StartedService_GetClashModeStatus_FullMethodName = "/daemon.StartedService/GetClashModeStatus"
|
||||||
|
StartedService_SubscribeClashMode_FullMethodName = "/daemon.StartedService/SubscribeClashMode"
|
||||||
|
StartedService_SetClashMode_FullMethodName = "/daemon.StartedService/SetClashMode"
|
||||||
|
StartedService_URLTest_FullMethodName = "/daemon.StartedService/URLTest"
|
||||||
|
StartedService_SelectOutbound_FullMethodName = "/daemon.StartedService/SelectOutbound"
|
||||||
|
StartedService_SetGroupExpand_FullMethodName = "/daemon.StartedService/SetGroupExpand"
|
||||||
|
StartedService_GetSystemProxyStatus_FullMethodName = "/daemon.StartedService/GetSystemProxyStatus"
|
||||||
|
StartedService_SetSystemProxyEnabled_FullMethodName = "/daemon.StartedService/SetSystemProxyEnabled"
|
||||||
|
StartedService_SubscribeConnections_FullMethodName = "/daemon.StartedService/SubscribeConnections"
|
||||||
|
StartedService_CloseConnection_FullMethodName = "/daemon.StartedService/CloseConnection"
|
||||||
|
StartedService_CloseAllConnections_FullMethodName = "/daemon.StartedService/CloseAllConnections"
|
||||||
|
StartedService_GetDeprecatedWarnings_FullMethodName = "/daemon.StartedService/GetDeprecatedWarnings"
|
||||||
|
StartedService_SubscribeHelperEvents_FullMethodName = "/daemon.StartedService/SubscribeHelperEvents"
|
||||||
|
StartedService_SendHelperResponse_FullMethodName = "/daemon.StartedService/SendHelperResponse"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartedServiceClient is the client API for StartedService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type StartedServiceClient interface {
|
||||||
|
StopService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
ReloadService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SubscribeServiceStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceStatus], error)
|
||||||
|
SubscribeLog(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Log], error)
|
||||||
|
GetDefaultLogLevel(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DefaultLogLevel, error)
|
||||||
|
SubscribeStatus(ctx context.Context, in *SubscribeStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Status], error)
|
||||||
|
SubscribeGroups(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Groups], error)
|
||||||
|
GetClashModeStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ClashModeStatus, error)
|
||||||
|
SubscribeClashMode(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ClashMode], error)
|
||||||
|
SetClashMode(ctx context.Context, in *ClashMode, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
URLTest(ctx context.Context, in *URLTestRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SelectOutbound(ctx context.Context, in *SelectOutboundRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SetGroupExpand(ctx context.Context, in *SetGroupExpandRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
GetSystemProxyStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemProxyStatus, error)
|
||||||
|
SetSystemProxyEnabled(ctx context.Context, in *SetSystemProxyEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Connections], error)
|
||||||
|
CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
CloseAllConnections(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
GetDeprecatedWarnings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DeprecatedWarnings, error)
|
||||||
|
SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error)
|
||||||
|
SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type startedServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStartedServiceClient(cc grpc.ClientConnInterface) StartedServiceClient {
|
||||||
|
return &startedServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) StopService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_StopService_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) ReloadService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_ReloadService_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeServiceStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceStatus], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[0], StartedService_SubscribeServiceStatus_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, ServiceStatus]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeServiceStatusClient = grpc.ServerStreamingClient[ServiceStatus]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeLog(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Log], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[1], StartedService_SubscribeLog_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, Log]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeLogClient = grpc.ServerStreamingClient[Log]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetDefaultLogLevel(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DefaultLogLevel, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(DefaultLogLevel)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetDefaultLogLevel_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeStatus(ctx context.Context, in *SubscribeStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Status], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[2], StartedService_SubscribeStatus_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[SubscribeStatusRequest, Status]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeStatusClient = grpc.ServerStreamingClient[Status]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeGroups(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Groups], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[3], StartedService_SubscribeGroups_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, Groups]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeGroupsClient = grpc.ServerStreamingClient[Groups]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetClashModeStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ClashModeStatus, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(ClashModeStatus)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetClashModeStatus_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeClashMode(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ClashMode], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[4], StartedService_SubscribeClashMode_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, ClashMode]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeClashModeClient = grpc.ServerStreamingClient[ClashMode]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SetClashMode(ctx context.Context, in *ClashMode, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SetClashMode_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) URLTest(ctx context.Context, in *URLTestRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_URLTest_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SelectOutbound(ctx context.Context, in *SelectOutboundRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SelectOutbound_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SetGroupExpand(ctx context.Context, in *SetGroupExpandRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SetGroupExpand_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetSystemProxyStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemProxyStatus, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(SystemProxyStatus)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetSystemProxyStatus_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SetSystemProxyEnabled(ctx context.Context, in *SetSystemProxyEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SetSystemProxyEnabled_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Connections], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[5], StartedService_SubscribeConnections_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[SubscribeConnectionsRequest, Connections]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeConnectionsClient = grpc.ServerStreamingClient[Connections]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_CloseConnection_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) CloseAllConnections(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_CloseAllConnections_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetDeprecatedWarnings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DeprecatedWarnings, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(DeprecatedWarnings)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetDeprecatedWarnings_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[6], StartedService_SubscribeHelperEvents_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, HelperRequest]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeHelperEventsClient = grpc.ServerStreamingClient[HelperRequest]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SendHelperResponse_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartedServiceServer is the server API for StartedService service.
|
||||||
|
// All implementations must embed UnimplementedStartedServiceServer
|
||||||
|
// for forward compatibility.
|
||||||
|
type StartedServiceServer interface {
|
||||||
|
StopService(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
|
ReloadService(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
|
SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error
|
||||||
|
SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error
|
||||||
|
GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error)
|
||||||
|
SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error
|
||||||
|
SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error
|
||||||
|
GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error)
|
||||||
|
SubscribeClashMode(*emptypb.Empty, grpc.ServerStreamingServer[ClashMode]) error
|
||||||
|
SetClashMode(context.Context, *ClashMode) (*emptypb.Empty, error)
|
||||||
|
URLTest(context.Context, *URLTestRequest) (*emptypb.Empty, error)
|
||||||
|
SelectOutbound(context.Context, *SelectOutboundRequest) (*emptypb.Empty, error)
|
||||||
|
SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error)
|
||||||
|
GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error)
|
||||||
|
SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error)
|
||||||
|
SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error
|
||||||
|
CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error)
|
||||||
|
CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
|
GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error)
|
||||||
|
SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error
|
||||||
|
SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error)
|
||||||
|
mustEmbedUnimplementedStartedServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedStartedServiceServer must be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedStartedServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) StopService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method StopService not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) ReloadService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ReloadService not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeServiceStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeLog not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetDefaultLogLevel not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeGroups not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetClashModeStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeClashMode(*emptypb.Empty, grpc.ServerStreamingServer[ClashMode]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeClashMode not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SetClashMode(context.Context, *ClashMode) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SetClashMode not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) URLTest(context.Context, *URLTestRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method URLTest not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SelectOutbound(context.Context, *SelectOutboundRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SelectOutbound not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SetGroupExpand not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetSystemProxyStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SetSystemProxyEnabled not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeConnections not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CloseConnection not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CloseAllConnections not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetDeprecatedWarnings not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeHelperEvents not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SendHelperResponse not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedStartedServiceServer) mustEmbedUnimplementedStartedServiceServer() {}
|
||||||
|
func (UnimplementedStartedServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeStartedServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to StartedServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeStartedServiceServer interface {
|
||||||
|
mustEmbedUnimplementedStartedServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterStartedServiceServer(s grpc.ServiceRegistrar, srv StartedServiceServer) {
|
||||||
|
// If the following call pancis, it indicates UnimplementedStartedServiceServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&StartedService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_StopService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).StopService(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_StopService_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).StopService(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_ReloadService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).ReloadService(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_ReloadService_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).ReloadService(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeServiceStatus_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeServiceStatus(m, &grpc.GenericServerStream[emptypb.Empty, ServiceStatus]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeServiceStatusServer = grpc.ServerStreamingServer[ServiceStatus]
|
||||||
|
|
||||||
|
func _StartedService_SubscribeLog_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeLog(m, &grpc.GenericServerStream[emptypb.Empty, Log]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeLogServer = grpc.ServerStreamingServer[Log]
|
||||||
|
|
||||||
|
func _StartedService_GetDefaultLogLevel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetDefaultLogLevel(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetDefaultLogLevel_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetDefaultLogLevel(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeStatus_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(SubscribeStatusRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeStatus(m, &grpc.GenericServerStream[SubscribeStatusRequest, Status]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeStatusServer = grpc.ServerStreamingServer[Status]
|
||||||
|
|
||||||
|
func _StartedService_SubscribeGroups_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeGroups(m, &grpc.GenericServerStream[emptypb.Empty, Groups]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeGroupsServer = grpc.ServerStreamingServer[Groups]
|
||||||
|
|
||||||
|
func _StartedService_GetClashModeStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetClashModeStatus(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetClashModeStatus_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetClashModeStatus(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeClashMode_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeClashMode(m, &grpc.GenericServerStream[emptypb.Empty, ClashMode]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeClashModeServer = grpc.ServerStreamingServer[ClashMode]
|
||||||
|
|
||||||
|
func _StartedService_SetClashMode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ClashMode)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SetClashMode(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SetClashMode_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SetClashMode(ctx, req.(*ClashMode))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_URLTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(URLTestRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).URLTest(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_URLTest_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).URLTest(ctx, req.(*URLTestRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SelectOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SelectOutboundRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SelectOutbound(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SelectOutbound_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SelectOutbound(ctx, req.(*SelectOutboundRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SetGroupExpand_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SetGroupExpandRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SetGroupExpand(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SetGroupExpand_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SetGroupExpand(ctx, req.(*SetGroupExpandRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_GetSystemProxyStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetSystemProxyStatus(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetSystemProxyStatus_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetSystemProxyStatus(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SetSystemProxyEnabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SetSystemProxyEnabledRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SetSystemProxyEnabled(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SetSystemProxyEnabled_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SetSystemProxyEnabled(ctx, req.(*SetSystemProxyEnabledRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeConnections_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(SubscribeConnectionsRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeConnections(m, &grpc.GenericServerStream[SubscribeConnectionsRequest, Connections]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeConnectionsServer = grpc.ServerStreamingServer[Connections]
|
||||||
|
|
||||||
|
func _StartedService_CloseConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CloseConnectionRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).CloseConnection(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_CloseConnection_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).CloseConnection(ctx, req.(*CloseConnectionRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_CloseAllConnections_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).CloseAllConnections(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_CloseAllConnections_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).CloseAllConnections(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_GetDeprecatedWarnings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetDeprecatedWarnings(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetDeprecatedWarnings_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetDeprecatedWarnings(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeHelperEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeHelperEvents(m, &grpc.GenericServerStream[emptypb.Empty, HelperRequest]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeHelperEventsServer = grpc.ServerStreamingServer[HelperRequest]
|
||||||
|
|
||||||
|
func _StartedService_SendHelperResponse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(HelperResponse)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SendHelperResponse(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SendHelperResponse_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SendHelperResponse(ctx, req.(*HelperResponse))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartedService_ServiceDesc is the grpc.ServiceDesc for StartedService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var StartedService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "daemon.StartedService",
|
||||||
|
HandlerType: (*StartedServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "StopService",
|
||||||
|
Handler: _StartedService_StopService_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ReloadService",
|
||||||
|
Handler: _StartedService_ReloadService_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetDefaultLogLevel",
|
||||||
|
Handler: _StartedService_GetDefaultLogLevel_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetClashModeStatus",
|
||||||
|
Handler: _StartedService_GetClashModeStatus_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SetClashMode",
|
||||||
|
Handler: _StartedService_SetClashMode_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "URLTest",
|
||||||
|
Handler: _StartedService_URLTest_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SelectOutbound",
|
||||||
|
Handler: _StartedService_SelectOutbound_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SetGroupExpand",
|
||||||
|
Handler: _StartedService_SetGroupExpand_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetSystemProxyStatus",
|
||||||
|
Handler: _StartedService_GetSystemProxyStatus_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SetSystemProxyEnabled",
|
||||||
|
Handler: _StartedService_SetSystemProxyEnabled_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CloseConnection",
|
||||||
|
Handler: _StartedService_CloseConnection_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CloseAllConnections",
|
||||||
|
Handler: _StartedService_CloseAllConnections_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetDeprecatedWarnings",
|
||||||
|
Handler: _StartedService_GetDeprecatedWarnings_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SendHelperResponse",
|
||||||
|
Handler: _StartedService_SendHelperResponse_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeServiceStatus",
|
||||||
|
Handler: _StartedService_SubscribeServiceStatus_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeLog",
|
||||||
|
Handler: _StartedService_SubscribeLog_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeStatus",
|
||||||
|
Handler: _StartedService_SubscribeStatus_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeGroups",
|
||||||
|
Handler: _StartedService_SubscribeGroups_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeClashMode",
|
||||||
|
Handler: _StartedService_SubscribeClashMode_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeConnections",
|
||||||
|
Handler: _StartedService_SubscribeConnections_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeHelperEvents",
|
||||||
|
Handler: _StartedService_SubscribeHelperEvents_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "daemon/started_service.proto",
|
||||||
|
}
|
||||||
@@ -95,20 +95,6 @@ func (c *Client) Start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractNegativeTTL(response *dns.Msg) (uint32, bool) {
|
|
||||||
for _, record := range response.Ns {
|
|
||||||
if soa, isSOA := record.(*dns.SOA); isSOA {
|
|
||||||
soaTTL := soa.Header().Ttl
|
|
||||||
soaMinimum := soa.Minttl
|
|
||||||
if soaTTL < soaMinimum {
|
|
||||||
return soaTTL, true
|
|
||||||
}
|
|
||||||
return soaMinimum, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) {
|
func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, message *dns.Msg, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error) {
|
||||||
if len(message.Question) == 0 {
|
if len(message.Question) == 0 {
|
||||||
if c.logger != nil {
|
if c.logger != nil {
|
||||||
@@ -228,7 +214,7 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
response.Answer = append(response.Answer, validResponse.Answer...)
|
response.Answer = append(response.Answer, validResponse.Answer...)
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
disableCache = disableCache || (response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError)
|
disableCache = disableCache || response.Rcode != dns.RcodeSuccess || len(response.Answer) == 0
|
||||||
if responseChecker != nil {
|
if responseChecker != nil {
|
||||||
var rejected bool
|
var rejected bool
|
||||||
// TODO: add accept_any rule and support to check response instead of addresses
|
// TODO: add accept_any rule and support to check response instead of addresses
|
||||||
@@ -265,12 +251,6 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var timeToLive uint32
|
var timeToLive uint32
|
||||||
if len(response.Answer) == 0 {
|
|
||||||
if soaTTL, hasSOA := extractNegativeTTL(response); hasSOA {
|
|
||||||
timeToLive = soaTTL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if timeToLive == 0 {
|
|
||||||
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
for _, recordList := range [][]dns.RR{response.Answer, response.Ns, response.Extra} {
|
||||||
for _, record := range recordList {
|
for _, record := range recordList {
|
||||||
if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive {
|
if timeToLive == 0 || record.Header().Ttl > 0 && record.Header().Ttl < timeToLive {
|
||||||
@@ -278,7 +258,6 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if options.RewriteTTL != nil {
|
if options.RewriteTTL != nil {
|
||||||
timeToLive = *options.RewriteTTL
|
timeToLive = *options.RewriteTTL
|
||||||
}
|
}
|
||||||
@@ -385,19 +364,15 @@ func (c *Client) LookupCache(domain string, strategy C.DomainStrategy) ([]netip.
|
|||||||
Qtype: dns.TypeA,
|
Qtype: dns.TypeA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if response4 == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
response6, _ := c.loadResponse(dns.Question{
|
response6, _ := c.loadResponse(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeAAAA,
|
Qtype: dns.TypeAAAA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if response6 == nil {
|
if response4 != nil || response6 != nil {
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return sortAddresses(MessageToAddresses(response4), MessageToAddresses(response6), strategy), true
|
return sortAddresses(MessageToAddresses(response4), MessageToAddresses(response6), strategy), true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
R "github.com/sagernet/sing-box/route/rule"
|
R "github.com/sagernet/sing-box/route/rule"
|
||||||
@@ -38,7 +37,7 @@ type Router struct {
|
|||||||
rules []adapter.DNSRule
|
rules []adapter.DNSRule
|
||||||
defaultDomainStrategy C.DomainStrategy
|
defaultDomainStrategy C.DomainStrategy
|
||||||
dnsReverseMapping freelru.Cache[netip.Addr, string]
|
dnsReverseMapping freelru.Cache[netip.Addr, string]
|
||||||
platformInterface platform.Interface
|
platformInterface adapter.PlatformInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOptions) *Router {
|
func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOptions) *Router {
|
||||||
@@ -386,7 +385,12 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
|||||||
if rule != nil {
|
if rule != nil {
|
||||||
switch action := rule.Action().(type) {
|
switch action := rule.Action().(type) {
|
||||||
case *R.RuleActionReject:
|
case *R.RuleActionReject:
|
||||||
return nil, &R.RejectedError{Cause: action.Error(ctx)}
|
switch action.Method {
|
||||||
|
case C.RuleActionRejectMethodDefault:
|
||||||
|
return nil, nil
|
||||||
|
case C.RuleActionRejectMethodDrop:
|
||||||
|
return nil, tun.ErrDrop
|
||||||
|
}
|
||||||
case *R.RuleActionPredefined:
|
case *R.RuleActionPredefined:
|
||||||
if action.Rcode != mDNS.RcodeSuccess {
|
if action.Rcode != mDNS.RcodeSuccess {
|
||||||
err = RcodeError(action.Rcode)
|
err = RcodeError(action.Rcode)
|
||||||
|
|||||||
@@ -75,6 +75,5 @@ func (h *HTTPSTransportWrapper) Clone() *HTTPSTransportWrapper {
|
|||||||
http2Transport: &http2.Transport{
|
http2Transport: &http2.Transport{
|
||||||
DialTLSContext: h.http2Transport.DialTLSContext,
|
DialTLSContext: h.http2Transport.DialTLSContext,
|
||||||
},
|
},
|
||||||
fallback: h.fallback,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ func (t *Transport) Start(stage adapter.StartStage) error {
|
|||||||
switch stage {
|
switch stage {
|
||||||
case adapter.StartStateInitialize:
|
case adapter.StartStateInitialize:
|
||||||
if !t.preferGo {
|
if !t.preferGo {
|
||||||
if isSystemdResolvedManaged() {
|
|
||||||
resolvedResolver, err := NewResolvedResolver(t.ctx, t.logger)
|
resolvedResolver, err := NewResolvedResolver(t.ctx, t.logger)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = resolvedResolver.Start()
|
err = resolvedResolver.Start()
|
||||||
@@ -65,7 +64,6 @@ func (t *Transport) Start(stage adapter.StartStage) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,11 +82,12 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
question := message.Question[0]
|
question := message.Question[0]
|
||||||
|
domain := dns.FqdnToDomain(question.Name)
|
||||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||||
addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name))
|
addresses := t.hosts.Lookup(domain)
|
||||||
if len(addresses) > 0 {
|
if len(addresses) > 0 {
|
||||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return t.exchange(ctx, message, question.Name)
|
return t.exchange(ctx, message, domain)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,14 +96,15 @@ func (t *Transport) Close() error {
|
|||||||
|
|
||||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||||
question := message.Question[0]
|
question := message.Question[0]
|
||||||
|
domain := dns.FqdnToDomain(question.Name)
|
||||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||||
addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name))
|
addresses := t.hosts.Lookup(domain)
|
||||||
if len(addresses) > 0 {
|
if len(addresses) > 0 {
|
||||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !t.fallback {
|
if !t.fallback {
|
||||||
return t.exchange(ctx, message, question.Name)
|
return t.exchange(ctx, message, domain)
|
||||||
}
|
}
|
||||||
if !C.IsIos {
|
if !C.IsIos {
|
||||||
if t.dhcpTransport != nil {
|
if t.dhcpTransport != nil {
|
||||||
@@ -115,7 +116,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
|||||||
}
|
}
|
||||||
if t.preferGo {
|
if t.preferGo {
|
||||||
// Assuming the user knows what they are doing, we still execute the query which will fail.
|
// Assuming the user knows what they are doing, we still execute the query which will fail.
|
||||||
return t.exchange(ctx, message, question.Name)
|
return t.exchange(ctx, message, domain)
|
||||||
}
|
}
|
||||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||||
var network string
|
var network string
|
||||||
@@ -124,7 +125,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
|||||||
} else {
|
} else {
|
||||||
network = "ip6"
|
network = "ip6"
|
||||||
}
|
}
|
||||||
addresses, err := t.resolver.LookupNetIP(ctx, network, question.Name)
|
addresses, err := t.resolver.LookupNetIP(ctx, network, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var dnsError *net.DNSError
|
var dnsError *net.DNSError
|
||||||
if errors.As(err, &dnsError) && dnsError.IsNotFound {
|
if errors.As(err, &dnsError) && dnsError.IsNotFound {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@@ -24,25 +22,6 @@ import (
|
|||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isSystemdResolvedManaged() bool {
|
|
||||||
resolvContent, err := os.Open("/etc/resolv.conf")
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer resolvContent.Close()
|
|
||||||
scanner := bufio.NewScanner(resolvContent)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if line == "" || line[0] != '#' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if strings.Contains(line, "systemd-resolved") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type DBusResolvedResolver struct {
|
type DBusResolvedResolver struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
@@ -209,7 +188,7 @@ func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*ResolvedObje
|
|||||||
int32(defaultInterface.Index),
|
int32(defaultInterface.Index),
|
||||||
)
|
)
|
||||||
if call.Err != nil {
|
if call.Err != nil {
|
||||||
return nil, call.Err
|
return nil, err
|
||||||
}
|
}
|
||||||
var linkPath dbus.ObjectPath
|
var linkPath dbus.ObjectPath
|
||||||
err = call.Store(&linkPath)
|
err = call.Store(&linkPath)
|
||||||
@@ -235,13 +214,16 @@ func (t *DBusResolvedResolver) checkResolved(ctx context.Context) (*ResolvedObje
|
|||||||
return nil, E.New("No appropriate name servers or networks for name found")
|
return nil, E.New("No appropriate name servers or networks for name found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, E.New("link has no DNS servers configured")
|
return &ResolvedObject{
|
||||||
}
|
BusObject: dbusObject,
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
return &ResolvedObject{
|
return &ResolvedObject{
|
||||||
BusObject: dbusObject,
|
BusObject: dbusObject,
|
||||||
InterfaceIndex: int32(defaultInterface.Index),
|
InterfaceIndex: int32(defaultInterface.Index),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) {
|
func (t *DBusResolvedResolver) updateDefaultInterface(defaultInterface *control.Interface, flags int) {
|
||||||
t.updateStatus()
|
t.updateStatus()
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isSystemdResolvedManaged() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
|
func NewResolvedResolver(ctx context.Context, logger logger.ContextLogger) (ResolvedResolver, error) {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,97 +2,10 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 1.13.0-alpha.28
|
#### 1.13.0-alpha.20
|
||||||
|
|
||||||
* Update quic-go to v0.57.1
|
|
||||||
* Add `disable_tcp_keep_alive`, `tcp_keep_alive` and `tcp_keep_alive_interval` options for dial fields **1**
|
|
||||||
* Update default TCP keep-alive initial period from 10 minutes to 5 minutes
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial/#tcp_keep_alive).
|
|
||||||
|
|
||||||
__Unfortunately, for non-technical reasons, we are currently unable to notarize the standalone version of the macOS client:
|
|
||||||
because system extensions require signatures to function, we have had to temporarily halt its release.__
|
|
||||||
|
|
||||||
__We plan to fix the App Store release issue and launch a new standalone desktop client, but until then,
|
|
||||||
only clients on TestFlight will be available (unless you have an Apple Developer Program and compile from source code).__
|
|
||||||
|
|
||||||
#### 1.12.13
|
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
__Unfortunately, for non-technical reasons, we are currently unable to notarize the standalone version of the macOS client:
|
|
||||||
because system extensions require signatures to function, we have had to temporarily halt its release.__
|
|
||||||
|
|
||||||
__We plan to fix the App Store release issue and launch a new standalone desktop client, but until then,
|
|
||||||
only clients on TestFlight will be available (unless you have an Apple Developer Program and compile from source code).__
|
|
||||||
|
|
||||||
#### 1.12.12
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
#### 1.13.0-alpha.26
|
|
||||||
|
|
||||||
* Update quic-go to v0.55.0
|
|
||||||
* Fix memory leak in hysteria2
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
#### 1.12.11
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
#### 1.13.0-alpha.24
|
|
||||||
|
|
||||||
* Add Claude Code Multiplexer service **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
CCM (Claude Code Multiplexer) service allows you to access your local Claude Code subscription remotely through custom tokens, eliminating the need for OAuth authentication on remote clients.
|
|
||||||
|
|
||||||
See [CCM](/configuration/service/ccm).
|
|
||||||
|
|
||||||
#### 1.13.0-alpha.23
|
|
||||||
|
|
||||||
* Fix compatibility with MPTCP **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
`auto_redirect` now rejects MPTCP connections by default to fix compatibility issues,
|
|
||||||
but you can change it to bypass the sing-box via the new `exclude_mptcp` option.
|
|
||||||
|
|
||||||
See [TUN](/configuration/inbound/tun/#exclude_mptcp).
|
|
||||||
|
|
||||||
#### 1.13.0-alpha.22
|
|
||||||
|
|
||||||
* Update uTLS to v1.8.1 **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
This update fixes an critical issue that could cause simulated Chrome fingerprints to be detected,
|
|
||||||
see https://github.com/refraction-networking/utls/pull/375.
|
|
||||||
|
|
||||||
#### 1.12.10
|
|
||||||
|
|
||||||
* Update uTLS to v1.8.1 **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
This update fixes an critical issue that could cause simulated Chrome fingerprints to be detected,
|
|
||||||
see https://github.com/refraction-networking/utls/pull/375.
|
|
||||||
|
|
||||||
#### 1.13.0-alpha.21
|
|
||||||
|
|
||||||
* Fix missing mTLS support in client options **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
See [TLS](/configuration/shared/tls/).
|
|
||||||
|
|
||||||
#### 1.12.9
|
#### 1.12.9
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
@@ -214,8 +127,7 @@ See [Tailscale](/configuration/endpoint/tailscale/).
|
|||||||
|
|
||||||
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
||||||
|
|
||||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches
|
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||||
from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
|
||||||
|
|
||||||
**7**:
|
**7**:
|
||||||
|
|
||||||
@@ -277,8 +189,7 @@ See [Tun](/configuration/inbound/tun/#loopback_address).
|
|||||||
|
|
||||||
We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack.
|
We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack.
|
||||||
|
|
||||||
The following data was tested
|
The following data was tested using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro.
|
||||||
using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro.
|
|
||||||
|
|
||||||
| Version | Stack | MTU | Upload | Download |
|
| Version | Stack | MTU | Upload | Download |
|
||||||
|-------------|--------|-------|--------|----------|
|
|-------------|--------|-------|--------|----------|
|
||||||
@@ -579,8 +490,7 @@ See [AnyTLS Inbound](/configuration/inbound/anytls/) and [AnyTLS Outbound](/conf
|
|||||||
|
|
||||||
**2**:
|
**2**:
|
||||||
|
|
||||||
`resolve` route action now accepts `disable_cache` and other options like in DNS route actions,
|
`resolve` route action now accepts `disable_cache` and other options like in DNS route actions, see [Route Action](/configuration/route/rule_action).
|
||||||
see [Route Action](/configuration/route/rule_action).
|
|
||||||
|
|
||||||
**3**:
|
**3**:
|
||||||
|
|
||||||
@@ -611,8 +521,7 @@ See [Tailscale](/configuration/endpoint/tailscale/).
|
|||||||
|
|
||||||
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
||||||
|
|
||||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches
|
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
||||||
from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
|
||||||
|
|
||||||
### 1.11.3
|
### 1.11.3
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ platform-specific function implementation, such as TUN transparent proxy impleme
|
|||||||
|
|
||||||
!!! failure ""
|
!!! failure ""
|
||||||
|
|
||||||
Due to non-technical reasons, we are temporarily unable to update the sing-box app on the App Store and release the standalone version of the macOS client (TestFlight users are not affected)
|
We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected).
|
||||||
|
|
||||||
## :material-graph: Requirements
|
## :material-graph: Requirements
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ platform-specific function implementation, such as TUN transparent proxy impleme
|
|||||||
|
|
||||||
## :material-download: Download
|
## :material-download: Download
|
||||||
|
|
||||||
* ~~[App Store](https://apps.apple.com/app/sing-box-vt/id6673731168)~~
|
* [App Store](https://apps.apple.com/app/sing-box-vt/id6673731168)
|
||||||
* TestFlight (Beta)
|
* TestFlight (Beta)
|
||||||
|
|
||||||
TestFlight quota is only available to [sponsors](https://github.com/sponsors/nekohasekai)
|
TestFlight quota is only available to [sponsors](https://github.com/sponsors/nekohasekai)
|
||||||
@@ -26,15 +26,15 @@ TestFlight quota is only available to [sponsors](https://github.com/sponsors/nek
|
|||||||
Once you donate, you can get an invitation by join our Telegram group for sponsors from [@yet_another_sponsor_bot](https://t.me/yet_another_sponsor_bot)
|
Once you donate, you can get an invitation by join our Telegram group for sponsors from [@yet_another_sponsor_bot](https://t.me/yet_another_sponsor_bot)
|
||||||
or sending us your Apple ID [via email](mailto:contact@sagernet.org).
|
or sending us your Apple ID [via email](mailto:contact@sagernet.org).
|
||||||
|
|
||||||
## ~~:material-file-download: Download (macOS standalone version)~~
|
## :material-file-download: Download (macOS standalone version)
|
||||||
|
|
||||||
* ~~[Homebrew Cask](https://formulae.brew.sh/cask/sfm)~~
|
* [Homebrew Cask](https://formulae.brew.sh/cask/sfm)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# brew install sfm
|
brew install sfm
|
||||||
```
|
```
|
||||||
|
|
||||||
* ~~[GitHub Releases](https://github.com/SagerNet/sing-box/releases)~~
|
* [GitHub Releases](https://github.com/SagerNet/sing-box/releases)
|
||||||
|
|
||||||
## :material-source-repository: Source code
|
## :material-source-repository: Source code
|
||||||
|
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ Match default interface address.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and Apple platforms, or on Linux.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi SSID.
|
Match WiFi SSID.
|
||||||
|
|
||||||
@@ -420,7 +420,7 @@ Match WiFi SSID.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and Apple platforms, or on Linux.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi BSSID.
|
Match WiFi BSSID.
|
||||||
|
|
||||||
|
|||||||
@@ -411,7 +411,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端和 Linux 中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi SSID。
|
匹配 WiFi SSID。
|
||||||
|
|
||||||
@@ -419,7 +419,7 @@ Available values: `wifi`, `cellular`, `ethernet` and `other`.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端和 Linux 中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi BSSID。
|
匹配 WiFi BSSID。
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.13.0"
|
|
||||||
|
|
||||||
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
:material-plus: [loopback_address](#loopback_address)
|
:material-plus: [loopback_address](#loopback_address)
|
||||||
@@ -67,7 +63,6 @@ icon: material/new-box
|
|||||||
"auto_redirect": true,
|
"auto_redirect": true,
|
||||||
"auto_redirect_input_mark": "0x2023",
|
"auto_redirect_input_mark": "0x2023",
|
||||||
"auto_redirect_output_mark": "0x2024",
|
"auto_redirect_output_mark": "0x2024",
|
||||||
"exclude_mptcp": false,
|
|
||||||
"loopback_address": [
|
"loopback_address": [
|
||||||
"10.7.0.1"
|
"10.7.0.1"
|
||||||
],
|
],
|
||||||
@@ -283,20 +278,6 @@ Connection output mark used by `auto_redirect`.
|
|||||||
|
|
||||||
`0x2024` is used by default.
|
`0x2024` is used by default.
|
||||||
|
|
||||||
#### exclude_mptcp
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
!!! quote ""
|
|
||||||
|
|
||||||
Only supported on Linux with nftables and requires `auto_route` and `auto_redirect` enabled.
|
|
||||||
|
|
||||||
MPTCP cannot be transparently proxied due to protocol limitations.
|
|
||||||
|
|
||||||
Such traffic is usually created by Apple systems.
|
|
||||||
|
|
||||||
When enabled, MPTCP connections will bypass sing-box and connect directly, otherwise, will be rejected to avoid errors by default.
|
|
||||||
|
|
||||||
#### loopback_address
|
#### loopback_address
|
||||||
|
|
||||||
!!! question "Since sing-box 1.12.0"
|
!!! question "Since sing-box 1.12.0"
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "sing-box 1.13.0 中的更改"
|
|
||||||
|
|
||||||
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [loopback_address](#loopback_address)
|
:material-plus: [loopback_address](#loopback_address)
|
||||||
@@ -67,7 +63,6 @@ icon: material/new-box
|
|||||||
"auto_redirect": true,
|
"auto_redirect": true,
|
||||||
"auto_redirect_input_mark": "0x2023",
|
"auto_redirect_input_mark": "0x2023",
|
||||||
"auto_redirect_output_mark": "0x2024",
|
"auto_redirect_output_mark": "0x2024",
|
||||||
"exclude_mptcp": false,
|
|
||||||
"loopback_address": [
|
"loopback_address": [
|
||||||
"10.7.0.1"
|
"10.7.0.1"
|
||||||
],
|
],
|
||||||
@@ -282,20 +277,6 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
默认使用 `0x2024`。
|
默认使用 `0x2024`。
|
||||||
|
|
||||||
#### exclude_mptcp
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
!!! quote ""
|
|
||||||
|
|
||||||
仅支持 Linux,且需要 nftables,`auto_route` 和 `auto_redirect` 已启用。
|
|
||||||
|
|
||||||
由于协议限制,MPTCP 无法被透明代理。
|
|
||||||
|
|
||||||
此类流量通常由 Apple 系统创建。
|
|
||||||
|
|
||||||
启用时,MPTCP 连接将绕过 sing-box 直接连接,否则,将被拒绝以避免错误。
|
|
||||||
|
|
||||||
#### loopback_address
|
#### loopback_address
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ Match default interface address.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and Apple platforms, or on Linux.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi SSID.
|
Match WiFi SSID.
|
||||||
|
|
||||||
@@ -438,7 +438,7 @@ Match WiFi SSID.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and Apple platforms, or on Linux.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi BSSID.
|
Match WiFi BSSID.
|
||||||
|
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ icon: material/new-box
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端和 Linux 中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi SSID。
|
匹配 WiFi SSID。
|
||||||
|
|
||||||
@@ -435,7 +435,7 @@ icon: material/new-box
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端和 Linux 中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi BSSID。
|
匹配 WiFi BSSID。
|
||||||
|
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
# CCM
|
|
||||||
|
|
||||||
CCM (Claude Code Multiplexer) service is a multiplexing service that allows you to access your local Claude Code subscription remotely through custom tokens.
|
|
||||||
|
|
||||||
It handles OAuth authentication with Claude's API on your local machine while allowing remote Claude Code to authenticate using Auth Tokens via the `ANTHROPIC_AUTH_TOKEN` environment variable.
|
|
||||||
|
|
||||||
### Structure
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "ccm",
|
|
||||||
|
|
||||||
... // Listen Fields
|
|
||||||
|
|
||||||
"credential_path": "",
|
|
||||||
"usages_path": "",
|
|
||||||
"users": [],
|
|
||||||
"headers": {},
|
|
||||||
"detour": "",
|
|
||||||
"tls": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Listen Fields
|
|
||||||
|
|
||||||
See [Listen Fields](/configuration/shared/listen/) for details.
|
|
||||||
|
|
||||||
### Fields
|
|
||||||
|
|
||||||
#### credential_path
|
|
||||||
|
|
||||||
Path to the Claude Code OAuth credentials file.
|
|
||||||
|
|
||||||
If not specified, defaults to:
|
|
||||||
- `$CLAUDE_CONFIG_DIR/.credentials.json` if `CLAUDE_CONFIG_DIR` environment variable is set
|
|
||||||
- `~/.claude/.credentials.json` otherwise
|
|
||||||
|
|
||||||
On macOS, credentials are read from the system keychain first, then fall back to the file if unavailable.
|
|
||||||
|
|
||||||
Refreshed tokens are automatically written back to the same location.
|
|
||||||
|
|
||||||
#### usages_path
|
|
||||||
|
|
||||||
Path to the file for storing aggregated API usage statistics.
|
|
||||||
|
|
||||||
Usage tracking is disabled if not specified.
|
|
||||||
|
|
||||||
When enabled, the service tracks and saves comprehensive statistics including:
|
|
||||||
- Request counts
|
|
||||||
- Token usage (input, output, cache read, cache creation)
|
|
||||||
- Calculated costs in USD based on Claude API pricing
|
|
||||||
|
|
||||||
Statistics are organized by model, context window (200k standard vs 1M premium), and optionally by user when authentication is enabled.
|
|
||||||
|
|
||||||
The statistics file is automatically saved every minute and upon service shutdown.
|
|
||||||
|
|
||||||
#### users
|
|
||||||
|
|
||||||
List of authorized users for token authentication.
|
|
||||||
|
|
||||||
If empty, no authentication is required.
|
|
||||||
|
|
||||||
Claude Code authenticates by setting the `ANTHROPIC_AUTH_TOKEN` environment variable to their token value.
|
|
||||||
|
|
||||||
#### headers
|
|
||||||
|
|
||||||
Custom HTTP headers to send to the Claude API.
|
|
||||||
|
|
||||||
These headers will override any existing headers with the same name.
|
|
||||||
|
|
||||||
#### detour
|
|
||||||
|
|
||||||
Outbound tag for connecting to the Claude API.
|
|
||||||
|
|
||||||
#### tls
|
|
||||||
|
|
||||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"services": [
|
|
||||||
{
|
|
||||||
"type": "ccm",
|
|
||||||
"listen": "127.0.0.1",
|
|
||||||
"listen_port": 8080
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Connect to the CCM service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export ANTHROPIC_BASE_URL="http://127.0.0.1:8080"
|
|
||||||
export ANTHROPIC_AUTH_TOKEN="sk-ant-ccm-auth-token-not-required-in-this-context"
|
|
||||||
|
|
||||||
claude
|
|
||||||
```
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
# CCM
|
|
||||||
|
|
||||||
CCM(Claude Code 多路复用器)服务是一个多路复用服务,允许您通过自定义令牌远程访问本地的 Claude Code 订阅。
|
|
||||||
|
|
||||||
它在本地机器上处理与 Claude API 的 OAuth 身份验证,同时允许远程 Claude Code 通过 `ANTHROPIC_AUTH_TOKEN` 环境变量使用认证令牌进行身份验证。
|
|
||||||
|
|
||||||
### 结构
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "ccm",
|
|
||||||
|
|
||||||
... // 监听字段
|
|
||||||
|
|
||||||
"credential_path": "",
|
|
||||||
"usages_path": "",
|
|
||||||
"users": [],
|
|
||||||
"headers": {},
|
|
||||||
"detour": "",
|
|
||||||
"tls": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 监听字段
|
|
||||||
|
|
||||||
参阅 [监听字段](/zh/configuration/shared/listen/) 了解详情。
|
|
||||||
|
|
||||||
### 字段
|
|
||||||
|
|
||||||
#### credential_path
|
|
||||||
|
|
||||||
Claude Code OAuth 凭据文件的路径。
|
|
||||||
|
|
||||||
如果未指定,默认值为:
|
|
||||||
- 如果设置了 `CLAUDE_CONFIG_DIR` 环境变量,则使用 `$CLAUDE_CONFIG_DIR/.credentials.json`
|
|
||||||
- 否则使用 `~/.claude/.credentials.json`
|
|
||||||
|
|
||||||
在 macOS 上,首先从系统钥匙串读取凭据,如果不可用则回退到文件。
|
|
||||||
|
|
||||||
刷新的令牌会自动写回相同位置。
|
|
||||||
|
|
||||||
#### usages_path
|
|
||||||
|
|
||||||
用于存储聚合 API 使用统计信息的文件路径。
|
|
||||||
|
|
||||||
如果未指定,使用跟踪将被禁用。
|
|
||||||
|
|
||||||
启用后,服务会跟踪并保存全面的统计信息,包括:
|
|
||||||
- 请求计数
|
|
||||||
- 令牌使用量(输入、输出、缓存读取、缓存创建)
|
|
||||||
- 基于 Claude API 定价计算的美元成本
|
|
||||||
|
|
||||||
统计信息按模型、上下文窗口(200k 标准版 vs 1M 高级版)以及可选的用户(启用身份验证时)进行组织。
|
|
||||||
|
|
||||||
统计文件每分钟自动保存一次,并在服务关闭时保存。
|
|
||||||
|
|
||||||
#### users
|
|
||||||
|
|
||||||
用于令牌身份验证的授权用户列表。
|
|
||||||
|
|
||||||
如果为空,则不需要身份验证。
|
|
||||||
|
|
||||||
Claude Code 通过设置 `ANTHROPIC_AUTH_TOKEN` 环境变量为其令牌值进行身份验证。
|
|
||||||
|
|
||||||
#### headers
|
|
||||||
|
|
||||||
发送到 Claude API 的自定义 HTTP 头。
|
|
||||||
|
|
||||||
这些头会覆盖同名的现有头。
|
|
||||||
|
|
||||||
#### detour
|
|
||||||
|
|
||||||
用于连接 Claude API 的出站标签。
|
|
||||||
|
|
||||||
#### tls
|
|
||||||
|
|
||||||
TLS 配置,参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
|
||||||
|
|
||||||
### 示例
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"services": [
|
|
||||||
{
|
|
||||||
"type": "ccm",
|
|
||||||
"listen": "127.0.0.1",
|
|
||||||
"listen_port": 8080
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
连接到 CCM 服务:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export ANTHROPIC_BASE_URL="http://127.0.0.1:8080"
|
|
||||||
export ANTHROPIC_AUTH_TOKEN="sk-ant-ccm-auth-token-not-required-in-this-context"
|
|
||||||
|
|
||||||
claude
|
|
||||||
```
|
|
||||||
@@ -23,7 +23,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
| Type | Format |
|
| Type | Format |
|
||||||
|------------|------------------------|
|
|------------|------------------------|
|
||||||
| `ccm` | [CCM](./ccm) |
|
|
||||||
| `derp` | [DERP](./derp) |
|
| `derp` | [DERP](./derp) |
|
||||||
| `resolved` | [Resolved](./resolved) |
|
| `resolved` | [Resolved](./resolved) |
|
||||||
| `ssm-api` | [SSM API](./ssm-api) |
|
| `ssm-api` | [SSM API](./ssm-api) |
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
| 类型 | 格式 |
|
| 类型 | 格式 |
|
||||||
|-----------|------------------------|
|
|-----------|------------------------|
|
||||||
| `ccm` | [CCM](./ccm) |
|
|
||||||
| `derp` | [DERP](./derp) |
|
| `derp` | [DERP](./derp) |
|
||||||
| `resolved`| [Resolved](./resolved) |
|
| `resolved`| [Resolved](./resolved) |
|
||||||
| `ssm-api` | [SSM API](./ssm-api) |
|
| `ssm-api` | [SSM API](./ssm-api) |
|
||||||
|
|||||||
@@ -2,12 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.13.0"
|
|
||||||
|
|
||||||
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
|
||||||
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
|
|
||||||
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
:material-plus: [domain_resolver](#domain_resolver)
|
:material-plus: [domain_resolver](#domain_resolver)
|
||||||
@@ -35,9 +29,6 @@ icon: material/new-box
|
|||||||
"connect_timeout": "",
|
"connect_timeout": "",
|
||||||
"tcp_fast_open": false,
|
"tcp_fast_open": false,
|
||||||
"tcp_multi_path": false,
|
"tcp_multi_path": false,
|
||||||
"disable_tcp_keep_alive": false,
|
|
||||||
"tcp_keep_alive": "",
|
|
||||||
"tcp_keep_alive_interval": "",
|
|
||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
|
|
||||||
"domain_resolver": "", // or {}
|
"domain_resolver": "", // or {}
|
||||||
@@ -121,30 +112,6 @@ Enable TCP Fast Open.
|
|||||||
|
|
||||||
Enable TCP Multi Path.
|
Enable TCP Multi Path.
|
||||||
|
|
||||||
#### disable_tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
Disable TCP keep alive.
|
|
||||||
|
|
||||||
#### tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
Default value changed from `10m` to `5m`.
|
|
||||||
|
|
||||||
TCP keep-alive initial period.
|
|
||||||
|
|
||||||
`5m` will be used by default.
|
|
||||||
|
|
||||||
#### tcp_keep_alive_interval
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
TCP keep-alive interval.
|
|
||||||
|
|
||||||
`75s` will be used by default.
|
|
||||||
|
|
||||||
#### udp_fragment
|
#### udp_fragment
|
||||||
|
|
||||||
Enable UDP fragmentation.
|
Enable UDP fragmentation.
|
||||||
|
|||||||
@@ -2,12 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "sing-box 1.13.0 中的更改"
|
|
||||||
|
|
||||||
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
|
||||||
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
|
|
||||||
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [domain_resolver](#domain_resolver)
|
:material-plus: [domain_resolver](#domain_resolver)
|
||||||
@@ -35,11 +29,7 @@ icon: material/new-box
|
|||||||
"connect_timeout": "",
|
"connect_timeout": "",
|
||||||
"tcp_fast_open": false,
|
"tcp_fast_open": false,
|
||||||
"tcp_multi_path": false,
|
"tcp_multi_path": false,
|
||||||
"disable_tcp_keep_alive": false,
|
|
||||||
"tcp_keep_alive": "",
|
|
||||||
"tcp_keep_alive_interval": "",
|
|
||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
|
|
||||||
"domain_resolver": "", // 或 {}
|
"domain_resolver": "", // 或 {}
|
||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
"network_type": [],
|
"network_type": [],
|
||||||
@@ -119,30 +109,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
启用 TCP Multi Path。
|
启用 TCP Multi Path。
|
||||||
|
|
||||||
#### disable_tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
禁用 TCP keep alive。
|
|
||||||
|
|
||||||
#### tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
默认值从 `10m` 更改为 `5m`。
|
|
||||||
|
|
||||||
TCP keep-alive 初始周期。
|
|
||||||
|
|
||||||
默认使用 `5m`。
|
|
||||||
|
|
||||||
#### tcp_keep_alive_interval
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
TCP keep-alive 间隔。
|
|
||||||
|
|
||||||
默认使用 `75s`。
|
|
||||||
|
|
||||||
#### udp_fragment
|
#### udp_fragment
|
||||||
|
|
||||||
启用 UDP 分段。
|
启用 UDP 分段。
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.13.0"
|
|
||||||
|
|
||||||
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
|
||||||
:material-alert: [tcp_keep_alive](#tcp_keep_alive)
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
:material-plus: [netns](#netns)
|
:material-plus: [netns](#netns)
|
||||||
@@ -34,9 +29,6 @@ icon: material/new-box
|
|||||||
"netns": "",
|
"netns": "",
|
||||||
"tcp_fast_open": false,
|
"tcp_fast_open": false,
|
||||||
"tcp_multi_path": false,
|
"tcp_multi_path": false,
|
||||||
"disable_tcp_keep_alive": false,
|
|
||||||
"tcp_keep_alive": "",
|
|
||||||
"tcp_keep_alive_interval": "",
|
|
||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
"udp_timeout": "",
|
"udp_timeout": "",
|
||||||
"detour": "",
|
"detour": "",
|
||||||
@@ -109,28 +101,6 @@ Enable TCP Fast Open.
|
|||||||
|
|
||||||
Enable TCP Multi Path.
|
Enable TCP Multi Path.
|
||||||
|
|
||||||
#### disable_tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
Disable TCP keep alive.
|
|
||||||
|
|
||||||
#### tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
Default value changed from `10m` to `5m`.
|
|
||||||
|
|
||||||
TCP keep alive initial period.
|
|
||||||
|
|
||||||
`5m` will be used by default.
|
|
||||||
|
|
||||||
#### tcp_keep_alive_interval
|
|
||||||
|
|
||||||
TCP keep-alive interval.
|
|
||||||
|
|
||||||
`75s` will be used by default.
|
|
||||||
|
|
||||||
#### udp_fragment
|
#### udp_fragment
|
||||||
|
|
||||||
Enable UDP fragmentation.
|
Enable UDP fragmentation.
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
icon: material/new-box
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
!!! quote "sing-box 1.13.0 中的更改"
|
|
||||||
|
|
||||||
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
|
||||||
:material-alert: [tcp_keep_alive](#tcp_keep_alive)
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
:material-plus: [netns](#netns)
|
:material-plus: [netns](#netns)
|
||||||
@@ -34,9 +29,6 @@ icon: material/new-box
|
|||||||
"netns": "",
|
"netns": "",
|
||||||
"tcp_fast_open": false,
|
"tcp_fast_open": false,
|
||||||
"tcp_multi_path": false,
|
"tcp_multi_path": false,
|
||||||
"disable_tcp_keep_alive": false,
|
|
||||||
"tcp_keep_alive": "",
|
|
||||||
"tcp_keep_alive_interval": "",
|
|
||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
"udp_timeout": "",
|
"udp_timeout": "",
|
||||||
"detour": "",
|
"detour": "",
|
||||||
@@ -109,28 +101,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
启用 TCP Multi Path。
|
启用 TCP Multi Path。
|
||||||
|
|
||||||
#### disable_tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
禁用 TCP keep alive。
|
|
||||||
|
|
||||||
#### tcp_keep_alive
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
默认值从 `10m` 更改为 `5m`。
|
|
||||||
|
|
||||||
TCP keep alive 初始周期。
|
|
||||||
|
|
||||||
默认使用 `5m`。
|
|
||||||
|
|
||||||
#### tcp_keep_alive_interval
|
|
||||||
|
|
||||||
TCP keep-alive 间隔。
|
|
||||||
|
|
||||||
默认使用 `75s`。
|
|
||||||
|
|
||||||
#### udp_fragment
|
#### udp_fragment
|
||||||
|
|
||||||
启用 UDP 分段。
|
启用 UDP 分段。
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ icon: material/new-box
|
|||||||
:material-plus: [kernel_rx](#kernel_rx)
|
:material-plus: [kernel_rx](#kernel_rx)
|
||||||
:material-plus: [curve_preferences](#curve_preferences)
|
:material-plus: [curve_preferences](#curve_preferences)
|
||||||
:material-plus: [certificate_public_key_sha256](#certificate_public_key_sha256)
|
:material-plus: [certificate_public_key_sha256](#certificate_public_key_sha256)
|
||||||
|
:material-plus: [client_authentication](#client_authentication)
|
||||||
:material-plus: [client_certificate](#client_certificate)
|
:material-plus: [client_certificate](#client_certificate)
|
||||||
:material-plus: [client_certificate_path](#client_certificate_path)
|
:material-plus: [client_certificate_path](#client_certificate_path)
|
||||||
:material-plus: [client_key](#client_key)
|
|
||||||
:material-plus: [client_key_path](#client_key_path)
|
|
||||||
:material-plus: [client_authentication](#client_authentication)
|
|
||||||
:material-plus: [client_certificate_public_key_sha256](#client_certificate_public_key_sha256)
|
:material-plus: [client_certificate_public_key_sha256](#client_certificate_public_key_sha256)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
@@ -103,14 +101,9 @@ icon: material/new-box
|
|||||||
"min_version": "",
|
"min_version": "",
|
||||||
"max_version": "",
|
"max_version": "",
|
||||||
"cipher_suites": [],
|
"cipher_suites": [],
|
||||||
"curve_preferences": [],
|
|
||||||
"certificate": "",
|
"certificate": "",
|
||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
"certificate_public_key_sha256": [],
|
"certificate_public_key_sha256": [],
|
||||||
"client_certificate": [],
|
|
||||||
"client_certificate_path": "",
|
|
||||||
"client_key": [],
|
|
||||||
"client_key_path": "",
|
|
||||||
"fragment": false,
|
"fragment": false,
|
||||||
"fragment_fallback_delay": "",
|
"fragment_fallback_delay": "",
|
||||||
"record_fragment": false,
|
"record_fragment": false,
|
||||||
@@ -265,38 +258,6 @@ openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform d
|
|||||||
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
```
|
```
|
||||||
|
|
||||||
#### client_certificate
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
==Client only==
|
|
||||||
|
|
||||||
Client certificate chain line array, in PEM format.
|
|
||||||
|
|
||||||
#### client_certificate_path
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
==Client only==
|
|
||||||
|
|
||||||
The path to client certificate chain, in PEM format.
|
|
||||||
|
|
||||||
#### client_key
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
==Client only==
|
|
||||||
|
|
||||||
Client private key line array, in PEM format.
|
|
||||||
|
|
||||||
#### client_key_path
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
|
||||||
|
|
||||||
==Client only==
|
|
||||||
|
|
||||||
The path to client private key, in PEM format.
|
|
||||||
|
|
||||||
#### key
|
#### key
|
||||||
|
|
||||||
==Server only==
|
==Server only==
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ icon: material/new-box
|
|||||||
:material-plus: [kernel_rx](#kernel_rx)
|
:material-plus: [kernel_rx](#kernel_rx)
|
||||||
:material-plus: [curve_preferences](#curve_preferences)
|
:material-plus: [curve_preferences](#curve_preferences)
|
||||||
:material-plus: [certificate_public_key_sha256](#certificate_public_key_sha256)
|
:material-plus: [certificate_public_key_sha256](#certificate_public_key_sha256)
|
||||||
|
:material-plus: [client_authentication](#client_authentication)
|
||||||
:material-plus: [client_certificate](#client_certificate)
|
:material-plus: [client_certificate](#client_certificate)
|
||||||
:material-plus: [client_certificate_path](#client_certificate_path)
|
:material-plus: [client_certificate_path](#client_certificate_path)
|
||||||
:material-plus: [client_key](#client_key)
|
|
||||||
:material-plus: [client_key_path](#client_key_path)
|
|
||||||
:material-plus: [client_authentication](#client_authentication)
|
|
||||||
:material-plus: [client_certificate_public_key_sha256](#client_certificate_public_key_sha256)
|
:material-plus: [client_certificate_public_key_sha256](#client_certificate_public_key_sha256)
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
@@ -103,14 +101,9 @@ icon: material/new-box
|
|||||||
"min_version": "",
|
"min_version": "",
|
||||||
"max_version": "",
|
"max_version": "",
|
||||||
"cipher_suites": [],
|
"cipher_suites": [],
|
||||||
"curve_preferences": [],
|
|
||||||
"certificate": "",
|
"certificate": "",
|
||||||
"certificate_path": "",
|
"certificate_path": "",
|
||||||
"certificate_public_key_sha256": [],
|
"certificate_public_key_sha256": [],
|
||||||
"client_certificate": [],
|
|
||||||
"client_certificate_path": "",
|
|
||||||
"client_key": [],
|
|
||||||
"client_key_path": "",
|
|
||||||
"fragment": false,
|
"fragment": false,
|
||||||
"fragment_fallback_delay": "",
|
"fragment_fallback_delay": "",
|
||||||
"record_fragment": false,
|
"record_fragment": false,
|
||||||
@@ -260,38 +253,6 @@ openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform d
|
|||||||
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
|
||||||
```
|
```
|
||||||
|
|
||||||
#### client_certificate
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
==仅客户端==
|
|
||||||
|
|
||||||
客户端证书链行数组,PEM 格式。
|
|
||||||
|
|
||||||
#### client_certificate_path
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
==仅客户端==
|
|
||||||
|
|
||||||
客户端证书链路径,PEM 格式。
|
|
||||||
|
|
||||||
#### client_key
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
==仅客户端==
|
|
||||||
|
|
||||||
客户端私钥行数组,PEM 格式。
|
|
||||||
|
|
||||||
#### client_key_path
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
|
||||||
|
|
||||||
==仅客户端==
|
|
||||||
|
|
||||||
客户端私钥路径,PEM 格式。
|
|
||||||
|
|
||||||
#### key
|
#### key
|
||||||
|
|
||||||
==仅服务器==
|
==仅服务器==
|
||||||
|
|||||||
@@ -11,22 +11,16 @@ the project maintainer via [GitHub Sponsors](https://github.com/sponsors/nekohas
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Commercial Sponsors
|
### Special Sponsors
|
||||||
|
|
||||||
> [Warp](https://go.warp.dev/sing-box), Built for coding with multiple AI agents.
|
**Viral Tech, Inc.**
|
||||||
|
|
||||||
[](https://go.warp.dev/sing-box)
|
|
||||||
|
|
||||||
## Special Sponsors
|
|
||||||
|
|
||||||
> Viral Tech, Inc.
|
|
||||||
|
|
||||||
Helping us re-list sing-box apps on the Apple Store.
|
Helping us re-list sing-box apps on the Apple Store.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> [JetBrains](https://www.jetbrains.com)
|
[](https://www.jetbrains.com)
|
||||||
|
|
||||||
Free license for the amazing IDEs.
|
Free license for the amazing IDEs.
|
||||||
|
|
||||||
[](https://www.jetbrains.com)
|
---
|
||||||
|
|||||||
@@ -45,15 +45,15 @@ func (t TrackerMetadata) MarshalJSON() ([]byte, error) {
|
|||||||
if t.Metadata.ProcessInfo != nil {
|
if t.Metadata.ProcessInfo != nil {
|
||||||
if t.Metadata.ProcessInfo.ProcessPath != "" {
|
if t.Metadata.ProcessInfo.ProcessPath != "" {
|
||||||
processPath = t.Metadata.ProcessInfo.ProcessPath
|
processPath = t.Metadata.ProcessInfo.ProcessPath
|
||||||
} else if t.Metadata.ProcessInfo.PackageName != "" {
|
} else if t.Metadata.ProcessInfo.AndroidPackageName != "" {
|
||||||
processPath = t.Metadata.ProcessInfo.PackageName
|
processPath = t.Metadata.ProcessInfo.AndroidPackageName
|
||||||
}
|
}
|
||||||
if processPath == "" {
|
if processPath == "" {
|
||||||
if t.Metadata.ProcessInfo.UserId != -1 {
|
if t.Metadata.ProcessInfo.UserId != -1 {
|
||||||
processPath = F.ToString(t.Metadata.ProcessInfo.UserId)
|
processPath = F.ToString(t.Metadata.ProcessInfo.UserId)
|
||||||
}
|
}
|
||||||
} else if t.Metadata.ProcessInfo.User != "" {
|
} else if t.Metadata.ProcessInfo.UserName != "" {
|
||||||
processPath = F.ToString(processPath, " (", t.Metadata.ProcessInfo.User, ")")
|
processPath = F.ToString(processPath, " (", t.Metadata.ProcessInfo.UserName, ")")
|
||||||
} else if t.Metadata.ProcessInfo.UserId != -1 {
|
} else if t.Metadata.ProcessInfo.UserId != -1 {
|
||||||
processPath = F.ToString(processPath, " (", t.Metadata.ProcessInfo.UserId, ")")
|
processPath = F.ToString(processPath, " (", t.Metadata.ProcessInfo.UserId, ")")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,7 @@ package libbox
|
|||||||
const (
|
const (
|
||||||
CommandLog int32 = iota
|
CommandLog int32 = iota
|
||||||
CommandStatus
|
CommandStatus
|
||||||
CommandServiceReload
|
|
||||||
CommandServiceClose
|
|
||||||
CommandCloseConnections
|
|
||||||
CommandGroup
|
CommandGroup
|
||||||
CommandSelectOutbound
|
|
||||||
CommandURLTest
|
|
||||||
CommandGroupExpand
|
|
||||||
CommandClashMode
|
CommandClashMode
|
||||||
CommandSetClashMode
|
|
||||||
CommandGetSystemProxyStatus
|
|
||||||
CommandSetSystemProxyEnabled
|
|
||||||
CommandConnections
|
CommandConnections
|
||||||
CommandCloseConnection
|
|
||||||
CommandGetDeprecatedNotes
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) SetClashMode(newMode string) error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandSetClashMode))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = varbin.Write(conn, binary.BigEndian, newMode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
|
|
||||||
newMode, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
service := s.service
|
|
||||||
if service == nil {
|
|
||||||
return writeError(conn, E.New("service not ready"))
|
|
||||||
}
|
|
||||||
service.clashServer.(*clashapi.Server).SetMode(newMode)
|
|
||||||
return writeError(conn, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandClient) handleModeConn(conn net.Conn) {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
newMode, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
c.handler.Disconnected(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.handler.UpdateClashMode(newMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleModeConn(conn net.Conn) error {
|
|
||||||
ctx := connKeepAlive(conn)
|
|
||||||
for s.service == nil {
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
continue
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := writeClashModeList(conn, s.service.clashServer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-s.modeUpdate:
|
|
||||||
err = varbin.Write(conn, binary.BigEndian, s.service.clashServer.Mode())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readClashModeList(reader io.Reader) (modeList []string, currentMode string, err error) {
|
|
||||||
var modeListLength uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &modeListLength)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if modeListLength == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
modeList = make([]string, modeListLength)
|
|
||||||
for i := 0; i < int(modeListLength); i++ {
|
|
||||||
modeList[i], err = varbin.ReadValue[string](reader, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentMode, err = varbin.ReadValue[string](reader, binary.BigEndian)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeClashModeList(writer io.Writer, clashServer adapter.ClashServer) error {
|
|
||||||
modeList := clashServer.ModeList()
|
|
||||||
err := binary.Write(writer, binary.BigEndian, uint16(len(modeList)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(modeList) > 0 {
|
|
||||||
for _, mode := range modeList {
|
|
||||||
err = varbin.Write(writer, binary.BigEndian, mode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = varbin.Write(writer, binary.BigEndian, clashServer.Mode())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,32 +1,46 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/daemon"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandClient struct {
|
type CommandClient struct {
|
||||||
handler CommandClientHandler
|
handler CommandClientHandler
|
||||||
conn net.Conn
|
grpcConn *grpc.ClientConn
|
||||||
|
grpcClient daemon.StartedServiceClient
|
||||||
options CommandClientOptions
|
options CommandClientOptions
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
clientMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandClientOptions struct {
|
type CommandClientOptions struct {
|
||||||
Command int32
|
Command int32
|
||||||
|
Commands Int32Iterator
|
||||||
StatusInterval int64
|
StatusInterval int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandClientHandler interface {
|
type CommandClientHandler interface {
|
||||||
Connected()
|
Connected()
|
||||||
Disconnected(message string)
|
Disconnected(message string)
|
||||||
|
SetDefaultLogLevel(level int32)
|
||||||
ClearLogs()
|
ClearLogs()
|
||||||
WriteLogs(messageList StringIterator)
|
WriteLogs(messageList LogIterator)
|
||||||
WriteStatus(message *StatusMessage)
|
WriteStatus(message *StatusMessage)
|
||||||
WriteGroups(message OutboundGroupIterator)
|
WriteGroups(message OutboundGroupIterator)
|
||||||
InitializeClashMode(modeList StringIterator, currentMode string)
|
InitializeClashMode(modeList StringIterator, currentMode string)
|
||||||
@@ -34,6 +48,17 @@ type CommandClientHandler interface {
|
|||||||
WriteConnections(message *Connections)
|
WriteConnections(message *Connections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogEntry struct {
|
||||||
|
Level int32
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogIterator interface {
|
||||||
|
Len() int32
|
||||||
|
HasNext() bool
|
||||||
|
Next() *LogEntry
|
||||||
|
}
|
||||||
|
|
||||||
func NewStandaloneCommandClient() *CommandClient {
|
func NewStandaloneCommandClient() *CommandClient {
|
||||||
return new(CommandClient)
|
return new(CommandClient)
|
||||||
}
|
}
|
||||||
@@ -45,24 +70,24 @@ func NewCommandClient(handler CommandClientHandler, options *CommandClientOption
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) directConnect() (net.Conn, error) {
|
func (c *CommandClient) grpcDial() (*grpc.ClientConn, error) {
|
||||||
if !sTVOS {
|
var target string
|
||||||
return net.DialUnix("unix", nil, &net.UnixAddr{
|
if C.IsDarwin {
|
||||||
Name: filepath.Join(sBasePath, "command.sock"),
|
port := sCommandServerListenPort
|
||||||
Net: "unix",
|
if port == 0 {
|
||||||
})
|
port = 8964
|
||||||
} else {
|
|
||||||
return net.Dial("tcp", "127.0.0.1:8964")
|
|
||||||
}
|
}
|
||||||
|
target = net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port)))
|
||||||
|
} else {
|
||||||
|
target = "unix://" + filepath.Join(sBasePath, "command.sock")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) directConnectWithRetry() (net.Conn, error) {
|
|
||||||
var (
|
var (
|
||||||
conn net.Conn
|
conn *grpc.ClientConn
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
conn, err = c.directConnect()
|
conn, err = grpc.NewClient(target, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
@@ -72,79 +97,365 @@ func (c *CommandClient) directConnectWithRetry() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) Connect() error {
|
func (c *CommandClient) Connect() error {
|
||||||
common.Close(c.conn)
|
c.clientMutex.Lock()
|
||||||
conn, err := c.directConnectWithRetry()
|
common.Close(common.PtrOrNil(c.grpcConn))
|
||||||
|
|
||||||
|
conn, err := c.grpcDial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.clientMutex.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.conn = conn
|
c.grpcConn = conn
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(c.options.Command))
|
c.grpcClient = daemon.NewStartedServiceClient(conn)
|
||||||
if err != nil {
|
c.ctx, c.cancel = context.WithCancel(context.Background())
|
||||||
return err
|
c.clientMutex.Unlock()
|
||||||
}
|
|
||||||
switch c.options.Command {
|
var commands []int32
|
||||||
case CommandLog:
|
if c.options.Commands != nil {
|
||||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
commands = iteratorToArray[int32](c.options.Commands)
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "write interval")
|
|
||||||
}
|
|
||||||
c.handler.Connected()
|
|
||||||
go c.handleLogConn(conn)
|
|
||||||
case CommandStatus:
|
|
||||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "write interval")
|
|
||||||
}
|
|
||||||
c.handler.Connected()
|
|
||||||
go c.handleStatusConn(conn)
|
|
||||||
case CommandGroup:
|
|
||||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "write interval")
|
|
||||||
}
|
|
||||||
c.handler.Connected()
|
|
||||||
go c.handleGroupConn(conn)
|
|
||||||
case CommandClashMode:
|
|
||||||
var (
|
|
||||||
modeList []string
|
|
||||||
currentMode string
|
|
||||||
)
|
|
||||||
modeList, currentMode, err = readClashModeList(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if sFixAndroidStack {
|
|
||||||
go func() {
|
|
||||||
c.handler.Connected()
|
|
||||||
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
|
|
||||||
if len(modeList) == 0 {
|
|
||||||
conn.Close()
|
|
||||||
c.handler.Disconnected(os.ErrInvalid.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
} else {
|
} else {
|
||||||
|
commands = []int32{c.options.Command}
|
||||||
|
}
|
||||||
c.handler.Connected()
|
c.handler.Connected()
|
||||||
c.handler.InitializeClashMode(newIterator(modeList), currentMode)
|
for _, command := range commands {
|
||||||
if len(modeList) == 0 {
|
switch command {
|
||||||
conn.Close()
|
case CommandLog:
|
||||||
c.handler.Disconnected(os.ErrInvalid.Error())
|
go c.handleLogStream()
|
||||||
}
|
case CommandStatus:
|
||||||
}
|
go c.handleStatusStream()
|
||||||
if len(modeList) == 0 {
|
case CommandGroup:
|
||||||
return nil
|
go c.handleGroupStream()
|
||||||
}
|
case CommandClashMode:
|
||||||
go c.handleModeConn(conn)
|
go c.handleClashModeStream()
|
||||||
case CommandConnections:
|
case CommandConnections:
|
||||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
go c.handleConnectionsStream()
|
||||||
if err != nil {
|
default:
|
||||||
return E.Cause(err, "write interval")
|
return E.New("unknown command: ", command)
|
||||||
}
|
}
|
||||||
c.handler.Connected()
|
|
||||||
go c.handleConnectionsConn(conn)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) Disconnect() error {
|
func (c *CommandClient) Disconnect() error {
|
||||||
return common.Close(c.conn)
|
c.clientMutex.Lock()
|
||||||
|
defer c.clientMutex.Unlock()
|
||||||
|
if c.cancel != nil {
|
||||||
|
c.cancel()
|
||||||
|
}
|
||||||
|
return common.Close(common.PtrOrNil(c.grpcConn))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) getClientForCall() (daemon.StartedServiceClient, error) {
|
||||||
|
c.clientMutex.RLock()
|
||||||
|
if c.grpcClient != nil {
|
||||||
|
defer c.clientMutex.RUnlock()
|
||||||
|
return c.grpcClient, nil
|
||||||
|
}
|
||||||
|
c.clientMutex.RUnlock()
|
||||||
|
|
||||||
|
c.clientMutex.Lock()
|
||||||
|
defer c.clientMutex.Unlock()
|
||||||
|
|
||||||
|
if c.grpcClient != nil {
|
||||||
|
return c.grpcClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := c.grpcDial()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.grpcConn = conn
|
||||||
|
c.grpcClient = daemon.NewStartedServiceClient(conn)
|
||||||
|
if c.ctx == nil {
|
||||||
|
c.ctx, c.cancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
return c.grpcClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) getStreamContext() (daemon.StartedServiceClient, context.Context) {
|
||||||
|
c.clientMutex.RLock()
|
||||||
|
defer c.clientMutex.RUnlock()
|
||||||
|
return c.grpcClient, c.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) handleLogStream() {
|
||||||
|
client, ctx := c.getStreamContext()
|
||||||
|
stream, err := client.SubscribeLog(ctx, &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defaultLogLevel, err := client.GetDefaultLogLevel(ctx, &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.handler.SetDefaultLogLevel(int32(defaultLogLevel.Level))
|
||||||
|
for {
|
||||||
|
logMessage, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if logMessage.Reset_ {
|
||||||
|
c.handler.ClearLogs()
|
||||||
|
}
|
||||||
|
var messages []*LogEntry
|
||||||
|
for _, msg := range logMessage.Messages {
|
||||||
|
messages = append(messages, &LogEntry{
|
||||||
|
Level: int32(msg.Level),
|
||||||
|
Message: msg.Message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
c.handler.WriteLogs(newIterator(messages))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) handleStatusStream() {
|
||||||
|
client, ctx := c.getStreamContext()
|
||||||
|
interval := c.options.StatusInterval
|
||||||
|
|
||||||
|
stream, err := client.SubscribeStatus(ctx, &daemon.SubscribeStatusRequest{
|
||||||
|
Interval: interval,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
status, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.handler.WriteStatus(StatusMessageFromGRPC(status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) handleGroupStream() {
|
||||||
|
client, ctx := c.getStreamContext()
|
||||||
|
|
||||||
|
stream, err := client.SubscribeGroups(ctx, &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
groups, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.handler.WriteGroups(OutboundGroupIteratorFromGRPC(groups))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) handleClashModeStream() {
|
||||||
|
client, ctx := c.getStreamContext()
|
||||||
|
|
||||||
|
modeStatus, err := client.GetClashModeStatus(ctx, &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if sFixAndroidStack {
|
||||||
|
go func() {
|
||||||
|
c.handler.Connected()
|
||||||
|
c.handler.InitializeClashMode(newIterator(modeStatus.ModeList), modeStatus.CurrentMode)
|
||||||
|
if len(modeStatus.ModeList) == 0 {
|
||||||
|
c.handler.Disconnected(os.ErrInvalid.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
c.handler.Connected()
|
||||||
|
c.handler.InitializeClashMode(newIterator(modeStatus.ModeList), modeStatus.CurrentMode)
|
||||||
|
if len(modeStatus.ModeList) == 0 {
|
||||||
|
c.handler.Disconnected(os.ErrInvalid.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(modeStatus.ModeList) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stream, err := client.SubscribeClashMode(ctx, &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
mode, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.handler.UpdateClashMode(mode.Mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) handleConnectionsStream() {
|
||||||
|
client, ctx := c.getStreamContext()
|
||||||
|
interval := c.options.StatusInterval
|
||||||
|
|
||||||
|
stream, err := client.SubscribeConnections(ctx, &daemon.SubscribeConnectionsRequest{
|
||||||
|
Interval: interval,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var connections Connections
|
||||||
|
for {
|
||||||
|
conns, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
c.handler.Disconnected(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
connections.input = ConnectionsFromGRPC(conns)
|
||||||
|
c.handler.WriteConnections(&connections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.SelectOutbound(context.Background(), &daemon.SelectOutboundRequest{
|
||||||
|
GroupTag: groupTag,
|
||||||
|
OutboundTag: outboundTag,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) URLTest(groupTag string) error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.URLTest(context.Background(), &daemon.URLTestRequest{
|
||||||
|
OutboundTag: groupTag,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) SetClashMode(newMode string) error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.SetClashMode(context.Background(), &daemon.ClashMode{
|
||||||
|
Mode: newMode,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) CloseConnection(connId string) error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.CloseConnection(context.Background(), &daemon.CloseConnectionRequest{
|
||||||
|
Id: connId,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) CloseConnections() error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.CloseAllConnections(context.Background(), &emptypb.Empty{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) ServiceReload() error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.ReloadService(context.Background(), &emptypb.Empty{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) ServiceClose() error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.StopService(context.Background(), &emptypb.Empty{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := client.GetSystemProxyStatus(context.Background(), &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return SystemProxyStatusFromGRPC(status), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.SetSystemProxyEnabled(context.Background(), &daemon.SetSystemProxyEnabledRequest{
|
||||||
|
Enabled: isEnabled,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) GetDeprecatedNotes() (DeprecatedNoteIterator, error) {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings, err := client.GetDeprecatedWarnings(context.Background(), &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var notes []*DeprecatedNote
|
||||||
|
for _, warning := range warnings.Warnings {
|
||||||
|
notes = append(notes, &DeprecatedNote{
|
||||||
|
Description: warning.Message,
|
||||||
|
MigrationLink: warning.MigrationLink,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return newIterator(notes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.SetGroupExpand(context.Background(), &daemon.SetGroupExpandRequest{
|
||||||
|
GroupTag: groupTag,
|
||||||
|
IsExpand: isExpand,
|
||||||
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
|
||||||
"github.com/sagernet/sing/common/binary"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
|
|
||||||
"github.com/gofrs/uuid/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) CloseConnection(connId string) error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandCloseConnection))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
writer := bufio.NewWriter(conn)
|
|
||||||
err = varbin.Write(writer, binary.BigEndian, connId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = writer.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleCloseConnection(conn net.Conn) error {
|
|
||||||
reader := bufio.NewReader(conn)
|
|
||||||
var connId string
|
|
||||||
err := varbin.Read(reader, binary.BigEndian, &connId)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read connection id")
|
|
||||||
}
|
|
||||||
service := s.service
|
|
||||||
if service == nil {
|
|
||||||
return writeError(conn, E.New("service not ready"))
|
|
||||||
}
|
|
||||||
targetConn := service.clashServer.(*clashapi.Server).TrafficManager().Connection(uuid.FromStringOrNil(connId))
|
|
||||||
if targetConn == nil {
|
|
||||||
return writeError(conn, E.New("connection already closed"))
|
|
||||||
}
|
|
||||||
targetConn.Close()
|
|
||||||
return writeError(conn, nil)
|
|
||||||
}
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"net"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
|
||||||
"github.com/sagernet/sing/common/binary"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
|
|
||||||
"github.com/gofrs/uuid/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) handleConnectionsConn(conn net.Conn) {
|
|
||||||
defer conn.Close()
|
|
||||||
reader := bufio.NewReader(conn)
|
|
||||||
var (
|
|
||||||
rawConnections []Connection
|
|
||||||
connections Connections
|
|
||||||
)
|
|
||||||
for {
|
|
||||||
rawConnections = nil
|
|
||||||
err := varbin.Read(reader, binary.BigEndian, &rawConnections)
|
|
||||||
if err != nil {
|
|
||||||
c.handler.Disconnected(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
connections.input = rawConnections
|
|
||||||
c.handler.WriteConnections(&connections)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleConnectionsConn(conn net.Conn) error {
|
|
||||||
var interval int64
|
|
||||||
err := binary.Read(conn, binary.BigEndian, &interval)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read interval")
|
|
||||||
}
|
|
||||||
ticker := time.NewTicker(time.Duration(interval))
|
|
||||||
defer ticker.Stop()
|
|
||||||
ctx := connKeepAlive(conn)
|
|
||||||
var trafficManager *trafficontrol.Manager
|
|
||||||
for {
|
|
||||||
service := s.service
|
|
||||||
if service != nil {
|
|
||||||
trafficManager = service.clashServer.(*clashapi.Server).TrafficManager()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-ticker.C:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
connections = make(map[uuid.UUID]*Connection)
|
|
||||||
outConnections []Connection
|
|
||||||
)
|
|
||||||
writer := bufio.NewWriter(conn)
|
|
||||||
for {
|
|
||||||
outConnections = outConnections[:0]
|
|
||||||
for _, connection := range trafficManager.Connections() {
|
|
||||||
outConnections = append(outConnections, newConnection(connections, connection, false))
|
|
||||||
}
|
|
||||||
for _, connection := range trafficManager.ClosedConnections() {
|
|
||||||
outConnections = append(outConnections, newConnection(connections, connection, true))
|
|
||||||
}
|
|
||||||
err = varbin.Write(writer, binary.BigEndian, outConnections)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = writer.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-ticker.C:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
ConnectionStateAll = iota
|
|
||||||
ConnectionStateActive
|
|
||||||
ConnectionStateClosed
|
|
||||||
)
|
|
||||||
|
|
||||||
type Connections struct {
|
|
||||||
input []Connection
|
|
||||||
filtered []Connection
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connections) FilterState(state int32) {
|
|
||||||
c.filtered = c.filtered[:0]
|
|
||||||
switch state {
|
|
||||||
case ConnectionStateAll:
|
|
||||||
c.filtered = append(c.filtered, c.input...)
|
|
||||||
case ConnectionStateActive:
|
|
||||||
for _, connection := range c.input {
|
|
||||||
if connection.ClosedAt == 0 {
|
|
||||||
c.filtered = append(c.filtered, connection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ConnectionStateClosed:
|
|
||||||
for _, connection := range c.input {
|
|
||||||
if connection.ClosedAt != 0 {
|
|
||||||
c.filtered = append(c.filtered, connection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connections) SortByDate() {
|
|
||||||
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
|
||||||
if x.CreatedAt < y.CreatedAt {
|
|
||||||
return 1
|
|
||||||
} else if x.CreatedAt > y.CreatedAt {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return strings.Compare(y.ID, x.ID)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connections) SortByTraffic() {
|
|
||||||
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
|
||||||
xTraffic := x.Uplink + x.Downlink
|
|
||||||
yTraffic := y.Uplink + y.Downlink
|
|
||||||
if xTraffic < yTraffic {
|
|
||||||
return 1
|
|
||||||
} else if xTraffic > yTraffic {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return strings.Compare(y.ID, x.ID)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connections) SortByTrafficTotal() {
|
|
||||||
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
|
||||||
xTraffic := x.UplinkTotal + x.DownlinkTotal
|
|
||||||
yTraffic := y.UplinkTotal + y.DownlinkTotal
|
|
||||||
if xTraffic < yTraffic {
|
|
||||||
return 1
|
|
||||||
} else if xTraffic > yTraffic {
|
|
||||||
return -1
|
|
||||||
} else {
|
|
||||||
return strings.Compare(y.ID, x.ID)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connections) Iterator() ConnectionIterator {
|
|
||||||
return newPtrIterator(c.filtered)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Connection struct {
|
|
||||||
ID string
|
|
||||||
Inbound string
|
|
||||||
InboundType string
|
|
||||||
IPVersion int32
|
|
||||||
Network string
|
|
||||||
Source string
|
|
||||||
Destination string
|
|
||||||
Domain string
|
|
||||||
Protocol string
|
|
||||||
User string
|
|
||||||
FromOutbound string
|
|
||||||
CreatedAt int64
|
|
||||||
ClosedAt int64
|
|
||||||
Uplink int64
|
|
||||||
Downlink int64
|
|
||||||
UplinkTotal int64
|
|
||||||
DownlinkTotal int64
|
|
||||||
Rule string
|
|
||||||
Outbound string
|
|
||||||
OutboundType string
|
|
||||||
ChainList []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connection) Chain() StringIterator {
|
|
||||||
return newIterator(c.ChainList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Connection) DisplayDestination() string {
|
|
||||||
destination := M.ParseSocksaddr(c.Destination)
|
|
||||||
if destination.IsIP() && c.Domain != "" {
|
|
||||||
destination = M.Socksaddr{
|
|
||||||
Fqdn: c.Domain,
|
|
||||||
Port: destination.Port,
|
|
||||||
}
|
|
||||||
return destination.String()
|
|
||||||
}
|
|
||||||
return c.Destination
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConnectionIterator interface {
|
|
||||||
Next() *Connection
|
|
||||||
HasNext() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol.TrackerMetadata, isClosed bool) Connection {
|
|
||||||
if oldConnection, loaded := connections[metadata.ID]; loaded {
|
|
||||||
if isClosed {
|
|
||||||
if oldConnection.ClosedAt == 0 {
|
|
||||||
oldConnection.Uplink = 0
|
|
||||||
oldConnection.Downlink = 0
|
|
||||||
oldConnection.ClosedAt = metadata.ClosedAt.UnixMilli()
|
|
||||||
}
|
|
||||||
return *oldConnection
|
|
||||||
}
|
|
||||||
lastUplink := oldConnection.UplinkTotal
|
|
||||||
lastDownlink := oldConnection.DownlinkTotal
|
|
||||||
uplinkTotal := metadata.Upload.Load()
|
|
||||||
downlinkTotal := metadata.Download.Load()
|
|
||||||
oldConnection.Uplink = uplinkTotal - lastUplink
|
|
||||||
oldConnection.Downlink = downlinkTotal - lastDownlink
|
|
||||||
oldConnection.UplinkTotal = uplinkTotal
|
|
||||||
oldConnection.DownlinkTotal = downlinkTotal
|
|
||||||
return *oldConnection
|
|
||||||
}
|
|
||||||
var rule string
|
|
||||||
if metadata.Rule != nil {
|
|
||||||
rule = metadata.Rule.String()
|
|
||||||
}
|
|
||||||
uplinkTotal := metadata.Upload.Load()
|
|
||||||
downlinkTotal := metadata.Download.Load()
|
|
||||||
uplink := uplinkTotal
|
|
||||||
downlink := downlinkTotal
|
|
||||||
var closedAt int64
|
|
||||||
if !metadata.ClosedAt.IsZero() {
|
|
||||||
closedAt = metadata.ClosedAt.UnixMilli()
|
|
||||||
uplink = 0
|
|
||||||
downlink = 0
|
|
||||||
}
|
|
||||||
connection := Connection{
|
|
||||||
ID: metadata.ID.String(),
|
|
||||||
Inbound: metadata.Metadata.Inbound,
|
|
||||||
InboundType: metadata.Metadata.InboundType,
|
|
||||||
IPVersion: int32(metadata.Metadata.IPVersion),
|
|
||||||
Network: metadata.Metadata.Network,
|
|
||||||
Source: metadata.Metadata.Source.String(),
|
|
||||||
Destination: metadata.Metadata.Destination.String(),
|
|
||||||
Domain: metadata.Metadata.Domain,
|
|
||||||
Protocol: metadata.Metadata.Protocol,
|
|
||||||
User: metadata.Metadata.User,
|
|
||||||
FromOutbound: metadata.Metadata.Outbound,
|
|
||||||
CreatedAt: metadata.CreatedAt.UnixMilli(),
|
|
||||||
ClosedAt: closedAt,
|
|
||||||
Uplink: uplink,
|
|
||||||
Downlink: downlink,
|
|
||||||
UplinkTotal: uplinkTotal,
|
|
||||||
DownlinkTotal: downlinkTotal,
|
|
||||||
Rule: rule,
|
|
||||||
Outbound: metadata.Outbound,
|
|
||||||
OutboundType: metadata.OutboundType,
|
|
||||||
ChainList: metadata.Chain,
|
|
||||||
}
|
|
||||||
connections[metadata.ID] = &connection
|
|
||||||
return connection
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
runtimeDebug "runtime/debug"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/conntrack"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) CloseConnections() error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
return binary.Write(conn, binary.BigEndian, uint8(CommandCloseConnections))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleCloseConnections(conn net.Conn) error {
|
|
||||||
conntrack.Close()
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
runtimeDebug.FreeOSMemory()
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
"github.com/sagernet/sing/service"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) GetDeprecatedNotes() (DeprecatedNoteIterator, error) {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandGetDeprecatedNotes))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = readError(conn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var features []deprecated.Note
|
|
||||||
err = varbin.Read(conn, binary.BigEndian, &features)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return newIterator(common.Map(features, func(it deprecated.Note) *DeprecatedNote { return (*DeprecatedNote)(&it) })), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleGetDeprecatedNotes(conn net.Conn) error {
|
|
||||||
boxService := s.service
|
|
||||||
if boxService == nil {
|
|
||||||
return writeError(conn, E.New("service not ready"))
|
|
||||||
}
|
|
||||||
err := writeError(conn, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return varbin.Write(conn, binary.BigEndian, service.FromContext[deprecated.Manager](boxService.ctx).(*deprecatedManager).Get())
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
|
||||||
"github.com/sagernet/sing-box/protocol/group"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
"github.com/sagernet/sing/service"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) handleGroupConn(conn net.Conn) {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
groups, err := readGroups(conn)
|
|
||||||
if err != nil {
|
|
||||||
c.handler.Disconnected(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.handler.WriteGroups(groups)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
|
||||||
var interval int64
|
|
||||||
err := binary.Read(conn, binary.BigEndian, &interval)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read interval")
|
|
||||||
}
|
|
||||||
ticker := time.NewTicker(time.Duration(interval))
|
|
||||||
defer ticker.Stop()
|
|
||||||
ctx := connKeepAlive(conn)
|
|
||||||
writer := bufio.NewWriter(conn)
|
|
||||||
for {
|
|
||||||
service := s.service
|
|
||||||
if service != nil {
|
|
||||||
err = writeGroups(writer, service)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = binary.Write(writer, binary.BigEndian, uint16(0))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = writer.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-ticker.C:
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-s.urlTestUpdate:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutboundGroup struct {
|
|
||||||
Tag string
|
|
||||||
Type string
|
|
||||||
Selectable bool
|
|
||||||
Selected string
|
|
||||||
IsExpand bool
|
|
||||||
ItemList []*OutboundGroupItem
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
|
|
||||||
return newIterator(g.ItemList)
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutboundGroupIterator interface {
|
|
||||||
Next() *OutboundGroup
|
|
||||||
HasNext() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutboundGroupItem struct {
|
|
||||||
Tag string
|
|
||||||
Type string
|
|
||||||
URLTestTime int64
|
|
||||||
URLTestDelay int32
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutboundGroupItemIterator interface {
|
|
||||||
Next() *OutboundGroupItem
|
|
||||||
HasNext() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
|
|
||||||
groups, err := varbin.ReadValue[[]*OutboundGroup](reader, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return newIterator(groups), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeGroups(writer io.Writer, boxService *BoxService) error {
|
|
||||||
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
|
|
||||||
cacheFile := service.FromContext[adapter.CacheFile](boxService.ctx)
|
|
||||||
outbounds := boxService.instance.Outbound().Outbounds()
|
|
||||||
var iGroups []adapter.OutboundGroup
|
|
||||||
for _, it := range outbounds {
|
|
||||||
if group, isGroup := it.(adapter.OutboundGroup); isGroup {
|
|
||||||
iGroups = append(iGroups, group)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var groups []OutboundGroup
|
|
||||||
for _, iGroup := range iGroups {
|
|
||||||
var outboundGroup OutboundGroup
|
|
||||||
outboundGroup.Tag = iGroup.Tag()
|
|
||||||
outboundGroup.Type = iGroup.Type()
|
|
||||||
_, outboundGroup.Selectable = iGroup.(*group.Selector)
|
|
||||||
outboundGroup.Selected = iGroup.Now()
|
|
||||||
if cacheFile != nil {
|
|
||||||
if isExpand, loaded := cacheFile.LoadGroupExpand(outboundGroup.Tag); loaded {
|
|
||||||
outboundGroup.IsExpand = isExpand
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, itemTag := range iGroup.All() {
|
|
||||||
itemOutbound, isLoaded := boxService.instance.Outbound().Outbound(itemTag)
|
|
||||||
if !isLoaded {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var item OutboundGroupItem
|
|
||||||
item.Tag = itemTag
|
|
||||||
item.Type = itemOutbound.Type()
|
|
||||||
if history := historyStorage.LoadURLTestHistory(adapter.OutboundTag(itemOutbound)); history != nil {
|
|
||||||
item.URLTestTime = history.Time.Unix()
|
|
||||||
item.URLTestDelay = int32(history.Delay)
|
|
||||||
}
|
|
||||||
outboundGroup.ItemList = append(outboundGroup.ItemList, &item)
|
|
||||||
}
|
|
||||||
if len(outboundGroup.ItemList) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
groups = append(groups, outboundGroup)
|
|
||||||
}
|
|
||||||
return varbin.Write(writer, binary.BigEndian, groups)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandGroupExpand))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = varbin.Write(conn, binary.BigEndian, groupTag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = binary.Write(conn, binary.BigEndian, isExpand)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
|
||||||
groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var isExpand bool
|
|
||||||
err = binary.Read(conn, binary.BigEndian, &isExpand)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
serviceNow := s.service
|
|
||||||
if serviceNow == nil {
|
|
||||||
return writeError(conn, E.New("service not ready"))
|
|
||||||
}
|
|
||||||
cacheFile := service.FromContext[adapter.CacheFile](serviceNow.ctx)
|
|
||||||
if cacheFile != nil {
|
|
||||||
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
|
|
||||||
if err != nil {
|
|
||||||
return writeError(conn, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return writeError(conn, nil)
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/binary"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *CommandServer) ResetLog() {
|
|
||||||
s.access.Lock()
|
|
||||||
defer s.access.Unlock()
|
|
||||||
s.savedLines.Init()
|
|
||||||
select {
|
|
||||||
case s.logReset <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) WriteMessage(message string) {
|
|
||||||
s.subscriber.Emit(message)
|
|
||||||
s.access.Lock()
|
|
||||||
s.savedLines.PushBack(message)
|
|
||||||
if s.savedLines.Len() > s.maxLines {
|
|
||||||
s.savedLines.Remove(s.savedLines.Front())
|
|
||||||
}
|
|
||||||
s.access.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
|
||||||
var (
|
|
||||||
interval int64
|
|
||||||
timer *time.Timer
|
|
||||||
)
|
|
||||||
err := binary.Read(conn, binary.BigEndian, &interval)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read interval")
|
|
||||||
}
|
|
||||||
timer = time.NewTimer(time.Duration(interval))
|
|
||||||
if !timer.Stop() {
|
|
||||||
<-timer.C
|
|
||||||
}
|
|
||||||
var savedLines []string
|
|
||||||
s.access.Lock()
|
|
||||||
savedLines = make([]string, 0, s.savedLines.Len())
|
|
||||||
for element := s.savedLines.Front(); element != nil; element = element.Next() {
|
|
||||||
savedLines = append(savedLines, element.Value)
|
|
||||||
}
|
|
||||||
s.access.Unlock()
|
|
||||||
subscription, done, err := s.observer.Subscribe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer s.observer.UnSubscribe(subscription)
|
|
||||||
writer := bufio.NewWriter(conn)
|
|
||||||
select {
|
|
||||||
case <-s.logReset:
|
|
||||||
err = writer.WriteByte(1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = writer.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if len(savedLines) > 0 {
|
|
||||||
err = writer.WriteByte(0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = varbin.Write(writer, binary.BigEndian, savedLines)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx := connKeepAlive(conn)
|
|
||||||
var logLines []string
|
|
||||||
for {
|
|
||||||
err = writer.Flush()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-s.logReset:
|
|
||||||
err = writer.WriteByte(1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case <-done:
|
|
||||||
return nil
|
|
||||||
case logLine := <-subscription:
|
|
||||||
logLines = logLines[:0]
|
|
||||||
logLines = append(logLines, logLine)
|
|
||||||
timer.Reset(time.Duration(interval))
|
|
||||||
loopLogs:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case logLine = <-subscription:
|
|
||||||
logLines = append(logLines, logLine)
|
|
||||||
case <-timer.C:
|
|
||||||
break loopLogs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = writer.WriteByte(0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = varbin.Write(writer, binary.BigEndian, logLines)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandClient) handleLogConn(conn net.Conn) {
|
|
||||||
reader := bufio.NewReader(conn)
|
|
||||||
for {
|
|
||||||
messageType, err := reader.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
c.handler.Disconnected(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var messages []string
|
|
||||||
switch messageType {
|
|
||||||
case 0:
|
|
||||||
err = varbin.Read(reader, binary.BigEndian, &messages)
|
|
||||||
if err != nil {
|
|
||||||
c.handler.Disconnected(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.handler.WriteLogs(newIterator(messages))
|
|
||||||
case 1:
|
|
||||||
c.handler.ClearLogs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func connKeepAlive(reader io.Reader) context.Context {
|
|
||||||
ctx, cancel := context.WithCancelCause(context.Background())
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
_, err := reader.Read(make([]byte, 1))
|
|
||||||
if err != nil {
|
|
||||||
cancel(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) ServiceReload() error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceReload))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleServiceReload(conn net.Conn) error {
|
|
||||||
rErr := s.handler.ServiceReload()
|
|
||||||
err := binary.Write(conn, binary.BigEndian, rErr != nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rErr != nil {
|
|
||||||
return varbin.Write(conn, binary.BigEndian, rErr.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandClient) ServiceClose() error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceClose))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleServiceClose(conn net.Conn) error {
|
|
||||||
rErr := s.service.Close()
|
|
||||||
s.handler.PostServiceClose()
|
|
||||||
err := binary.Write(conn, binary.BigEndian, rErr != nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rErr != nil {
|
|
||||||
return varbin.Write(conn, binary.BigEndian, rErr.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/protocol/group"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandSelectOutbound))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = varbin.Write(conn, binary.BigEndian, groupTag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = varbin.Write(conn, binary.BigEndian, outboundTag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
|
|
||||||
groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
outboundTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
service := s.service
|
|
||||||
if service == nil {
|
|
||||||
return writeError(conn, E.New("service not ready"))
|
|
||||||
}
|
|
||||||
outboundGroup, isLoaded := service.instance.Outbound().Outbound(groupTag)
|
|
||||||
if !isLoaded {
|
|
||||||
return writeError(conn, E.New("selector not found: ", groupTag))
|
|
||||||
}
|
|
||||||
selector, isSelector := outboundGroup.(*group.Selector)
|
|
||||||
if !isSelector {
|
|
||||||
return writeError(conn, E.New("outbound is not a selector: ", groupTag))
|
|
||||||
}
|
|
||||||
if !selector.SelectOutbound(outboundTag) {
|
|
||||||
return writeError(conn, E.New("outbound not found in selector: ", outboundTag))
|
|
||||||
}
|
|
||||||
return writeError(conn, nil)
|
|
||||||
}
|
|
||||||
@@ -1,182 +1,225 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/daemon"
|
||||||
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/debug"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/observable"
|
|
||||||
"github.com/sagernet/sing/common/x/list"
|
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandServer struct {
|
type CommandServer struct {
|
||||||
listener net.Listener
|
*daemon.StartedService
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
handler CommandServerHandler
|
handler CommandServerHandler
|
||||||
|
platformInterface PlatformInterface
|
||||||
access sync.Mutex
|
platformWrapper *platformInterfaceWrapper
|
||||||
savedLines list.List[string]
|
grpcServer *grpc.Server
|
||||||
maxLines int
|
listener net.Listener
|
||||||
subscriber *observable.Subscriber[string]
|
endPauseTimer *time.Timer
|
||||||
observer *observable.Observer[string]
|
|
||||||
service *BoxService
|
|
||||||
|
|
||||||
// These channels only work with a single client. if multi-client support is needed, replace with Subscriber/Observer
|
|
||||||
urlTestUpdate chan struct{}
|
|
||||||
modeUpdate chan struct{}
|
|
||||||
logReset chan struct{}
|
|
||||||
|
|
||||||
closedConnections []Connection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandServerHandler interface {
|
type CommandServerHandler interface {
|
||||||
|
ServiceStop() error
|
||||||
ServiceReload() error
|
ServiceReload() error
|
||||||
PostServiceClose()
|
GetSystemProxyStatus() (*SystemProxyStatus, error)
|
||||||
GetSystemProxyStatus() *SystemProxyStatus
|
SetSystemProxyEnabled(enabled bool) error
|
||||||
SetSystemProxyEnabled(isEnabled bool) error
|
WriteDebugMessage(message string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServer {
|
func NewCommandServer(handler CommandServerHandler, platformInterface PlatformInterface) (*CommandServer, error) {
|
||||||
|
ctx := BaseContext(platformInterface)
|
||||||
|
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
platformWrapper := &platformInterfaceWrapper{
|
||||||
|
iif: platformInterface,
|
||||||
|
useProcFS: platformInterface.UseProcFS(),
|
||||||
|
}
|
||||||
|
service.MustRegister[adapter.PlatformInterface](ctx, platformWrapper)
|
||||||
server := &CommandServer{
|
server := &CommandServer{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
maxLines: int(maxLines),
|
platformInterface: platformInterface,
|
||||||
subscriber: observable.NewSubscriber[string](128),
|
platformWrapper: platformWrapper,
|
||||||
urlTestUpdate: make(chan struct{}, 1),
|
|
||||||
modeUpdate: make(chan struct{}, 1),
|
|
||||||
logReset: make(chan struct{}, 1),
|
|
||||||
}
|
|
||||||
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) SetService(newService *BoxService) {
|
|
||||||
if newService != nil {
|
|
||||||
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
|
|
||||||
newService.clashServer.(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
|
|
||||||
}
|
|
||||||
s.service = newService
|
|
||||||
s.notifyURLTestUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) notifyURLTestUpdate() {
|
|
||||||
select {
|
|
||||||
case s.urlTestUpdate <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
server.StartedService = daemon.NewStartedService(daemon.ServiceOptions{
|
||||||
|
Context: ctx,
|
||||||
|
Platform: platformWrapper,
|
||||||
|
PlatformHandler: (*platformHandler)(server),
|
||||||
|
Debug: sDebug,
|
||||||
|
LogMaxLines: sLogMaxLines,
|
||||||
|
WorkingDirectory: sBasePath,
|
||||||
|
TempDirectory: os.TempDir(),
|
||||||
|
UserID: sUserID,
|
||||||
|
GroupID: sGroupID,
|
||||||
|
SystemProxyEnabled: false,
|
||||||
|
})
|
||||||
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) Start() error {
|
func (s *CommandServer) Start() error {
|
||||||
if !sTVOS {
|
var (
|
||||||
return s.listenUNIX()
|
listener net.Listener
|
||||||
} else {
|
err error
|
||||||
return s.listenTCP()
|
)
|
||||||
}
|
if C.IsAndroid && sCommandServerListenPort == 0 {
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) listenUNIX() error {
|
|
||||||
sockPath := filepath.Join(sBasePath, "command.sock")
|
sockPath := filepath.Join(sBasePath, "command.sock")
|
||||||
os.Remove(sockPath)
|
os.Remove(sockPath)
|
||||||
listener, err := net.ListenUnix("unix", &net.UnixAddr{
|
listener, err = net.ListenUnix("unix", &net.UnixAddr{
|
||||||
Name: sockPath,
|
Name: sockPath,
|
||||||
Net: "unix",
|
Net: "unix",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "listen ", sockPath)
|
return E.Cause(err, "listen command server")
|
||||||
}
|
}
|
||||||
|
if sUserID != os.Getuid() {
|
||||||
err = os.Chown(sockPath, sUserID, sGroupID)
|
err = os.Chown(sockPath, sUserID, sGroupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
listener.Close()
|
listener.Close()
|
||||||
os.Remove(sockPath)
|
os.Remove(sockPath)
|
||||||
return E.Cause(err, "chown")
|
return E.Cause(err, "chown")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
port := sCommandServerListenPort
|
||||||
|
if port == 0 {
|
||||||
|
port = 8964
|
||||||
|
}
|
||||||
|
listener, err = net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port))))
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "listen command server")
|
||||||
|
}
|
||||||
|
}
|
||||||
s.listener = listener
|
s.listener = listener
|
||||||
go s.loopConnection(listener)
|
s.grpcServer = grpc.NewServer()
|
||||||
|
daemon.RegisterStartedServiceServer(s.grpcServer, s.StartedService)
|
||||||
|
go s.grpcServer.Serve(listener)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) listenTCP() error {
|
func (s *CommandServer) Close() {
|
||||||
listener, err := net.Listen("tcp", "127.0.0.1:8964")
|
s.cancel()
|
||||||
if err != nil {
|
if s.grpcServer != nil {
|
||||||
return E.Cause(err, "listen")
|
s.grpcServer.Stop()
|
||||||
}
|
}
|
||||||
s.listener = listener
|
common.Close(s.listener)
|
||||||
go s.loopConnection(listener)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) Close() error {
|
type OverrideOptions struct {
|
||||||
return common.Close(
|
AutoRedirect bool
|
||||||
s.listener,
|
IncludePackage StringIterator
|
||||||
s.observer,
|
ExcludePackage StringIterator
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) loopConnection(listener net.Listener) {
|
func (s *CommandServer) StartOrReloadService(configContent string, options *OverrideOptions) error {
|
||||||
for {
|
return s.StartedService.StartOrReloadService(configContent, &daemon.OverrideOptions{
|
||||||
conn, err := listener.Accept()
|
AutoRedirect: options.AutoRedirect,
|
||||||
if err != nil {
|
IncludePackage: iteratorToArray(options.IncludePackage),
|
||||||
|
ExcludePackage: iteratorToArray(options.ExcludePackage),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) CloseService() error {
|
||||||
|
return s.StartedService.CloseService()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) WriteMessage(level int32, message string) {
|
||||||
|
s.StartedService.WriteMessage(log.Level(level), message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) SetError(message string) {
|
||||||
|
s.StartedService.SetError(E.New(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) NeedWIFIState() bool {
|
||||||
|
instance := s.StartedService.Instance()
|
||||||
|
if instance == nil || instance.Box() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return instance.Box().Network().NeedWIFIState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) Pause() {
|
||||||
|
instance := s.StartedService.Instance()
|
||||||
|
if instance == nil || instance.PauseManager() == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
instance.PauseManager().DevicePause()
|
||||||
hErr := s.handleConnection(conn)
|
if C.IsIos {
|
||||||
if hErr != nil && !E.IsClosed(err) {
|
if s.endPauseTimer == nil {
|
||||||
if debug.Enabled {
|
s.endPauseTimer = time.AfterFunc(time.Minute, instance.PauseManager().DeviceWake)
|
||||||
log.Warn("log-server: process connection: ", hErr)
|
} else {
|
||||||
|
s.endPauseTimer.Reset(time.Minute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleConnection(conn net.Conn) error {
|
func (s *CommandServer) Wake() {
|
||||||
defer conn.Close()
|
instance := s.StartedService.Instance()
|
||||||
var command uint8
|
if instance == nil || instance.PauseManager() == nil {
|
||||||
err := binary.Read(conn, binary.BigEndian, &command)
|
return
|
||||||
|
}
|
||||||
|
if !C.IsIos {
|
||||||
|
instance.PauseManager().DeviceWake()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) ResetNetwork() {
|
||||||
|
instance := s.StartedService.Instance()
|
||||||
|
if instance == nil || instance.Box() == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance.Box().Router().ResetNetwork()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) UpdateWIFIState() {
|
||||||
|
instance := s.StartedService.Instance()
|
||||||
|
if instance == nil || instance.Box() == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance.Box().Network().UpdateWIFIState()
|
||||||
|
}
|
||||||
|
|
||||||
|
type platformHandler CommandServer
|
||||||
|
|
||||||
|
func (h *platformHandler) ServiceStop() error {
|
||||||
|
return (*CommandServer)(h).handler.ServiceStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *platformHandler) ServiceReload() error {
|
||||||
|
return (*CommandServer)(h).handler.ServiceReload()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *platformHandler) SystemProxyStatus() (*daemon.SystemProxyStatus, error) {
|
||||||
|
status, err := (*CommandServer)(h).handler.GetSystemProxyStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "read command")
|
return nil, err
|
||||||
}
|
}
|
||||||
switch int32(command) {
|
return &daemon.SystemProxyStatus{
|
||||||
case CommandLog:
|
Enabled: status.Enabled,
|
||||||
return s.handleLogConn(conn)
|
Available: status.Available,
|
||||||
case CommandStatus:
|
}, nil
|
||||||
return s.handleStatusConn(conn)
|
|
||||||
case CommandServiceReload:
|
|
||||||
return s.handleServiceReload(conn)
|
|
||||||
case CommandServiceClose:
|
|
||||||
return s.handleServiceClose(conn)
|
|
||||||
case CommandCloseConnections:
|
|
||||||
return s.handleCloseConnections(conn)
|
|
||||||
case CommandGroup:
|
|
||||||
return s.handleGroupConn(conn)
|
|
||||||
case CommandSelectOutbound:
|
|
||||||
return s.handleSelectOutbound(conn)
|
|
||||||
case CommandURLTest:
|
|
||||||
return s.handleURLTest(conn)
|
|
||||||
case CommandGroupExpand:
|
|
||||||
return s.handleSetGroupExpand(conn)
|
|
||||||
case CommandClashMode:
|
|
||||||
return s.handleModeConn(conn)
|
|
||||||
case CommandSetClashMode:
|
|
||||||
return s.handleSetClashMode(conn)
|
|
||||||
case CommandGetSystemProxyStatus:
|
|
||||||
return s.handleGetSystemProxyStatus(conn)
|
|
||||||
case CommandSetSystemProxyEnabled:
|
|
||||||
return s.handleSetSystemProxyEnabled(conn)
|
|
||||||
case CommandConnections:
|
|
||||||
return s.handleConnectionsConn(conn)
|
|
||||||
case CommandCloseConnection:
|
|
||||||
return s.handleCloseConnection(conn)
|
|
||||||
case CommandGetDeprecatedNotes:
|
|
||||||
return s.handleGetDeprecatedNotes(conn)
|
|
||||||
default:
|
|
||||||
return E.New("unknown command: ", command)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *platformHandler) SetSystemProxyEnabled(enabled bool) error {
|
||||||
|
return (*CommandServer)(h).handler.SetSystemProxyEnabled(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *platformHandler) WriteDebugMessage(message string) {
|
||||||
|
(*CommandServer)(h).handler.WriteDebugMessage(message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func readError(reader io.Reader) error {
|
|
||||||
var hasError bool
|
|
||||||
err := binary.Read(reader, binary.BigEndian, &hasError)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if hasError {
|
|
||||||
errorMessage, err := varbin.ReadValue[string](reader, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return E.New(errorMessage)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeError(writer io.Writer, wErr error) error {
|
|
||||||
err := binary.Write(writer, binary.BigEndian, wErr != nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if wErr != nil {
|
|
||||||
err = varbin.Write(writer, binary.BigEndian, wErr.Error())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/conntrack"
|
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/memory"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StatusMessage struct {
|
|
||||||
Memory int64
|
|
||||||
Goroutines int32
|
|
||||||
ConnectionsIn int32
|
|
||||||
ConnectionsOut int32
|
|
||||||
TrafficAvailable bool
|
|
||||||
Uplink int64
|
|
||||||
Downlink int64
|
|
||||||
UplinkTotal int64
|
|
||||||
DownlinkTotal int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) readStatus() StatusMessage {
|
|
||||||
var message StatusMessage
|
|
||||||
message.Memory = int64(memory.Inuse())
|
|
||||||
message.Goroutines = int32(runtime.NumGoroutine())
|
|
||||||
message.ConnectionsOut = int32(conntrack.Count())
|
|
||||||
|
|
||||||
if s.service != nil {
|
|
||||||
message.TrafficAvailable = true
|
|
||||||
trafficManager := s.service.clashServer.(*clashapi.Server).TrafficManager()
|
|
||||||
message.UplinkTotal, message.DownlinkTotal = trafficManager.Total()
|
|
||||||
message.ConnectionsIn = int32(trafficManager.ConnectionsLen())
|
|
||||||
}
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
|
||||||
var interval int64
|
|
||||||
err := binary.Read(conn, binary.BigEndian, &interval)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "read interval")
|
|
||||||
}
|
|
||||||
ticker := time.NewTicker(time.Duration(interval))
|
|
||||||
defer ticker.Stop()
|
|
||||||
ctx := connKeepAlive(conn)
|
|
||||||
status := s.readStatus()
|
|
||||||
uploadTotal := status.UplinkTotal
|
|
||||||
downloadTotal := status.DownlinkTotal
|
|
||||||
for {
|
|
||||||
err = binary.Write(conn, binary.BigEndian, status)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-ticker.C:
|
|
||||||
}
|
|
||||||
status = s.readStatus()
|
|
||||||
upload := status.UplinkTotal - uploadTotal
|
|
||||||
download := status.DownlinkTotal - downloadTotal
|
|
||||||
uploadTotal = status.UplinkTotal
|
|
||||||
downloadTotal = status.DownlinkTotal
|
|
||||||
status.Uplink = upload
|
|
||||||
status.Downlink = download
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandClient) handleStatusConn(conn net.Conn) {
|
|
||||||
for {
|
|
||||||
var message StatusMessage
|
|
||||||
err := binary.Read(conn, binary.BigEndian, &message)
|
|
||||||
if err != nil {
|
|
||||||
c.handler.Disconnected(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.handler.WriteStatus(&message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SystemProxyStatus struct {
|
|
||||||
Available bool
|
|
||||||
Enabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
|
|
||||||
conn, err := c.directConnectWithRetry()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandGetSystemProxyStatus))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var status SystemProxyStatus
|
|
||||||
err = binary.Read(conn, binary.BigEndian, &status.Available)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if status.Available {
|
|
||||||
err = binary.Read(conn, binary.BigEndian, &status.Enabled)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleGetSystemProxyStatus(conn net.Conn) error {
|
|
||||||
status := s.handler.GetSystemProxyStatus()
|
|
||||||
err := binary.Write(conn, binary.BigEndian, status.Available)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if status.Available {
|
|
||||||
err = binary.Write(conn, binary.BigEndian, status.Enabled)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandSetSystemProxyEnabled))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = binary.Write(conn, binary.BigEndian, isEnabled)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleSetSystemProxyEnabled(conn net.Conn) error {
|
|
||||||
var isEnabled bool
|
|
||||||
err := binary.Read(conn, binary.BigEndian, &isEnabled)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = s.handler.SetSystemProxyEnabled(isEnabled)
|
|
||||||
if err != nil {
|
|
||||||
return writeError(conn, err)
|
|
||||||
}
|
|
||||||
return writeError(conn, nil)
|
|
||||||
}
|
|
||||||
276
experimental/libbox/command_types.go
Normal file
276
experimental/libbox/command_types.go
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/daemon"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StatusMessage struct {
|
||||||
|
Memory int64
|
||||||
|
Goroutines int32
|
||||||
|
ConnectionsIn int32
|
||||||
|
ConnectionsOut int32
|
||||||
|
TrafficAvailable bool
|
||||||
|
Uplink int64
|
||||||
|
Downlink int64
|
||||||
|
UplinkTotal int64
|
||||||
|
DownlinkTotal int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemProxyStatus struct {
|
||||||
|
Available bool
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutboundGroup struct {
|
||||||
|
Tag string
|
||||||
|
Type string
|
||||||
|
Selectable bool
|
||||||
|
Selected string
|
||||||
|
IsExpand bool
|
||||||
|
ItemList []*OutboundGroupItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
|
||||||
|
return newIterator(g.ItemList)
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutboundGroupIterator interface {
|
||||||
|
Next() *OutboundGroup
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutboundGroupItem struct {
|
||||||
|
Tag string
|
||||||
|
Type string
|
||||||
|
URLTestTime int64
|
||||||
|
URLTestDelay int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutboundGroupItemIterator interface {
|
||||||
|
Next() *OutboundGroupItem
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnectionStateAll = iota
|
||||||
|
ConnectionStateActive
|
||||||
|
ConnectionStateClosed
|
||||||
|
)
|
||||||
|
|
||||||
|
type Connections struct {
|
||||||
|
input []Connection
|
||||||
|
filtered []Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) FilterState(state int32) {
|
||||||
|
c.filtered = c.filtered[:0]
|
||||||
|
switch state {
|
||||||
|
case ConnectionStateAll:
|
||||||
|
c.filtered = append(c.filtered, c.input...)
|
||||||
|
case ConnectionStateActive:
|
||||||
|
for _, connection := range c.input {
|
||||||
|
if connection.ClosedAt == 0 {
|
||||||
|
c.filtered = append(c.filtered, connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ConnectionStateClosed:
|
||||||
|
for _, connection := range c.input {
|
||||||
|
if connection.ClosedAt != 0 {
|
||||||
|
c.filtered = append(c.filtered, connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) SortByDate() {
|
||||||
|
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
||||||
|
if x.CreatedAt < y.CreatedAt {
|
||||||
|
return 1
|
||||||
|
} else if x.CreatedAt > y.CreatedAt {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return strings.Compare(y.ID, x.ID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) SortByTraffic() {
|
||||||
|
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
||||||
|
xTraffic := x.Uplink + x.Downlink
|
||||||
|
yTraffic := y.Uplink + y.Downlink
|
||||||
|
if xTraffic < yTraffic {
|
||||||
|
return 1
|
||||||
|
} else if xTraffic > yTraffic {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return strings.Compare(y.ID, x.ID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) SortByTrafficTotal() {
|
||||||
|
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
||||||
|
xTraffic := x.UplinkTotal + x.DownlinkTotal
|
||||||
|
yTraffic := y.UplinkTotal + y.DownlinkTotal
|
||||||
|
if xTraffic < yTraffic {
|
||||||
|
return 1
|
||||||
|
} else if xTraffic > yTraffic {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return strings.Compare(y.ID, x.ID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) Iterator() ConnectionIterator {
|
||||||
|
return newPtrIterator(c.filtered)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Connection struct {
|
||||||
|
ID string
|
||||||
|
Inbound string
|
||||||
|
InboundType string
|
||||||
|
IPVersion int32
|
||||||
|
Network string
|
||||||
|
Source string
|
||||||
|
Destination string
|
||||||
|
Domain string
|
||||||
|
Protocol string
|
||||||
|
User string
|
||||||
|
FromOutbound string
|
||||||
|
CreatedAt int64
|
||||||
|
ClosedAt int64
|
||||||
|
Uplink int64
|
||||||
|
Downlink int64
|
||||||
|
UplinkTotal int64
|
||||||
|
DownlinkTotal int64
|
||||||
|
Rule string
|
||||||
|
Outbound string
|
||||||
|
OutboundType string
|
||||||
|
ChainList []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) Chain() StringIterator {
|
||||||
|
return newIterator(c.ChainList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) DisplayDestination() string {
|
||||||
|
destination := M.ParseSocksaddr(c.Destination)
|
||||||
|
if destination.IsIP() && c.Domain != "" {
|
||||||
|
destination = M.Socksaddr{
|
||||||
|
Fqdn: c.Domain,
|
||||||
|
Port: destination.Port,
|
||||||
|
}
|
||||||
|
return destination.String()
|
||||||
|
}
|
||||||
|
return c.Destination
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionIterator interface {
|
||||||
|
Next() *Connection
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatusMessageFromGRPC(status *daemon.Status) *StatusMessage {
|
||||||
|
if status == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &StatusMessage{
|
||||||
|
Memory: int64(status.Memory),
|
||||||
|
Goroutines: status.Goroutines,
|
||||||
|
ConnectionsIn: status.ConnectionsIn,
|
||||||
|
ConnectionsOut: status.ConnectionsOut,
|
||||||
|
TrafficAvailable: status.TrafficAvailable,
|
||||||
|
Uplink: status.Uplink,
|
||||||
|
Downlink: status.Downlink,
|
||||||
|
UplinkTotal: status.UplinkTotal,
|
||||||
|
DownlinkTotal: status.DownlinkTotal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OutboundGroupIteratorFromGRPC(groups *daemon.Groups) OutboundGroupIterator {
|
||||||
|
if groups == nil || len(groups.Group) == 0 {
|
||||||
|
return newIterator([]*OutboundGroup{})
|
||||||
|
}
|
||||||
|
var libboxGroups []*OutboundGroup
|
||||||
|
for _, g := range groups.Group {
|
||||||
|
libboxGroup := &OutboundGroup{
|
||||||
|
Tag: g.Tag,
|
||||||
|
Type: g.Type,
|
||||||
|
Selectable: g.Selectable,
|
||||||
|
Selected: g.Selected,
|
||||||
|
IsExpand: g.IsExpand,
|
||||||
|
}
|
||||||
|
for _, item := range g.Items {
|
||||||
|
libboxGroup.ItemList = append(libboxGroup.ItemList, &OutboundGroupItem{
|
||||||
|
Tag: item.Tag,
|
||||||
|
Type: item.Type,
|
||||||
|
URLTestTime: item.UrlTestTime,
|
||||||
|
URLTestDelay: item.UrlTestDelay,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
libboxGroups = append(libboxGroups, libboxGroup)
|
||||||
|
}
|
||||||
|
return newIterator(libboxGroups)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConnectionFromGRPC(conn *daemon.Connection) Connection {
|
||||||
|
return Connection{
|
||||||
|
ID: conn.Id,
|
||||||
|
Inbound: conn.Inbound,
|
||||||
|
InboundType: conn.InboundType,
|
||||||
|
IPVersion: conn.IpVersion,
|
||||||
|
Network: conn.Network,
|
||||||
|
Source: conn.Source,
|
||||||
|
Destination: conn.Destination,
|
||||||
|
Domain: conn.Domain,
|
||||||
|
Protocol: conn.Protocol,
|
||||||
|
User: conn.User,
|
||||||
|
FromOutbound: conn.FromOutbound,
|
||||||
|
CreatedAt: conn.CreatedAt,
|
||||||
|
ClosedAt: conn.ClosedAt,
|
||||||
|
Uplink: conn.Uplink,
|
||||||
|
Downlink: conn.Downlink,
|
||||||
|
UplinkTotal: conn.UplinkTotal,
|
||||||
|
DownlinkTotal: conn.DownlinkTotal,
|
||||||
|
Rule: conn.Rule,
|
||||||
|
Outbound: conn.Outbound,
|
||||||
|
OutboundType: conn.OutboundType,
|
||||||
|
ChainList: conn.ChainList,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConnectionsFromGRPC(connections *daemon.Connections) []Connection {
|
||||||
|
if connections == nil || len(connections.Connections) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var libboxConnections []Connection
|
||||||
|
for _, conn := range connections.Connections {
|
||||||
|
libboxConnections = append(libboxConnections, ConnectionFromGRPC(conn))
|
||||||
|
}
|
||||||
|
return libboxConnections
|
||||||
|
}
|
||||||
|
|
||||||
|
func SystemProxyStatusFromGRPC(status *daemon.SystemProxyStatus) *SystemProxyStatus {
|
||||||
|
if status == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &SystemProxyStatus{
|
||||||
|
Available: status.Available,
|
||||||
|
Enabled: status.Enabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SystemProxyStatusToGRPC(status *SystemProxyStatus) *daemon.SystemProxyStatus {
|
||||||
|
if status == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &daemon.SystemProxyStatus{
|
||||||
|
Available: status.Available,
|
||||||
|
Enabled: status.Enabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
|
||||||
"github.com/sagernet/sing-box/protocol/group"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/batch"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
"github.com/sagernet/sing/service"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *CommandClient) URLTest(groupTag string) error {
|
|
||||||
conn, err := c.directConnect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandURLTest))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = varbin.Write(conn, binary.BigEndian, groupTag)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return readError(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
|
||||||
groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
serviceNow := s.service
|
|
||||||
if serviceNow == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
abstractOutboundGroup, isLoaded := serviceNow.instance.Outbound().Outbound(groupTag)
|
|
||||||
if !isLoaded {
|
|
||||||
return writeError(conn, E.New("outbound group not found: ", groupTag))
|
|
||||||
}
|
|
||||||
outboundGroup, isOutboundGroup := abstractOutboundGroup.(adapter.OutboundGroup)
|
|
||||||
if !isOutboundGroup {
|
|
||||||
return writeError(conn, E.New("outbound is not a group: ", groupTag))
|
|
||||||
}
|
|
||||||
urlTest, isURLTest := abstractOutboundGroup.(*group.URLTest)
|
|
||||||
if isURLTest {
|
|
||||||
go urlTest.CheckOutbounds()
|
|
||||||
} else {
|
|
||||||
historyStorage := service.PtrFromContext[urltest.HistoryStorage](serviceNow.ctx)
|
|
||||||
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
|
||||||
itOutbound, _ := serviceNow.instance.Outbound().Outbound(it)
|
|
||||||
return itOutbound
|
|
||||||
}), func(it adapter.Outbound) bool {
|
|
||||||
if it == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, isGroup := it.(adapter.OutboundGroup)
|
|
||||||
return !isGroup
|
|
||||||
})
|
|
||||||
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
|
|
||||||
for _, detour := range outbounds {
|
|
||||||
outboundToTest := detour
|
|
||||||
outboundTag := outboundToTest.Tag()
|
|
||||||
b.Go(outboundTag, func() (any, error) {
|
|
||||||
t, err := urltest.URLTest(serviceNow.ctx, "", outboundToTest)
|
|
||||||
if err != nil {
|
|
||||||
historyStorage.DeleteURLTestHistory(outboundTag)
|
|
||||||
} else {
|
|
||||||
historyStorage.StoreURLTestHistory(outboundTag, &adapter.URLTestHistory{
|
|
||||||
Time: time.Now(),
|
|
||||||
Delay: t,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return writeError(conn, nil)
|
|
||||||
}
|
|
||||||
@@ -3,19 +3,16 @@ package libbox
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"net/netip"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box"
|
box "github.com/sagernet/sing-box"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/dns"
|
"github.com/sagernet/sing-box/dns"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/include"
|
"github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
tun "github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
@@ -55,7 +52,7 @@ func CheckConfig(configContent string) error {
|
|||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
ctx = service.ContextWith[platform.Interface](ctx, (*platformInterfaceStub)(nil))
|
ctx = service.ContextWith[adapter.PlatformInterface](ctx, (*platformInterfaceStub)(nil))
|
||||||
instance, err := box.New(box.Options{
|
instance, err := box.New(box.Options{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Options: options,
|
Options: options,
|
||||||
@@ -80,7 +77,11 @@ func (s *platformInterfaceStub) AutoDetectInterfaceControl(fd int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
func (s *platformInterfaceStub) UsePlatformInterface() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) OpenInterface(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +93,11 @@ func (s *platformInterfaceStub) CreateDefaultInterfaceMonitor(logger logger.Logg
|
|||||||
return (*interfaceMonitorStub)(nil)
|
return (*interfaceMonitorStub)(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) Interfaces() ([]adapter.NetworkInterface, error) {
|
func (s *platformInterfaceStub) UsePlatformNetworkInterfaces() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) NetworkInterfaces() ([]adapter.NetworkInterface, error) {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,13 +105,17 @@ func (s *platformInterfaceStub) UnderNetworkExtension() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) IncludeAllNetworks() bool {
|
func (s *platformInterfaceStub) NetworkExtensionIncludeAllNetworks() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) ClearDNSCache() {
|
func (s *platformInterfaceStub) ClearDNSCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) RequestPermissionForWIFIState() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) UsePlatformWIFIMonitor() bool {
|
func (s *platformInterfaceStub) UsePlatformWIFIMonitor() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -119,11 +128,27 @@ func (s *platformInterfaceStub) SystemCertificates() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*process.Info, error) {
|
func (s *platformInterfaceStub) UsePlatformConnectionOwnerFinder() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) FindConnectionOwner(request *adapter.FindConnectionOwnerRequest) (*adapter.ConnectionOwner, error) {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
|
func (s *platformInterfaceStub) UsePlatformNotification() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) SendNotification(notification *adapter.Notification) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) UsePlatformLocalDNSTransport() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) LocalDNSTransport() dns.TransportConstructorFunc[option.LocalDNSServerOptions] {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,22 +77,27 @@ func NewHTTPClient() HTTPClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) ModernTLS() {
|
func (c *httpClient) ModernTLS() {
|
||||||
c.tls.MinVersion = tls.VersionTLS12
|
c.setTLSVersion(tls.VersionTLS12, 0, func(suite *tls.CipherSuite) bool { return true })
|
||||||
c.tls.CipherSuites = common.Map(tls.CipherSuites(), func(it *tls.CipherSuite) uint16 { return it.ID })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) RestrictedTLS() {
|
func (c *httpClient) RestrictedTLS() {
|
||||||
c.tls.MinVersion = tls.VersionTLS13
|
c.setTLSVersion(tls.VersionTLS13, 0, func(suite *tls.CipherSuite) bool {
|
||||||
c.tls.CipherSuites = common.Map(common.Filter(tls.CipherSuites(), func(it *tls.CipherSuite) bool {
|
return common.Contains(suite.SupportedVersions, uint16(tls.VersionTLS13))
|
||||||
return common.Contains(it.SupportedVersions, uint16(tls.VersionTLS13))
|
})
|
||||||
}), func(it *tls.CipherSuite) uint16 {
|
}
|
||||||
|
|
||||||
|
func (c *httpClient) setTLSVersion(minVersion, maxVersion uint16, filter func(*tls.CipherSuite) bool) {
|
||||||
|
c.tls.MinVersion = minVersion
|
||||||
|
if maxVersion != 0 {
|
||||||
|
c.tls.MaxVersion = maxVersion
|
||||||
|
}
|
||||||
|
c.tls.CipherSuites = common.Map(common.Filter(tls.CipherSuites(), filter), func(it *tls.CipherSuite) uint16 {
|
||||||
return it.ID
|
return it.ID
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) PinnedTLS12() {
|
func (c *httpClient) PinnedTLS12() {
|
||||||
c.tls.MinVersion = tls.VersionTLS12
|
c.setTLSVersion(tls.VersionTLS12, tls.VersionTLS12, func(suite *tls.CipherSuite) bool { return true })
|
||||||
c.tls.MaxVersion = tls.VersionTLS12
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) PinnedSHA256(sumHex string) {
|
func (c *httpClient) PinnedSHA256(sumHex string) {
|
||||||
@@ -178,9 +183,7 @@ func (r *httpRequest) SetUserAgent(userAgent string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *httpRequest) SetContent(content []byte) {
|
func (r *httpRequest) SetContent(content []byte) {
|
||||||
buffer := bytes.Buffer{}
|
r.request.Body = io.NopCloser(bytes.NewReader(content))
|
||||||
buffer.Write(content)
|
|
||||||
r.request.Body = io.NopCloser(bytes.NewReader(buffer.Bytes()))
|
|
||||||
r.request.ContentLength = int64(len(content))
|
r.request.ContentLength = int64(len(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ type StringIterator interface {
|
|||||||
Next() string
|
Next() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Int32Iterator interface {
|
||||||
|
Len() int32
|
||||||
|
HasNext() bool
|
||||||
|
Next() int32
|
||||||
|
}
|
||||||
|
|
||||||
var _ StringIterator = (*iterator[string])(nil)
|
var _ StringIterator = (*iterator[string])(nil)
|
||||||
|
|
||||||
type iterator[T any] struct {
|
type iterator[T any] struct {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sagernet/sing-tun"
|
tun "github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ type PlatformInterface interface {
|
|||||||
UsePlatformAutoDetectInterfaceControl() bool
|
UsePlatformAutoDetectInterfaceControl() bool
|
||||||
AutoDetectInterfaceControl(fd int32) error
|
AutoDetectInterfaceControl(fd int32) error
|
||||||
OpenTun(options TunOptions) (int32, error)
|
OpenTun(options TunOptions) (int32, error)
|
||||||
WriteLog(message string)
|
|
||||||
UseProcFS() bool
|
UseProcFS() bool
|
||||||
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||||
PackageNameByUid(uid int32) (string, error)
|
PackageNameByUid(uid int32) (string, error)
|
||||||
@@ -26,11 +25,6 @@ type PlatformInterface interface {
|
|||||||
SendNotification(notification *Notification) error
|
SendNotification(notification *Notification) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type TunInterface interface {
|
|
||||||
FileDescriptor() int32
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type InterfaceUpdateListener interface {
|
type InterfaceUpdateListener interface {
|
||||||
UpdateDefaultInterface(interfaceName string, interfaceIndex int32, isExpensive bool, isConstrained bool)
|
UpdateDefaultInterface(interfaceName string, interfaceIndex int32, isExpensive bool, isConstrained bool)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package platform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/process"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
"github.com/sagernet/sing-tun"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Interface interface {
|
|
||||||
Initialize(networkManager adapter.NetworkManager) error
|
|
||||||
UsePlatformAutoDetectInterfaceControl() bool
|
|
||||||
AutoDetectInterfaceControl(fd int) error
|
|
||||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
|
||||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
|
||||||
Interfaces() ([]adapter.NetworkInterface, error)
|
|
||||||
UnderNetworkExtension() bool
|
|
||||||
IncludeAllNetworks() bool
|
|
||||||
ClearDNSCache()
|
|
||||||
UsePlatformWIFIMonitor() bool
|
|
||||||
ReadWIFIState() adapter.WIFIState
|
|
||||||
SystemCertificates() []string
|
|
||||||
process.Searcher
|
|
||||||
SendNotification(notification *Notification) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Notification struct {
|
|
||||||
Identifier string
|
|
||||||
TypeName string
|
|
||||||
TypeID int32
|
|
||||||
Title string
|
|
||||||
Subtitle string
|
|
||||||
Body string
|
|
||||||
OpenURL string
|
|
||||||
}
|
|
||||||
@@ -3,121 +3,25 @@ package libbox
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
runtimeDebug "runtime/debug"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
"github.com/sagernet/sing-box/daemon"
|
||||||
|
"github.com/sagernet/sing-box/dns"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
tun "github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/service"
|
|
||||||
"github.com/sagernet/sing/service/pause"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BoxService struct {
|
var _ daemon.PlatformInterface = (*platformInterfaceWrapper)(nil)
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
urlTestHistoryStorage adapter.URLTestHistoryStorage
|
|
||||||
instance *box.Box
|
|
||||||
clashServer adapter.ClashServer
|
|
||||||
pauseManager pause.Manager
|
|
||||||
|
|
||||||
iOSPauseFields
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
|
||||||
ctx := BaseContext(platformInterface)
|
|
||||||
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
|
|
||||||
options, err := parseConfig(ctx, configContent)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
runtimeDebug.FreeOSMemory()
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
|
||||||
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
|
||||||
platformWrapper := &platformInterfaceWrapper{
|
|
||||||
iif: platformInterface,
|
|
||||||
useProcFS: platformInterface.UseProcFS(),
|
|
||||||
}
|
|
||||||
service.MustRegister[platform.Interface](ctx, platformWrapper)
|
|
||||||
instance, err := box.New(box.Options{
|
|
||||||
Context: ctx,
|
|
||||||
Options: options,
|
|
||||||
PlatformLogWriter: platformWrapper,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
cancel()
|
|
||||||
return nil, E.Cause(err, "create service")
|
|
||||||
}
|
|
||||||
runtimeDebug.FreeOSMemory()
|
|
||||||
return &BoxService{
|
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
instance: instance,
|
|
||||||
urlTestHistoryStorage: urlTestHistoryStorage,
|
|
||||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
|
||||||
clashServer: service.FromContext[adapter.ClashServer](ctx),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) Start() error {
|
|
||||||
if sFixAndroidStack {
|
|
||||||
var err error
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
err = s.instance.Start()
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
<-done
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
return s.instance.Start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) Close() error {
|
|
||||||
s.cancel()
|
|
||||||
s.urlTestHistoryStorage.Close()
|
|
||||||
var err error
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
err = s.instance.Close()
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return err
|
|
||||||
case <-time.After(C.FatalStopTimeout):
|
|
||||||
os.Exit(1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) NeedWIFIState() bool {
|
|
||||||
return s.instance.Network().NeedWIFIState()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ platform.Interface = (*platformInterfaceWrapper)(nil)
|
|
||||||
_ log.PlatformWriter = (*platformInterfaceWrapper)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type platformInterfaceWrapper struct {
|
type platformInterfaceWrapper struct {
|
||||||
iif PlatformInterface
|
iif PlatformInterface
|
||||||
@@ -143,7 +47,11 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error {
|
|||||||
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
func (w *platformInterfaceWrapper) UsePlatformInterface() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) OpenInterface(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||||
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
||||||
return nil, E.New("platform: unsupported uid options")
|
return nil, E.New("platform: unsupported uid options")
|
||||||
}
|
}
|
||||||
@@ -172,6 +80,10 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
|
|||||||
return tun.New(*options)
|
return tun.New(*options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
|
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
|
||||||
return &platformDefaultInterfaceMonitor{
|
return &platformDefaultInterfaceMonitor{
|
||||||
platformInterfaceWrapper: w,
|
platformInterfaceWrapper: w,
|
||||||
@@ -179,7 +91,11 @@ func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) Interfaces() ([]adapter.NetworkInterface, error) {
|
func (w *platformInterfaceWrapper) UsePlatformNetworkInterfaces() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) NetworkInterfaces() ([]adapter.NetworkInterface, error) {
|
||||||
interfaceIterator, err := w.iif.GetInterfaces()
|
interfaceIterator, err := w.iif.GetInterfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -216,7 +132,7 @@ func (w *platformInterfaceWrapper) UnderNetworkExtension() bool {
|
|||||||
return w.iif.UnderNetworkExtension()
|
return w.iif.UnderNetworkExtension()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) IncludeAllNetworks() bool {
|
func (w *platformInterfaceWrapper) NetworkExtensionIncludeAllNetworks() bool {
|
||||||
return w.iif.IncludeAllNetworks()
|
return w.iif.IncludeAllNetworks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +140,10 @@ func (w *platformInterfaceWrapper) ClearDNSCache() {
|
|||||||
w.iif.ClearDNSCache()
|
w.iif.ClearDNSCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) RequestPermissionForWIFIState() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) UsePlatformWIFIMonitor() bool {
|
func (w *platformInterfaceWrapper) UsePlatformWIFIMonitor() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -240,41 +160,70 @@ func (w *platformInterfaceWrapper) SystemCertificates() []string {
|
|||||||
return iteratorToArray[string](w.iif.SystemCertificates())
|
return iteratorToArray[string](w.iif.SystemCertificates())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*process.Info, error) {
|
func (w *platformInterfaceWrapper) UsePlatformConnectionOwnerFinder() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConnectionOwnerRequest) (*adapter.ConnectionOwner, error) {
|
||||||
var uid int32
|
var uid int32
|
||||||
if w.useProcFS {
|
if w.useProcFS {
|
||||||
|
var source netip.AddrPort
|
||||||
|
var destination netip.AddrPort
|
||||||
|
sourceAddr, _ := netip.ParseAddr(request.SourceAddress)
|
||||||
|
source = netip.AddrPortFrom(sourceAddr, uint16(request.SourcePort))
|
||||||
|
destAddr, _ := netip.ParseAddr(request.DestinationAddress)
|
||||||
|
destination = netip.AddrPortFrom(destAddr, uint16(request.DestinationPort))
|
||||||
|
|
||||||
|
var network string
|
||||||
|
switch request.IpProtocol {
|
||||||
|
case int32(syscall.IPPROTO_TCP):
|
||||||
|
network = "tcp"
|
||||||
|
case int32(syscall.IPPROTO_UDP):
|
||||||
|
network = "udp"
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown protocol: ", request.IpProtocol)
|
||||||
|
}
|
||||||
|
|
||||||
uid = procfs.ResolveSocketByProcSearch(network, source, destination)
|
uid = procfs.ResolveSocketByProcSearch(network, source, destination)
|
||||||
if uid == -1 {
|
if uid == -1 {
|
||||||
return nil, E.New("procfs: not found")
|
return nil, E.New("procfs: not found")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var ipProtocol int32
|
|
||||||
switch N.NetworkName(network) {
|
|
||||||
case N.NetworkTCP:
|
|
||||||
ipProtocol = syscall.IPPROTO_TCP
|
|
||||||
case N.NetworkUDP:
|
|
||||||
ipProtocol = syscall.IPPROTO_UDP
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown network: ", network)
|
|
||||||
}
|
|
||||||
var err error
|
var err error
|
||||||
uid, err = w.iif.FindConnectionOwner(ipProtocol, source.Addr().String(), int32(source.Port()), destination.Addr().String(), int32(destination.Port()))
|
uid, err = w.iif.FindConnectionOwner(request.IpProtocol, request.SourceAddress, request.SourcePort, request.DestinationAddress, request.DestinationPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageName, _ := w.iif.PackageNameByUid(uid)
|
packageName, _ := w.iif.PackageNameByUid(uid)
|
||||||
return &process.Info{UserId: uid, PackageName: packageName}, nil
|
return &adapter.ConnectionOwner{
|
||||||
|
UserId: uid,
|
||||||
|
AndroidPackageName: packageName,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) DisableColors() bool {
|
func (w *platformInterfaceWrapper) DisableColors() bool {
|
||||||
return runtime.GOOS != "android"
|
return runtime.GOOS != "android"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) WriteMessage(level log.Level, message string) {
|
func (w *platformInterfaceWrapper) UsePlatformNotification() bool {
|
||||||
w.iif.WriteLog(message)
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) SendNotification(notification *platform.Notification) error {
|
func (w *platformInterfaceWrapper) SendNotification(notification *adapter.Notification) error {
|
||||||
return w.iif.SendNotification((*Notification)(notification))
|
return w.iif.SendNotification((*Notification)(notification))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) UsePlatformLocalDNSTransport() bool {
|
||||||
|
return C.IsAndroid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) LocalDNSTransport() dns.TransportConstructorFunc[option.LocalDNSServerOptions] {
|
||||||
|
localTransport := w.iif.LocalDNSTransport()
|
||||||
|
if localTransport == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return func(ctx context.Context, logger log.ContextLogger, tag string, options option.LocalDNSServerOptions) (adapter.DNSTransport, error) {
|
||||||
|
return newPlatformTransport(localTransport, tag, options), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
)
|
|
||||||
|
|
||||||
type iOSPauseFields struct {
|
|
||||||
endPauseTimer *time.Timer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) Pause() {
|
|
||||||
s.pauseManager.DevicePause()
|
|
||||||
if C.IsIos {
|
|
||||||
if s.endPauseTimer == nil {
|
|
||||||
s.endPauseTimer = time.AfterFunc(time.Minute, s.pauseManager.DeviceWake)
|
|
||||||
} else {
|
|
||||||
s.endPauseTimer.Reset(time.Minute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) Wake() {
|
|
||||||
if !C.IsIos {
|
|
||||||
s.pauseManager.DeviceWake()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) ResetNetwork() {
|
|
||||||
s.instance.Router().ResetNetwork()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) UpdateWIFIState() {
|
|
||||||
s.instance.Network().UpdateWIFIState()
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,7 @@ package libbox
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@@ -19,50 +17,48 @@ var (
|
|||||||
sTempPath string
|
sTempPath string
|
||||||
sUserID int
|
sUserID int
|
||||||
sGroupID int
|
sGroupID int
|
||||||
sTVOS bool
|
|
||||||
sFixAndroidStack bool
|
sFixAndroidStack bool
|
||||||
|
sCommandServerListenPort uint16
|
||||||
|
sCommandServerSecret string
|
||||||
|
sLogMaxLines int
|
||||||
|
sDebug bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
debug.SetPanicOnFault(true)
|
debug.SetPanicOnFault(true)
|
||||||
|
debug.SetTraceback("all")
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetupOptions struct {
|
type SetupOptions struct {
|
||||||
BasePath string
|
BasePath string
|
||||||
WorkingPath string
|
WorkingPath string
|
||||||
TempPath string
|
TempPath string
|
||||||
Username string
|
|
||||||
IsTVOS bool
|
|
||||||
FixAndroidStack bool
|
FixAndroidStack bool
|
||||||
|
CommandServerListenPort int32
|
||||||
|
CommandServerSecret string
|
||||||
|
LogMaxLines int
|
||||||
|
Debug bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Setup(options *SetupOptions) error {
|
func Setup(options *SetupOptions) error {
|
||||||
sBasePath = options.BasePath
|
sBasePath = options.BasePath
|
||||||
sWorkingPath = options.WorkingPath
|
sWorkingPath = options.WorkingPath
|
||||||
sTempPath = options.TempPath
|
sTempPath = options.TempPath
|
||||||
if options.Username != "" {
|
|
||||||
sUser, err := user.Lookup(options.Username)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sUserID, _ = strconv.Atoi(sUser.Uid)
|
|
||||||
sGroupID, _ = strconv.Atoi(sUser.Gid)
|
|
||||||
} else {
|
|
||||||
sUserID = os.Getuid()
|
sUserID = os.Getuid()
|
||||||
sGroupID = os.Getgid()
|
sGroupID = os.Getgid()
|
||||||
}
|
|
||||||
sTVOS = options.IsTVOS
|
|
||||||
|
|
||||||
// TODO: remove after fixed
|
// TODO: remove after fixed
|
||||||
// https://github.com/golang/go/issues/68760
|
// https://github.com/golang/go/issues/68760
|
||||||
sFixAndroidStack = options.FixAndroidStack
|
sFixAndroidStack = options.FixAndroidStack
|
||||||
|
|
||||||
|
sCommandServerListenPort = uint16(options.CommandServerListenPort)
|
||||||
|
sCommandServerSecret = options.CommandServerSecret
|
||||||
|
sLogMaxLines = options.LogMaxLines
|
||||||
|
sDebug = options.Debug
|
||||||
|
|
||||||
os.MkdirAll(sWorkingPath, 0o777)
|
os.MkdirAll(sWorkingPath, 0o777)
|
||||||
os.MkdirAll(sTempPath, 0o777)
|
os.MkdirAll(sTempPath, 0o777)
|
||||||
if options.Username != "" {
|
|
||||||
os.Chown(sWorkingPath, sUserID, sGroupID)
|
|
||||||
os.Chown(sTempPath, sUserID, sGroupID)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
tun "github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|||||||
44
go.mod
44
go.mod
@@ -3,44 +3,40 @@ module github.com/sagernet/sing-box
|
|||||||
go 1.24.7
|
go 1.24.7
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/anthropics/anthropic-sdk-go v1.14.0
|
|
||||||
github.com/anytls/sing-anytls v0.0.11
|
github.com/anytls/sing-anytls v0.0.11
|
||||||
github.com/caddyserver/certmagic v0.23.0
|
github.com/caddyserver/certmagic v0.23.0
|
||||||
github.com/coder/websocket v1.8.13
|
github.com/coder/websocket v1.8.13
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/database64128/tfo-go/v2 v2.3.1
|
github.com/database64128/tfo-go/v2 v2.2.2
|
||||||
github.com/go-chi/chi/v5 v5.2.2
|
github.com/go-chi/chi/v5 v5.2.2
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466
|
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466
|
||||||
github.com/gofrs/uuid/v5 v5.3.2
|
github.com/gofrs/uuid/v5 v5.3.2
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f
|
github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f
|
||||||
github.com/keybase/go-keychain v0.0.1
|
|
||||||
github.com/libdns/alidns v1.0.5-libdns.v1.beta1
|
github.com/libdns/alidns v1.0.5-libdns.v1.beta1
|
||||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6
|
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/metacubex/utls v1.8.3
|
github.com/metacubex/utls v1.8.0
|
||||||
github.com/mholt/acmez/v3 v3.1.2
|
github.com/mholt/acmez/v3 v3.1.2
|
||||||
github.com/miekg/dns v1.1.67
|
github.com/miekg/dns v1.1.67
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1
|
github.com/oschwald/maxminddb-golang v1.13.1
|
||||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/cronet-go v0.0.0-20251209141152-67502c396ef4
|
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251209141601-d8f29fa5b269
|
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.8
|
github.com/sagernet/gomobile v0.1.8
|
||||||
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
|
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
|
||||||
github.com/sagernet/quic-go v0.57.1-sing-box-mod.1
|
github.com/sagernet/quic-go v0.54.0-sing-box-mod.3
|
||||||
github.com/sagernet/sing v0.8.0-beta.6.0.20251207063731-56fd482ce1c6
|
github.com/sagernet/sing v0.8.0-beta.5
|
||||||
github.com/sagernet/sing-mux v0.3.3
|
github.com/sagernet/sing-mux v0.3.3
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.5
|
github.com/sagernet/sing-quic v0.6.0-beta.3
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.11
|
github.com/sagernet/sing-tun v0.8.0-beta.10
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
||||||
github.com/sagernet/smux v1.5.34-mod.2
|
github.com/sagernet/smux v1.5.34-mod.2
|
||||||
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.4
|
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.3
|
||||||
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288
|
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
@@ -70,7 +66,7 @@ require (
|
|||||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
|
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
|
||||||
github.com/database64128/netx-go v0.1.1 // indirect
|
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||||
@@ -104,26 +100,8 @@ require (
|
|||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
||||||
github.com/quic-go/qpack v0.6.0 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/safchain/ethtool v0.3.0 // indirect
|
github.com/safchain/ethtool v0.3.0 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_386 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251209141152-67502c396ef4 // indirect
|
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
@@ -135,10 +113,6 @@ require (
|
|||||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
|
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
|
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da // indirect
|
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da // indirect
|
||||||
github.com/tidwall/gjson v1.18.0 // indirect
|
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
|
||||||
github.com/tidwall/sjson v1.2.5 // indirect
|
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||||
|
|||||||
90
go.sum
90
go.sum
@@ -8,8 +8,6 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V
|
|||||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/anthropics/anthropic-sdk-go v1.14.0 h1:EzNQvnZlaDHe2UPkoUySDz3ixRgNbwKdH8KtFpv7pi4=
|
|
||||||
github.com/anthropics/anthropic-sdk-go v1.14.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
|
|
||||||
github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc=
|
github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc=
|
||||||
github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
|
github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
|
||||||
github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
|
github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
|
||||||
@@ -27,10 +25,10 @@ github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||||
github.com/database64128/netx-go v0.1.1 h1:dT5LG7Gs7zFZBthFBbzWE6K8wAHjSNAaK7wCYZT7NzM=
|
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a h1:t4SDi0pmNkryzKdM4QF3o5vqSP4GRjeZD/6j3nyxNP0=
|
||||||
github.com/database64128/netx-go v0.1.1/go.mod h1:LNlYVipaYkQArRFDNNJ02VkNV+My9A5XR/IGS7sIBQc=
|
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a/go.mod h1:7K2NQKbabB5mBl41vF6YayYl5g7YpDwc4dQ5iMpP3Lg=
|
||||||
github.com/database64128/tfo-go/v2 v2.3.1 h1:EGE+ELd5/AQ0X6YBlQ9RgKs8+kciNhgN3d8lRvfEJQw=
|
github.com/database64128/tfo-go/v2 v2.2.2 h1:BxynF4qGF5ct3DpPLEG62uyJZ3LQhqaf0Ken+kyy7PM=
|
||||||
github.com/database64128/tfo-go/v2 v2.3.1/go.mod h1:k9wcpg/8i5zenspBkc9jUEYehpZZccBnCElzOJB++bU=
|
github.com/database64128/tfo-go/v2 v2.2.2/go.mod h1:2IW8jppdBwdVMjA08uEyMNnqiAHKUlqAA+J8NrsfktY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
@@ -97,8 +95,6 @@ github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f/go.mod h1:zhFlBe
|
|||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
||||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
|
||||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||||
@@ -120,8 +116,8 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ
|
|||||||
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
|
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
|
||||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||||
github.com/metacubex/utls v1.8.3 h1:0m/yCxm3SK6kWve2lKiFb1pue1wHitJ8sQQD4Ikqde4=
|
github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac=
|
||||||
github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ=
|
||||||
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
||||||
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||||
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
|
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
|
||||||
@@ -139,8 +135,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
|||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
|
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
|
||||||
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
|
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
|
||||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
||||||
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
||||||
@@ -150,46 +146,6 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
|
|||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||||
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||||
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
||||||
github.com/sagernet/cronet-go v0.0.0-20251209141152-67502c396ef4 h1:BfitgSppBdvn5gqwRLNA5Nhu67YvZxQKM9n3b1j1dgI=
|
|
||||||
github.com/sagernet/cronet-go v0.0.0-20251209141152-67502c396ef4/go.mod h1:l5IZJLEWpDGJbrF0qBHgxAVBPsAxKOLa1BYDh6B2sdI=
|
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251209141601-d8f29fa5b269 h1:dA79nNuqhUIGcw7DP3ifRXtJq39rE/UWZPfmQ6QS40w=
|
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251209141601-d8f29fa5b269/go.mod h1:iLjzAv2hALBTxeC10i99ludp7jU6U3dw/yXbn0x3Ek8=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251209141152-67502c396ef4 h1:eN1EtBxDDOvWW7Q0+a1UzBcnYs6u2EqgHgLYOPFwHSE=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251209141152-67502c396ef4/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251209141152-67502c396ef4 h1:mQSHDat1i4Q5/+CvvRGTAAvT2vaYtci671/v5nC4FIM=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251209141152-67502c396ef4 h1:TLVfFFNvGEPBZzFUecr1r32A0hsS6oeiEQWVQlDys+g=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251209141152-67502c396ef4/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251209141152-67502c396ef4 h1:fCk6J6Shm+47s21JIpZuKLA0GD29HsGmUOCB3QkM7wc=
|
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251209141152-67502c396ef4 h1:lFaYkrltdVGtHoTtcTGCNP0lwGnwcCvZxJOCOyMtVcg=
|
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251209141152-67502c396ef4 h1:AdaoXuHTt5PnXUyDR/jcz2lBoA0osvOvORH9zUkTYtw=
|
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251209141152-67502c396ef4 h1:d5qnUEz2E3GBiZTf0FcUx6zH70rxqHN6rKifhv3ww0g=
|
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251209141152-67502c396ef4 h1:SBgtdbs/VPOlKZmh+ieSvg1FAhqZgah27u9U1TNqzLk=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251209141152-67502c396ef4/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251209141152-67502c396ef4 h1:N4hzsQK1RMT/1cZdlVXgWJLnThM1SqSd8xKqMGVe2BM=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251209141152-67502c396ef4/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251209141152-67502c396ef4 h1:nnms0N+jFr78znmeTXNEZT3oa9M7QNJ3BZyKlh6xxPU=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251209141152-67502c396ef4 h1:tQsiwZZO13yXPVG10FRKRvABMzVMta3HTMEyrKGVitg=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251209141152-67502c396ef4/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251209141152-67502c396ef4 h1:/FAmfFm+22TIYAabODI6INOF6XgVoKiJrvo4JJk1QHI=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251209141152-67502c396ef4/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251209141152-67502c396ef4 h1:c/mtA2g+ScCBGBfa26hakxAVXurD4nhFW/RI0yb4KVM=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251209141152-67502c396ef4 h1:iRq0SWRDfCf1sKN9R4+5EHWYak0Zfla5li0jhRwBCBA=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251209141152-67502c396ef4/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251209141152-67502c396ef4 h1:GAQrA1S/cSNxmEeIcQU3jLVbPAU0GlJAM2iDcsKpphA=
|
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251209141152-67502c396ef4/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_386 v0.0.0-20251209141152-67502c396ef4 h1:Wix35ah7sy4oLTp06kjIqIX0CvtL2o05LXgaZLdDmzg=
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_386 v0.0.0-20251209141152-67502c396ef4/go.mod h1:rnS7D+ULJX2PrP0Cy+05GS0mRZ2PP6+gVSroZKt8fjk=
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251209141152-67502c396ef4 h1:RA5r4IkF/Zvlq+4CwuWqZkb7xt3Fpj2HYUkzcSae1fQ=
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251209141152-67502c396ef4 h1:7lD3/vGXsmyfOuZN17yZe2u/UFVmOJpb9J7vz/cD0b8=
|
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251209141152-67502c396ef4/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
|
|
||||||
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
||||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||||
github.com/sagernet/gomobile v0.1.8 h1:vXgoN0pjsMONAaYCTdsKBX2T1kxuS7sbT/mZ7PElGoo=
|
github.com/sagernet/gomobile v0.1.8 h1:vXgoN0pjsMONAaYCTdsKBX2T1kxuS7sbT/mZ7PElGoo=
|
||||||
@@ -200,29 +156,29 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
|
|||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||||
github.com/sagernet/quic-go v0.57.1-sing-box-mod.1 h1:6fhKbfA0b7L1CVekayV1g87uJFtMXFE0rFXR48SRrWI=
|
github.com/sagernet/quic-go v0.54.0-sing-box-mod.3 h1:12pJN/zdpRltLG8l8JA65QYy/a+Mz938yAN3ZQUinbo=
|
||||||
github.com/sagernet/quic-go v0.57.1-sing-box-mod.1/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
github.com/sagernet/quic-go v0.54.0-sing-box-mod.3/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
|
||||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing v0.8.0-beta.6.0.20251207063731-56fd482ce1c6 h1:EYaDzllFzNYnzQ9xH/ieSAXct4wQ8pD45kgNMo7RPZc=
|
github.com/sagernet/sing v0.8.0-beta.5 h1:Cm4CnLQGNyG5Jl1U9pKWAjFUcbjchGGqn1xeXzfI5kw=
|
||||||
github.com/sagernet/sing v0.8.0-beta.6.0.20251207063731-56fd482ce1c6/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.8.0-beta.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
||||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.5 h1:kZfRLmsPxAgl0usZUgomDurLn7ZZ26lJWIpGow9ZWR4=
|
github.com/sagernet/sing-quic v0.6.0-beta.3 h1:Z2vt49f9vNtHc9BbF9foI859n4+NAOV3gBeB1LuzL1Q=
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.5/go.mod h1:9D9GANrK33NjWCe1VkU5L5+8MxU39WrduBSmHuHz8GA=
|
github.com/sagernet/sing-quic v0.6.0-beta.3/go.mod h1:2/swrSS6wG6MyQA5Blq31VEWitHgBju+yZE8cPK1J5I=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.11 h1:xVi8VcVkvz2o+3v1PLv5MOkFpiVCwjLjucVlmigDi5c=
|
github.com/sagernet/sing-tun v0.8.0-beta.10 h1:sHqSXTvzKPDF67AwZdoBV5FA91tFdWGfA1AbenIbpA4=
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.11/go.mod h1:eWETzl4AwaxGKiZTpDIDVJLTBz9cfIdoZwaZY1jlSjg=
|
github.com/sagernet/sing-tun v0.8.0-beta.10/go.mod h1:eWETzl4AwaxGKiZTpDIDVJLTBz9cfIdoZwaZY1jlSjg=
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
||||||
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.4 h1:Ceg+9Ug+qAFgEchGodlHmMOY2h7KktQQDAyuoIsPbos=
|
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.3 h1:OGoHEw76F3F4keIGcOwB/5U+P1N3i+hLlgC7rvSnub0=
|
||||||
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.4/go.mod h1:YdN/avjce8sqPFLT9E1uEh8gPewNSnC41U4ZhBJ+ACw=
|
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.3/go.mod h1:YdN/avjce8sqPFLT9E1uEh8gPewNSnC41U4ZhBJ+ACw=
|
||||||
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288 h1:E2tZFeg9mGYGQ7E7BbxMv1cU35HxwgRm6tPKI2Pp7DA=
|
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288 h1:E2tZFeg9mGYGQ7E7BbxMv1cU35HxwgRm6tPKI2Pp7DA=
|
||||||
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288/go.mod h1:WUxgxUDZoCF2sxVmW+STSxatP02Qn3FcafTiI2BLtE0=
|
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288/go.mod h1:WUxgxUDZoCF2sxVmW+STSxatP02Qn3FcafTiI2BLtE0=
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||||
@@ -260,16 +216,6 @@ github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da h1:jVRUZPRs
|
|||||||
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
|
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
|
||||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
|
||||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
|
||||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
|
||||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
//go:build with_ccm && (!darwin || cgo)
|
|
||||||
|
|
||||||
package include
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing-box/adapter/service"
|
|
||||||
"github.com/sagernet/sing-box/service/ccm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func registerCCMService(registry *service.Registry) {
|
|
||||||
ccm.RegisterService(registry)
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
//go:build !with_ccm
|
|
||||||
|
|
||||||
package include
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/adapter/service"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func registerCCMService(registry *service.Registry) {
|
|
||||||
service.Register[option.CCMServiceOptions](registry, C.TypeCCM, func(ctx context.Context, logger log.ContextLogger, tag string, options option.CCMServiceOptions) (adapter.Service, error) {
|
|
||||||
return nil, E.New(`CCM is not included in this build, rebuild with -tags with_CCM`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user