mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
147 Commits
dev-ts-rel
...
renovate/d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d63c128a6e | ||
|
|
58ccf82e0b | ||
|
|
ceab244329 | ||
|
|
58fcdceca2 | ||
|
|
98af3c0ad6 | ||
|
|
172a9d5e4e | ||
|
|
aba8346bd6 | ||
|
|
d8e269e0ac | ||
|
|
c45ea8dfac | ||
|
|
a2d313c59b | ||
|
|
15722b06dd | ||
|
|
d230dae0a5 | ||
|
|
e11dbf3a8e | ||
|
|
baa9f29f0d | ||
|
|
55b6e7dbfe | ||
|
|
a05e05a47c | ||
|
|
c1dc6cb0fb | ||
|
|
432fe1b3c9 | ||
|
|
8dd8897fd8 | ||
|
|
ff58edb1c1 | ||
|
|
79bab39502 | ||
|
|
a4d5d59901 | ||
|
|
1af14a0237 | ||
|
|
944a9986d9 | ||
|
|
60a1e4c866 | ||
|
|
5d67c131fa | ||
|
|
b9cc87d35a | ||
|
|
490d501257 | ||
|
|
725e4adc46 | ||
|
|
4a14d39cad | ||
|
|
8ec58c96f5 | ||
|
|
e8450b2e61 | ||
|
|
30c3855e4b | ||
|
|
ccf90aee8a | ||
|
|
e6c03fd448 | ||
|
|
e0f1cdf464 | ||
|
|
8d88c6532f | ||
|
|
3890bd2be7 | ||
|
|
6cd1eb9b94 | ||
|
|
f196b7a583 | ||
|
|
bd9935eebb | ||
|
|
0e0e838ff5 | ||
|
|
0caebd3171 | ||
|
|
7d2944eba9 | ||
|
|
a5db2feb5e | ||
|
|
708ceb3d29 | ||
|
|
157e33f2a4 | ||
|
|
1d4fb83313 | ||
|
|
85f5f6cebb | ||
|
|
6a750f4522 | ||
|
|
46c2cc37c3 | ||
|
|
aa8dd6e44f | ||
|
|
4e94a64dcc | ||
|
|
494990f914 | ||
|
|
95ccb837d3 | ||
|
|
24b33a43fc | ||
|
|
8ae16aa452 | ||
|
|
bf4a9edc89 | ||
|
|
78b4eac974 | ||
|
|
a34868468f | ||
|
|
e392c70b6f | ||
|
|
511d1bb3fa | ||
|
|
4273ffa77e | ||
|
|
f5ccf746ea | ||
|
|
b2d90b7d86 | ||
|
|
e0a78fde07 | ||
|
|
203f4134b0 | ||
|
|
c2b697a778 | ||
|
|
ddec2ab282 | ||
|
|
35ff7d1fb4 | ||
|
|
cba18635c8 | ||
|
|
0d8c7a9c5d | ||
|
|
faff3174a3 | ||
|
|
2fc1b672cc | ||
|
|
143983b585 | ||
|
|
4afdf4153a | ||
|
|
750dc9c3e0 | ||
|
|
48b7adde7d | ||
|
|
0585f6d065 | ||
|
|
8101a7b0bd | ||
|
|
e8620587dd | ||
|
|
a89680fa2d | ||
|
|
b919039c43 | ||
|
|
9b0960bb5a | ||
|
|
ad7b982242 | ||
|
|
7e68013b05 | ||
|
|
ac427b98f4 | ||
|
|
a5fb467db2 | ||
|
|
a930356b04 | ||
|
|
5bc0dfa9dd | ||
|
|
743b460e51 | ||
|
|
8d8ca282a1 | ||
|
|
cd56eaaba2 | ||
|
|
e92938364d | ||
|
|
1c4614318e | ||
|
|
0f5cda4169 | ||
|
|
d87c9fd242 | ||
|
|
fce21607bd | ||
|
|
3dc285be8c | ||
|
|
79bbce3db3 | ||
|
|
dfd95b2615 | ||
|
|
ab0869c972 | ||
|
|
9ac0539ffd | ||
|
|
cb4deb0c20 | ||
|
|
6b90b61358 | ||
|
|
ed1ee4c3a4 | ||
|
|
7f3ea8dbd8 | ||
|
|
12b055989b | ||
|
|
49056b5060 | ||
|
|
c530995832 | ||
|
|
60d81a73d9 | ||
|
|
e9c46cc359 | ||
|
|
9110851af3 | ||
|
|
107f92381b | ||
|
|
f84129ca79 | ||
|
|
44fafcef73 | ||
|
|
a5e09fcd43 | ||
|
|
387b42c9c2 | ||
|
|
044eb728cb | ||
|
|
2be8a45f14 | ||
|
|
1336987756 | ||
|
|
e3473d3de0 | ||
|
|
bba92146b1 | ||
|
|
48f84b31d6 | ||
|
|
1c846df903 | ||
|
|
0bd98a300f | ||
|
|
87eaf3ce6e | ||
|
|
239e6ec701 | ||
|
|
5be1887f92 | ||
|
|
65264afdf9 | ||
|
|
fecdbf20de | ||
|
|
1f03080540 | ||
|
|
737162e75a | ||
|
|
51ce402dbb | ||
|
|
8b404b5a4c | ||
|
|
3ce94d50dd | ||
|
|
29d56fca9c | ||
|
|
ab18010ee1 | ||
|
|
e69c202c79 | ||
|
|
0a812f2a46 | ||
|
|
fffe9fc566 | ||
|
|
6fdf27a701 | ||
|
|
7fa7d4f0a9 | ||
|
|
f511ebc1d4 | ||
|
|
84bbdc2eba | ||
|
|
568612fc70 | ||
|
|
d78828fd81 |
@@ -14,6 +14,7 @@
|
|||||||
--depends kmod-inet-diag
|
--depends kmod-inet-diag
|
||||||
--depends kmod-tun
|
--depends kmod-tun
|
||||||
--depends firewall4
|
--depends firewall4
|
||||||
|
--depends kmod-nft-queue
|
||||||
|
|
||||||
--before-remove release/config/openwrt.prerm
|
--before-remove release/config/openwrt.prerm
|
||||||
|
|
||||||
|
|||||||
2
.github/CRONET_GO_VERSION
vendored
2
.github/CRONET_GO_VERSION
vendored
@@ -1 +1 @@
|
|||||||
1cc61ad20399081362ccbc18d650432d1a6d42ec
|
dc1cda1fe28740ba069934ab62aeb8ef85388332
|
||||||
|
|||||||
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.5"
|
VERSION="1.25.7"
|
||||||
|
|
||||||
mkdir -p $HOME/go
|
mkdir -p $HOME/go
|
||||||
cd $HOME/go
|
cd $HOME/go
|
||||||
|
|||||||
2
.github/update_cronet.sh
vendored
2
.github/update_cronet.sh
vendored
@@ -10,4 +10,4 @@ git -C $PROJECTS/cronet-go fetch origin go
|
|||||||
go get -x github.com/sagernet/cronet-go/all@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
go get -x github.com/sagernet/cronet-go/all@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
||||||
go get -x github.com/sagernet/cronet-go@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
go get -x github.com/sagernet/cronet-go@$(git -C $PROJECTS/cronet-go rev-parse origin/go)
|
||||||
go mod tidy
|
go mod tidy
|
||||||
git -C $PROJECTS/cronet-go rev-parse origin/HEAD > "$SCRIPT_DIR/CRONET_GO_VERSION"
|
git -C $PROJECTS/cronet-go rev-parse origin/go > "$SCRIPT_DIR/CRONET_GO_VERSION"
|
||||||
|
|||||||
13
.github/update_cronet_dev.sh
vendored
Executable file
13
.github/update_cronet_dev.sh
vendored
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e -o pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(dirname "$0")
|
||||||
|
PROJECTS=$SCRIPT_DIR/../..
|
||||||
|
|
||||||
|
git -C $PROJECTS/cronet-go fetch origin dev
|
||||||
|
git -C $PROJECTS/cronet-go fetch origin go_dev
|
||||||
|
go get -x github.com/sagernet/cronet-go/all@$(git -C $PROJECTS/cronet-go rev-parse origin/go_dev)
|
||||||
|
go get -x github.com/sagernet/cronet-go@$(git -C $PROJECTS/cronet-go rev-parse origin/go_dev)
|
||||||
|
go mod tidy
|
||||||
|
git -C $PROJECTS/cronet-go rev-parse origin/dev > "$SCRIPT_DIR/CRONET_GO_VERSION"
|
||||||
40
.github/workflows/build.yml
vendored
40
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -69,19 +69,19 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- { os: linux, arch: amd64, variant: purego, naive: true, openwrt: "x86_64" }
|
- { os: linux, arch: amd64, variant: purego, naive: true }
|
||||||
- { os: linux, arch: amd64, variant: glibc, naive: true }
|
- { os: linux, arch: amd64, variant: glibc, naive: true }
|
||||||
- { os: linux, arch: amd64, variant: musl, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
- { os: linux, arch: amd64, variant: musl, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
||||||
|
|
||||||
- { os: linux, arch: arm64, variant: purego, naive: true, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
- { os: linux, arch: arm64, variant: purego, naive: true }
|
||||||
- { os: linux, arch: arm64, variant: glibc, naive: true }
|
- { os: linux, arch: arm64, variant: glibc, naive: true }
|
||||||
- { os: linux, arch: arm64, variant: musl, naive: true, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
- { os: linux, arch: arm64, variant: musl, naive: true, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
||||||
|
|
||||||
- { os: linux, arch: "386", go386: sse2, openwrt: "i386_pentium4" }
|
- { os: linux, arch: "386", go386: sse2 }
|
||||||
- { os: linux, arch: "386", variant: glibc, naive: true, go386: sse2 }
|
- { os: linux, arch: "386", variant: glibc, naive: true, go386: sse2 }
|
||||||
- { os: linux, arch: "386", variant: musl, naive: true, go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
- { os: linux, arch: "386", variant: musl, naive: true, go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
||||||
|
|
||||||
- { os: linux, arch: arm, goarm: "7", openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
- { os: linux, arch: arm, goarm: "7" }
|
||||||
- { os: linux, arch: arm, variant: glibc, naive: true, goarm: "7" }
|
- { os: linux, arch: arm, variant: glibc, naive: true, goarm: "7" }
|
||||||
- { os: linux, arch: arm, variant: musl, naive: true, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
- { os: linux, arch: arm, variant: musl, naive: true, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ jobs:
|
|||||||
if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
|
if: ${{ ! (matrix.legacy_win7 || matrix.legacy_go124) }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- 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
|
||||||
@@ -206,7 +206,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
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' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
@@ -228,7 +228,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
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' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@@ -243,7 +243,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
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' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@@ -258,7 +258,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
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' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
@@ -278,7 +278,7 @@ jobs:
|
|||||||
export CXX="${CC}++"
|
export CXX="${CC}++"
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@@ -369,12 +369,8 @@ jobs:
|
|||||||
-p "dist/openwrt.deb" \
|
-p "dist/openwrt.deb" \
|
||||||
--architecture all \
|
--architecture all \
|
||||||
dist/sing-box=/usr/bin/sing-box
|
dist/sing-box=/usr/bin/sing-box
|
||||||
SUFFIX=""
|
|
||||||
if [[ "${{ matrix.variant }}" == "musl" ]]; then
|
|
||||||
SUFFIX="_musl"
|
|
||||||
fi
|
|
||||||
for architecture in ${{ matrix.openwrt }}; do
|
for architecture in ${{ matrix.openwrt }}; do
|
||||||
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}${SUFFIX}.ipk"
|
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
|
||||||
done
|
done
|
||||||
rm "dist/openwrt.deb"
|
rm "dist/openwrt.deb"
|
||||||
- name: Archive
|
- name: Archive
|
||||||
@@ -445,7 +441,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
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' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@@ -505,7 +501,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_naive_outbound,with_purego,badlinkname,tfogo_checklinkname0" `
|
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_naive_outbound,with_purego,badlinkname,tfogo_checklinkname0" `
|
||||||
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0" `
|
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0" `
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
@@ -517,7 +513,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" `
|
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" `
|
||||||
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0" `
|
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0" `
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
@@ -575,7 +571,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -665,7 +661,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -764,7 +760,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
if: matrix.if
|
if: matrix.if
|
||||||
run: |-
|
run: |-
|
||||||
|
|||||||
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@@ -103,7 +103,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
|
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
|
||||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -s -w -buildid= -checklinkname=0" \
|
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@@ -115,7 +115,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
|
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
|
||||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -s -w -buildid= -checklinkname=0" \
|
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
|
|||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
with:
|
with:
|
||||||
version: v2.4.0
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
install-mode: binary
|
install-mode: binary
|
||||||
verify: false
|
verify: false
|
||||||
|
|||||||
8
.github/workflows/linux.yml
vendored
8
.github/workflows/linux.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.5
|
go-version: ^1.25.7
|
||||||
- name: Clone cronet-go
|
- name: Clone cronet-go
|
||||||
if: matrix.naive
|
if: matrix.naive
|
||||||
run: |
|
run: |
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
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' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@@ -141,7 +141,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
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' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
|
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
COPY . /go/src/github.com/sagernet/sing-box
|
COPY . /go/src/github.com/sagernet/sing-box
|
||||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||||
@@ -15,7 +15,7 @@ RUN set -ex \
|
|||||||
&& go build -v -trimpath -tags \
|
&& go build -v -trimpath -tags \
|
||||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" \
|
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,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\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -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>"
|
||||||
|
|||||||
105
Makefile
105
Makefile
@@ -6,7 +6,7 @@ GOHOSTOS = $(shell go env GOHOSTOS)
|
|||||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||||
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest)
|
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest)
|
||||||
|
|
||||||
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid= -checklinkname=0"
|
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0"
|
||||||
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS)"
|
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS)"
|
||||||
MAIN = ./cmd/sing-box
|
MAIN = ./cmd/sing-box
|
||||||
PREFIX ?= $(shell go env GOPATH)
|
PREFIX ?= $(shell go env GOPATH)
|
||||||
@@ -41,7 +41,7 @@ fmt_docs:
|
|||||||
go run ./cmd/internal/format_docs
|
go run ./cmd/internal/format_docs
|
||||||
|
|
||||||
fmt_install:
|
fmt_install:
|
||||||
go install -v mvdan.cc/gofumpt@v0.8.0
|
go install -v mvdan.cc/gofumpt@latest
|
||||||
go install -v github.com/daixiang0/gci@latest
|
go install -v github.com/daixiang0/gci@latest
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@@ -52,7 +52,7 @@ lint:
|
|||||||
GOOS=freebsd golangci-lint run ./...
|
GOOS=freebsd golangci-lint run ./...
|
||||||
|
|
||||||
lint_install:
|
lint_install:
|
||||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
@go run ./cmd/internal/protogen
|
@go run ./cmd/internal/protogen
|
||||||
@@ -89,12 +89,12 @@ update_android_version:
|
|||||||
go run ./cmd/internal/update_android_version
|
go run ./cmd/internal/update_android_version
|
||||||
|
|
||||||
build_android:
|
build_android:
|
||||||
cd ../sing-box-for-android && ./gradlew :app:clean :app:assemblePlayRelease :app:assembleOtherRelease && ./gradlew --stop
|
cd ../sing-box-for-android && ./gradlew :app:clean :app:assembleOtherRelease :app:assembleOtherLegacyRelease && ./gradlew --stop
|
||||||
|
|
||||||
upload_android:
|
upload_android:
|
||||||
mkdir -p dist/release_android
|
mkdir -p dist/release_android
|
||||||
cp ../sing-box-for-android/app/build/outputs/apk/play/release/*.apk dist/release_android
|
cp ../sing-box-for-android/app/build/outputs/apk/other/release/*.apk dist/release_android
|
||||||
cp ../sing-box-for-android/app/build/outputs/apk/other/release/*-universal.apk dist/release_android
|
cp ../sing-box-for-android/app/build/outputs/apk/otherLegacy/release/*.apk dist/release_android
|
||||||
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release_android
|
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release_android
|
||||||
rm -rf dist/release_android
|
rm -rf dist/release_android
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ build_ios:
|
|||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
rm -rf build/SFI.xcarchive && \
|
rm -rf build/SFI.xcarchive && \
|
||||||
xcodebuild clean -scheme SFI && \
|
xcodebuild clean -scheme SFI && \
|
||||||
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
|
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
|
||||||
|
|
||||||
upload_ios_app_store:
|
upload_ios_app_store:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
@@ -130,7 +130,7 @@ release_ios: build_ios upload_ios_app_store
|
|||||||
build_macos:
|
build_macos:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
rm -rf build/SFM.xcarchive && \
|
rm -rf build/SFM.xcarchive && \
|
||||||
xcodebuild archive -scheme SFM -configuration Release -archivePath build/SFM.xcarchive -allowProvisioningUpdates
|
xcodebuild archive -scheme SFM -configuration Release -archivePath build/SFM.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
|
||||||
|
|
||||||
upload_macos_app_store:
|
upload_macos_app_store:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
@@ -139,54 +139,50 @@ upload_macos_app_store:
|
|||||||
release_macos: build_macos upload_macos_app_store
|
release_macos: build_macos upload_macos_app_store
|
||||||
|
|
||||||
build_macos_standalone:
|
build_macos_standalone:
|
||||||
cd ../sing-box-for-apple && \
|
$(MAKE) -C ../sing-box-for-apple archive_macos_standalone
|
||||||
rm -rf build/SFM.System.xcarchive && \
|
|
||||||
xcodebuild archive -scheme SFM.System -configuration Release -archivePath build/SFM.System.xcarchive -allowProvisioningUpdates
|
|
||||||
|
|
||||||
build_macos_dmg:
|
build_macos_dmg:
|
||||||
rm -rf dist/SFM
|
$(MAKE) -C ../sing-box-for-apple build_macos_dmg
|
||||||
mkdir -p dist/SFM
|
|
||||||
cd ../sing-box-for-apple && \
|
build_macos_pkg:
|
||||||
rm -rf build/SFM.System && \
|
$(MAKE) -C ../sing-box-for-apple build_macos_pkg
|
||||||
rm -rf build/SFM.dmg && \
|
|
||||||
xcodebuild -exportArchive \
|
|
||||||
-archivePath "build/SFM.System.xcarchive" \
|
|
||||||
-exportOptionsPlist SFM.System/Export.plist -allowProvisioningUpdates \
|
|
||||||
-exportPath "build/SFM.System" && \
|
|
||||||
create-dmg \
|
|
||||||
--volname "sing-box" \
|
|
||||||
--volicon "build/SFM.System/SFM.app/Contents/Resources/AppIcon.icns" \
|
|
||||||
--icon "SFM.app" 0 0 \
|
|
||||||
--hide-extension "SFM.app" \
|
|
||||||
--app-drop-link 0 0 \
|
|
||||||
--skip-jenkins \
|
|
||||||
"../sing-box/dist/SFM/SFM.dmg" "build/SFM.System/SFM.app"
|
|
||||||
|
|
||||||
notarize_macos_dmg:
|
notarize_macos_dmg:
|
||||||
xcrun notarytool submit "dist/SFM/SFM.dmg" --wait \
|
$(MAKE) -C ../sing-box-for-apple notarize_macos_dmg
|
||||||
--keychain-profile "notarytool-password" \
|
|
||||||
--no-s3-acceleration
|
notarize_macos_pkg:
|
||||||
|
$(MAKE) -C ../sing-box-for-apple notarize_macos_pkg
|
||||||
|
|
||||||
upload_macos_dmg:
|
upload_macos_dmg:
|
||||||
cd dist/SFM && \
|
mkdir -p dist/SFM
|
||||||
cp SFM.dmg "SFM-${VERSION}-universal.dmg" && \
|
cp ../sing-box-for-apple/build/SFM-Apple.dmg "dist/SFM/SFM-${VERSION}-Apple.dmg"
|
||||||
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dmg"
|
cp ../sing-box-for-apple/build/SFM-Intel.dmg "dist/SFM/SFM-${VERSION}-Intel.dmg"
|
||||||
|
cp ../sing-box-for-apple/build/SFM-Universal.dmg "dist/SFM/SFM-${VERSION}-Universal.dmg"
|
||||||
|
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Apple.dmg"
|
||||||
|
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.dmg"
|
||||||
|
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.dmg"
|
||||||
|
|
||||||
|
upload_macos_pkg:
|
||||||
|
mkdir -p dist/SFM
|
||||||
|
cp ../sing-box-for-apple/build/SFM-Apple.pkg "dist/SFM/SFM-${VERSION}-Apple.pkg"
|
||||||
|
cp ../sing-box-for-apple/build/SFM-Intel.pkg "dist/SFM/SFM-${VERSION}-Intel.pkg"
|
||||||
|
cp ../sing-box-for-apple/build/SFM-Universal.pkg "dist/SFM/SFM-${VERSION}-Universal.pkg"
|
||||||
|
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Apple.pkg"
|
||||||
|
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.pkg"
|
||||||
|
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.pkg"
|
||||||
|
|
||||||
upload_macos_dsyms:
|
upload_macos_dsyms:
|
||||||
pushd ../sing-box-for-apple/build/SFM.System.xcarchive && \
|
mkdir -p dist/SFM
|
||||||
zip -r SFM.dSYMs.zip dSYMs && \
|
cd ../sing-box-for-apple/build/SFM.System-universal.xcarchive && zip -r SFM.dSYMs.zip dSYMs
|
||||||
mv SFM.dSYMs.zip ../../../sing-box/dist/SFM && \
|
cp ../sing-box-for-apple/build/SFM.System-universal.xcarchive/SFM.dSYMs.zip "dist/SFM/SFM-${VERSION}.dSYMs.zip"
|
||||||
popd && \
|
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}.dSYMs.zip"
|
||||||
cd dist/SFM && \
|
|
||||||
cp SFM.dSYMs.zip "SFM-${VERSION}-universal.dSYMs.zip" && \
|
|
||||||
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dSYMs.zip"
|
|
||||||
|
|
||||||
release_macos_standalone: build_macos_standalone build_macos_dmg notarize_macos_dmg upload_macos_dmg upload_macos_dsyms
|
release_macos_standalone: build_macos_pkg notarize_macos_pkg upload_macos_pkg upload_macos_dsyms
|
||||||
|
|
||||||
build_tvos:
|
build_tvos:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
rm -rf build/SFT.xcarchive && \
|
rm -rf build/SFT.xcarchive && \
|
||||||
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive -allowProvisioningUpdates
|
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
|
||||||
|
|
||||||
upload_tvos_app_store:
|
upload_tvos_app_store:
|
||||||
cd ../sing-box-for-apple && \
|
cd ../sing-box-for-apple && \
|
||||||
@@ -210,12 +206,12 @@ update_apple_version:
|
|||||||
update_macos_version:
|
update_macos_version:
|
||||||
MACOS_PROJECT_VERSION=$(shell go run -v ./cmd/internal/app_store_connect next_macos_project_version) go run ./cmd/internal/update_apple_version
|
MACOS_PROJECT_VERSION=$(shell go run -v ./cmd/internal/app_store_connect next_macos_project_version) go run ./cmd/internal/update_apple_version
|
||||||
|
|
||||||
release_apple: lib_ios update_apple_version release_ios release_macos release_tvos release_macos_standalone
|
release_apple: lib_apple update_apple_version release_ios release_macos release_tvos release_macos_standalone
|
||||||
|
|
||||||
release_apple_beta: update_apple_version release_ios release_macos release_tvos
|
release_apple_beta: update_apple_version release_ios release_macos release_tvos
|
||||||
|
|
||||||
publish_testflight:
|
publish_testflight:
|
||||||
go run -v ./cmd/internal/app_store_connect publish_testflight
|
go run -v ./cmd/internal/app_store_connect publish_testflight $(filter-out $@,$(MAKECMDGOALS))
|
||||||
|
|
||||||
prepare_app_store:
|
prepare_app_store:
|
||||||
go run -v ./cmd/internal/app_store_connect prepare_app_store
|
go run -v ./cmd/internal/app_store_connect prepare_app_store
|
||||||
@@ -238,22 +234,18 @@ test_stdio:
|
|||||||
lib_android:
|
lib_android:
|
||||||
go run ./cmd/internal/build_libbox -target android
|
go run ./cmd/internal/build_libbox -target android
|
||||||
|
|
||||||
lib_android_debug:
|
|
||||||
go run ./cmd/internal/build_libbox -target android -debug
|
|
||||||
|
|
||||||
lib_apple:
|
lib_apple:
|
||||||
go run ./cmd/internal/build_libbox -target apple
|
go run ./cmd/internal/build_libbox -target apple
|
||||||
|
|
||||||
lib_ios:
|
lib_android_new:
|
||||||
go run ./cmd/internal/build_libbox -target apple -platform ios -debug
|
go run ./cmd/internal/build_libbox_newffi -target android
|
||||||
|
|
||||||
lib:
|
lib_apple_new:
|
||||||
go run ./cmd/internal/build_libbox -target android
|
go run ./cmd/internal/build_libbox_newffi -target apple
|
||||||
go run ./cmd/internal/build_libbox -target ios
|
|
||||||
|
|
||||||
lib_install:
|
lib_install:
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.10
|
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.11
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.10
|
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.11
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
venv/bin/mkdocs serve
|
venv/bin/mkdocs serve
|
||||||
@@ -273,3 +265,6 @@ update:
|
|||||||
git fetch
|
git fetch
|
||||||
git reset FETCH_HEAD --hard
|
git reset FETCH_HEAD --hard
|
||||||
git clean -fdx
|
git clean -fdx
|
||||||
|
|
||||||
|
%:
|
||||||
|
@:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/observable"
|
"github.com/sagernet/sing/common/observable"
|
||||||
@@ -68,7 +69,11 @@ func (s *SavedBinary) MarshalBinary() ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = varbin.Write(&buffer, binary.BigEndian, s.Content)
|
_, err = varbin.WriteUvarint(&buffer, uint64(len(s.Content)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = buffer.Write(s.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -76,7 +81,11 @@ func (s *SavedBinary) MarshalBinary() ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = varbin.Write(&buffer, binary.BigEndian, s.LastEtag)
|
_, err = varbin.WriteUvarint(&buffer, uint64(len(s.LastEtag)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = buffer.WriteString(s.LastEtag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -90,7 +99,12 @@ func (s *SavedBinary) UnmarshalBinary(data []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = varbin.Read(reader, binary.BigEndian, &s.Content)
|
contentLength, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Content = make([]byte, contentLength)
|
||||||
|
_, err = io.ReadFull(reader, s.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -100,10 +114,16 @@ func (s *SavedBinary) UnmarshalBinary(data []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.LastUpdated = time.Unix(lastUpdated, 0)
|
s.LastUpdated = time.Unix(lastUpdated, 0)
|
||||||
err = varbin.Read(reader, binary.BigEndian, &s.LastEtag)
|
etagLength, err := binary.ReadUvarint(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
etagBytes := make([]byte, etagLength)
|
||||||
|
_, err = io.ReadFull(reader, etagBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.LastEtag = string(etagBytes)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type Router interface {
|
|||||||
ConnectionRouterEx
|
ConnectionRouterEx
|
||||||
RuleSet(tag string) (RuleSet, bool)
|
RuleSet(tag string) (RuleSet, bool)
|
||||||
Rules() []Rule
|
Rules() []Rule
|
||||||
|
NeedFindProcess() bool
|
||||||
AppendTracker(tracker ConnectionTracker)
|
AppendTracker(tracker ConnectionTracker)
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule clients/android updated: fe128a6cd7...6491eff61e
Submodule clients/apple updated: 532c140f05...38e8b3eda9
@@ -100,11 +100,32 @@ findVersion:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func publishTestflight(ctx context.Context) error {
|
func publishTestflight(ctx context.Context) error {
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
return E.New("platform required: ios, macos, or tvos")
|
||||||
|
}
|
||||||
|
var platform asc.Platform
|
||||||
|
switch os.Args[2] {
|
||||||
|
case "ios":
|
||||||
|
platform = asc.PlatformIOS
|
||||||
|
case "macos":
|
||||||
|
platform = asc.PlatformMACOS
|
||||||
|
case "tvos":
|
||||||
|
platform = asc.PlatformTVOS
|
||||||
|
default:
|
||||||
|
return E.New("unknown platform: ", os.Args[2])
|
||||||
|
}
|
||||||
|
|
||||||
tagVersion, err := build_shared.ReadTagVersion()
|
tagVersion, err := build_shared.ReadTagVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tag := tagVersion.VersionString()
|
tag := tagVersion.VersionString()
|
||||||
|
|
||||||
|
releaseNotes := F.ToString("sing-box ", tagVersion.String())
|
||||||
|
if len(os.Args) >= 4 {
|
||||||
|
releaseNotes = strings.Join(os.Args[3:], " ")
|
||||||
|
}
|
||||||
|
|
||||||
client := createClient(20 * time.Minute)
|
client := createClient(20 * time.Minute)
|
||||||
|
|
||||||
log.Info(tag, " list build IDs")
|
log.Info(tag, " list build IDs")
|
||||||
@@ -115,97 +136,76 @@ func publishTestflight(ctx context.Context) error {
|
|||||||
buildIDs := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string {
|
buildIDs := common.Map(buildIDsResponse.Data, func(it asc.RelationshipData) string {
|
||||||
return it.ID
|
return it.ID
|
||||||
})
|
})
|
||||||
var platforms []asc.Platform
|
|
||||||
if len(os.Args) == 3 {
|
|
||||||
switch os.Args[2] {
|
|
||||||
case "ios":
|
|
||||||
platforms = []asc.Platform{asc.PlatformIOS}
|
|
||||||
case "macos":
|
|
||||||
platforms = []asc.Platform{asc.PlatformMACOS}
|
|
||||||
case "tvos":
|
|
||||||
platforms = []asc.Platform{asc.PlatformTVOS}
|
|
||||||
default:
|
|
||||||
return E.New("unknown platform: ", os.Args[2])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
platforms = []asc.Platform{
|
|
||||||
asc.PlatformIOS,
|
|
||||||
asc.PlatformMACOS,
|
|
||||||
asc.PlatformTVOS,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
waitingForProcess := false
|
waitingForProcess := false
|
||||||
for _, platform := range platforms {
|
log.Info(string(platform), " list builds")
|
||||||
log.Info(string(platform), " list builds")
|
for {
|
||||||
for {
|
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
||||||
builds, _, err := client.Builds.ListBuilds(ctx, &asc.ListBuildsQuery{
|
FilterApp: []string{appID},
|
||||||
FilterApp: []string{appID},
|
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
||||||
FilterPreReleaseVersionPlatform: []string{string(platform)},
|
})
|
||||||
})
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
build := builds.Data[0]
|
|
||||||
if !waitingForProcess && (common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute) {
|
|
||||||
log.Info(string(platform), " ", tag, " waiting for process")
|
|
||||||
time.Sleep(15 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *build.Attributes.ProcessingState != "VALID" {
|
|
||||||
waitingForProcess = true
|
|
||||||
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
|
|
||||||
time.Sleep(15 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Info(string(platform), " ", tag, " list localizations")
|
|
||||||
localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, build.ID, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool {
|
|
||||||
return *it.Attributes.Locale == "en-US"
|
|
||||||
})
|
|
||||||
if localization.ID == "" {
|
|
||||||
log.Fatal(string(platform), " ", tag, " no en-US localization found")
|
|
||||||
}
|
|
||||||
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
|
|
||||||
log.Info(string(platform), " ", tag, " update localization")
|
|
||||||
_, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr(
|
|
||||||
F.ToString("sing-box ", tagVersion.String()),
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info(string(platform), " ", tag, " publish")
|
|
||||||
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
|
|
||||||
if response != nil && (response.StatusCode == http.StatusUnprocessableEntity || response.StatusCode == http.StatusNotFound) {
|
|
||||||
log.Info("waiting for process")
|
|
||||||
time.Sleep(15 * time.Second)
|
|
||||||
continue
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info(string(platform), " ", tag, " list submissions")
|
|
||||||
betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{
|
|
||||||
FilterBuild: []string{build.ID},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(betaSubmissions.Data) == 0 {
|
|
||||||
log.Info(string(platform), " ", tag, " create submission")
|
|
||||||
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "ANOTHER_BUILD_IN_REVIEW") {
|
|
||||||
log.Error(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
build := builds.Data[0]
|
||||||
|
log.Info(string(platform), " ", tag, " found build: ", build.ID, " (", *build.Attributes.Version, ")")
|
||||||
|
if !waitingForProcess && (common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute) {
|
||||||
|
log.Info(string(platform), " ", tag, " waiting for process")
|
||||||
|
time.Sleep(15 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if *build.Attributes.ProcessingState != "VALID" {
|
||||||
|
waitingForProcess = true
|
||||||
|
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
|
||||||
|
time.Sleep(15 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Info(string(platform), " ", tag, " list localizations")
|
||||||
|
localizations, _, err := client.TestFlight.ListBetaBuildLocalizationsForBuild(ctx, build.ID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
localization := common.Find(localizations.Data, func(it asc.BetaBuildLocalization) bool {
|
||||||
|
return *it.Attributes.Locale == "en-US"
|
||||||
|
})
|
||||||
|
if localization.ID == "" {
|
||||||
|
log.Fatal(string(platform), " ", tag, " no en-US localization found")
|
||||||
|
}
|
||||||
|
if localization.Attributes == nil || localization.Attributes.WhatsNew == nil || *localization.Attributes.WhatsNew == "" {
|
||||||
|
log.Info(string(platform), " ", tag, " update localization")
|
||||||
|
_, _, err = client.TestFlight.UpdateBetaBuildLocalization(ctx, localization.ID, common.Ptr(releaseNotes))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info(string(platform), " ", tag, " publish")
|
||||||
|
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
|
||||||
|
if response != nil && (response.StatusCode == http.StatusUnprocessableEntity || response.StatusCode == http.StatusNotFound) {
|
||||||
|
log.Info("waiting for process")
|
||||||
|
time.Sleep(15 * time.Second)
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info(string(platform), " ", tag, " list submissions")
|
||||||
|
betaSubmissions, _, err := client.TestFlight.ListBetaAppReviewSubmissions(ctx, &asc.ListBetaAppReviewSubmissionsQuery{
|
||||||
|
FilterBuild: []string{build.ID},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(betaSubmissions.Data) == 0 {
|
||||||
|
log.Info(string(platform), " ", tag, " create submission")
|
||||||
|
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "ANOTHER_BUILD_IN_REVIEW") {
|
||||||
|
log.Error(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
debugEnabled bool
|
debugEnabled bool
|
||||||
target string
|
target string
|
||||||
platform string
|
platform string
|
||||||
withTailscale bool
|
// withTailscale bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
|
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
|
||||||
flag.StringVar(&target, "target", "android", "target platform")
|
flag.StringVar(&target, "target", "android", "target platform")
|
||||||
flag.StringVar(&platform, "platform", "", "specify platform")
|
flag.StringVar(&platform, "platform", "", "specify platform")
|
||||||
flag.BoolVar(&withTailscale, "with-tailscale", false, "build tailscale for iOS and tvOS")
|
// flag.BoolVar(&withTailscale, "with-tailscale", false, "build tailscale for iOS and tvOS")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -48,7 +48,7 @@ var (
|
|||||||
debugFlags []string
|
debugFlags []string
|
||||||
sharedTags []string
|
sharedTags []string
|
||||||
darwinTags []string
|
darwinTags []string
|
||||||
memcTags []string
|
// memcTags []string
|
||||||
notMemcTags []string
|
notMemcTags []string
|
||||||
debugTags []string
|
debugTags []string
|
||||||
)
|
)
|
||||||
@@ -60,12 +60,13 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
currentTag = "unknown"
|
currentTag = "unknown"
|
||||||
}
|
}
|
||||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0")
|
||||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0")
|
||||||
|
|
||||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
||||||
darwinTags = append(darwinTags, "with_dhcp")
|
darwinTags = append(darwinTags, "with_dhcp", "grpcnotrace")
|
||||||
memcTags = append(memcTags, "with_tailscale")
|
// memcTags = append(memcTags, "with_tailscale")
|
||||||
|
sharedTags = append(sharedTags, "with_tailscale", "ts_omit_logtail", "ts_omit_ssh", "ts_omit_drive", "ts_omit_taildrop", "ts_omit_webclient", "ts_omit_doctor", "ts_omit_capture", "ts_omit_kube", "ts_omit_aws", "ts_omit_synology", "ts_omit_bird")
|
||||||
notMemcTags = append(notMemcTags, "with_low_memory")
|
notMemcTags = append(notMemcTags, "with_low_memory")
|
||||||
debugTags = append(debugTags, "debug")
|
debugTags = append(debugTags, "debug")
|
||||||
}
|
}
|
||||||
@@ -164,7 +165,7 @@ func buildAndroid() {
|
|||||||
|
|
||||||
// Build main variant (SDK 23)
|
// Build main variant (SDK 23)
|
||||||
mainTags := append([]string{}, sharedTags...)
|
mainTags := append([]string{}, sharedTags...)
|
||||||
mainTags = append(mainTags, memcTags...)
|
// mainTags = append(mainTags, memcTags...)
|
||||||
if debugEnabled {
|
if debugEnabled {
|
||||||
mainTags = append(mainTags, debugTags...)
|
mainTags = append(mainTags, debugTags...)
|
||||||
}
|
}
|
||||||
@@ -176,7 +177,7 @@ func buildAndroid() {
|
|||||||
|
|
||||||
// Build legacy variant (SDK 21, no naive outbound)
|
// Build legacy variant (SDK 21, no naive outbound)
|
||||||
legacyTags := filterTags(sharedTags, "with_naive_outbound")
|
legacyTags := filterTags(sharedTags, "with_naive_outbound")
|
||||||
legacyTags = append(legacyTags, memcTags...)
|
// legacyTags = append(legacyTags, memcTags...)
|
||||||
if debugEnabled {
|
if debugEnabled {
|
||||||
legacyTags = append(legacyTags, debugTags...)
|
legacyTags = append(legacyTags, debugTags...)
|
||||||
}
|
}
|
||||||
@@ -194,7 +195,7 @@ func buildApple() {
|
|||||||
} else if debugEnabled {
|
} else if debugEnabled {
|
||||||
bindTarget = "ios"
|
bindTarget = "ios"
|
||||||
} else {
|
} else {
|
||||||
bindTarget = "ios,tvos,macos"
|
bindTarget = "ios,iossimulator,tvos,tvossimulator,macos"
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
@@ -204,9 +205,9 @@ func buildApple() {
|
|||||||
"-libname=box",
|
"-libname=box",
|
||||||
"-tags-not-macos=with_low_memory",
|
"-tags-not-macos=with_low_memory",
|
||||||
}
|
}
|
||||||
if !withTailscale {
|
//if !withTailscale {
|
||||||
args = append(args, "-tags-macos="+strings.Join(memcTags, ","))
|
// args = append(args, "-tags-macos="+strings.Join(memcTags, ","))
|
||||||
}
|
//}
|
||||||
|
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
args = append(args, sharedFlags...)
|
args = append(args, sharedFlags...)
|
||||||
@@ -215,9 +216,9 @@ func buildApple() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tags := append(sharedTags, darwinTags...)
|
tags := append(sharedTags, darwinTags...)
|
||||||
if withTailscale {
|
//if withTailscale {
|
||||||
tags = append(tags, memcTags...)
|
// tags = append(tags, memcTags...)
|
||||||
}
|
//}
|
||||||
if debugEnabled {
|
if debugEnabled {
|
||||||
tags = append(tags, debugTags...)
|
tags = append(tags, debugTags...)
|
||||||
}
|
}
|
||||||
|
|||||||
93
cmd/internal/build_libbox_newffi/main.go
Normal file
93
cmd/internal/build_libbox_newffi/main.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
)
|
||||||
|
|
||||||
|
var target string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&target, "target", "android", "target platform (android or apple)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"generate",
|
||||||
|
"-v",
|
||||||
|
"--config", "experimental/libbox/ffi.json",
|
||||||
|
"--platform-type", target,
|
||||||
|
}
|
||||||
|
command := exec.Command("sing-ffi", args...)
|
||||||
|
command.Stdout = os.Stdout
|
||||||
|
command.Stderr = os.Stderr
|
||||||
|
err := command.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
copyArtifacts(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyArtifacts(target string) {
|
||||||
|
switch target {
|
||||||
|
case "android":
|
||||||
|
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
||||||
|
if rw.IsDir(copyPath) {
|
||||||
|
copyPath, _ = filepath.Abs(copyPath)
|
||||||
|
for _, name := range []string{"libbox.aar", "libbox-legacy.aar"} {
|
||||||
|
artifactPath, found := findArtifactPath(name)
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
targetPath := filepath.Join(target, artifactPath)
|
||||||
|
os.RemoveAll(targetPath)
|
||||||
|
err := os.Rename(artifactPath, targetPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Info("copied ", name, " to ", copyPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "apple":
|
||||||
|
copyPath := filepath.Join("..", "sing-box-for-apple")
|
||||||
|
if rw.IsDir(copyPath) {
|
||||||
|
sourceDir, found := findArtifactPath("Libbox.xcframework")
|
||||||
|
if !found {
|
||||||
|
log.Fatal("Libbox.xcframework not found in current directory or experimental/libbox")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetDir := filepath.Join(copyPath, "Libbox.xcframework")
|
||||||
|
targetDir, _ = filepath.Abs(targetDir)
|
||||||
|
err := os.RemoveAll(targetDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.Rename(sourceDir, targetDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Info("copied ", sourceDir, " to ", targetDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findArtifactPath(name string) (string, bool) {
|
||||||
|
candidates := []string{
|
||||||
|
name,
|
||||||
|
filepath.Join("experimental", "libbox", name),
|
||||||
|
}
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
if rw.IsFile(candidate) || rw.IsDir(candidate) {
|
||||||
|
return candidate, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
@@ -137,6 +137,12 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath))
|
dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath))
|
||||||
listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath))
|
listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath))
|
||||||
}
|
}
|
||||||
|
if options.BindAddressNoPort {
|
||||||
|
if !C.IsLinux {
|
||||||
|
return nil, E.New("`bind_address_no_port` is only supported on Linux")
|
||||||
|
}
|
||||||
|
dialer.Control = control.Append(dialer.Control, control.BindAddressNoPort())
|
||||||
|
}
|
||||||
if options.ConnectTimeout != 0 {
|
if options.ConnectTimeout != 0 {
|
||||||
dialer.Timeout = time.Duration(options.ConnectTimeout)
|
dialer.Timeout = time.Duration(options.ConnectTimeout)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
234
common/geosite/compat_test.go
Normal file
234
common/geosite/compat_test.go
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
package geosite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/varbin"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Old implementation using varbin reflection-based serialization
|
||||||
|
|
||||||
|
func oldWriteString(writer varbin.Writer, value string) error {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.Write(writer, binary.BigEndian, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldWriteItem(writer varbin.Writer, item Item) error {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.Write(writer, binary.BigEndian, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldReadString(reader varbin.Reader) (string, error) {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.ReadValue[string](reader, binary.BigEndian)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldReadItem(reader varbin.Reader) (Item, error) {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.ReadValue[Item](reader, binary.BigEndian)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{"empty", ""},
|
||||||
|
{"single_char", "a"},
|
||||||
|
{"ascii", "example.com"},
|
||||||
|
{"utf8", "测试域名.中国"},
|
||||||
|
{"special_chars", "\x00\xff\n\t"},
|
||||||
|
{"127_bytes", strings.Repeat("x", 127)},
|
||||||
|
{"128_bytes", strings.Repeat("x", 128)},
|
||||||
|
{"16383_bytes", strings.Repeat("x", 16383)},
|
||||||
|
{"16384_bytes", strings.Repeat("x", 16384)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Old write
|
||||||
|
var oldBuf bytes.Buffer
|
||||||
|
err := oldWriteString(&oldBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// New write
|
||||||
|
var newBuf bytes.Buffer
|
||||||
|
err = writeString(&newBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Bytes must match
|
||||||
|
require.Equal(t, oldBuf.Bytes(), newBuf.Bytes(),
|
||||||
|
"mismatch for %q\nold: %x\nnew: %x", tc.name, oldBuf.Bytes(), newBuf.Bytes())
|
||||||
|
|
||||||
|
// New write -> old read
|
||||||
|
readBack, err := oldReadString(bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.input, readBack)
|
||||||
|
|
||||||
|
// Old write -> new read
|
||||||
|
readBack2, err := readString(bufio.NewReader(bytes.NewReader(oldBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.input, readBack2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestItemCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Note: varbin.Write has a bug where struct values (not pointers) don't write their fields
|
||||||
|
// because field.CanSet() returns false for non-addressable values.
|
||||||
|
// The old geosite code passed Item values to varbin.Write, which silently wrote nothing.
|
||||||
|
// The new code correctly writes Type + Value using manual serialization.
|
||||||
|
// This test verifies the new serialization format and round-trip correctness.
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input Item
|
||||||
|
}{
|
||||||
|
{"domain_empty", Item{Type: RuleTypeDomain, Value: ""}},
|
||||||
|
{"domain_normal", Item{Type: RuleTypeDomain, Value: "example.com"}},
|
||||||
|
{"domain_suffix", Item{Type: RuleTypeDomainSuffix, Value: ".example.com"}},
|
||||||
|
{"domain_keyword", Item{Type: RuleTypeDomainKeyword, Value: "google"}},
|
||||||
|
{"domain_regex", Item{Type: RuleTypeDomainRegex, Value: `^.*\.example\.com$`}},
|
||||||
|
{"utf8_domain", Item{Type: RuleTypeDomain, Value: "测试.com"}},
|
||||||
|
{"long_domain", Item{Type: RuleTypeDomainSuffix, Value: strings.Repeat("a", 200) + ".com"}},
|
||||||
|
{"128_bytes_value", Item{Type: RuleTypeDomain, Value: strings.Repeat("x", 128)}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// New write
|
||||||
|
var newBuf bytes.Buffer
|
||||||
|
err := newBuf.WriteByte(byte(tc.input.Type))
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = writeString(&newBuf, tc.input.Value)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify format: Type (1 byte) + Value (uvarint len + bytes)
|
||||||
|
require.True(t, len(newBuf.Bytes()) >= 1, "output too short")
|
||||||
|
require.Equal(t, byte(tc.input.Type), newBuf.Bytes()[0], "type byte mismatch")
|
||||||
|
|
||||||
|
// New write -> old read (varbin can read correctly when given addressable target)
|
||||||
|
readBack, err := oldReadItem(bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.input, readBack)
|
||||||
|
|
||||||
|
// New write -> new read
|
||||||
|
reader := bufio.NewReader(bytes.NewReader(newBuf.Bytes()))
|
||||||
|
typeByte, err := reader.ReadByte()
|
||||||
|
require.NoError(t, err)
|
||||||
|
value, err := readString(reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.input, Item{Type: ItemType(typeByte), Value: value})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeositeWriteReadCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input map[string][]Item
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"empty_map",
|
||||||
|
map[string][]Item{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"single_code_empty_items",
|
||||||
|
map[string][]Item{"test": {}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"single_code_single_item",
|
||||||
|
map[string][]Item{"test": {{Type: RuleTypeDomain, Value: "a.com"}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"single_code_multi_items",
|
||||||
|
map[string][]Item{
|
||||||
|
"test": {
|
||||||
|
{Type: RuleTypeDomain, Value: "a.com"},
|
||||||
|
{Type: RuleTypeDomainSuffix, Value: ".b.com"},
|
||||||
|
{Type: RuleTypeDomainKeyword, Value: "keyword"},
|
||||||
|
{Type: RuleTypeDomainRegex, Value: `^.*$`},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multi_code",
|
||||||
|
map[string][]Item{
|
||||||
|
"cn": {{Type: RuleTypeDomain, Value: "baidu.com"}, {Type: RuleTypeDomainSuffix, Value: ".cn"}},
|
||||||
|
"us": {{Type: RuleTypeDomain, Value: "google.com"}},
|
||||||
|
"jp": {{Type: RuleTypeDomainSuffix, Value: ".jp"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"utf8_values",
|
||||||
|
map[string][]Item{
|
||||||
|
"test": {
|
||||||
|
{Type: RuleTypeDomain, Value: "测试.中国"},
|
||||||
|
{Type: RuleTypeDomainSuffix, Value: ".テスト"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"large_items",
|
||||||
|
generateLargeItems(1000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Write using new implementation
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := Write(&buf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Read back and verify
|
||||||
|
reader, codes, err := NewReader(bytes.NewReader(buf.Bytes()))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify all codes exist
|
||||||
|
codeSet := make(map[string]bool)
|
||||||
|
for _, code := range codes {
|
||||||
|
codeSet[code] = true
|
||||||
|
}
|
||||||
|
for code := range tc.input {
|
||||||
|
require.True(t, codeSet[code], "missing code: %s", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify items match
|
||||||
|
for code, expectedItems := range tc.input {
|
||||||
|
items, err := reader.Read(code)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedItems, items, "items mismatch for code: %s", code)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateLargeItems(count int) map[string][]Item {
|
||||||
|
items := make([]Item, count)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
items[i] = Item{
|
||||||
|
Type: ItemType(i % 4),
|
||||||
|
Value: strings.Repeat("x", i%200) + ".com",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map[string][]Item{"large": items}
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/varbin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
@@ -78,7 +77,7 @@ func (r *Reader) readMetadata() error {
|
|||||||
codeIndex uint64
|
codeIndex uint64
|
||||||
codeLength uint64
|
codeLength uint64
|
||||||
)
|
)
|
||||||
code, err = varbin.ReadValue[string](reader, binary.BigEndian)
|
code, err = readString(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -112,9 +111,16 @@ func (r *Reader) Read(code string) ([]Item, error) {
|
|||||||
}
|
}
|
||||||
r.bufferedReader.Reset(r.reader)
|
r.bufferedReader.Reset(r.reader)
|
||||||
itemList := make([]Item, r.domainLength[code])
|
itemList := make([]Item, r.domainLength[code])
|
||||||
err = varbin.Read(r.bufferedReader, binary.BigEndian, &itemList)
|
for i := range itemList {
|
||||||
if err != nil {
|
typeByte, err := r.bufferedReader.ReadByte()
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
itemList[i].Type = ItemType(typeByte)
|
||||||
|
itemList[i].Value, err = readString(r.bufferedReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return itemList, nil
|
return itemList, nil
|
||||||
}
|
}
|
||||||
@@ -135,3 +141,18 @@ func (r *readCounter) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readString(reader io.ByteReader) (string, error) {
|
||||||
|
length, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
bytes := make([]byte, length)
|
||||||
|
for i := range bytes {
|
||||||
|
bytes[i], err = reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(bytes), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package geosite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/varbin"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
@@ -20,7 +19,11 @@ func Write(writer varbin.Writer, domains map[string][]Item) error {
|
|||||||
for _, code := range keys {
|
for _, code := range keys {
|
||||||
index[code] = content.Len()
|
index[code] = content.Len()
|
||||||
for _, item := range domains[code] {
|
for _, item := range domains[code] {
|
||||||
err := varbin.Write(content, binary.BigEndian, item)
|
err := content.WriteByte(byte(item.Type))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = writeString(content, item.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -38,7 +41,7 @@ func Write(writer varbin.Writer, domains map[string][]Item) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, code := range keys {
|
for _, code := range keys {
|
||||||
err = varbin.Write(writer, binary.BigEndian, code)
|
err = writeString(writer, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -59,3 +62,12 @@ func Write(writer varbin.Writer, domains map[string][]Item) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeString(writer varbin.Writer, value string) error {
|
||||||
|
_, err := varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write([]byte(value))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -303,8 +303,6 @@ find:
|
|||||||
metadata.Protocol = C.ProtocolQUIC
|
metadata.Protocol = C.ProtocolQUIC
|
||||||
fingerprint, err := ja3.Compute(buffer.Bytes())
|
fingerprint, err := ja3.Compute(buffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
metadata.Protocol = C.ProtocolQUIC
|
|
||||||
metadata.Client = C.ClientChromium
|
|
||||||
metadata.SniffContext = fragments
|
metadata.SniffContext = fragments
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return E.Cause1(ErrNeedMoreData, err)
|
||||||
}
|
}
|
||||||
@@ -334,7 +332,7 @@ find:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if count(frameTypeList, frameTypeCrypto) > 1 || count(frameTypeList, frameTypePing) > 0 {
|
if count(frameTypeList, frameTypeCrypto) > 1 || count(frameTypeList, frameTypePing) > 0 {
|
||||||
if maybeUQUIC(fingerprint) {
|
if isQUICGo(fingerprint) {
|
||||||
metadata.Client = C.ClientQUICGo
|
metadata.Client = C.ClientQUICGo
|
||||||
} else {
|
} else {
|
||||||
metadata.Client = C.ClientChromium
|
metadata.Client = C.ClientChromium
|
||||||
|
|||||||
@@ -1,21 +1,29 @@
|
|||||||
package sniff
|
package sniff
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/ja3"
|
"github.com/sagernet/sing-box/common/ja3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chromium sends separate client hello packets, but UQUIC has not yet implemented this behavior
|
const (
|
||||||
// The cronet without this behavior does not have version 115
|
// X25519Kyber768Draft00 - post-quantum curve used by Go crypto/tls
|
||||||
var uQUICChrome115 = &ja3.ClientHello{
|
x25519Kyber768Draft00 uint16 = 0x11EC // 4588
|
||||||
Version: tls.VersionTLS12,
|
// renegotiation_info extension used by Go crypto/tls
|
||||||
CipherSuites: []uint16{4865, 4866, 4867},
|
extensionRenegotiationInfo uint16 = 0xFF01 // 65281
|
||||||
Extensions: []uint16{0, 10, 13, 16, 27, 43, 45, 51, 57, 17513},
|
)
|
||||||
EllipticCurves: []uint16{29, 23, 24},
|
|
||||||
SignatureAlgorithms: []uint16{1027, 2052, 1025, 1283, 2053, 1281, 2054, 1537, 513},
|
|
||||||
}
|
|
||||||
|
|
||||||
func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
|
// isQUICGo detects native quic-go by checking for Go crypto/tls specific features.
|
||||||
return !uQUICChrome115.Equals(fingerprint, true)
|
// Note: uQUIC with Chromium mimicry cannot be reliably distinguished from real Chromium
|
||||||
|
// since it uses the same TLS fingerprint, so it will be identified as Chromium.
|
||||||
|
func isQUICGo(fingerprint *ja3.ClientHello) bool {
|
||||||
|
for _, curve := range fingerprint.EllipticCurves {
|
||||||
|
if curve == x25519Kyber768Draft00 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ext := range fingerprint.Extensions {
|
||||||
|
if ext == extensionRenegotiationInfo {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
188
common/sniff/quic_capture_test.go
Normal file
188
common/sniff/quic_capture_test.go
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
package sniff_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/quic-go"
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/sniff"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSniffQUICQuicGoFingerprint(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
const testSNI = "test.example.com"
|
||||||
|
|
||||||
|
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer udpConn.Close()
|
||||||
|
|
||||||
|
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
|
||||||
|
packetsChan := make(chan [][]byte, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var packets [][]byte
|
||||||
|
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
buf := make([]byte, 2048)
|
||||||
|
n, _, err := udpConn.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
packets = append(packets, buf[:n])
|
||||||
|
}
|
||||||
|
packetsChan <- packets
|
||||||
|
}()
|
||||||
|
|
||||||
|
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
ServerName: testSNI,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
NextProtos: []string{"h3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
|
||||||
|
|
||||||
|
select {
|
||||||
|
case packets := <-packetsChan:
|
||||||
|
t.Logf("Captured %d packets", len(packets))
|
||||||
|
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
for i, pkt := range packets {
|
||||||
|
err := sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
|
t.Logf("Packet %d: err=%v, domain=%s, client=%s", i, err, metadata.Domain, metadata.Client)
|
||||||
|
if metadata.Domain != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("\n=== quic-go TLS Fingerprint Analysis ===")
|
||||||
|
t.Logf("Domain: %s", metadata.Domain)
|
||||||
|
t.Logf("Client: %s", metadata.Client)
|
||||||
|
t.Logf("Protocol: %s", metadata.Protocol)
|
||||||
|
|
||||||
|
// The client should be identified as quic-go, not chromium
|
||||||
|
// Current issue: it's being identified as chromium
|
||||||
|
if metadata.Client == "chromium" {
|
||||||
|
t.Log("WARNING: quic-go is being misidentified as chromium!")
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("Timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSniffQUICInitialFromQuicGo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const testSNI = "test.example.com"
|
||||||
|
|
||||||
|
// Create UDP listener to capture ALL initial packets
|
||||||
|
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer udpConn.Close()
|
||||||
|
|
||||||
|
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
|
||||||
|
|
||||||
|
// Channel to receive captured packets
|
||||||
|
packetsChan := make(chan [][]byte, 1)
|
||||||
|
|
||||||
|
// Start goroutine to capture packets
|
||||||
|
go func() {
|
||||||
|
var packets [][]byte
|
||||||
|
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||||
|
for i := 0; i < 5; i++ { // Capture up to 5 packets
|
||||||
|
buf := make([]byte, 2048)
|
||||||
|
n, _, err := udpConn.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
packets = append(packets, buf[:n])
|
||||||
|
}
|
||||||
|
packetsChan <- packets
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create QUIC client connection (will fail but we capture the initial packet)
|
||||||
|
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
ServerName: testSNI,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
NextProtos: []string{"h3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// This will fail (no server) but sends initial packet
|
||||||
|
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
|
||||||
|
|
||||||
|
// Wait for captured packets
|
||||||
|
select {
|
||||||
|
case packets := <-packetsChan:
|
||||||
|
t.Logf("Captured %d QUIC packets", len(packets))
|
||||||
|
|
||||||
|
for i, packet := range packets {
|
||||||
|
t.Logf("Packet %d: length=%d, first 30 bytes: %x", i, len(packet), packet[:min(30, len(packet))])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test sniffer with first packet
|
||||||
|
if len(packets) > 0 {
|
||||||
|
var metadata adapter.InboundContext
|
||||||
|
err := sniff.QUICClientHello(context.Background(), &metadata, packets[0])
|
||||||
|
|
||||||
|
t.Logf("First packet sniff error: %v", err)
|
||||||
|
t.Logf("Protocol: %s", metadata.Protocol)
|
||||||
|
t.Logf("Domain: %s", metadata.Domain)
|
||||||
|
t.Logf("Client: %s", metadata.Client)
|
||||||
|
|
||||||
|
// If first packet needs more data, try with subsequent packets
|
||||||
|
// IMPORTANT: reuse metadata to accumulate CRYPTO fragments via SniffContext
|
||||||
|
if errors.Is(err, sniff.ErrNeedMoreData) && len(packets) > 1 {
|
||||||
|
t.Log("First packet needs more data, trying subsequent packets with shared context...")
|
||||||
|
for i := 1; i < len(packets); i++ {
|
||||||
|
// Reuse same metadata to accumulate fragments
|
||||||
|
err = sniff.QUICClientHello(context.Background(), &metadata, packets[i])
|
||||||
|
t.Logf("Packet %d sniff result: err=%v, domain=%s, sniffCtx=%v", i, err, metadata.Domain, metadata.SniffContext != nil)
|
||||||
|
if metadata.Domain != "" || (err != nil && !errors.Is(err, sniff.ErrNeedMoreData)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print hex dump for debugging
|
||||||
|
t.Logf("First packet hex:\n%s", hex.Dump(packets[0][:min(256, len(packets[0]))]))
|
||||||
|
|
||||||
|
// Log final results
|
||||||
|
t.Logf("Final: Protocol=%s, Domain=%s, Client=%s", metadata.Protocol, metadata.Domain, metadata.Client)
|
||||||
|
|
||||||
|
// Verify SNI extraction
|
||||||
|
if metadata.Domain == "" {
|
||||||
|
t.Errorf("Failed to extract SNI, expected: %s", testSNI)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, testSNI, metadata.Domain, "SNI should match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check client identification - quic-go should be identified as quic-go, not chromium
|
||||||
|
t.Logf("Client identified as: %s (expected: quic-go)", metadata.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("Timeout waiting for QUIC packets")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ func TestSniffQUICChromeNew(t *testing.T) {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
require.Empty(t, metadata.Client)
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
||||||
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
|
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -39,7 +39,7 @@ func TestSniffQUICChromium(t *testing.T) {
|
|||||||
var metadata adapter.InboundContext
|
var metadata adapter.InboundContext
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
require.Empty(t, metadata.Client)
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
||||||
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
|
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
@@ -505,7 +506,24 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readRuleItemString(reader varbin.Reader) ([]string, error) {
|
func readRuleItemString(reader varbin.Reader) ([]string, error) {
|
||||||
return varbin.ReadValue[[]string](reader, binary.BigEndian)
|
length, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]string, length)
|
||||||
|
for i := range result {
|
||||||
|
strLen, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf := make([]byte, strLen)
|
||||||
|
_, err = io.ReadFull(reader, buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result[i] = string(buf)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) error {
|
func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) error {
|
||||||
@@ -513,11 +531,34 @@ func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return varbin.Write(writer, binary.BigEndian, value)
|
_, err = varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, s := range value {
|
||||||
|
_, err = varbin.WriteUvarint(writer, uint64(len(s)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readRuleItemUint8[E ~uint8](reader varbin.Reader) ([]E, error) {
|
func readRuleItemUint8[E ~uint8](reader varbin.Reader) ([]E, error) {
|
||||||
return varbin.ReadValue[[]E](reader, binary.BigEndian)
|
length, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]E, length)
|
||||||
|
_, err = io.ReadFull(reader, *(*[]byte)(unsafe.Pointer(&result)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeRuleItemUint8[E ~uint8](writer varbin.Writer, itemType uint8, value []E) error {
|
func writeRuleItemUint8[E ~uint8](writer varbin.Writer, itemType uint8, value []E) error {
|
||||||
@@ -525,11 +566,25 @@ func writeRuleItemUint8[E ~uint8](writer varbin.Writer, itemType uint8, value []
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return varbin.Write(writer, binary.BigEndian, value)
|
_, err = varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write(*(*[]byte)(unsafe.Pointer(&value)))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func readRuleItemUint16(reader varbin.Reader) ([]uint16, error) {
|
func readRuleItemUint16(reader varbin.Reader) ([]uint16, error) {
|
||||||
return varbin.ReadValue[[]uint16](reader, binary.BigEndian)
|
length, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]uint16, length)
|
||||||
|
err = binary.Read(reader, binary.BigEndian, result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeRuleItemUint16(writer varbin.Writer, itemType uint8, value []uint16) error {
|
func writeRuleItemUint16(writer varbin.Writer, itemType uint8, value []uint16) error {
|
||||||
@@ -537,7 +592,11 @@ func writeRuleItemUint16(writer varbin.Writer, itemType uint8, value []uint16) e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return varbin.Write(writer, binary.BigEndian, value)
|
_, err = varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binary.Write(writer, binary.BigEndian, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeRuleItemCIDR(writer varbin.Writer, itemType uint8, value []string) error {
|
func writeRuleItemCIDR(writer varbin.Writer, itemType uint8, value []string) error {
|
||||||
|
|||||||
494
common/srs/compat_test.go
Normal file
494
common/srs/compat_test.go
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
package srs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"net/netip"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
"github.com/sagernet/sing/common/varbin"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go4.org/netipx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Old implementations using varbin reflection-based serialization
|
||||||
|
|
||||||
|
func oldWriteStringSlice(writer varbin.Writer, value []string) error {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.Write(writer, binary.BigEndian, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldReadStringSlice(reader varbin.Reader) ([]string, error) {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.ReadValue[[]string](reader, binary.BigEndian)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldWriteUint8Slice[E ~uint8](writer varbin.Writer, value []E) error {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.Write(writer, binary.BigEndian, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldReadUint8Slice[E ~uint8](reader varbin.Reader) ([]E, error) {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.ReadValue[[]E](reader, binary.BigEndian)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldWriteUint16Slice(writer varbin.Writer, value []uint16) error {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.Write(writer, binary.BigEndian, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldReadUint16Slice(reader varbin.Reader) ([]uint16, error) {
|
||||||
|
//nolint:staticcheck
|
||||||
|
return varbin.ReadValue[[]uint16](reader, binary.BigEndian)
|
||||||
|
}
|
||||||
|
|
||||||
|
func oldWritePrefix(writer varbin.Writer, prefix netip.Prefix) error {
|
||||||
|
//nolint:staticcheck
|
||||||
|
err := varbin.Write(writer, binary.BigEndian, prefix.Addr().AsSlice())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binary.Write(writer, binary.BigEndian, uint8(prefix.Bits()))
|
||||||
|
}
|
||||||
|
|
||||||
|
type oldIPRangeData struct {
|
||||||
|
From []byte
|
||||||
|
To []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: The old writeIPSet had a bug where varbin.Write(writer, binary.BigEndian, data)
|
||||||
|
// with a struct VALUE (not pointer) silently wrote nothing because field.CanSet() returned false.
|
||||||
|
// This caused IP range data to be missing from the output.
|
||||||
|
// The new implementation correctly writes all range data.
|
||||||
|
//
|
||||||
|
// The old readIPSet used varbin.Read with a pre-allocated slice, which worked because
|
||||||
|
// slice elements are addressable and CanSet() returns true for them.
|
||||||
|
//
|
||||||
|
// For compatibility testing, we verify:
|
||||||
|
// 1. New write produces correct output with range data
|
||||||
|
// 2. New read can parse the new format correctly
|
||||||
|
// 3. Round-trip works correctly
|
||||||
|
|
||||||
|
func oldReadIPSet(reader varbin.Reader) (*netipx.IPSet, error) {
|
||||||
|
version, err := reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if version != 1 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var length uint64
|
||||||
|
err = binary.Read(reader, binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ranges := make([]oldIPRangeData, length)
|
||||||
|
//nolint:staticcheck
|
||||||
|
err = varbin.Read(reader, binary.BigEndian, &ranges)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mySet := &myIPSet{
|
||||||
|
rr: make([]myIPRange, len(ranges)),
|
||||||
|
}
|
||||||
|
for i, rangeData := range ranges {
|
||||||
|
mySet.rr[i].from = M.AddrFromIP(rangeData.From)
|
||||||
|
mySet.rr[i].to = M.AddrFromIP(rangeData.To)
|
||||||
|
}
|
||||||
|
return (*netipx.IPSet)(unsafe.Pointer(mySet)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New write functions (without itemType prefix for testing)
|
||||||
|
|
||||||
|
func newWriteStringSlice(writer varbin.Writer, value []string) error {
|
||||||
|
_, err := varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, s := range value {
|
||||||
|
_, err = varbin.WriteUvarint(writer, uint64(len(s)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWriteUint8Slice[E ~uint8](writer varbin.Writer, value []E) error {
|
||||||
|
_, err := varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write(*(*[]byte)(unsafe.Pointer(&value)))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWriteUint16Slice(writer varbin.Writer, value []uint16) error {
|
||||||
|
_, err := varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binary.Write(writer, binary.BigEndian, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWritePrefix(writer varbin.Writer, prefix netip.Prefix) error {
|
||||||
|
addrSlice := prefix.Addr().AsSlice()
|
||||||
|
_, err := varbin.WriteUvarint(writer, uint64(len(addrSlice)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write(addrSlice)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writer.WriteByte(uint8(prefix.Bits()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests
|
||||||
|
|
||||||
|
func TestStringSliceCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
}{
|
||||||
|
{"nil", nil},
|
||||||
|
{"empty", []string{}},
|
||||||
|
{"single_empty", []string{""}},
|
||||||
|
{"single", []string{"test"}},
|
||||||
|
{"multi", []string{"a", "b", "c"}},
|
||||||
|
{"with_empty", []string{"a", "", "c"}},
|
||||||
|
{"utf8", []string{"测试", "テスト", "тест"}},
|
||||||
|
{"long_string", []string{strings.Repeat("x", 128)}},
|
||||||
|
{"many_elements", generateStrings(128)},
|
||||||
|
{"many_elements_256", generateStrings(256)},
|
||||||
|
{"127_byte_string", []string{strings.Repeat("x", 127)}},
|
||||||
|
{"128_byte_string", []string{strings.Repeat("x", 128)}},
|
||||||
|
{"mixed_lengths", []string{"a", strings.Repeat("b", 100), "", strings.Repeat("c", 200)}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Old write
|
||||||
|
var oldBuf bytes.Buffer
|
||||||
|
err := oldWriteStringSlice(&oldBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// New write
|
||||||
|
var newBuf bytes.Buffer
|
||||||
|
err = newWriteStringSlice(&newBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Bytes must match
|
||||||
|
require.Equal(t, oldBuf.Bytes(), newBuf.Bytes(),
|
||||||
|
"mismatch for %q\nold: %x\nnew: %x", tc.name, oldBuf.Bytes(), newBuf.Bytes())
|
||||||
|
|
||||||
|
// New write -> old read
|
||||||
|
readBack, err := oldReadStringSlice(bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireStringSliceEqual(t, tc.input, readBack)
|
||||||
|
|
||||||
|
// Old write -> new read
|
||||||
|
readBack2, err := readRuleItemString(bufio.NewReader(bytes.NewReader(oldBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireStringSliceEqual(t, tc.input, readBack2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint8SliceCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input []uint8
|
||||||
|
}{
|
||||||
|
{"nil", nil},
|
||||||
|
{"empty", []uint8{}},
|
||||||
|
{"single_zero", []uint8{0}},
|
||||||
|
{"single_max", []uint8{255}},
|
||||||
|
{"multi", []uint8{0, 1, 127, 128, 255}},
|
||||||
|
{"boundary", []uint8{0x00, 0x7f, 0x80, 0xff}},
|
||||||
|
{"sequential", generateUint8Slice(256)},
|
||||||
|
{"127_elements", generateUint8Slice(127)},
|
||||||
|
{"128_elements", generateUint8Slice(128)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Old write
|
||||||
|
var oldBuf bytes.Buffer
|
||||||
|
err := oldWriteUint8Slice(&oldBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// New write
|
||||||
|
var newBuf bytes.Buffer
|
||||||
|
err = newWriteUint8Slice(&newBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Bytes must match
|
||||||
|
require.Equal(t, oldBuf.Bytes(), newBuf.Bytes(),
|
||||||
|
"mismatch for %q\nold: %x\nnew: %x", tc.name, oldBuf.Bytes(), newBuf.Bytes())
|
||||||
|
|
||||||
|
// New write -> old read
|
||||||
|
readBack, err := oldReadUint8Slice[uint8](bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireUint8SliceEqual(t, tc.input, readBack)
|
||||||
|
|
||||||
|
// Old write -> new read
|
||||||
|
readBack2, err := readRuleItemUint8[uint8](bufio.NewReader(bytes.NewReader(oldBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireUint8SliceEqual(t, tc.input, readBack2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint16SliceCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input []uint16
|
||||||
|
}{
|
||||||
|
{"nil", nil},
|
||||||
|
{"empty", []uint16{}},
|
||||||
|
{"single_zero", []uint16{0}},
|
||||||
|
{"single_max", []uint16{65535}},
|
||||||
|
{"multi", []uint16{0, 255, 256, 32767, 32768, 65535}},
|
||||||
|
{"ports", []uint16{80, 443, 8080, 8443}},
|
||||||
|
{"127_elements", generateUint16Slice(127)},
|
||||||
|
{"128_elements", generateUint16Slice(128)},
|
||||||
|
{"256_elements", generateUint16Slice(256)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Old write
|
||||||
|
var oldBuf bytes.Buffer
|
||||||
|
err := oldWriteUint16Slice(&oldBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// New write
|
||||||
|
var newBuf bytes.Buffer
|
||||||
|
err = newWriteUint16Slice(&newBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Bytes must match
|
||||||
|
require.Equal(t, oldBuf.Bytes(), newBuf.Bytes(),
|
||||||
|
"mismatch for %q\nold: %x\nnew: %x", tc.name, oldBuf.Bytes(), newBuf.Bytes())
|
||||||
|
|
||||||
|
// New write -> old read
|
||||||
|
readBack, err := oldReadUint16Slice(bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireUint16SliceEqual(t, tc.input, readBack)
|
||||||
|
|
||||||
|
// Old write -> new read
|
||||||
|
readBack2, err := readRuleItemUint16(bufio.NewReader(bytes.NewReader(oldBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireUint16SliceEqual(t, tc.input, readBack2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrefixCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input netip.Prefix
|
||||||
|
}{
|
||||||
|
{"ipv4_0", netip.MustParsePrefix("0.0.0.0/0")},
|
||||||
|
{"ipv4_8", netip.MustParsePrefix("10.0.0.0/8")},
|
||||||
|
{"ipv4_16", netip.MustParsePrefix("192.168.0.0/16")},
|
||||||
|
{"ipv4_24", netip.MustParsePrefix("192.168.1.0/24")},
|
||||||
|
{"ipv4_32", netip.MustParsePrefix("1.2.3.4/32")},
|
||||||
|
{"ipv6_0", netip.MustParsePrefix("::/0")},
|
||||||
|
{"ipv6_64", netip.MustParsePrefix("2001:db8::/64")},
|
||||||
|
{"ipv6_128", netip.MustParsePrefix("::1/128")},
|
||||||
|
{"ipv6_full", netip.MustParsePrefix("2001:0db8:85a3:0000:0000:8a2e:0370:7334/128")},
|
||||||
|
{"ipv4_private", netip.MustParsePrefix("172.16.0.0/12")},
|
||||||
|
{"ipv6_link_local", netip.MustParsePrefix("fe80::/10")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Old write
|
||||||
|
var oldBuf bytes.Buffer
|
||||||
|
err := oldWritePrefix(&oldBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// New write
|
||||||
|
var newBuf bytes.Buffer
|
||||||
|
err = newWritePrefix(&newBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Bytes must match
|
||||||
|
require.Equal(t, oldBuf.Bytes(), newBuf.Bytes(),
|
||||||
|
"mismatch for %q\nold: %x\nnew: %x", tc.name, oldBuf.Bytes(), newBuf.Bytes())
|
||||||
|
|
||||||
|
// New write -> new read (no old read for prefix)
|
||||||
|
readBack, err := readPrefix(bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.input, readBack)
|
||||||
|
|
||||||
|
// Old write -> new read
|
||||||
|
readBack2, err := readPrefix(bufio.NewReader(bytes.NewReader(oldBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.input, readBack2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPSetCompat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Note: The old writeIPSet was buggy (varbin.Write with struct values wrote nothing).
|
||||||
|
// This test verifies the new implementation writes correct data and round-trips correctly.
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input *netipx.IPSet
|
||||||
|
}{
|
||||||
|
{"single_ipv4", buildIPSet("1.2.3.4")},
|
||||||
|
{"ipv4_range", buildIPSet("192.168.0.0/16")},
|
||||||
|
{"multi_ipv4", buildIPSet("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")},
|
||||||
|
{"single_ipv6", buildIPSet("::1")},
|
||||||
|
{"ipv6_range", buildIPSet("2001:db8::/32")},
|
||||||
|
{"mixed", buildIPSet("10.0.0.0/8", "::1", "2001:db8::/32")},
|
||||||
|
{"large", buildLargeIPSet(100)},
|
||||||
|
{"adjacent_ranges", buildIPSet("192.168.0.0/24", "192.168.1.0/24", "192.168.2.0/24")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// New write
|
||||||
|
var newBuf bytes.Buffer
|
||||||
|
err := writeIPSet(&newBuf, tc.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify format starts with version byte (1) + uint64 count
|
||||||
|
require.True(t, len(newBuf.Bytes()) >= 9, "output too short")
|
||||||
|
require.Equal(t, byte(1), newBuf.Bytes()[0], "version byte mismatch")
|
||||||
|
|
||||||
|
// New write -> old read (varbin.Read with pre-allocated slice works correctly)
|
||||||
|
readBack, err := oldReadIPSet(bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireIPSetEqual(t, tc.input, readBack)
|
||||||
|
|
||||||
|
// New write -> new read
|
||||||
|
readBack2, err := readIPSet(bufio.NewReader(bytes.NewReader(newBuf.Bytes())))
|
||||||
|
require.NoError(t, err)
|
||||||
|
requireIPSetEqual(t, tc.input, readBack2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
func generateStrings(count int) []string {
|
||||||
|
result := make([]string, count)
|
||||||
|
for i := range result {
|
||||||
|
result[i] = strings.Repeat("x", i%50)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateUint8Slice(count int) []uint8 {
|
||||||
|
result := make([]uint8, count)
|
||||||
|
for i := range result {
|
||||||
|
result[i] = uint8(i % 256)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateUint16Slice(count int) []uint16 {
|
||||||
|
result := make([]uint16, count)
|
||||||
|
for i := range result {
|
||||||
|
result[i] = uint16(i * 257)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildIPSet(cidrs ...string) *netipx.IPSet {
|
||||||
|
var builder netipx.IPSetBuilder
|
||||||
|
for _, cidr := range cidrs {
|
||||||
|
prefix, err := netip.ParsePrefix(cidr)
|
||||||
|
if err != nil {
|
||||||
|
addr, err := netip.ParseAddr(cidr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
builder.Add(addr)
|
||||||
|
} else {
|
||||||
|
builder.AddPrefix(prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set, _ := builder.IPSet()
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLargeIPSet(count int) *netipx.IPSet {
|
||||||
|
var builder netipx.IPSetBuilder
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
prefix := netip.PrefixFrom(netip.AddrFrom4([4]byte{10, byte(i / 256), byte(i % 256), 0}), 24)
|
||||||
|
builder.AddPrefix(prefix)
|
||||||
|
}
|
||||||
|
set, _ := builder.IPSet()
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireStringSliceEqual(t *testing.T, expected, actual []string) {
|
||||||
|
t.Helper()
|
||||||
|
if len(expected) == 0 && len(actual) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireUint8SliceEqual(t *testing.T, expected, actual []uint8) {
|
||||||
|
t.Helper()
|
||||||
|
if len(expected) == 0 && len(actual) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireUint16SliceEqual(t *testing.T, expected, actual []uint16) {
|
||||||
|
t.Helper()
|
||||||
|
if len(expected) == 0 && len(actual) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireIPSetEqual(t *testing.T, expected, actual *netipx.IPSet) {
|
||||||
|
t.Helper()
|
||||||
|
expectedRanges := expected.Ranges()
|
||||||
|
actualRanges := actual.Ranges()
|
||||||
|
require.Equal(t, len(expectedRanges), len(actualRanges), "range count mismatch")
|
||||||
|
for i := range expectedRanges {
|
||||||
|
require.Equal(t, expectedRanges[i].From(), actualRanges[i].From(), "range[%d].from mismatch", i)
|
||||||
|
require.Equal(t, expectedRanges[i].To(), actualRanges[i].To(), "range[%d].to mismatch", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package srs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -9,11 +10,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func readPrefix(reader varbin.Reader) (netip.Prefix, error) {
|
func readPrefix(reader varbin.Reader) (netip.Prefix, error) {
|
||||||
addrSlice, err := varbin.ReadValue[[]byte](reader, binary.BigEndian)
|
addrLen, err := binary.ReadUvarint(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return netip.Prefix{}, err
|
return netip.Prefix{}, err
|
||||||
}
|
}
|
||||||
prefixBits, err := varbin.ReadValue[uint8](reader, binary.BigEndian)
|
addrSlice := make([]byte, addrLen)
|
||||||
|
_, err = io.ReadFull(reader, addrSlice)
|
||||||
|
if err != nil {
|
||||||
|
return netip.Prefix{}, err
|
||||||
|
}
|
||||||
|
prefixBits, err := reader.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return netip.Prefix{}, err
|
return netip.Prefix{}, err
|
||||||
}
|
}
|
||||||
@@ -21,11 +27,16 @@ func readPrefix(reader varbin.Reader) (netip.Prefix, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writePrefix(writer varbin.Writer, prefix netip.Prefix) error {
|
func writePrefix(writer varbin.Writer, prefix netip.Prefix) error {
|
||||||
err := varbin.Write(writer, binary.BigEndian, prefix.Addr().AsSlice())
|
addrSlice := prefix.Addr().AsSlice()
|
||||||
|
_, err := varbin.WriteUvarint(writer, uint64(len(addrSlice)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = binary.Write(writer, binary.BigEndian, uint8(prefix.Bits()))
|
_, err = writer.Write(addrSlice)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = writer.WriteByte(uint8(prefix.Bits()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package srs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
"github.com/sagernet/sing/common/varbin"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
|
|
||||||
@@ -22,11 +22,6 @@ type myIPRange struct {
|
|||||||
to netip.Addr
|
to netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
type myIPRangeData struct {
|
|
||||||
From []byte
|
|
||||||
To []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func readIPSet(reader varbin.Reader) (*netipx.IPSet, error) {
|
func readIPSet(reader varbin.Reader) (*netipx.IPSet, error) {
|
||||||
version, err := reader.ReadByte()
|
version, err := reader.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -41,17 +36,30 @@ func readIPSet(reader varbin.Reader) (*netipx.IPSet, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ranges := make([]myIPRangeData, length)
|
|
||||||
err = varbin.Read(reader, binary.BigEndian, &ranges)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mySet := &myIPSet{
|
mySet := &myIPSet{
|
||||||
rr: make([]myIPRange, len(ranges)),
|
rr: make([]myIPRange, length),
|
||||||
}
|
}
|
||||||
for i, rangeData := range ranges {
|
for i := range mySet.rr {
|
||||||
mySet.rr[i].from = M.AddrFromIP(rangeData.From)
|
fromLen, err := binary.ReadUvarint(reader)
|
||||||
mySet.rr[i].to = M.AddrFromIP(rangeData.To)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fromBytes := make([]byte, fromLen)
|
||||||
|
_, err = io.ReadFull(reader, fromBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
toLen, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
toBytes := make([]byte, toLen)
|
||||||
|
_, err = io.ReadFull(reader, toBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mySet.rr[i].from = M.AddrFromIP(fromBytes)
|
||||||
|
mySet.rr[i].to = M.AddrFromIP(toBytes)
|
||||||
}
|
}
|
||||||
return (*netipx.IPSet)(unsafe.Pointer(mySet)), nil
|
return (*netipx.IPSet)(unsafe.Pointer(mySet)), nil
|
||||||
}
|
}
|
||||||
@@ -61,18 +69,27 @@ func writeIPSet(writer varbin.Writer, set *netipx.IPSet) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dataList := common.Map((*myIPSet)(unsafe.Pointer(set)).rr, func(rr myIPRange) myIPRangeData {
|
mySet := (*myIPSet)(unsafe.Pointer(set))
|
||||||
return myIPRangeData{
|
err = binary.Write(writer, binary.BigEndian, uint64(len(mySet.rr)))
|
||||||
From: rr.from.AsSlice(),
|
|
||||||
To: rr.to.AsSlice(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
err = binary.Write(writer, binary.BigEndian, uint64(len(dataList)))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, data := range dataList {
|
for _, rr := range mySet.rr {
|
||||||
err = varbin.Write(writer, binary.BigEndian, data)
|
fromBytes := rr.from.AsSlice()
|
||||||
|
_, err = varbin.WriteUvarint(writer, uint64(len(fromBytes)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write(fromBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
toBytes := rr.to.AsSlice()
|
||||||
|
_, err = varbin.WriteUvarint(writer, uint64(len(toBytes)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write(toBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
|
"github.com/libdns/acmedns"
|
||||||
"github.com/libdns/alidns"
|
"github.com/libdns/alidns"
|
||||||
"github.com/libdns/cloudflare"
|
"github.com/libdns/cloudflare"
|
||||||
"github.com/mholt/acmez/v3/acme"
|
"github.com/mholt/acmez/v3/acme"
|
||||||
@@ -126,6 +127,13 @@ func startACME(ctx context.Context, logger logger.Logger, options option.Inbound
|
|||||||
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
APIToken: dnsOptions.CloudflareOptions.APIToken,
|
||||||
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
ZoneToken: dnsOptions.CloudflareOptions.ZoneToken,
|
||||||
}
|
}
|
||||||
|
case C.DNSProviderACMEDNS:
|
||||||
|
solver.DNSProvider = &acmedns.Provider{
|
||||||
|
Username: dnsOptions.ACMEDNSOptions.Username,
|
||||||
|
Password: dnsOptions.ACMEDNSOptions.Password,
|
||||||
|
Subdomain: dnsOptions.ACMEDNSOptions.Subdomain,
|
||||||
|
ServerURL: dnsOptions.ACMEDNSOptions.ServerURL,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
|
return nil, nil, E.New("unsupported ACME DNS01 provider type: " + dnsOptions.Provider)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,4 +33,5 @@ const (
|
|||||||
const (
|
const (
|
||||||
DNSProviderAliDNS = "alidns"
|
DNSProviderAliDNS = "alidns"
|
||||||
DNSProviderCloudflare = "cloudflare"
|
DNSProviderCloudflare = "cloudflare"
|
||||||
|
DNSProviderACMEDNS = "acmedns"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,702 +0,0 @@
|
|||||||
package daemon
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
unsafe "unsafe"
|
|
||||||
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type SubscribeHelperRequestRequest struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
AcceptGetWIFIStateRequests bool `protobuf:"varint,1,opt,name=acceptGetWIFIStateRequests,proto3" json:"acceptGetWIFIStateRequests,omitempty"`
|
|
||||||
AcceptFindConnectionOwnerRequests bool `protobuf:"varint,2,opt,name=acceptFindConnectionOwnerRequests,proto3" json:"acceptFindConnectionOwnerRequests,omitempty"`
|
|
||||||
AcceptSendNotificationRequests bool `protobuf:"varint,3,opt,name=acceptSendNotificationRequests,proto3" json:"acceptSendNotificationRequests,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeHelperRequestRequest) Reset() {
|
|
||||||
*x = SubscribeHelperRequestRequest{}
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeHelperRequestRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SubscribeHelperRequestRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *SubscribeHelperRequestRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[0]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use SubscribeHelperRequestRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*SubscribeHelperRequestRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_helper_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeHelperRequestRequest) GetAcceptGetWIFIStateRequests() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.AcceptGetWIFIStateRequests
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeHelperRequestRequest) GetAcceptFindConnectionOwnerRequests() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.AcceptFindConnectionOwnerRequests
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeHelperRequestRequest) GetAcceptSendNotificationRequests() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.AcceptSendNotificationRequests
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperRequest struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
|
||||||
// Types that are valid to be assigned to Request:
|
|
||||||
//
|
|
||||||
// *HelperRequest_GetWIFIState
|
|
||||||
// *HelperRequest_FindConnectionOwner
|
|
||||||
// *HelperRequest_SendNotification
|
|
||||||
Request isHelperRequest_Request `protobuf_oneof:"request"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperRequest) Reset() {
|
|
||||||
*x = HelperRequest{}
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*HelperRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *HelperRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[1]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use HelperRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*HelperRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_helper_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperRequest) GetId() int64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Id
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperRequest) GetRequest() isHelperRequest_Request {
|
|
||||||
if x != nil {
|
|
||||||
return x.Request
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperRequest) GetGetWIFIState() *emptypb.Empty {
|
|
||||||
if x != nil {
|
|
||||||
if x, ok := x.Request.(*HelperRequest_GetWIFIState); ok {
|
|
||||||
return x.GetWIFIState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperRequest) GetFindConnectionOwner() *FindConnectionOwnerRequest {
|
|
||||||
if x != nil {
|
|
||||||
if x, ok := x.Request.(*HelperRequest_FindConnectionOwner); ok {
|
|
||||||
return x.FindConnectionOwner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperRequest) GetSendNotification() *Notification {
|
|
||||||
if x != nil {
|
|
||||||
if x, ok := x.Request.(*HelperRequest_SendNotification); ok {
|
|
||||||
return x.SendNotification
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type isHelperRequest_Request interface {
|
|
||||||
isHelperRequest_Request()
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperRequest_GetWIFIState struct {
|
|
||||||
GetWIFIState *emptypb.Empty `protobuf:"bytes,2,opt,name=getWIFIState,proto3,oneof"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperRequest_FindConnectionOwner struct {
|
|
||||||
FindConnectionOwner *FindConnectionOwnerRequest `protobuf:"bytes,3,opt,name=findConnectionOwner,proto3,oneof"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperRequest_SendNotification struct {
|
|
||||||
SendNotification *Notification `protobuf:"bytes,4,opt,name=sendNotification,proto3,oneof"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*HelperRequest_GetWIFIState) isHelperRequest_Request() {}
|
|
||||||
|
|
||||||
func (*HelperRequest_FindConnectionOwner) isHelperRequest_Request() {}
|
|
||||||
|
|
||||||
func (*HelperRequest_SendNotification) isHelperRequest_Request() {}
|
|
||||||
|
|
||||||
type FindConnectionOwnerRequest struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
IpProtocol int32 `protobuf:"varint,1,opt,name=ipProtocol,proto3" json:"ipProtocol,omitempty"`
|
|
||||||
SourceAddress string `protobuf:"bytes,2,opt,name=sourceAddress,proto3" json:"sourceAddress,omitempty"`
|
|
||||||
SourcePort int32 `protobuf:"varint,3,opt,name=sourcePort,proto3" json:"sourcePort,omitempty"`
|
|
||||||
DestinationAddress string `protobuf:"bytes,4,opt,name=destinationAddress,proto3" json:"destinationAddress,omitempty"`
|
|
||||||
DestinationPort int32 `protobuf:"varint,5,opt,name=destinationPort,proto3" json:"destinationPort,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) Reset() {
|
|
||||||
*x = FindConnectionOwnerRequest{}
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[2]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*FindConnectionOwnerRequest) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[2]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use FindConnectionOwnerRequest.ProtoReflect.Descriptor instead.
|
|
||||||
func (*FindConnectionOwnerRequest) Descriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_helper_proto_rawDescGZIP(), []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) GetIpProtocol() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.IpProtocol
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) GetSourceAddress() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.SourceAddress
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) GetSourcePort() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.SourcePort
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) GetDestinationAddress() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.DestinationAddress
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *FindConnectionOwnerRequest) GetDestinationPort() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.DestinationPort
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type Notification struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
|
|
||||||
TypeName string `protobuf:"bytes,2,opt,name=typeName,proto3" json:"typeName,omitempty"`
|
|
||||||
TypeId int32 `protobuf:"varint,3,opt,name=typeId,proto3" json:"typeId,omitempty"`
|
|
||||||
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
|
|
||||||
Subtitle string `protobuf:"bytes,5,opt,name=subtitle,proto3" json:"subtitle,omitempty"`
|
|
||||||
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
|
|
||||||
OpenURL string `protobuf:"bytes,7,opt,name=openURL,proto3" json:"openURL,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) Reset() {
|
|
||||||
*x = Notification{}
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[3]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Notification) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Notification) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[3]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Notification) Descriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_helper_proto_rawDescGZIP(), []int{3}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) GetIdentifier() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Identifier
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) GetTypeName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.TypeName
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) GetTypeId() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.TypeId
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) GetTitle() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Title
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) GetSubtitle() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Subtitle
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) GetBody() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Body
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Notification) GetOpenURL() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.OpenURL
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperResponse struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
|
||||||
// Types that are valid to be assigned to Response:
|
|
||||||
//
|
|
||||||
// *HelperResponse_WifiState
|
|
||||||
// *HelperResponse_Error
|
|
||||||
// *HelperResponse_ConnectionOwner
|
|
||||||
Response isHelperResponse_Response `protobuf_oneof:"response"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperResponse) Reset() {
|
|
||||||
*x = HelperResponse{}
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[4]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperResponse) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*HelperResponse) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *HelperResponse) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[4]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use HelperResponse.ProtoReflect.Descriptor instead.
|
|
||||||
func (*HelperResponse) Descriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_helper_proto_rawDescGZIP(), []int{4}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperResponse) GetId() int64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Id
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperResponse) GetResponse() isHelperResponse_Response {
|
|
||||||
if x != nil {
|
|
||||||
return x.Response
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperResponse) GetWifiState() *WIFIState {
|
|
||||||
if x != nil {
|
|
||||||
if x, ok := x.Response.(*HelperResponse_WifiState); ok {
|
|
||||||
return x.WifiState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperResponse) GetError() string {
|
|
||||||
if x != nil {
|
|
||||||
if x, ok := x.Response.(*HelperResponse_Error); ok {
|
|
||||||
return x.Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *HelperResponse) GetConnectionOwner() *ConnectionOwner {
|
|
||||||
if x != nil {
|
|
||||||
if x, ok := x.Response.(*HelperResponse_ConnectionOwner); ok {
|
|
||||||
return x.ConnectionOwner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type isHelperResponse_Response interface {
|
|
||||||
isHelperResponse_Response()
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperResponse_WifiState struct {
|
|
||||||
WifiState *WIFIState `protobuf:"bytes,2,opt,name=wifiState,proto3,oneof"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperResponse_Error struct {
|
|
||||||
Error string `protobuf:"bytes,3,opt,name=error,proto3,oneof"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HelperResponse_ConnectionOwner struct {
|
|
||||||
ConnectionOwner *ConnectionOwner `protobuf:"bytes,4,opt,name=connectionOwner,proto3,oneof"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*HelperResponse_WifiState) isHelperResponse_Response() {}
|
|
||||||
|
|
||||||
func (*HelperResponse_Error) isHelperResponse_Response() {}
|
|
||||||
|
|
||||||
func (*HelperResponse_ConnectionOwner) isHelperResponse_Response() {}
|
|
||||||
|
|
||||||
type ConnectionOwner struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
UserId int32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"`
|
|
||||||
UserName string `protobuf:"bytes,2,opt,name=userName,proto3" json:"userName,omitempty"`
|
|
||||||
ProcessPath string `protobuf:"bytes,3,opt,name=processPath,proto3" json:"processPath,omitempty"`
|
|
||||||
AndroidPackageName string `protobuf:"bytes,4,opt,name=androidPackageName,proto3" json:"androidPackageName,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ConnectionOwner) Reset() {
|
|
||||||
*x = ConnectionOwner{}
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[5]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ConnectionOwner) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ConnectionOwner) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *ConnectionOwner) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[5]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ConnectionOwner.ProtoReflect.Descriptor instead.
|
|
||||||
func (*ConnectionOwner) Descriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_helper_proto_rawDescGZIP(), []int{5}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ConnectionOwner) GetUserId() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.UserId
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ConnectionOwner) GetUserName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.UserName
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ConnectionOwner) GetProcessPath() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.ProcessPath
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ConnectionOwner) GetAndroidPackageName() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.AndroidPackageName
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type WIFIState struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
Ssid string `protobuf:"bytes,1,opt,name=ssid,proto3" json:"ssid,omitempty"`
|
|
||||||
Bssid string `protobuf:"bytes,2,opt,name=bssid,proto3" json:"bssid,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WIFIState) Reset() {
|
|
||||||
*x = WIFIState{}
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[6]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WIFIState) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*WIFIState) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *WIFIState) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_daemon_helper_proto_msgTypes[6]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use WIFIState.ProtoReflect.Descriptor instead.
|
|
||||||
func (*WIFIState) Descriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_helper_proto_rawDescGZIP(), []int{6}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WIFIState) GetSsid() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Ssid
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WIFIState) GetBssid() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Bssid
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_daemon_helper_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
const file_daemon_helper_proto_rawDesc = "" +
|
|
||||||
"\n" +
|
|
||||||
"\x13daemon/helper.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xf5\x01\n" +
|
|
||||||
"\x1dSubscribeHelperRequestRequest\x12>\n" +
|
|
||||||
"\x1aacceptGetWIFIStateRequests\x18\x01 \x01(\bR\x1aacceptGetWIFIStateRequests\x12L\n" +
|
|
||||||
"!acceptFindConnectionOwnerRequests\x18\x02 \x01(\bR!acceptFindConnectionOwnerRequests\x12F\n" +
|
|
||||||
"\x1eacceptSendNotificationRequests\x18\x03 \x01(\bR\x1eacceptSendNotificationRequests\"\x84\x02\n" +
|
|
||||||
"\rHelperRequest\x12\x0e\n" +
|
|
||||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12<\n" +
|
|
||||||
"\fgetWIFIState\x18\x02 \x01(\v2\x16.google.protobuf.EmptyH\x00R\fgetWIFIState\x12V\n" +
|
|
||||||
"\x13findConnectionOwner\x18\x03 \x01(\v2\".daemon.FindConnectionOwnerRequestH\x00R\x13findConnectionOwner\x12B\n" +
|
|
||||||
"\x10sendNotification\x18\x04 \x01(\v2\x14.daemon.NotificationH\x00R\x10sendNotificationB\t\n" +
|
|
||||||
"\arequest\"\xdc\x01\n" +
|
|
||||||
"\x1aFindConnectionOwnerRequest\x12\x1e\n" +
|
|
||||||
"\n" +
|
|
||||||
"ipProtocol\x18\x01 \x01(\x05R\n" +
|
|
||||||
"ipProtocol\x12$\n" +
|
|
||||||
"\rsourceAddress\x18\x02 \x01(\tR\rsourceAddress\x12\x1e\n" +
|
|
||||||
"\n" +
|
|
||||||
"sourcePort\x18\x03 \x01(\x05R\n" +
|
|
||||||
"sourcePort\x12.\n" +
|
|
||||||
"\x12destinationAddress\x18\x04 \x01(\tR\x12destinationAddress\x12(\n" +
|
|
||||||
"\x0fdestinationPort\x18\x05 \x01(\x05R\x0fdestinationPort\"\xc2\x01\n" +
|
|
||||||
"\fNotification\x12\x1e\n" +
|
|
||||||
"\n" +
|
|
||||||
"identifier\x18\x01 \x01(\tR\n" +
|
|
||||||
"identifier\x12\x1a\n" +
|
|
||||||
"\btypeName\x18\x02 \x01(\tR\btypeName\x12\x16\n" +
|
|
||||||
"\x06typeId\x18\x03 \x01(\x05R\x06typeId\x12\x14\n" +
|
|
||||||
"\x05title\x18\x04 \x01(\tR\x05title\x12\x1a\n" +
|
|
||||||
"\bsubtitle\x18\x05 \x01(\tR\bsubtitle\x12\x12\n" +
|
|
||||||
"\x04body\x18\x06 \x01(\tR\x04body\x12\x18\n" +
|
|
||||||
"\aopenURL\x18\a \x01(\tR\aopenURL\"\xbc\x01\n" +
|
|
||||||
"\x0eHelperResponse\x12\x0e\n" +
|
|
||||||
"\x02id\x18\x01 \x01(\x03R\x02id\x121\n" +
|
|
||||||
"\twifiState\x18\x02 \x01(\v2\x11.daemon.WIFIStateH\x00R\twifiState\x12\x16\n" +
|
|
||||||
"\x05error\x18\x03 \x01(\tH\x00R\x05error\x12C\n" +
|
|
||||||
"\x0fconnectionOwner\x18\x04 \x01(\v2\x17.daemon.ConnectionOwnerH\x00R\x0fconnectionOwnerB\n" +
|
|
||||||
"\n" +
|
|
||||||
"\bresponse\"\x97\x01\n" +
|
|
||||||
"\x0fConnectionOwner\x12\x16\n" +
|
|
||||||
"\x06userId\x18\x01 \x01(\x05R\x06userId\x12\x1a\n" +
|
|
||||||
"\buserName\x18\x02 \x01(\tR\buserName\x12 \n" +
|
|
||||||
"\vprocessPath\x18\x03 \x01(\tR\vprocessPath\x12.\n" +
|
|
||||||
"\x12androidPackageName\x18\x04 \x01(\tR\x12androidPackageName\"5\n" +
|
|
||||||
"\tWIFIState\x12\x12\n" +
|
|
||||||
"\x04ssid\x18\x01 \x01(\tR\x04ssid\x12\x14\n" +
|
|
||||||
"\x05bssid\x18\x02 \x01(\tR\x05bssidB%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_daemon_helper_proto_rawDescOnce sync.Once
|
|
||||||
file_daemon_helper_proto_rawDescData []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_daemon_helper_proto_rawDescGZIP() []byte {
|
|
||||||
file_daemon_helper_proto_rawDescOnce.Do(func() {
|
|
||||||
file_daemon_helper_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)))
|
|
||||||
})
|
|
||||||
return file_daemon_helper_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_daemon_helper_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
|
||||||
file_daemon_helper_proto_goTypes = []any{
|
|
||||||
(*SubscribeHelperRequestRequest)(nil), // 0: daemon.SubscribeHelperRequestRequest
|
|
||||||
(*HelperRequest)(nil), // 1: daemon.HelperRequest
|
|
||||||
(*FindConnectionOwnerRequest)(nil), // 2: daemon.FindConnectionOwnerRequest
|
|
||||||
(*Notification)(nil), // 3: daemon.Notification
|
|
||||||
(*HelperResponse)(nil), // 4: daemon.HelperResponse
|
|
||||||
(*ConnectionOwner)(nil), // 5: daemon.ConnectionOwner
|
|
||||||
(*WIFIState)(nil), // 6: daemon.WIFIState
|
|
||||||
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var file_daemon_helper_proto_depIdxs = []int32{
|
|
||||||
7, // 0: daemon.HelperRequest.getWIFIState:type_name -> google.protobuf.Empty
|
|
||||||
2, // 1: daemon.HelperRequest.findConnectionOwner:type_name -> daemon.FindConnectionOwnerRequest
|
|
||||||
3, // 2: daemon.HelperRequest.sendNotification:type_name -> daemon.Notification
|
|
||||||
6, // 3: daemon.HelperResponse.wifiState:type_name -> daemon.WIFIState
|
|
||||||
5, // 4: daemon.HelperResponse.connectionOwner:type_name -> daemon.ConnectionOwner
|
|
||||||
5, // [5:5] is the sub-list for method output_type
|
|
||||||
5, // [5:5] is the sub-list for method input_type
|
|
||||||
5, // [5:5] is the sub-list for extension type_name
|
|
||||||
5, // [5:5] is the sub-list for extension extendee
|
|
||||||
0, // [0:5] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_daemon_helper_proto_init() }
|
|
||||||
func file_daemon_helper_proto_init() {
|
|
||||||
if File_daemon_helper_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
file_daemon_helper_proto_msgTypes[1].OneofWrappers = []any{
|
|
||||||
(*HelperRequest_GetWIFIState)(nil),
|
|
||||||
(*HelperRequest_FindConnectionOwner)(nil),
|
|
||||||
(*HelperRequest_SendNotification)(nil),
|
|
||||||
}
|
|
||||||
file_daemon_helper_proto_msgTypes[4].OneofWrappers = []any{
|
|
||||||
(*HelperResponse_WifiState)(nil),
|
|
||||||
(*HelperResponse_Error)(nil),
|
|
||||||
(*HelperResponse_ConnectionOwner)(nil),
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)),
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 7,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_daemon_helper_proto_goTypes,
|
|
||||||
DependencyIndexes: file_daemon_helper_proto_depIdxs,
|
|
||||||
MessageInfos: file_daemon_helper_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_daemon_helper_proto = out.File
|
|
||||||
file_daemon_helper_proto_goTypes = nil
|
|
||||||
file_daemon_helper_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package daemon;
|
|
||||||
option go_package = "github.com/sagernet/sing-box/daemon";
|
|
||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
|
||||||
|
|
||||||
message SubscribeHelperRequestRequest {
|
|
||||||
bool acceptGetWIFIStateRequests = 1;
|
|
||||||
bool acceptFindConnectionOwnerRequests = 2;
|
|
||||||
bool acceptSendNotificationRequests = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message HelperRequest {
|
|
||||||
int64 id = 1;
|
|
||||||
oneof request {
|
|
||||||
google.protobuf.Empty getWIFIState = 2;
|
|
||||||
FindConnectionOwnerRequest findConnectionOwner = 3;
|
|
||||||
Notification sendNotification = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message FindConnectionOwnerRequest {
|
|
||||||
int32 ipProtocol = 1;
|
|
||||||
string sourceAddress = 2;
|
|
||||||
int32 sourcePort = 3;
|
|
||||||
string destinationAddress = 4;
|
|
||||||
int32 destinationPort = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Notification {
|
|
||||||
string identifier = 1;
|
|
||||||
string typeName = 2;
|
|
||||||
int32 typeId = 3;
|
|
||||||
string title = 4;
|
|
||||||
string subtitle = 5;
|
|
||||||
string body = 6;
|
|
||||||
string openURL = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message HelperResponse {
|
|
||||||
int64 id = 1;
|
|
||||||
oneof response {
|
|
||||||
WIFIState wifiState = 2;
|
|
||||||
string error = 3;
|
|
||||||
ConnectionOwner connectionOwner = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message ConnectionOwner {
|
|
||||||
int32 userId = 1;
|
|
||||||
string userName = 2;
|
|
||||||
string processPath = 3;
|
|
||||||
string androidPackageName = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message WIFIState {
|
|
||||||
string ssid = 1;
|
|
||||||
string bssid = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
"github.com/sagernet/sing-box/include"
|
"github.com/sagernet/sing-box/include"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
"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/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
@@ -103,6 +104,7 @@ func (s *StartedService) newInstance(profileContent string, overrideOptions *Ove
|
|||||||
i.clashServer = service.FromContext[adapter.ClashServer](ctx)
|
i.clashServer = service.FromContext[adapter.ClashServer](ctx)
|
||||||
i.pauseManager = service.FromContext[pause.Manager](ctx)
|
i.pauseManager = service.FromContext[pause.Manager](ctx)
|
||||||
i.cacheFile = service.FromContext[adapter.CacheFile](ctx)
|
i.cacheFile = service.FromContext[adapter.CacheFile](ctx)
|
||||||
|
log.SetStdLogger(boxInstance.LogFactory().Logger())
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ type StartedService struct {
|
|||||||
urlTestHistoryStorage *urltest.HistoryStorage
|
urlTestHistoryStorage *urltest.HistoryStorage
|
||||||
clashModeSubscriber *observable.Subscriber[struct{}]
|
clashModeSubscriber *observable.Subscriber[struct{}]
|
||||||
clashModeObserver *observable.Observer[struct{}]
|
clashModeObserver *observable.Observer[struct{}]
|
||||||
|
|
||||||
|
connectionEventSubscriber *observable.Subscriber[trafficontrol.ConnectionEvent]
|
||||||
|
connectionEventObserver *observable.Observer[trafficontrol.ConnectionEvent]
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceOptions struct {
|
type ServiceOptions struct {
|
||||||
@@ -83,17 +86,19 @@ func NewStartedService(options ServiceOptions) *StartedService {
|
|||||||
// userID: options.UserID,
|
// userID: options.UserID,
|
||||||
// groupID: options.GroupID,
|
// groupID: options.GroupID,
|
||||||
// systemProxyEnabled: options.SystemProxyEnabled,
|
// systemProxyEnabled: options.SystemProxyEnabled,
|
||||||
serviceStatus: &ServiceStatus{Status: ServiceStatus_IDLE},
|
serviceStatus: &ServiceStatus{Status: ServiceStatus_IDLE},
|
||||||
serviceStatusSubscriber: observable.NewSubscriber[*ServiceStatus](4),
|
serviceStatusSubscriber: observable.NewSubscriber[*ServiceStatus](4),
|
||||||
logSubscriber: observable.NewSubscriber[*log.Entry](128),
|
logSubscriber: observable.NewSubscriber[*log.Entry](128),
|
||||||
urlTestSubscriber: observable.NewSubscriber[struct{}](1),
|
urlTestSubscriber: observable.NewSubscriber[struct{}](1),
|
||||||
urlTestHistoryStorage: urltest.NewHistoryStorage(),
|
urlTestHistoryStorage: urltest.NewHistoryStorage(),
|
||||||
clashModeSubscriber: observable.NewSubscriber[struct{}](1),
|
clashModeSubscriber: observable.NewSubscriber[struct{}](1),
|
||||||
|
connectionEventSubscriber: observable.NewSubscriber[trafficontrol.ConnectionEvent](256),
|
||||||
}
|
}
|
||||||
s.serviceStatusObserver = observable.NewObserver(s.serviceStatusSubscriber, 2)
|
s.serviceStatusObserver = observable.NewObserver(s.serviceStatusSubscriber, 2)
|
||||||
s.logObserver = observable.NewObserver(s.logSubscriber, 64)
|
s.logObserver = observable.NewObserver(s.logSubscriber, 64)
|
||||||
s.urlTestObserver = observable.NewObserver(s.urlTestSubscriber, 1)
|
s.urlTestObserver = observable.NewObserver(s.urlTestSubscriber, 1)
|
||||||
s.clashModeObserver = observable.NewObserver(s.clashModeSubscriber, 1)
|
s.clashModeObserver = observable.NewObserver(s.clashModeSubscriber, 1)
|
||||||
|
s.connectionEventObserver = observable.NewObserver(s.connectionEventSubscriber, 64)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +188,7 @@ func (s *StartedService) StartOrReloadService(profileContent string, options *Ov
|
|||||||
instance.urlTestHistoryStorage.SetHook(s.urlTestSubscriber)
|
instance.urlTestHistoryStorage.SetHook(s.urlTestSubscriber)
|
||||||
if instance.clashServer != nil {
|
if instance.clashServer != nil {
|
||||||
instance.clashServer.SetModeUpdateHook(s.clashModeSubscriber)
|
instance.clashServer.SetModeUpdateHook(s.clashModeSubscriber)
|
||||||
|
instance.clashServer.(*clashapi.Server).TrafficManager().SetEventHook(s.connectionEventSubscriber)
|
||||||
}
|
}
|
||||||
s.serviceAccess.Unlock()
|
s.serviceAccess.Unlock()
|
||||||
err = instance.Start()
|
err = instance.Start()
|
||||||
@@ -666,7 +672,7 @@ func (s *StartedService) SetSystemProxyEnabled(ctx context.Context, request *Set
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StartedService) SubscribeConnections(request *SubscribeConnectionsRequest, server grpc.ServerStreamingServer[Connections]) error {
|
func (s *StartedService) SubscribeConnections(request *SubscribeConnectionsRequest, server grpc.ServerStreamingServer[ConnectionEvents]) error {
|
||||||
err := s.waitForStarted(server.Context())
|
err := s.waitForStarted(server.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -674,69 +680,260 @@ func (s *StartedService) SubscribeConnections(request *SubscribeConnectionsReque
|
|||||||
s.serviceAccess.RLock()
|
s.serviceAccess.RLock()
|
||||||
boxService := s.instance
|
boxService := s.instance
|
||||||
s.serviceAccess.RUnlock()
|
s.serviceAccess.RUnlock()
|
||||||
ticker := time.NewTicker(time.Duration(request.Interval))
|
|
||||||
defer ticker.Stop()
|
if boxService.clashServer == nil {
|
||||||
|
return E.New("clash server not available")
|
||||||
|
}
|
||||||
|
|
||||||
trafficManager := boxService.clashServer.(*clashapi.Server).TrafficManager()
|
trafficManager := boxService.clashServer.(*clashapi.Server).TrafficManager()
|
||||||
var (
|
|
||||||
connections = make(map[uuid.UUID]*Connection)
|
subscription, done, err := s.connectionEventObserver.Subscribe()
|
||||||
outConnections []*Connection
|
if err != nil {
|
||||||
)
|
return err
|
||||||
|
}
|
||||||
|
defer s.connectionEventObserver.UnSubscribe(subscription)
|
||||||
|
|
||||||
|
connectionSnapshots := make(map[uuid.UUID]connectionSnapshot)
|
||||||
|
initialEvents := s.buildInitialConnectionState(trafficManager, connectionSnapshots)
|
||||||
|
err = server.Send(&ConnectionEvents{
|
||||||
|
Events: initialEvents,
|
||||||
|
Reset_: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
interval := time.Duration(request.Interval)
|
||||||
|
if interval <= 0 {
|
||||||
|
interval = time.Second
|
||||||
|
}
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
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 {
|
select {
|
||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
return s.ctx.Err()
|
return s.ctx.Err()
|
||||||
case <-server.Context().Done():
|
case <-server.Context().Done():
|
||||||
return server.Context().Err()
|
return server.Context().Err()
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case event := <-subscription:
|
||||||
|
var pendingEvents []*ConnectionEvent
|
||||||
|
if protoEvent := s.applyConnectionEvent(event, connectionSnapshots); protoEvent != nil {
|
||||||
|
pendingEvents = append(pendingEvents, protoEvent)
|
||||||
|
}
|
||||||
|
drain:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event = <-subscription:
|
||||||
|
if protoEvent := s.applyConnectionEvent(event, connectionSnapshots); protoEvent != nil {
|
||||||
|
pendingEvents = append(pendingEvents, protoEvent)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break drain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(pendingEvents) > 0 {
|
||||||
|
err = server.Send(&ConnectionEvents{Events: pendingEvents})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
|
protoEvents := s.buildTrafficUpdates(trafficManager, connectionSnapshots)
|
||||||
|
if len(protoEvents) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = server.Send(&ConnectionEvents{Events: protoEvents})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol.TrackerMetadata, isClosed bool) *Connection {
|
type connectionSnapshot struct {
|
||||||
if oldConnection, loaded := connections[metadata.ID]; loaded {
|
uplink int64
|
||||||
if isClosed {
|
downlink int64
|
||||||
if oldConnection.ClosedAt == 0 {
|
hadTraffic bool
|
||||||
oldConnection.Uplink = 0
|
}
|
||||||
oldConnection.Downlink = 0
|
|
||||||
oldConnection.ClosedAt = metadata.ClosedAt.UnixMilli()
|
func (s *StartedService) buildInitialConnectionState(manager *trafficontrol.Manager, snapshots map[uuid.UUID]connectionSnapshot) []*ConnectionEvent {
|
||||||
}
|
var events []*ConnectionEvent
|
||||||
return oldConnection
|
|
||||||
|
for _, metadata := range manager.Connections() {
|
||||||
|
events = append(events, &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
Connection: buildConnectionProto(metadata),
|
||||||
|
})
|
||||||
|
snapshots[metadata.ID] = connectionSnapshot{
|
||||||
|
uplink: metadata.Upload.Load(),
|
||||||
|
downlink: metadata.Download.Load(),
|
||||||
}
|
}
|
||||||
lastUplink := oldConnection.UplinkTotal
|
|
||||||
lastDownlink := oldConnection.DownlinkTotal
|
|
||||||
uplinkTotal := metadata.Upload.Load()
|
|
||||||
downlinkTotal := metadata.Download.Load()
|
|
||||||
oldConnection.Uplink = uplinkTotal - lastUplink
|
|
||||||
oldConnection.Downlink = downlinkTotal - lastDownlink
|
|
||||||
oldConnection.UplinkTotal = uplinkTotal
|
|
||||||
oldConnection.DownlinkTotal = downlinkTotal
|
|
||||||
return oldConnection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, metadata := range manager.ClosedConnections() {
|
||||||
|
conn := buildConnectionProto(metadata)
|
||||||
|
conn.ClosedAt = metadata.ClosedAt.UnixMilli()
|
||||||
|
events = append(events, &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
Connection: conn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) applyConnectionEvent(event trafficontrol.ConnectionEvent, snapshots map[uuid.UUID]connectionSnapshot) *ConnectionEvent {
|
||||||
|
switch event.Type {
|
||||||
|
case trafficontrol.ConnectionEventNew:
|
||||||
|
if _, exists := snapshots[event.ID]; exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
snapshots[event.ID] = connectionSnapshot{
|
||||||
|
uplink: event.Metadata.Upload.Load(),
|
||||||
|
downlink: event.Metadata.Download.Load(),
|
||||||
|
}
|
||||||
|
return &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||||
|
Id: event.ID.String(),
|
||||||
|
Connection: buildConnectionProto(event.Metadata),
|
||||||
|
}
|
||||||
|
case trafficontrol.ConnectionEventClosed:
|
||||||
|
delete(snapshots, event.ID)
|
||||||
|
protoEvent := &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_CLOSED,
|
||||||
|
Id: event.ID.String(),
|
||||||
|
}
|
||||||
|
closedAt := event.ClosedAt
|
||||||
|
if closedAt.IsZero() && !event.Metadata.ClosedAt.IsZero() {
|
||||||
|
closedAt = event.Metadata.ClosedAt
|
||||||
|
}
|
||||||
|
if closedAt.IsZero() {
|
||||||
|
closedAt = time.Now()
|
||||||
|
}
|
||||||
|
protoEvent.ClosedAt = closedAt.UnixMilli()
|
||||||
|
if event.Metadata.ID != uuid.Nil {
|
||||||
|
conn := buildConnectionProto(event.Metadata)
|
||||||
|
conn.ClosedAt = protoEvent.ClosedAt
|
||||||
|
protoEvent.Connection = conn
|
||||||
|
}
|
||||||
|
return protoEvent
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) buildTrafficUpdates(manager *trafficontrol.Manager, snapshots map[uuid.UUID]connectionSnapshot) []*ConnectionEvent {
|
||||||
|
activeConnections := manager.Connections()
|
||||||
|
activeIndex := make(map[uuid.UUID]*trafficontrol.TrackerMetadata, len(activeConnections))
|
||||||
|
var events []*ConnectionEvent
|
||||||
|
|
||||||
|
for _, metadata := range activeConnections {
|
||||||
|
activeIndex[metadata.ID] = metadata
|
||||||
|
currentUpload := metadata.Upload.Load()
|
||||||
|
currentDownload := metadata.Download.Load()
|
||||||
|
snapshot, exists := snapshots[metadata.ID]
|
||||||
|
if !exists {
|
||||||
|
snapshots[metadata.ID] = connectionSnapshot{
|
||||||
|
uplink: currentUpload,
|
||||||
|
downlink: currentDownload,
|
||||||
|
}
|
||||||
|
events = append(events, &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_NEW,
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
Connection: buildConnectionProto(metadata),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uplinkDelta := currentUpload - snapshot.uplink
|
||||||
|
downlinkDelta := currentDownload - snapshot.downlink
|
||||||
|
if uplinkDelta < 0 || downlinkDelta < 0 {
|
||||||
|
if snapshot.hadTraffic {
|
||||||
|
events = append(events, &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_UPDATE,
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
UplinkDelta: 0,
|
||||||
|
DownlinkDelta: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
snapshot.uplink = currentUpload
|
||||||
|
snapshot.downlink = currentDownload
|
||||||
|
snapshot.hadTraffic = false
|
||||||
|
snapshots[metadata.ID] = snapshot
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if uplinkDelta > 0 || downlinkDelta > 0 {
|
||||||
|
snapshot.uplink = currentUpload
|
||||||
|
snapshot.downlink = currentDownload
|
||||||
|
snapshot.hadTraffic = true
|
||||||
|
snapshots[metadata.ID] = snapshot
|
||||||
|
events = append(events, &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_UPDATE,
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
UplinkDelta: uplinkDelta,
|
||||||
|
DownlinkDelta: downlinkDelta,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if snapshot.hadTraffic {
|
||||||
|
snapshot.uplink = currentUpload
|
||||||
|
snapshot.downlink = currentDownload
|
||||||
|
snapshot.hadTraffic = false
|
||||||
|
snapshots[metadata.ID] = snapshot
|
||||||
|
events = append(events, &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_UPDATE,
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
UplinkDelta: 0,
|
||||||
|
DownlinkDelta: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var closedIndex map[uuid.UUID]*trafficontrol.TrackerMetadata
|
||||||
|
for id := range snapshots {
|
||||||
|
if _, exists := activeIndex[id]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if closedIndex == nil {
|
||||||
|
closedIndex = make(map[uuid.UUID]*trafficontrol.TrackerMetadata)
|
||||||
|
for _, metadata := range manager.ClosedConnections() {
|
||||||
|
closedIndex[metadata.ID] = metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedAt := time.Now()
|
||||||
|
var conn *Connection
|
||||||
|
if metadata, ok := closedIndex[id]; ok {
|
||||||
|
if !metadata.ClosedAt.IsZero() {
|
||||||
|
closedAt = metadata.ClosedAt
|
||||||
|
}
|
||||||
|
conn = buildConnectionProto(metadata)
|
||||||
|
conn.ClosedAt = closedAt.UnixMilli()
|
||||||
|
}
|
||||||
|
events = append(events, &ConnectionEvent{
|
||||||
|
Type: ConnectionEventType_CONNECTION_EVENT_CLOSED,
|
||||||
|
Id: id.String(),
|
||||||
|
ClosedAt: closedAt.UnixMilli(),
|
||||||
|
Connection: conn,
|
||||||
|
})
|
||||||
|
delete(snapshots, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildConnectionProto(metadata *trafficontrol.TrackerMetadata) *Connection {
|
||||||
var rule string
|
var rule string
|
||||||
if metadata.Rule != nil {
|
if metadata.Rule != nil {
|
||||||
rule = metadata.Rule.String()
|
rule = metadata.Rule.String()
|
||||||
}
|
}
|
||||||
uplinkTotal := metadata.Upload.Load()
|
uplinkTotal := metadata.Upload.Load()
|
||||||
downlinkTotal := metadata.Download.Load()
|
downlinkTotal := metadata.Download.Load()
|
||||||
uplink := uplinkTotal
|
|
||||||
downlink := downlinkTotal
|
|
||||||
var closedAt int64
|
|
||||||
if !metadata.ClosedAt.IsZero() {
|
|
||||||
closedAt = metadata.ClosedAt.UnixMilli()
|
|
||||||
uplink = 0
|
|
||||||
downlink = 0
|
|
||||||
}
|
|
||||||
var processInfo *ProcessInfo
|
var processInfo *ProcessInfo
|
||||||
if metadata.Metadata.ProcessInfo != nil {
|
if metadata.Metadata.ProcessInfo != nil {
|
||||||
processInfo = &ProcessInfo{
|
processInfo = &ProcessInfo{
|
||||||
@@ -747,7 +944,7 @@ func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol
|
|||||||
PackageName: metadata.Metadata.ProcessInfo.AndroidPackageName,
|
PackageName: metadata.Metadata.ProcessInfo.AndroidPackageName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connection := &Connection{
|
return &Connection{
|
||||||
Id: metadata.ID.String(),
|
Id: metadata.ID.String(),
|
||||||
Inbound: metadata.Metadata.Inbound,
|
Inbound: metadata.Metadata.Inbound,
|
||||||
InboundType: metadata.Metadata.InboundType,
|
InboundType: metadata.Metadata.InboundType,
|
||||||
@@ -760,9 +957,6 @@ func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol
|
|||||||
User: metadata.Metadata.User,
|
User: metadata.Metadata.User,
|
||||||
FromOutbound: metadata.Metadata.Outbound,
|
FromOutbound: metadata.Metadata.Outbound,
|
||||||
CreatedAt: metadata.CreatedAt.UnixMilli(),
|
CreatedAt: metadata.CreatedAt.UnixMilli(),
|
||||||
ClosedAt: closedAt,
|
|
||||||
Uplink: uplink,
|
|
||||||
Downlink: downlink,
|
|
||||||
UplinkTotal: uplinkTotal,
|
UplinkTotal: uplinkTotal,
|
||||||
DownlinkTotal: downlinkTotal,
|
DownlinkTotal: downlinkTotal,
|
||||||
Rule: rule,
|
Rule: rule,
|
||||||
@@ -771,8 +965,6 @@ func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol
|
|||||||
ChainList: metadata.Chain,
|
ChainList: metadata.Chain,
|
||||||
ProcessInfo: processInfo,
|
ProcessInfo: processInfo,
|
||||||
}
|
}
|
||||||
connections[metadata.ID] = connection
|
|
||||||
return connection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StartedService) CloseConnection(ctx context.Context, request *CloseConnectionRequest) (*emptypb.Empty, error) {
|
func (s *StartedService) CloseConnection(ctx context.Context, request *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||||
@@ -823,14 +1015,6 @@ func (s *StartedService) GetStartedAt(ctx context.Context, empty *emptypb.Empty)
|
|||||||
return &StartedAt{StartedAt: s.startedAt.UnixMilli()}, nil
|
return &StartedAt{StartedAt: s.startedAt.UnixMilli()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StartedService) SubscribeHelperEvents(empty *emptypb.Empty, server grpc.ServerStreamingServer[HelperRequest]) error {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StartedService) SendHelperResponse(ctx context.Context, response *HelperResponse) (*emptypb.Empty, error) {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
|
func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,104 +78,55 @@ func (LogLevel) EnumDescriptor() ([]byte, []int) {
|
|||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{0}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionFilter int32
|
type ConnectionEventType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ConnectionFilter_ALL ConnectionFilter = 0
|
ConnectionEventType_CONNECTION_EVENT_NEW ConnectionEventType = 0
|
||||||
ConnectionFilter_ACTIVE ConnectionFilter = 1
|
ConnectionEventType_CONNECTION_EVENT_UPDATE ConnectionEventType = 1
|
||||||
ConnectionFilter_CLOSED ConnectionFilter = 2
|
ConnectionEventType_CONNECTION_EVENT_CLOSED ConnectionEventType = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for ConnectionFilter.
|
// Enum value maps for ConnectionEventType.
|
||||||
var (
|
var (
|
||||||
ConnectionFilter_name = map[int32]string{
|
ConnectionEventType_name = map[int32]string{
|
||||||
0: "ALL",
|
0: "CONNECTION_EVENT_NEW",
|
||||||
1: "ACTIVE",
|
1: "CONNECTION_EVENT_UPDATE",
|
||||||
2: "CLOSED",
|
2: "CONNECTION_EVENT_CLOSED",
|
||||||
}
|
}
|
||||||
ConnectionFilter_value = map[string]int32{
|
ConnectionEventType_value = map[string]int32{
|
||||||
"ALL": 0,
|
"CONNECTION_EVENT_NEW": 0,
|
||||||
"ACTIVE": 1,
|
"CONNECTION_EVENT_UPDATE": 1,
|
||||||
"CLOSED": 2,
|
"CONNECTION_EVENT_CLOSED": 2,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (x ConnectionFilter) Enum() *ConnectionFilter {
|
func (x ConnectionEventType) Enum() *ConnectionEventType {
|
||||||
p := new(ConnectionFilter)
|
p := new(ConnectionEventType)
|
||||||
*p = x
|
*p = x
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x ConnectionFilter) String() string {
|
func (x ConnectionEventType) String() string {
|
||||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConnectionFilter) Descriptor() protoreflect.EnumDescriptor {
|
func (ConnectionEventType) Descriptor() protoreflect.EnumDescriptor {
|
||||||
return file_daemon_started_service_proto_enumTypes[1].Descriptor()
|
return file_daemon_started_service_proto_enumTypes[1].Descriptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ConnectionFilter) Type() protoreflect.EnumType {
|
func (ConnectionEventType) Type() protoreflect.EnumType {
|
||||||
return &file_daemon_started_service_proto_enumTypes[1]
|
return &file_daemon_started_service_proto_enumTypes[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x ConnectionFilter) Number() protoreflect.EnumNumber {
|
func (x ConnectionEventType) Number() protoreflect.EnumNumber {
|
||||||
return protoreflect.EnumNumber(x)
|
return protoreflect.EnumNumber(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use ConnectionFilter.Descriptor instead.
|
// Deprecated: Use ConnectionEventType.Descriptor instead.
|
||||||
func (ConnectionFilter) EnumDescriptor() ([]byte, []int) {
|
func (ConnectionEventType) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{1}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionSortBy int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
ConnectionSortBy_DATE ConnectionSortBy = 0
|
|
||||||
ConnectionSortBy_TRAFFIC ConnectionSortBy = 1
|
|
||||||
ConnectionSortBy_TOTAL_TRAFFIC ConnectionSortBy = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enum value maps for ConnectionSortBy.
|
|
||||||
var (
|
|
||||||
ConnectionSortBy_name = map[int32]string{
|
|
||||||
0: "DATE",
|
|
||||||
1: "TRAFFIC",
|
|
||||||
2: "TOTAL_TRAFFIC",
|
|
||||||
}
|
|
||||||
ConnectionSortBy_value = map[string]int32{
|
|
||||||
"DATE": 0,
|
|
||||||
"TRAFFIC": 1,
|
|
||||||
"TOTAL_TRAFFIC": 2,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (x ConnectionSortBy) Enum() *ConnectionSortBy {
|
|
||||||
p := new(ConnectionSortBy)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x ConnectionSortBy) String() string {
|
|
||||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ConnectionSortBy) Descriptor() protoreflect.EnumDescriptor {
|
|
||||||
return file_daemon_started_service_proto_enumTypes[2].Descriptor()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ConnectionSortBy) Type() protoreflect.EnumType {
|
|
||||||
return &file_daemon_started_service_proto_enumTypes[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x ConnectionSortBy) Number() protoreflect.EnumNumber {
|
|
||||||
return protoreflect.EnumNumber(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ConnectionSortBy.Descriptor instead.
|
|
||||||
func (ConnectionSortBy) EnumDescriptor() ([]byte, []int) {
|
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{2}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServiceStatus_Type int32
|
type ServiceStatus_Type int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -215,11 +166,11 @@ func (x ServiceStatus_Type) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ServiceStatus_Type) Descriptor() protoreflect.EnumDescriptor {
|
func (ServiceStatus_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||||
return file_daemon_started_service_proto_enumTypes[3].Descriptor()
|
return file_daemon_started_service_proto_enumTypes[2].Descriptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ServiceStatus_Type) Type() protoreflect.EnumType {
|
func (ServiceStatus_Type) Type() protoreflect.EnumType {
|
||||||
return &file_daemon_started_service_proto_enumTypes[3]
|
return &file_daemon_started_service_proto_enumTypes[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x ServiceStatus_Type) Number() protoreflect.EnumNumber {
|
func (x ServiceStatus_Type) Number() protoreflect.EnumNumber {
|
||||||
@@ -1114,8 +1065,6 @@ func (x *SetSystemProxyEnabledRequest) GetEnabled() bool {
|
|||||||
type SubscribeConnectionsRequest struct {
|
type SubscribeConnectionsRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Interval int64 `protobuf:"varint,1,opt,name=interval,proto3" json:"interval,omitempty"`
|
Interval int64 `protobuf:"varint,1,opt,name=interval,proto3" json:"interval,omitempty"`
|
||||||
Filter ConnectionFilter `protobuf:"varint,2,opt,name=filter,proto3,enum=daemon.ConnectionFilter" json:"filter,omitempty"`
|
|
||||||
SortBy ConnectionSortBy `protobuf:"varint,3,opt,name=sortBy,proto3,enum=daemon.ConnectionSortBy" json:"sortBy,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -1157,41 +1106,32 @@ func (x *SubscribeConnectionsRequest) GetInterval() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SubscribeConnectionsRequest) GetFilter() ConnectionFilter {
|
type ConnectionEvent struct {
|
||||||
if x != nil {
|
|
||||||
return x.Filter
|
|
||||||
}
|
|
||||||
return ConnectionFilter_ALL
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SubscribeConnectionsRequest) GetSortBy() ConnectionSortBy {
|
|
||||||
if x != nil {
|
|
||||||
return x.SortBy
|
|
||||||
}
|
|
||||||
return ConnectionSortBy_DATE
|
|
||||||
}
|
|
||||||
|
|
||||||
type Connections struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Connections []*Connection `protobuf:"bytes,1,rep,name=connections,proto3" json:"connections,omitempty"`
|
Type ConnectionEventType `protobuf:"varint,1,opt,name=type,proto3,enum=daemon.ConnectionEventType" json:"type,omitempty"`
|
||||||
|
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
Connection *Connection `protobuf:"bytes,3,opt,name=connection,proto3" json:"connection,omitempty"`
|
||||||
|
UplinkDelta int64 `protobuf:"varint,4,opt,name=uplinkDelta,proto3" json:"uplinkDelta,omitempty"`
|
||||||
|
DownlinkDelta int64 `protobuf:"varint,5,opt,name=downlinkDelta,proto3" json:"downlinkDelta,omitempty"`
|
||||||
|
ClosedAt int64 `protobuf:"varint,6,opt,name=closedAt,proto3" json:"closedAt,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Connections) Reset() {
|
func (x *ConnectionEvent) Reset() {
|
||||||
*x = Connections{}
|
*x = ConnectionEvent{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[17]
|
mi := &file_daemon_started_service_proto_msgTypes[17]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Connections) String() string {
|
func (x *ConnectionEvent) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Connections) ProtoMessage() {}
|
func (*ConnectionEvent) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Connections) ProtoReflect() protoreflect.Message {
|
func (x *ConnectionEvent) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[17]
|
mi := &file_daemon_started_service_proto_msgTypes[17]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@@ -1203,18 +1143,105 @@ func (x *Connections) ProtoReflect() protoreflect.Message {
|
|||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use Connections.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ConnectionEvent.ProtoReflect.Descriptor instead.
|
||||||
func (*Connections) Descriptor() ([]byte, []int) {
|
func (*ConnectionEvent) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{17}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{17}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Connections) GetConnections() []*Connection {
|
func (x *ConnectionEvent) GetType() ConnectionEventType {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Connections
|
return x.Type
|
||||||
|
}
|
||||||
|
return ConnectionEventType_CONNECTION_EVENT_NEW
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvent) GetId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvent) GetConnection() *Connection {
|
||||||
|
if x != nil {
|
||||||
|
return x.Connection
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvent) GetUplinkDelta() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.UplinkDelta
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvent) GetDownlinkDelta() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.DownlinkDelta
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvent) GetClosedAt() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.ClosedAt
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionEvents struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Events []*ConnectionEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"`
|
||||||
|
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvents) Reset() {
|
||||||
|
*x = ConnectionEvents{}
|
||||||
|
mi := &file_daemon_started_service_proto_msgTypes[18]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvents) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ConnectionEvents) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ConnectionEvents) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_started_service_proto_msgTypes[18]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionEvents.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ConnectionEvents) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{18}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvents) GetEvents() []*ConnectionEvent {
|
||||||
|
if x != nil {
|
||||||
|
return x.Events
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionEvents) GetReset_() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Reset_
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type Connection struct {
|
type Connection struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
@@ -1245,7 +1272,7 @@ type Connection struct {
|
|||||||
|
|
||||||
func (x *Connection) Reset() {
|
func (x *Connection) Reset() {
|
||||||
*x = Connection{}
|
*x = Connection{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[18]
|
mi := &file_daemon_started_service_proto_msgTypes[19]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1257,7 +1284,7 @@ func (x *Connection) String() string {
|
|||||||
func (*Connection) ProtoMessage() {}
|
func (*Connection) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Connection) ProtoReflect() protoreflect.Message {
|
func (x *Connection) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[18]
|
mi := &file_daemon_started_service_proto_msgTypes[19]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1270,7 +1297,7 @@ func (x *Connection) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Connection.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Connection.ProtoReflect.Descriptor instead.
|
||||||
func (*Connection) Descriptor() ([]byte, []int) {
|
func (*Connection) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{18}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{19}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Connection) GetId() string {
|
func (x *Connection) GetId() string {
|
||||||
@@ -1440,7 +1467,7 @@ type ProcessInfo struct {
|
|||||||
|
|
||||||
func (x *ProcessInfo) Reset() {
|
func (x *ProcessInfo) Reset() {
|
||||||
*x = ProcessInfo{}
|
*x = ProcessInfo{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[19]
|
mi := &file_daemon_started_service_proto_msgTypes[20]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1452,7 +1479,7 @@ func (x *ProcessInfo) String() string {
|
|||||||
func (*ProcessInfo) ProtoMessage() {}
|
func (*ProcessInfo) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ProcessInfo) ProtoReflect() protoreflect.Message {
|
func (x *ProcessInfo) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[19]
|
mi := &file_daemon_started_service_proto_msgTypes[20]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1465,7 +1492,7 @@ func (x *ProcessInfo) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use ProcessInfo.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ProcessInfo.ProtoReflect.Descriptor instead.
|
||||||
func (*ProcessInfo) Descriptor() ([]byte, []int) {
|
func (*ProcessInfo) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{19}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{20}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ProcessInfo) GetProcessId() uint32 {
|
func (x *ProcessInfo) GetProcessId() uint32 {
|
||||||
@@ -1512,7 +1539,7 @@ type CloseConnectionRequest struct {
|
|||||||
|
|
||||||
func (x *CloseConnectionRequest) Reset() {
|
func (x *CloseConnectionRequest) Reset() {
|
||||||
*x = CloseConnectionRequest{}
|
*x = CloseConnectionRequest{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[20]
|
mi := &file_daemon_started_service_proto_msgTypes[21]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1524,7 +1551,7 @@ func (x *CloseConnectionRequest) String() string {
|
|||||||
func (*CloseConnectionRequest) ProtoMessage() {}
|
func (*CloseConnectionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CloseConnectionRequest) ProtoReflect() protoreflect.Message {
|
func (x *CloseConnectionRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[20]
|
mi := &file_daemon_started_service_proto_msgTypes[21]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1537,7 +1564,7 @@ func (x *CloseConnectionRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use CloseConnectionRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use CloseConnectionRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*CloseConnectionRequest) Descriptor() ([]byte, []int) {
|
func (*CloseConnectionRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{20}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{21}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *CloseConnectionRequest) GetId() string {
|
func (x *CloseConnectionRequest) GetId() string {
|
||||||
@@ -1556,7 +1583,7 @@ type DeprecatedWarnings struct {
|
|||||||
|
|
||||||
func (x *DeprecatedWarnings) Reset() {
|
func (x *DeprecatedWarnings) Reset() {
|
||||||
*x = DeprecatedWarnings{}
|
*x = DeprecatedWarnings{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[21]
|
mi := &file_daemon_started_service_proto_msgTypes[22]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1568,7 +1595,7 @@ func (x *DeprecatedWarnings) String() string {
|
|||||||
func (*DeprecatedWarnings) ProtoMessage() {}
|
func (*DeprecatedWarnings) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DeprecatedWarnings) ProtoReflect() protoreflect.Message {
|
func (x *DeprecatedWarnings) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[21]
|
mi := &file_daemon_started_service_proto_msgTypes[22]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1581,7 +1608,7 @@ func (x *DeprecatedWarnings) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use DeprecatedWarnings.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DeprecatedWarnings.ProtoReflect.Descriptor instead.
|
||||||
func (*DeprecatedWarnings) Descriptor() ([]byte, []int) {
|
func (*DeprecatedWarnings) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{21}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{22}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DeprecatedWarnings) GetWarnings() []*DeprecatedWarning {
|
func (x *DeprecatedWarnings) GetWarnings() []*DeprecatedWarning {
|
||||||
@@ -1602,7 +1629,7 @@ type DeprecatedWarning struct {
|
|||||||
|
|
||||||
func (x *DeprecatedWarning) Reset() {
|
func (x *DeprecatedWarning) Reset() {
|
||||||
*x = DeprecatedWarning{}
|
*x = DeprecatedWarning{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[22]
|
mi := &file_daemon_started_service_proto_msgTypes[23]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1614,7 +1641,7 @@ func (x *DeprecatedWarning) String() string {
|
|||||||
func (*DeprecatedWarning) ProtoMessage() {}
|
func (*DeprecatedWarning) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DeprecatedWarning) ProtoReflect() protoreflect.Message {
|
func (x *DeprecatedWarning) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[22]
|
mi := &file_daemon_started_service_proto_msgTypes[23]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1627,7 +1654,7 @@ func (x *DeprecatedWarning) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use DeprecatedWarning.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DeprecatedWarning.ProtoReflect.Descriptor instead.
|
||||||
func (*DeprecatedWarning) Descriptor() ([]byte, []int) {
|
func (*DeprecatedWarning) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{22}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{23}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DeprecatedWarning) GetMessage() string {
|
func (x *DeprecatedWarning) GetMessage() string {
|
||||||
@@ -1660,7 +1687,7 @@ type StartedAt struct {
|
|||||||
|
|
||||||
func (x *StartedAt) Reset() {
|
func (x *StartedAt) Reset() {
|
||||||
*x = StartedAt{}
|
*x = StartedAt{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[23]
|
mi := &file_daemon_started_service_proto_msgTypes[24]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1672,7 +1699,7 @@ func (x *StartedAt) String() string {
|
|||||||
func (*StartedAt) ProtoMessage() {}
|
func (*StartedAt) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *StartedAt) ProtoReflect() protoreflect.Message {
|
func (x *StartedAt) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[23]
|
mi := &file_daemon_started_service_proto_msgTypes[24]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1685,7 +1712,7 @@ func (x *StartedAt) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use StartedAt.ProtoReflect.Descriptor instead.
|
// Deprecated: Use StartedAt.ProtoReflect.Descriptor instead.
|
||||||
func (*StartedAt) Descriptor() ([]byte, []int) {
|
func (*StartedAt) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{23}
|
return file_daemon_started_service_proto_rawDescGZIP(), []int{24}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *StartedAt) GetStartedAt() int64 {
|
func (x *StartedAt) GetStartedAt() int64 {
|
||||||
@@ -1705,7 +1732,7 @@ type Log_Message struct {
|
|||||||
|
|
||||||
func (x *Log_Message) Reset() {
|
func (x *Log_Message) Reset() {
|
||||||
*x = Log_Message{}
|
*x = Log_Message{}
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[24]
|
mi := &file_daemon_started_service_proto_msgTypes[25]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -1717,7 +1744,7 @@ func (x *Log_Message) String() string {
|
|||||||
func (*Log_Message) ProtoMessage() {}
|
func (*Log_Message) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Log_Message) ProtoReflect() protoreflect.Message {
|
func (x *Log_Message) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_started_service_proto_msgTypes[24]
|
mi := &file_daemon_started_service_proto_msgTypes[25]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1751,7 +1778,7 @@ var File_daemon_started_service_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_daemon_started_service_proto_rawDesc = "" +
|
const file_daemon_started_service_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x1cdaemon/started_service.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\x1a\x13daemon/helper.proto\"\xad\x01\n" +
|
"\x1cdaemon/started_service.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xad\x01\n" +
|
||||||
"\rServiceStatus\x122\n" +
|
"\rServiceStatus\x122\n" +
|
||||||
"\x06status\x18\x01 \x01(\x0e2\x1a.daemon.ServiceStatus.TypeR\x06status\x12\"\n" +
|
"\x06status\x18\x01 \x01(\x0e2\x1a.daemon.ServiceStatus.TypeR\x06status\x12\"\n" +
|
||||||
"\ferrorMessage\x18\x02 \x01(\tR\ferrorMessage\"D\n" +
|
"\ferrorMessage\x18\x02 \x01(\tR\ferrorMessage\"D\n" +
|
||||||
@@ -1818,13 +1845,21 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
|||||||
"\tavailable\x18\x01 \x01(\bR\tavailable\x12\x18\n" +
|
"\tavailable\x18\x01 \x01(\bR\tavailable\x12\x18\n" +
|
||||||
"\aenabled\x18\x02 \x01(\bR\aenabled\"8\n" +
|
"\aenabled\x18\x02 \x01(\bR\aenabled\"8\n" +
|
||||||
"\x1cSetSystemProxyEnabledRequest\x12\x18\n" +
|
"\x1cSetSystemProxyEnabledRequest\x12\x18\n" +
|
||||||
"\aenabled\x18\x01 \x01(\bR\aenabled\"\x9d\x01\n" +
|
"\aenabled\x18\x01 \x01(\bR\aenabled\"9\n" +
|
||||||
"\x1bSubscribeConnectionsRequest\x12\x1a\n" +
|
"\x1bSubscribeConnectionsRequest\x12\x1a\n" +
|
||||||
"\binterval\x18\x01 \x01(\x03R\binterval\x120\n" +
|
"\binterval\x18\x01 \x01(\x03R\binterval\"\xea\x01\n" +
|
||||||
"\x06filter\x18\x02 \x01(\x0e2\x18.daemon.ConnectionFilterR\x06filter\x120\n" +
|
"\x0fConnectionEvent\x12/\n" +
|
||||||
"\x06sortBy\x18\x03 \x01(\x0e2\x18.daemon.ConnectionSortByR\x06sortBy\"C\n" +
|
"\x04type\x18\x01 \x01(\x0e2\x1b.daemon.ConnectionEventTypeR\x04type\x12\x0e\n" +
|
||||||
"\vConnections\x124\n" +
|
"\x02id\x18\x02 \x01(\tR\x02id\x122\n" +
|
||||||
"\vconnections\x18\x01 \x03(\v2\x12.daemon.ConnectionR\vconnections\"\x95\x05\n" +
|
"\n" +
|
||||||
|
"connection\x18\x03 \x01(\v2\x12.daemon.ConnectionR\n" +
|
||||||
|
"connection\x12 \n" +
|
||||||
|
"\vuplinkDelta\x18\x04 \x01(\x03R\vuplinkDelta\x12$\n" +
|
||||||
|
"\rdownlinkDelta\x18\x05 \x01(\x03R\rdownlinkDelta\x12\x1a\n" +
|
||||||
|
"\bclosedAt\x18\x06 \x01(\x03R\bclosedAt\"Y\n" +
|
||||||
|
"\x10ConnectionEvents\x12/\n" +
|
||||||
|
"\x06events\x18\x01 \x03(\v2\x17.daemon.ConnectionEventR\x06events\x12\x14\n" +
|
||||||
|
"\x05reset\x18\x02 \x01(\bR\x05reset\"\x95\x05\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"Connection\x12\x0e\n" +
|
"Connection\x12\x0e\n" +
|
||||||
"\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n" +
|
"\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n" +
|
||||||
@@ -1873,17 +1908,11 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
|||||||
"\x04WARN\x10\x03\x12\b\n" +
|
"\x04WARN\x10\x03\x12\b\n" +
|
||||||
"\x04INFO\x10\x04\x12\t\n" +
|
"\x04INFO\x10\x04\x12\t\n" +
|
||||||
"\x05DEBUG\x10\x05\x12\t\n" +
|
"\x05DEBUG\x10\x05\x12\t\n" +
|
||||||
"\x05TRACE\x10\x06*3\n" +
|
"\x05TRACE\x10\x06*i\n" +
|
||||||
"\x10ConnectionFilter\x12\a\n" +
|
"\x13ConnectionEventType\x12\x18\n" +
|
||||||
"\x03ALL\x10\x00\x12\n" +
|
"\x14CONNECTION_EVENT_NEW\x10\x00\x12\x1b\n" +
|
||||||
"\n" +
|
"\x17CONNECTION_EVENT_UPDATE\x10\x01\x12\x1b\n" +
|
||||||
"\x06ACTIVE\x10\x01\x12\n" +
|
"\x17CONNECTION_EVENT_CLOSED\x10\x022\xe5\v\n" +
|
||||||
"\n" +
|
|
||||||
"\x06CLOSED\x10\x02*<\n" +
|
|
||||||
"\x10ConnectionSortBy\x12\b\n" +
|
|
||||||
"\x04DATE\x10\x00\x12\v\n" +
|
|
||||||
"\aTRAFFIC\x10\x01\x12\x11\n" +
|
|
||||||
"\rTOTAL_TRAFFIC\x10\x022\xf4\f\n" +
|
|
||||||
"\x0eStartedService\x12=\n" +
|
"\x0eStartedService\x12=\n" +
|
||||||
"\vStopService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12?\n" +
|
"\vStopService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12?\n" +
|
||||||
"\rReloadService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12K\n" +
|
"\rReloadService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12K\n" +
|
||||||
@@ -1900,14 +1929,12 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
|||||||
"\x0eSelectOutbound\x12\x1d.daemon.SelectOutboundRequest\x1a\x16.google.protobuf.Empty\"\x00\x12I\n" +
|
"\x0eSelectOutbound\x12\x1d.daemon.SelectOutboundRequest\x1a\x16.google.protobuf.Empty\"\x00\x12I\n" +
|
||||||
"\x0eSetGroupExpand\x12\x1d.daemon.SetGroupExpandRequest\x1a\x16.google.protobuf.Empty\"\x00\x12K\n" +
|
"\x0eSetGroupExpand\x12\x1d.daemon.SetGroupExpandRequest\x1a\x16.google.protobuf.Empty\"\x00\x12K\n" +
|
||||||
"\x14GetSystemProxyStatus\x12\x16.google.protobuf.Empty\x1a\x19.daemon.SystemProxyStatus\"\x00\x12W\n" +
|
"\x14GetSystemProxyStatus\x12\x16.google.protobuf.Empty\x1a\x19.daemon.SystemProxyStatus\"\x00\x12W\n" +
|
||||||
"\x15SetSystemProxyEnabled\x12$.daemon.SetSystemProxyEnabledRequest\x1a\x16.google.protobuf.Empty\"\x00\x12T\n" +
|
"\x15SetSystemProxyEnabled\x12$.daemon.SetSystemProxyEnabledRequest\x1a\x16.google.protobuf.Empty\"\x00\x12Y\n" +
|
||||||
"\x14SubscribeConnections\x12#.daemon.SubscribeConnectionsRequest\x1a\x13.daemon.Connections\"\x000\x01\x12K\n" +
|
"\x14SubscribeConnections\x12#.daemon.SubscribeConnectionsRequest\x1a\x18.daemon.ConnectionEvents\"\x000\x01\x12K\n" +
|
||||||
"\x0fCloseConnection\x12\x1e.daemon.CloseConnectionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12G\n" +
|
"\x0fCloseConnection\x12\x1e.daemon.CloseConnectionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12G\n" +
|
||||||
"\x13CloseAllConnections\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12M\n" +
|
"\x13CloseAllConnections\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12M\n" +
|
||||||
"\x15GetDeprecatedWarnings\x12\x16.google.protobuf.Empty\x1a\x1a.daemon.DeprecatedWarnings\"\x00\x12;\n" +
|
"\x15GetDeprecatedWarnings\x12\x16.google.protobuf.Empty\x1a\x1a.daemon.DeprecatedWarnings\"\x00\x12;\n" +
|
||||||
"\fGetStartedAt\x12\x16.google.protobuf.Empty\x1a\x11.daemon.StartedAt\"\x00\x12J\n" +
|
"\fGetStartedAt\x12\x16.google.protobuf.Empty\x1a\x11.daemon.StartedAt\"\x00B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||||
"\x15SubscribeHelperEvents\x12\x16.google.protobuf.Empty\x1a\x15.daemon.HelperRequest\"\x000\x01\x12F\n" +
|
|
||||||
"\x12SendHelperResponse\x12\x16.daemon.HelperResponse\x1a\x16.google.protobuf.Empty\"\x00B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_daemon_started_service_proto_rawDescOnce sync.Once
|
file_daemon_started_service_proto_rawDescOnce sync.Once
|
||||||
@@ -1922,31 +1949,31 @@ func file_daemon_started_service_proto_rawDescGZIP() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_daemon_started_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
|
file_daemon_started_service_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||||
file_daemon_started_service_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
|
file_daemon_started_service_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
|
||||||
file_daemon_started_service_proto_goTypes = []any{
|
file_daemon_started_service_proto_goTypes = []any{
|
||||||
(LogLevel)(0), // 0: daemon.LogLevel
|
(LogLevel)(0), // 0: daemon.LogLevel
|
||||||
(ConnectionFilter)(0), // 1: daemon.ConnectionFilter
|
(ConnectionEventType)(0), // 1: daemon.ConnectionEventType
|
||||||
(ConnectionSortBy)(0), // 2: daemon.ConnectionSortBy
|
(ServiceStatus_Type)(0), // 2: daemon.ServiceStatus.Type
|
||||||
(ServiceStatus_Type)(0), // 3: daemon.ServiceStatus.Type
|
(*ServiceStatus)(nil), // 3: daemon.ServiceStatus
|
||||||
(*ServiceStatus)(nil), // 4: daemon.ServiceStatus
|
(*ReloadServiceRequest)(nil), // 4: daemon.ReloadServiceRequest
|
||||||
(*ReloadServiceRequest)(nil), // 5: daemon.ReloadServiceRequest
|
(*SubscribeStatusRequest)(nil), // 5: daemon.SubscribeStatusRequest
|
||||||
(*SubscribeStatusRequest)(nil), // 6: daemon.SubscribeStatusRequest
|
(*Log)(nil), // 6: daemon.Log
|
||||||
(*Log)(nil), // 7: daemon.Log
|
(*DefaultLogLevel)(nil), // 7: daemon.DefaultLogLevel
|
||||||
(*DefaultLogLevel)(nil), // 8: daemon.DefaultLogLevel
|
(*Status)(nil), // 8: daemon.Status
|
||||||
(*Status)(nil), // 9: daemon.Status
|
(*Groups)(nil), // 9: daemon.Groups
|
||||||
(*Groups)(nil), // 10: daemon.Groups
|
(*Group)(nil), // 10: daemon.Group
|
||||||
(*Group)(nil), // 11: daemon.Group
|
(*GroupItem)(nil), // 11: daemon.GroupItem
|
||||||
(*GroupItem)(nil), // 12: daemon.GroupItem
|
(*URLTestRequest)(nil), // 12: daemon.URLTestRequest
|
||||||
(*URLTestRequest)(nil), // 13: daemon.URLTestRequest
|
(*SelectOutboundRequest)(nil), // 13: daemon.SelectOutboundRequest
|
||||||
(*SelectOutboundRequest)(nil), // 14: daemon.SelectOutboundRequest
|
(*SetGroupExpandRequest)(nil), // 14: daemon.SetGroupExpandRequest
|
||||||
(*SetGroupExpandRequest)(nil), // 15: daemon.SetGroupExpandRequest
|
(*ClashMode)(nil), // 15: daemon.ClashMode
|
||||||
(*ClashMode)(nil), // 16: daemon.ClashMode
|
(*ClashModeStatus)(nil), // 16: daemon.ClashModeStatus
|
||||||
(*ClashModeStatus)(nil), // 17: daemon.ClashModeStatus
|
(*SystemProxyStatus)(nil), // 17: daemon.SystemProxyStatus
|
||||||
(*SystemProxyStatus)(nil), // 18: daemon.SystemProxyStatus
|
(*SetSystemProxyEnabledRequest)(nil), // 18: daemon.SetSystemProxyEnabledRequest
|
||||||
(*SetSystemProxyEnabledRequest)(nil), // 19: daemon.SetSystemProxyEnabledRequest
|
(*SubscribeConnectionsRequest)(nil), // 19: daemon.SubscribeConnectionsRequest
|
||||||
(*SubscribeConnectionsRequest)(nil), // 20: daemon.SubscribeConnectionsRequest
|
(*ConnectionEvent)(nil), // 20: daemon.ConnectionEvent
|
||||||
(*Connections)(nil), // 21: daemon.Connections
|
(*ConnectionEvents)(nil), // 21: daemon.ConnectionEvents
|
||||||
(*Connection)(nil), // 22: daemon.Connection
|
(*Connection)(nil), // 22: daemon.Connection
|
||||||
(*ProcessInfo)(nil), // 23: daemon.ProcessInfo
|
(*ProcessInfo)(nil), // 23: daemon.ProcessInfo
|
||||||
(*CloseConnectionRequest)(nil), // 24: daemon.CloseConnectionRequest
|
(*CloseConnectionRequest)(nil), // 24: daemon.CloseConnectionRequest
|
||||||
@@ -1955,20 +1982,18 @@ var (
|
|||||||
(*StartedAt)(nil), // 27: daemon.StartedAt
|
(*StartedAt)(nil), // 27: daemon.StartedAt
|
||||||
(*Log_Message)(nil), // 28: daemon.Log.Message
|
(*Log_Message)(nil), // 28: daemon.Log.Message
|
||||||
(*emptypb.Empty)(nil), // 29: google.protobuf.Empty
|
(*emptypb.Empty)(nil), // 29: google.protobuf.Empty
|
||||||
(*HelperResponse)(nil), // 30: daemon.HelperResponse
|
|
||||||
(*HelperRequest)(nil), // 31: daemon.HelperRequest
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var file_daemon_started_service_proto_depIdxs = []int32{
|
var file_daemon_started_service_proto_depIdxs = []int32{
|
||||||
3, // 0: daemon.ServiceStatus.status:type_name -> daemon.ServiceStatus.Type
|
2, // 0: daemon.ServiceStatus.status:type_name -> daemon.ServiceStatus.Type
|
||||||
28, // 1: daemon.Log.messages:type_name -> daemon.Log.Message
|
28, // 1: daemon.Log.messages:type_name -> daemon.Log.Message
|
||||||
0, // 2: daemon.DefaultLogLevel.level:type_name -> daemon.LogLevel
|
0, // 2: daemon.DefaultLogLevel.level:type_name -> daemon.LogLevel
|
||||||
11, // 3: daemon.Groups.group:type_name -> daemon.Group
|
10, // 3: daemon.Groups.group:type_name -> daemon.Group
|
||||||
12, // 4: daemon.Group.items:type_name -> daemon.GroupItem
|
11, // 4: daemon.Group.items:type_name -> daemon.GroupItem
|
||||||
1, // 5: daemon.SubscribeConnectionsRequest.filter:type_name -> daemon.ConnectionFilter
|
1, // 5: daemon.ConnectionEvent.type:type_name -> daemon.ConnectionEventType
|
||||||
2, // 6: daemon.SubscribeConnectionsRequest.sortBy:type_name -> daemon.ConnectionSortBy
|
22, // 6: daemon.ConnectionEvent.connection:type_name -> daemon.Connection
|
||||||
22, // 7: daemon.Connections.connections:type_name -> daemon.Connection
|
20, // 7: daemon.ConnectionEvents.events:type_name -> daemon.ConnectionEvent
|
||||||
23, // 8: daemon.Connection.processInfo:type_name -> daemon.ProcessInfo
|
23, // 8: daemon.Connection.processInfo:type_name -> daemon.ProcessInfo
|
||||||
26, // 9: daemon.DeprecatedWarnings.warnings:type_name -> daemon.DeprecatedWarning
|
26, // 9: daemon.DeprecatedWarnings.warnings:type_name -> daemon.DeprecatedWarning
|
||||||
0, // 10: daemon.Log.Message.level:type_name -> daemon.LogLevel
|
0, // 10: daemon.Log.Message.level:type_name -> daemon.LogLevel
|
||||||
@@ -1978,48 +2003,44 @@ var file_daemon_started_service_proto_depIdxs = []int32{
|
|||||||
29, // 14: daemon.StartedService.SubscribeLog:input_type -> google.protobuf.Empty
|
29, // 14: daemon.StartedService.SubscribeLog:input_type -> google.protobuf.Empty
|
||||||
29, // 15: daemon.StartedService.GetDefaultLogLevel:input_type -> google.protobuf.Empty
|
29, // 15: daemon.StartedService.GetDefaultLogLevel:input_type -> google.protobuf.Empty
|
||||||
29, // 16: daemon.StartedService.ClearLogs:input_type -> google.protobuf.Empty
|
29, // 16: daemon.StartedService.ClearLogs:input_type -> google.protobuf.Empty
|
||||||
6, // 17: daemon.StartedService.SubscribeStatus:input_type -> daemon.SubscribeStatusRequest
|
5, // 17: daemon.StartedService.SubscribeStatus:input_type -> daemon.SubscribeStatusRequest
|
||||||
29, // 18: daemon.StartedService.SubscribeGroups:input_type -> google.protobuf.Empty
|
29, // 18: daemon.StartedService.SubscribeGroups:input_type -> google.protobuf.Empty
|
||||||
29, // 19: daemon.StartedService.GetClashModeStatus:input_type -> google.protobuf.Empty
|
29, // 19: daemon.StartedService.GetClashModeStatus:input_type -> google.protobuf.Empty
|
||||||
29, // 20: daemon.StartedService.SubscribeClashMode:input_type -> google.protobuf.Empty
|
29, // 20: daemon.StartedService.SubscribeClashMode:input_type -> google.protobuf.Empty
|
||||||
16, // 21: daemon.StartedService.SetClashMode:input_type -> daemon.ClashMode
|
15, // 21: daemon.StartedService.SetClashMode:input_type -> daemon.ClashMode
|
||||||
13, // 22: daemon.StartedService.URLTest:input_type -> daemon.URLTestRequest
|
12, // 22: daemon.StartedService.URLTest:input_type -> daemon.URLTestRequest
|
||||||
14, // 23: daemon.StartedService.SelectOutbound:input_type -> daemon.SelectOutboundRequest
|
13, // 23: daemon.StartedService.SelectOutbound:input_type -> daemon.SelectOutboundRequest
|
||||||
15, // 24: daemon.StartedService.SetGroupExpand:input_type -> daemon.SetGroupExpandRequest
|
14, // 24: daemon.StartedService.SetGroupExpand:input_type -> daemon.SetGroupExpandRequest
|
||||||
29, // 25: daemon.StartedService.GetSystemProxyStatus:input_type -> google.protobuf.Empty
|
29, // 25: daemon.StartedService.GetSystemProxyStatus:input_type -> google.protobuf.Empty
|
||||||
19, // 26: daemon.StartedService.SetSystemProxyEnabled:input_type -> daemon.SetSystemProxyEnabledRequest
|
18, // 26: daemon.StartedService.SetSystemProxyEnabled:input_type -> daemon.SetSystemProxyEnabledRequest
|
||||||
20, // 27: daemon.StartedService.SubscribeConnections:input_type -> daemon.SubscribeConnectionsRequest
|
19, // 27: daemon.StartedService.SubscribeConnections:input_type -> daemon.SubscribeConnectionsRequest
|
||||||
24, // 28: daemon.StartedService.CloseConnection:input_type -> daemon.CloseConnectionRequest
|
24, // 28: daemon.StartedService.CloseConnection:input_type -> daemon.CloseConnectionRequest
|
||||||
29, // 29: daemon.StartedService.CloseAllConnections:input_type -> google.protobuf.Empty
|
29, // 29: daemon.StartedService.CloseAllConnections:input_type -> google.protobuf.Empty
|
||||||
29, // 30: daemon.StartedService.GetDeprecatedWarnings:input_type -> google.protobuf.Empty
|
29, // 30: daemon.StartedService.GetDeprecatedWarnings:input_type -> google.protobuf.Empty
|
||||||
29, // 31: daemon.StartedService.GetStartedAt:input_type -> google.protobuf.Empty
|
29, // 31: daemon.StartedService.GetStartedAt:input_type -> google.protobuf.Empty
|
||||||
29, // 32: daemon.StartedService.SubscribeHelperEvents:input_type -> google.protobuf.Empty
|
29, // 32: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
|
||||||
30, // 33: daemon.StartedService.SendHelperResponse:input_type -> daemon.HelperResponse
|
29, // 33: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
|
||||||
29, // 34: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
|
3, // 34: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
|
||||||
29, // 35: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
|
6, // 35: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
|
||||||
4, // 36: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
|
7, // 36: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
|
||||||
7, // 37: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
|
29, // 37: daemon.StartedService.ClearLogs:output_type -> google.protobuf.Empty
|
||||||
8, // 38: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
|
8, // 38: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
|
||||||
29, // 39: daemon.StartedService.ClearLogs:output_type -> google.protobuf.Empty
|
9, // 39: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
|
||||||
9, // 40: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
|
16, // 40: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
|
||||||
10, // 41: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
|
15, // 41: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
|
||||||
17, // 42: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
|
29, // 42: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
|
||||||
16, // 43: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
|
29, // 43: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
|
||||||
29, // 44: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
|
29, // 44: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
|
||||||
29, // 45: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
|
29, // 45: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
|
||||||
29, // 46: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
|
17, // 46: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
|
||||||
29, // 47: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
|
29, // 47: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
|
||||||
18, // 48: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
|
21, // 48: daemon.StartedService.SubscribeConnections:output_type -> daemon.ConnectionEvents
|
||||||
29, // 49: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
|
29, // 49: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
|
||||||
21, // 50: daemon.StartedService.SubscribeConnections:output_type -> daemon.Connections
|
29, // 50: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
|
||||||
29, // 51: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
|
25, // 51: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
|
||||||
29, // 52: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
|
27, // 52: daemon.StartedService.GetStartedAt:output_type -> daemon.StartedAt
|
||||||
25, // 53: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
|
32, // [32:53] is the sub-list for method output_type
|
||||||
27, // 54: daemon.StartedService.GetStartedAt:output_type -> daemon.StartedAt
|
11, // [11:32] is the sub-list for method input_type
|
||||||
31, // 55: daemon.StartedService.SubscribeHelperEvents:output_type -> daemon.HelperRequest
|
|
||||||
29, // 56: daemon.StartedService.SendHelperResponse:output_type -> google.protobuf.Empty
|
|
||||||
34, // [34:57] is the sub-list for method output_type
|
|
||||||
11, // [11:34] is the sub-list for method input_type
|
|
||||||
11, // [11:11] is the sub-list for extension type_name
|
11, // [11:11] is the sub-list for extension type_name
|
||||||
11, // [11:11] is the sub-list for extension extendee
|
11, // [11:11] is the sub-list for extension extendee
|
||||||
0, // [0:11] is the sub-list for field type_name
|
0, // [0:11] is the sub-list for field type_name
|
||||||
@@ -2030,14 +2051,13 @@ func file_daemon_started_service_proto_init() {
|
|||||||
if File_daemon_started_service_proto != nil {
|
if File_daemon_started_service_proto != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file_daemon_helper_proto_init()
|
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_started_service_proto_rawDesc), len(file_daemon_started_service_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_started_service_proto_rawDesc), len(file_daemon_started_service_proto_rawDesc)),
|
||||||
NumEnums: 4,
|
NumEnums: 3,
|
||||||
NumMessages: 25,
|
NumMessages: 26,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package daemon;
|
|||||||
option go_package = "github.com/sagernet/sing-box/daemon";
|
option go_package = "github.com/sagernet/sing-box/daemon";
|
||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "daemon/helper.proto";
|
|
||||||
|
|
||||||
service StartedService {
|
service StartedService {
|
||||||
rpc StopService(google.protobuf.Empty) returns (google.protobuf.Empty);
|
rpc StopService(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
@@ -28,14 +27,11 @@ service StartedService {
|
|||||||
rpc GetSystemProxyStatus(google.protobuf.Empty) returns(SystemProxyStatus) {}
|
rpc GetSystemProxyStatus(google.protobuf.Empty) returns(SystemProxyStatus) {}
|
||||||
rpc SetSystemProxyEnabled(SetSystemProxyEnabledRequest) returns(google.protobuf.Empty) {}
|
rpc SetSystemProxyEnabled(SetSystemProxyEnabledRequest) returns(google.protobuf.Empty) {}
|
||||||
|
|
||||||
rpc SubscribeConnections(SubscribeConnectionsRequest) returns(stream Connections) {}
|
rpc SubscribeConnections(SubscribeConnectionsRequest) returns(stream ConnectionEvents) {}
|
||||||
rpc CloseConnection(CloseConnectionRequest) returns(google.protobuf.Empty) {}
|
rpc CloseConnection(CloseConnectionRequest) returns(google.protobuf.Empty) {}
|
||||||
rpc CloseAllConnections(google.protobuf.Empty) returns(google.protobuf.Empty) {}
|
rpc CloseAllConnections(google.protobuf.Empty) returns(google.protobuf.Empty) {}
|
||||||
rpc GetDeprecatedWarnings(google.protobuf.Empty) returns(DeprecatedWarnings) {}
|
rpc GetDeprecatedWarnings(google.protobuf.Empty) returns(DeprecatedWarnings) {}
|
||||||
rpc GetStartedAt(google.protobuf.Empty) returns(StartedAt) {}
|
rpc GetStartedAt(google.protobuf.Empty) returns(StartedAt) {}
|
||||||
|
|
||||||
rpc SubscribeHelperEvents(google.protobuf.Empty) returns(stream HelperRequest) {}
|
|
||||||
rpc SendHelperResponse(HelperResponse) returns(google.protobuf.Empty) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ServiceStatus {
|
message ServiceStatus {
|
||||||
@@ -147,24 +143,26 @@ message SetSystemProxyEnabledRequest {
|
|||||||
|
|
||||||
message SubscribeConnectionsRequest {
|
message SubscribeConnectionsRequest {
|
||||||
int64 interval = 1;
|
int64 interval = 1;
|
||||||
ConnectionFilter filter = 2;
|
|
||||||
ConnectionSortBy sortBy = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionFilter {
|
enum ConnectionEventType {
|
||||||
ALL = 0;
|
CONNECTION_EVENT_NEW = 0;
|
||||||
ACTIVE = 1;
|
CONNECTION_EVENT_UPDATE = 1;
|
||||||
CLOSED = 2;
|
CONNECTION_EVENT_CLOSED = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConnectionSortBy {
|
message ConnectionEvent {
|
||||||
DATE = 0;
|
ConnectionEventType type = 1;
|
||||||
TRAFFIC = 1;
|
string id = 2;
|
||||||
TOTAL_TRAFFIC = 2;
|
Connection connection = 3;
|
||||||
|
int64 uplinkDelta = 4;
|
||||||
|
int64 downlinkDelta = 5;
|
||||||
|
int64 closedAt = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Connections {
|
message ConnectionEvents {
|
||||||
repeated Connection connections = 1;
|
repeated ConnectionEvent events = 1;
|
||||||
|
bool reset = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Connection {
|
message Connection {
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ const (
|
|||||||
StartedService_CloseAllConnections_FullMethodName = "/daemon.StartedService/CloseAllConnections"
|
StartedService_CloseAllConnections_FullMethodName = "/daemon.StartedService/CloseAllConnections"
|
||||||
StartedService_GetDeprecatedWarnings_FullMethodName = "/daemon.StartedService/GetDeprecatedWarnings"
|
StartedService_GetDeprecatedWarnings_FullMethodName = "/daemon.StartedService/GetDeprecatedWarnings"
|
||||||
StartedService_GetStartedAt_FullMethodName = "/daemon.StartedService/GetStartedAt"
|
StartedService_GetStartedAt_FullMethodName = "/daemon.StartedService/GetStartedAt"
|
||||||
StartedService_SubscribeHelperEvents_FullMethodName = "/daemon.StartedService/SubscribeHelperEvents"
|
|
||||||
StartedService_SendHelperResponse_FullMethodName = "/daemon.StartedService/SendHelperResponse"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartedServiceClient is the client API for StartedService service.
|
// StartedServiceClient is the client API for StartedService service.
|
||||||
@@ -60,13 +58,11 @@ type StartedServiceClient interface {
|
|||||||
SetGroupExpand(ctx context.Context, in *SetGroupExpandRequest, 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)
|
GetSystemProxyStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemProxyStatus, error)
|
||||||
SetSystemProxyEnabled(ctx context.Context, in *SetSystemProxyEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, 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)
|
SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ConnectionEvents], error)
|
||||||
CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, 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)
|
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)
|
GetDeprecatedWarnings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DeprecatedWarnings, error)
|
||||||
GetStartedAt(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*StartedAt, error)
|
GetStartedAt(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*StartedAt, error)
|
||||||
SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error)
|
|
||||||
SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type startedServiceClient struct {
|
type startedServiceClient struct {
|
||||||
@@ -282,13 +278,13 @@ func (c *startedServiceClient) SetSystemProxyEnabled(ctx context.Context, in *Se
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Connections], error) {
|
func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ConnectionEvents], error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[5], StartedService_SubscribeConnections_FullMethodName, cOpts...)
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[5], StartedService_SubscribeConnections_FullMethodName, cOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
x := &grpc.GenericClientStream[SubscribeConnectionsRequest, Connections]{ClientStream: stream}
|
x := &grpc.GenericClientStream[SubscribeConnectionsRequest, ConnectionEvents]{ClientStream: stream}
|
||||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -299,7 +295,7 @@ func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *Sub
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// 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]
|
type StartedService_SubscribeConnectionsClient = grpc.ServerStreamingClient[ConnectionEvents]
|
||||||
|
|
||||||
func (c *startedServiceClient) CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
func (c *startedServiceClient) CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
@@ -341,35 +337,6 @@ func (c *startedServiceClient) GetStartedAt(ctx context.Context, in *emptypb.Emp
|
|||||||
return out, nil
|
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.
|
// StartedServiceServer is the server API for StartedService service.
|
||||||
// All implementations must embed UnimplementedStartedServiceServer
|
// All implementations must embed UnimplementedStartedServiceServer
|
||||||
// for forward compatibility.
|
// for forward compatibility.
|
||||||
@@ -390,13 +357,11 @@ type StartedServiceServer interface {
|
|||||||
SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error)
|
SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error)
|
||||||
GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error)
|
GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error)
|
||||||
SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error)
|
SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error)
|
||||||
SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error
|
SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[ConnectionEvents]) error
|
||||||
CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error)
|
CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error)
|
||||||
CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error)
|
GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error)
|
||||||
GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error)
|
GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error)
|
||||||
SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error
|
|
||||||
SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error)
|
|
||||||
mustEmbedUnimplementedStartedServiceServer()
|
mustEmbedUnimplementedStartedServiceServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,95 +373,87 @@ type StartedServiceServer interface {
|
|||||||
type UnimplementedStartedServiceServer struct{}
|
type UnimplementedStartedServiceServer struct{}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) StopService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) StopService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method StopService not implemented")
|
return nil, status.Error(codes.Unimplemented, "method StopService not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) ReloadService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) ReloadService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ReloadService not implemented")
|
return nil, status.Error(codes.Unimplemented, "method ReloadService not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error {
|
func (UnimplementedStartedServiceServer) SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SubscribeServiceStatus not implemented")
|
return status.Error(codes.Unimplemented, "method SubscribeServiceStatus not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error {
|
func (UnimplementedStartedServiceServer) SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SubscribeLog not implemented")
|
return status.Error(codes.Unimplemented, "method SubscribeLog not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error) {
|
func (UnimplementedStartedServiceServer) GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetDefaultLogLevel not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GetDefaultLogLevel not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) ClearLogs(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) ClearLogs(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ClearLogs not implemented")
|
return nil, status.Error(codes.Unimplemented, "method ClearLogs not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error {
|
func (UnimplementedStartedServiceServer) SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SubscribeStatus not implemented")
|
return status.Error(codes.Unimplemented, "method SubscribeStatus not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error {
|
func (UnimplementedStartedServiceServer) SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SubscribeGroups not implemented")
|
return status.Error(codes.Unimplemented, "method SubscribeGroups not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error) {
|
func (UnimplementedStartedServiceServer) GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetClashModeStatus not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GetClashModeStatus not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SubscribeClashMode(*emptypb.Empty, grpc.ServerStreamingServer[ClashMode]) error {
|
func (UnimplementedStartedServiceServer) SubscribeClashMode(*emptypb.Empty, grpc.ServerStreamingServer[ClashMode]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SubscribeClashMode not implemented")
|
return status.Error(codes.Unimplemented, "method SubscribeClashMode not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SetClashMode(context.Context, *ClashMode) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) SetClashMode(context.Context, *ClashMode) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SetClashMode not implemented")
|
return nil, status.Error(codes.Unimplemented, "method SetClashMode not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) URLTest(context.Context, *URLTestRequest) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) URLTest(context.Context, *URLTestRequest) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method URLTest not implemented")
|
return nil, status.Error(codes.Unimplemented, "method URLTest not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SelectOutbound(context.Context, *SelectOutboundRequest) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) SelectOutbound(context.Context, *SelectOutboundRequest) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SelectOutbound not implemented")
|
return nil, status.Error(codes.Unimplemented, "method SelectOutbound not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SetGroupExpand not implemented")
|
return nil, status.Error(codes.Unimplemented, "method SetGroupExpand not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error) {
|
func (UnimplementedStartedServiceServer) GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetSystemProxyStatus not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GetSystemProxyStatus not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SetSystemProxyEnabled not implemented")
|
return nil, status.Error(codes.Unimplemented, "method SetSystemProxyEnabled not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error {
|
func (UnimplementedStartedServiceServer) SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[ConnectionEvents]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SubscribeConnections not implemented")
|
return status.Error(codes.Unimplemented, "method SubscribeConnections not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method CloseConnection not implemented")
|
return nil, status.Error(codes.Unimplemented, "method CloseConnection not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
func (UnimplementedStartedServiceServer) CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method CloseAllConnections not implemented")
|
return nil, status.Error(codes.Unimplemented, "method CloseAllConnections not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error) {
|
func (UnimplementedStartedServiceServer) GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetDeprecatedWarnings not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GetDeprecatedWarnings not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error) {
|
func (UnimplementedStartedServiceServer) GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetStartedAt not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GetStartedAt not implemented")
|
||||||
}
|
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error {
|
|
||||||
return status.Errorf(codes.Unimplemented, "method SubscribeHelperEvents not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (UnimplementedStartedServiceServer) SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SendHelperResponse not implemented")
|
|
||||||
}
|
}
|
||||||
func (UnimplementedStartedServiceServer) mustEmbedUnimplementedStartedServiceServer() {}
|
func (UnimplementedStartedServiceServer) mustEmbedUnimplementedStartedServiceServer() {}
|
||||||
func (UnimplementedStartedServiceServer) testEmbeddedByValue() {}
|
func (UnimplementedStartedServiceServer) testEmbeddedByValue() {}
|
||||||
@@ -509,7 +466,7 @@ type UnsafeStartedServiceServer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RegisterStartedServiceServer(s grpc.ServiceRegistrar, srv StartedServiceServer) {
|
func RegisterStartedServiceServer(s grpc.ServiceRegistrar, srv StartedServiceServer) {
|
||||||
// If the following call pancis, it indicates UnimplementedStartedServiceServer was
|
// If the following call panics, it indicates UnimplementedStartedServiceServer was
|
||||||
// embedded by pointer and is nil. This will cause panics if an
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
// unimplemented method is ever invoked, so we test this at initialization
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
// time to prevent it from happening at runtime later due to I/O.
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
@@ -777,11 +734,11 @@ func _StartedService_SubscribeConnections_Handler(srv interface{}, stream grpc.S
|
|||||||
if err := stream.RecvMsg(m); err != nil {
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return srv.(StartedServiceServer).SubscribeConnections(m, &grpc.GenericServerStream[SubscribeConnectionsRequest, Connections]{ServerStream: stream})
|
return srv.(StartedServiceServer).SubscribeConnections(m, &grpc.GenericServerStream[SubscribeConnectionsRequest, ConnectionEvents]{ServerStream: stream})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// 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]
|
type StartedService_SubscribeConnectionsServer = grpc.ServerStreamingServer[ConnectionEvents]
|
||||||
|
|
||||||
func _StartedService_CloseConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _StartedService_CloseConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(CloseConnectionRequest)
|
in := new(CloseConnectionRequest)
|
||||||
@@ -855,35 +812,6 @@ func _StartedService_GetStartedAt_Handler(srv interface{}, ctx context.Context,
|
|||||||
return interceptor(ctx, in, info, handler)
|
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.
|
// StartedService_ServiceDesc is the grpc.ServiceDesc for StartedService service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
@@ -951,10 +879,6 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "GetStartedAt",
|
MethodName: "GetStartedAt",
|
||||||
Handler: _StartedService_GetStartedAt_Handler,
|
Handler: _StartedService_GetStartedAt_Handler,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
MethodName: "SendHelperResponse",
|
|
||||||
Handler: _StartedService_SendHelperResponse_Handler,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
@@ -987,11 +911,6 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
Handler: _StartedService_SubscribeConnections_Handler,
|
Handler: _StartedService_SubscribeConnections_Handler,
|
||||||
ServerStreams: true,
|
ServerStreams: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
StreamName: "SubscribeHelperEvents",
|
|
||||||
Handler: _StartedService_SubscribeHelperEvents_Handler,
|
|
||||||
ServerStreams: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Metadata: "daemon/started_service.proto",
|
Metadata: "daemon/started_service.proto",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,11 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
if c.cache != nil {
|
if c.cache != nil {
|
||||||
cond, loaded := c.cacheLock.LoadOrStore(question, make(chan struct{}))
|
cond, loaded := c.cacheLock.LoadOrStore(question, make(chan struct{}))
|
||||||
if loaded {
|
if loaded {
|
||||||
<-cond
|
select {
|
||||||
|
case <-cond:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defer func() {
|
defer func() {
|
||||||
c.cacheLock.Delete(question)
|
c.cacheLock.Delete(question)
|
||||||
@@ -154,7 +158,11 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
} else if c.transportCache != nil {
|
} else if c.transportCache != nil {
|
||||||
cond, loaded := c.transportCacheLock.LoadOrStore(question, make(chan struct{}))
|
cond, loaded := c.transportCacheLock.LoadOrStore(question, make(chan struct{}))
|
||||||
if loaded {
|
if loaded {
|
||||||
<-cond
|
select {
|
||||||
|
case <-cond:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defer func() {
|
defer func() {
|
||||||
c.transportCacheLock.Delete(question)
|
c.transportCacheLock.Delete(question)
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn ne
|
|||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
buffer.Reset()
|
||||||
_, _, err := buffer.ReadPacketFrom(packetConn)
|
_, _, err := buffer.ReadPacketFrom(packetConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.ErrShortBuffer) {
|
if errors.Is(err, io.ErrShortBuffer) {
|
||||||
|
|||||||
@@ -2,10 +2,294 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### 1.13.0-rc.3
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
Important changes since 1.12:
|
||||||
|
|
||||||
|
* Add NaiveProxy outbound **1**
|
||||||
|
* Add pre-match support for `auto_redirect` **2**
|
||||||
|
* Improve `auto_redirect` **3**
|
||||||
|
* Add Chrome Root Store certificate option **4**
|
||||||
|
* Add new options for ACME DNS-01 challenge providers **5**
|
||||||
|
* Add Wi-Fi state support for Linux and Windows **6**
|
||||||
|
* Add curve preferences, pinned public key SHA256, mTLS and ECH `query_server_name` for TLS options **7**
|
||||||
|
* Add kTLS support **8**
|
||||||
|
* Add ICMP echo (ping) proxy support **9**
|
||||||
|
* Add `interface_address`, `network_interface_address` and `default_interface_address` rule items **10**
|
||||||
|
* Add `preferred_by` route rule item **11**
|
||||||
|
* Improve `local` DNS server **12**
|
||||||
|
* Add `disable_tcp_keep_alive`, `tcp_keep_alive` and `tcp_keep_alive_interval` options for listen and dial fields **13**
|
||||||
|
* Add `bind_address_no_port` option for dial fields **14**
|
||||||
|
* Add system interface and relay server options for Tailscale endpoint **15**
|
||||||
|
* Add Claude Code Multiplexer service **16**
|
||||||
|
* Add OpenAI Codex Multiplexer service **17**
|
||||||
|
* Apple/Android: Refactor GUI
|
||||||
|
* Apple/Android: Add support for sharing configurations via [QRS](https://github.com/qifi-dev/qrs)
|
||||||
|
* Android: Add support for resisting VPN detection via Xposed
|
||||||
|
* Drop support for go1.23 **18**
|
||||||
|
* Drop support for Android 5.0 **19**
|
||||||
|
* Update uTLS to v1.8.2 **20**
|
||||||
|
* Update quic-go to v0.59.0
|
||||||
|
* Update gVisor to v20250811
|
||||||
|
* Update Tailscale to v1.92.4
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
NaiveProxy outbound now supports QUIC, ECH, UDP over TCP, and configurable QUIC congestion control.
|
||||||
|
|
||||||
|
Only available on Apple platforms, Android, Windows and some Linux architectures.
|
||||||
|
Each Windows release includes `libcronet.dll` —
|
||||||
|
ensure this file is in the same directory as `sing-box.exe` or in a directory listed in `PATH`.
|
||||||
|
|
||||||
|
See [NaiveProxy outbound](/configuration/outbound/naive/).
|
||||||
|
|
||||||
|
**2**:
|
||||||
|
|
||||||
|
`auto_redirect` now allows you to bypass sing-box for connections based on routing rules.
|
||||||
|
|
||||||
|
A new rule action `bypass` is introduced to support this feature. When matched during pre-match, the connection will bypass sing-box and connect directly.
|
||||||
|
|
||||||
|
This feature requires Linux with `auto_redirect` enabled.
|
||||||
|
|
||||||
|
See [Pre-match](/configuration/shared/pre-match/) and [Rule Action](/configuration/route/rule_action/#bypass).
|
||||||
|
|
||||||
|
**3**:
|
||||||
|
|
||||||
|
`auto_redirect` now rejects MPTCP connections by default to fix compatibility issues.
|
||||||
|
You can change it to bypass sing-box via the new `exclude_mptcp` option.
|
||||||
|
|
||||||
|
Adds a fallback iproute2 rule checked after system default rules (32766: main, 32767: default),
|
||||||
|
ensuring traffic is routed to the sing-box table when no route is found in system tables.
|
||||||
|
The rule index can be customized via `auto_redirect_iproute2_fallback_rule_index` (default: 32768).
|
||||||
|
|
||||||
|
See [TUN](/configuration/inbound/tun/#exclude_mptcp).
|
||||||
|
|
||||||
|
**4**:
|
||||||
|
|
||||||
|
Adds `chrome` as a new certificate store option alongside `mozilla`.
|
||||||
|
Both stores filter out China-based CA certificates.
|
||||||
|
|
||||||
|
See [Certificate](/configuration/certificate/#store).
|
||||||
|
|
||||||
|
**5**:
|
||||||
|
|
||||||
|
See [DNS-01 Challenge](/configuration/shared/dns01_challenge/).
|
||||||
|
|
||||||
|
**6**:
|
||||||
|
|
||||||
|
sing-box can now monitor Wi-Fi state on Linux and Windows to enable routing rules based on `wifi_ssid` and `wifi_bssid`.
|
||||||
|
|
||||||
|
See [Wi-Fi State](/configuration/shared/wifi-state/).
|
||||||
|
|
||||||
|
**7**:
|
||||||
|
|
||||||
|
See [TLS](/configuration/shared/tls/).
|
||||||
|
|
||||||
|
**8**:
|
||||||
|
|
||||||
|
Adds `kernel_tx` and `kernel_rx` options for TLS inbound.
|
||||||
|
Enables kernel-level TLS offloading via `splice(2)` on Linux 5.1+ with TLS 1.3.
|
||||||
|
|
||||||
|
See [TLS](/configuration/shared/tls/).
|
||||||
|
|
||||||
|
**9**:
|
||||||
|
|
||||||
|
sing-box can now proxy ICMP echo (ping) requests.
|
||||||
|
A new `icmp` network type is available for route rules.
|
||||||
|
Supported from TUN, WireGuard and Tailscale inbounds to Direct, WireGuard and Tailscale outbounds.
|
||||||
|
The `reject` action can also reply to ICMP echo requests.
|
||||||
|
|
||||||
|
**10**:
|
||||||
|
|
||||||
|
New rule items for matching based on interface IP addresses, available in route rules, DNS rules and rule-sets.
|
||||||
|
|
||||||
|
**11**:
|
||||||
|
|
||||||
|
Matches outbounds' preferred routes.
|
||||||
|
For Tailscale: MagicDNS domains and peers' allowed IPs. For WireGuard: peers' allowed IPs.
|
||||||
|
|
||||||
|
**12**:
|
||||||
|
|
||||||
|
The `local` DNS server now uses platform-native resolution:
|
||||||
|
`getaddrinfo`/libresolv on Apple platforms, systemd-resolved DBus on Linux.
|
||||||
|
A new `prefer_go` option is available to opt out.
|
||||||
|
|
||||||
|
See [Local DNS](/configuration/dns/server/local/).
|
||||||
|
|
||||||
|
**13**:
|
||||||
|
|
||||||
|
The default TCP keep-alive initial period has been updated from 10 minutes to 5 minutes.
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial/#tcp_keep_alive).
|
||||||
|
|
||||||
|
**14**:
|
||||||
|
|
||||||
|
Adds the Linux socket option `IP_BIND_ADDRESS_NO_PORT` support when explicitly binding to a source address.
|
||||||
|
|
||||||
|
This allows reusing the same source port for multiple connections, improving scalability for high-concurrency proxy scenarios.
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial/#bind_address_no_port).
|
||||||
|
|
||||||
|
**15**:
|
||||||
|
|
||||||
|
Tailscale endpoint can now create a system TUN interface to handle traffic directly.
|
||||||
|
New `relay_server_port` and `relay_server_static_endpoints` options for incoming relay connections.
|
||||||
|
|
||||||
|
See [Tailscale endpoint](/configuration/endpoint/tailscale/).
|
||||||
|
|
||||||
|
**16**:
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
**17**:
|
||||||
|
|
||||||
|
See [OCM](/configuration/service/ocm).
|
||||||
|
|
||||||
|
**18**:
|
||||||
|
|
||||||
|
Due to maintenance difficulties, sing-box 1.13.0 requires at least Go 1.24 to compile.
|
||||||
|
|
||||||
|
**19**:
|
||||||
|
|
||||||
|
Due to maintenance difficulties, sing-box 1.13.0 will be the last version to support Android 5.0,
|
||||||
|
and only through a separate legacy build (with `-legacy-android-5` suffix).
|
||||||
|
|
||||||
|
For standalone binaries, the minimum Android version has been raised to Android 6.0,
|
||||||
|
since Termux requires Android 7.0 or later.
|
||||||
|
|
||||||
|
**20**:
|
||||||
|
|
||||||
|
This update fixes missing padding extension for Chrome 120+ fingerprints.
|
||||||
|
|
||||||
|
Also, documentation has been updated with a warning about uTLS fingerprinting vulnerabilities.
|
||||||
|
uTLS is not recommended for censorship circumvention due to fundamental architectural limitations;
|
||||||
|
use NaiveProxy instead for TLS fingerprint resistance.
|
||||||
|
|
||||||
|
#### 1.12.21
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.13.0-rc.2
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.20
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.13.0-rc.1
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.19
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.13.0-beta.8
|
||||||
|
|
||||||
|
* Add fallback routing rule for `auto_redirect` **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
Adds a fallback iproute2 rule checked after system default rules (32766: main, 32767: default),
|
||||||
|
ensuring traffic is routed to the sing-box table when no route is found in system tables.
|
||||||
|
|
||||||
|
The rule index can be customized via `auto_redirect_iproute2_fallback_rule_index` (default: 32768).
|
||||||
|
|
||||||
|
#### 1.12.18
|
||||||
|
|
||||||
|
* Add fallback routing rule for `auto_redirect` **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
Adds a fallback iproute2 rule checked after system default rules (32766: main, 32767: default),
|
||||||
|
ensuring traffic is routed to the sing-box table when no route is found in system tables.
|
||||||
|
|
||||||
|
The rule index can be customized via `auto_redirect_iproute2_fallback_rule_index` (default: 32768).
|
||||||
|
|
||||||
|
#### 1.13.0-beta.6
|
||||||
|
|
||||||
|
* Update uTLS to v1.8.2 **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
This update fixes missing padding extension for Chrome 120+ fingerprints.
|
||||||
|
|
||||||
|
Also, documentation has been updated with a warning about uTLS fingerprinting vulnerabilities.
|
||||||
|
uTLS is not recommended for censorship circumvention due to fundamental architectural limitations;
|
||||||
|
use NaiveProxy instead for TLS fingerprint resistance.
|
||||||
|
|
||||||
|
#### 1.12.17
|
||||||
|
|
||||||
|
* Update uTLS to v1.8.2 **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
This update fixes missing padding extension for Chrome 120+ fingerprints.
|
||||||
|
|
||||||
|
Also, documentation has been updated with a warning about uTLS fingerprinting vulnerabilities.
|
||||||
|
uTLS is not recommended for censorship circumvention due to fundamental architectural limitations;
|
||||||
|
use NaiveProxy instead for TLS fingerprint resistance.
|
||||||
|
|
||||||
|
#### 1.13.0-beta.5
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.16
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.13.0-beta.4
|
||||||
|
|
||||||
|
* Apple/Android: Add support for sharing configurations via [QRS](https://github.com/qifi-dev/qrs)
|
||||||
|
* Android: Add support for resisting VPN detection via Xposed
|
||||||
|
* Update quic-go to v0.59.0
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.13.0-beta.2
|
||||||
|
|
||||||
|
* Add `bind_address_no_port` option for dial fields **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
Adds the Linux socket option `IP_BIND_ADDRESS_NO_PORT` support when explicitly binding to a source address.
|
||||||
|
|
||||||
|
This allows reusing the same source port for multiple connections, improving scalability for high-concurrency proxy scenarios.
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial/#bind_address_no_port).
|
||||||
|
|
||||||
|
#### 1.13.0-beta.1
|
||||||
|
|
||||||
|
* Add system interface support for Tailscale endpoint **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
Tailscale endpoint can now create a system TUN interface to handle traffic directly.
|
||||||
|
|
||||||
|
See [Tailscale endpoint](/configuration/endpoint/tailscale/#system_interface).
|
||||||
|
|
||||||
|
#### 1.12.15
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.13.0-alpha.36
|
||||||
|
|
||||||
|
* Downgrade quic-go to v0.57.1
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.13.0-alpha.35
|
#### 1.13.0-alpha.35
|
||||||
|
|
||||||
* Add pre-match support for `auto_redirect` **1**
|
* Add pre-match support for `auto_redirect` **1**
|
||||||
* Fix missing relay support for Tailscale **2**
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
**1**:
|
**1**:
|
||||||
@@ -18,10 +302,6 @@ This feature requires Linux with `auto_redirect` enabled.
|
|||||||
|
|
||||||
See [Pre-match](/configuration/shared/pre-match/) and [Rule Action](/configuration/route/rule_action/#bypass).
|
See [Pre-match](/configuration/shared/pre-match/) and [Rule Action](/configuration/route/rule_action/#bypass).
|
||||||
|
|
||||||
**2**:
|
|
||||||
|
|
||||||
See [Tailscale Endpoint](/configuration/endpoint/tailscale/#relay_server_port).
|
|
||||||
|
|
||||||
#### 1.13.0-alpha.34
|
#### 1.13.0-alpha.34
|
||||||
|
|
||||||
* Add Chrome Root Store certificate option **1**
|
* Add Chrome Root Store certificate option **1**
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ icon: material/new-box
|
|||||||
!!! quote "Changes in sing-box 1.13.0"
|
!!! quote "Changes in sing-box 1.13.0"
|
||||||
|
|
||||||
:material-plus: [relay_server_port](#relay_server_port)
|
:material-plus: [relay_server_port](#relay_server_port)
|
||||||
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
|
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
|
||||||
|
:material-plus: [system_interface](#system_interface)
|
||||||
|
:material-plus: [system_interface_name](#system_interface_name)
|
||||||
|
:material-plus: [system_interface_mtu](#system_interface_mtu)
|
||||||
|
|
||||||
!!! question "Since sing-box 1.12.0"
|
!!! question "Since sing-box 1.12.0"
|
||||||
|
|
||||||
@@ -27,8 +30,11 @@ icon: material/new-box
|
|||||||
"advertise_exit_node": false,
|
"advertise_exit_node": false,
|
||||||
"relay_server_port": 0,
|
"relay_server_port": 0,
|
||||||
"relay_server_static_endpoints": [],
|
"relay_server_static_endpoints": [],
|
||||||
|
"system_interface": false,
|
||||||
|
"system_interface_name": "",
|
||||||
|
"system_interface_mtu": 0,
|
||||||
"udp_timeout": "5m",
|
"udp_timeout": "5m",
|
||||||
|
|
||||||
... // Dial Fields
|
... // Dial Fields
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -98,12 +104,34 @@ Indicates whether the node should advertise itself as an exit node.
|
|||||||
|
|
||||||
#### relay_server_port
|
#### relay_server_port
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
The port to listen on for incoming relay connections from other Tailscale nodes.
|
The port to listen on for incoming relay connections from other Tailscale nodes.
|
||||||
|
|
||||||
#### relay_server_static_endpoints
|
#### relay_server_static_endpoints
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
Static endpoints to advertise for the relay server.
|
Static endpoints to advertise for the relay server.
|
||||||
|
|
||||||
|
#### system_interface
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
Create a system TUN interface for Tailscale.
|
||||||
|
|
||||||
|
#### system_interface_name
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
Custom TUN interface name. By default, `tailscale` (or `utun` on macOS) will be used.
|
||||||
|
|
||||||
|
#### system_interface_mtu
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
Override the TUN MTU. By default, Tailscale's own MTU is used.
|
||||||
|
|
||||||
#### udp_timeout
|
#### udp_timeout
|
||||||
|
|
||||||
UDP NAT expiration time.
|
UDP NAT expiration time.
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ icon: material/new-box
|
|||||||
!!! quote "sing-box 1.13.0 中的更改"
|
!!! quote "sing-box 1.13.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [relay_server_port](#relay_server_port)
|
:material-plus: [relay_server_port](#relay_server_port)
|
||||||
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
|
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
|
||||||
|
:material-plus: [system_interface](#system_interface)
|
||||||
|
:material-plus: [system_interface_name](#system_interface_name)
|
||||||
|
:material-plus: [system_interface_mtu](#system_interface_mtu)
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
@@ -27,6 +30,9 @@ icon: material/new-box
|
|||||||
"advertise_exit_node": false,
|
"advertise_exit_node": false,
|
||||||
"relay_server_port": 0,
|
"relay_server_port": 0,
|
||||||
"relay_server_static_endpoints": [],
|
"relay_server_static_endpoints": [],
|
||||||
|
"system_interface": false,
|
||||||
|
"system_interface_name": "",
|
||||||
|
"system_interface_mtu": 0,
|
||||||
"udp_timeout": "5m",
|
"udp_timeout": "5m",
|
||||||
|
|
||||||
... // 拨号字段
|
... // 拨号字段
|
||||||
@@ -97,12 +103,34 @@ icon: material/new-box
|
|||||||
|
|
||||||
#### relay_server_port
|
#### relay_server_port
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
监听来自其他 Tailscale 节点的中继连接的端口。
|
监听来自其他 Tailscale 节点的中继连接的端口。
|
||||||
|
|
||||||
#### relay_server_static_endpoints
|
#### relay_server_static_endpoints
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
为中继服务器通告的静态端点。
|
为中继服务器通告的静态端点。
|
||||||
|
|
||||||
|
#### system_interface
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
为 Tailscale 创建系统 TUN 接口。
|
||||||
|
|
||||||
|
#### system_interface_name
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
自定义 TUN 接口名。默认使用 `tailscale`(macOS 上为 `utun`)。
|
||||||
|
|
||||||
|
#### system_interface_mtu
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
覆盖 TUN 的 MTU。默认使用 Tailscale 自己的 MTU。
|
||||||
|
|
||||||
#### udp_timeout
|
#### udp_timeout
|
||||||
|
|
||||||
UDP NAT 过期时间。
|
UDP NAT 过期时间。
|
||||||
@@ -115,4 +143,4 @@ UDP NAT 过期时间。
|
|||||||
|
|
||||||
Tailscale 端点中的拨号字段仅控制它如何连接到控制平面,与实际连接无关。
|
Tailscale 端点中的拨号字段仅控制它如何连接到控制平面,与实际连接无关。
|
||||||
|
|
||||||
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
"method": "2022-blake3-aes-128-gcm",
|
"method": "2022-blake3-aes-128-gcm",
|
||||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
|
"managed": false,
|
||||||
"multiplex": {}
|
"multiplex": {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -86,6 +87,10 @@ Both if empty.
|
|||||||
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
|
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
|
||||||
| other methods | any string |
|
| other methods | any string |
|
||||||
|
|
||||||
|
#### managed
|
||||||
|
|
||||||
|
Defaults to `false`. Enable this when the inbound is managed by the [SSM API](/configuration/service/ssm-api) for dynamic user.
|
||||||
|
|
||||||
#### multiplex
|
#### multiplex
|
||||||
|
|
||||||
See [Multiplex](/configuration/shared/multiplex#inbound) for details.
|
See [Multiplex](/configuration/shared/multiplex#inbound) for details.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
"method": "2022-blake3-aes-128-gcm",
|
"method": "2022-blake3-aes-128-gcm",
|
||||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
|
"managed": false,
|
||||||
"multiplex": {}
|
"multiplex": {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -86,6 +87,10 @@
|
|||||||
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
|
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
|
||||||
| other methods | 任意字符串 |
|
| other methods | 任意字符串 |
|
||||||
|
|
||||||
|
#### managed
|
||||||
|
|
||||||
|
默认为 `false`。当该入站需要由 [SSM API](/zh/configuration/service/ssm-api) 管理用户时必须启用此字段。
|
||||||
|
|
||||||
#### multiplex
|
#### multiplex
|
||||||
|
|
||||||
参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。
|
参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ icon: material/new-box
|
|||||||
|
|
||||||
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
|
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
|
||||||
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
|
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
|
||||||
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
||||||
|
:material-plus: [auto_redirect_iproute2_fallback_rule_index](#auto_redirect_iproute2_fallback_rule_index)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ icon: material/new-box
|
|||||||
"auto_redirect_output_mark": "0x2024",
|
"auto_redirect_output_mark": "0x2024",
|
||||||
"auto_redirect_reset_mark": "0x2025",
|
"auto_redirect_reset_mark": "0x2025",
|
||||||
"auto_redirect_nfqueue": 100,
|
"auto_redirect_nfqueue": 100,
|
||||||
|
"auto_redirect_iproute2_fallback_rule_index": 32768,
|
||||||
"exclude_mptcp": false,
|
"exclude_mptcp": false,
|
||||||
"loopback_address": [
|
"loopback_address": [
|
||||||
"10.7.0.1"
|
"10.7.0.1"
|
||||||
@@ -303,6 +305,17 @@ NFQueue number used by `auto_redirect` pre-matching.
|
|||||||
|
|
||||||
`100` is used by default.
|
`100` is used by default.
|
||||||
|
|
||||||
|
#### auto_redirect_iproute2_fallback_rule_index
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.12.18"
|
||||||
|
|
||||||
|
Linux iproute2 fallback rule index generated by `auto_redirect`.
|
||||||
|
|
||||||
|
This rule is checked after system default rules (32766: main, 32767: default),
|
||||||
|
routing traffic to the sing-box table only when no route is found in system tables.
|
||||||
|
|
||||||
|
`32768` is used by default.
|
||||||
|
|
||||||
#### exclude_mptcp
|
#### exclude_mptcp
|
||||||
|
|
||||||
!!! question "Since sing-box 1.13.0"
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ icon: material/new-box
|
|||||||
|
|
||||||
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
|
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
|
||||||
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
|
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
|
||||||
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
:material-plus: [exclude_mptcp](#exclude_mptcp)
|
||||||
|
:material-plus: [auto_redirect_iproute2_fallback_rule_index](#auto_redirect_iproute2_fallback_rule_index)
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ icon: material/new-box
|
|||||||
"auto_redirect_output_mark": "0x2024",
|
"auto_redirect_output_mark": "0x2024",
|
||||||
"auto_redirect_reset_mark": "0x2025",
|
"auto_redirect_reset_mark": "0x2025",
|
||||||
"auto_redirect_nfqueue": 100,
|
"auto_redirect_nfqueue": 100,
|
||||||
|
"auto_redirect_iproute2_fallback_rule_index": 32768,
|
||||||
"exclude_mptcp": false,
|
"exclude_mptcp": false,
|
||||||
"loopback_address": [
|
"loopback_address": [
|
||||||
"10.7.0.1"
|
"10.7.0.1"
|
||||||
@@ -302,13 +304,24 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
默认使用 `100`。
|
默认使用 `100`。
|
||||||
|
|
||||||
|
#### auto_redirect_iproute2_fallback_rule_index
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.18 起"
|
||||||
|
|
||||||
|
`auto_redirect` 生成的 iproute2 回退规则索引。
|
||||||
|
|
||||||
|
此规则在系统默认规则(32766: main,32767: default)之后检查,
|
||||||
|
仅当系统路由表中未找到路由时才将流量路由到 sing-box 路由表。
|
||||||
|
|
||||||
|
默认使用 `32768`。
|
||||||
|
|
||||||
#### exclude_mptcp
|
#### exclude_mptcp
|
||||||
|
|
||||||
!!! question "自 sing-box 1.13.0 起"
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅支持 Linux,且需要 nftables,`auto_route` 和 `auto_redirect` 已启用。
|
仅支持 Linux,且需要 nftables,`auto_route` 和 `auto_redirect` 已启用。
|
||||||
|
|
||||||
由于协议限制,MPTCP 无法被透明代理。
|
由于协议限制,MPTCP 无法被透明代理。
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ icon: material/new-box
|
|||||||
|
|
||||||
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
||||||
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
|
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
|
||||||
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
|
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
|
||||||
|
:material-plus: [bind_address_no_port](#bind_address_no_port)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
!!! quote "Changes in sing-box 1.12.0"
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ icon: material/new-box
|
|||||||
"bind_interface": "",
|
"bind_interface": "",
|
||||||
"inet4_bind_address": "",
|
"inet4_bind_address": "",
|
||||||
"inet6_bind_address": "",
|
"inet6_bind_address": "",
|
||||||
|
"bind_address_no_port": false,
|
||||||
"routing_mark": 0,
|
"routing_mark": 0,
|
||||||
"reuse_addr": false,
|
"reuse_addr": false,
|
||||||
"netns": "",
|
"netns": "",
|
||||||
@@ -76,6 +78,18 @@ The IPv4 address to bind to.
|
|||||||
|
|
||||||
The IPv6 address to bind to.
|
The IPv6 address to bind to.
|
||||||
|
|
||||||
|
#### bind_address_no_port
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
Only supported on Linux.
|
||||||
|
|
||||||
|
Do not reserve a port when binding to a source address.
|
||||||
|
|
||||||
|
This allows reusing the same source port for multiple connections if the full 4-tuple (source IP, source port, destination IP, destination port) remains unique.
|
||||||
|
|
||||||
#### routing_mark
|
#### routing_mark
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ icon: material/new-box
|
|||||||
|
|
||||||
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
|
||||||
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
|
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
|
||||||
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
|
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
|
||||||
|
:material-plus: [bind_address_no_port](#bind_address_no_port)
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
!!! quote "sing-box 1.12.0 中的更改"
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ icon: material/new-box
|
|||||||
"bind_interface": "",
|
"bind_interface": "",
|
||||||
"inet4_bind_address": "",
|
"inet4_bind_address": "",
|
||||||
"inet6_bind_address": "",
|
"inet6_bind_address": "",
|
||||||
|
"bind_address_no_port": false,
|
||||||
"routing_mark": 0,
|
"routing_mark": 0,
|
||||||
"reuse_addr": false,
|
"reuse_addr": false,
|
||||||
"netns": "",
|
"netns": "",
|
||||||
@@ -76,6 +78,18 @@ icon: material/new-box
|
|||||||
|
|
||||||
要绑定的 IPv6 地址。
|
要绑定的 IPv6 地址。
|
||||||
|
|
||||||
|
#### bind_address_no_port
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
仅支持 Linux。
|
||||||
|
|
||||||
|
绑定到源地址时不保留端口。
|
||||||
|
|
||||||
|
这允许在完整的四元组(源 IP、源端口、目标 IP、目标端口)保持唯一的情况下,为多个连接复用同一源端口。
|
||||||
|
|
||||||
#### routing_mark
|
#### routing_mark
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ icon: material/new-box
|
|||||||
!!! quote "Changes in sing-box 1.13.0"
|
!!! quote "Changes in sing-box 1.13.0"
|
||||||
|
|
||||||
:material-plus: [alidns.security_token](#security_token)
|
:material-plus: [alidns.security_token](#security_token)
|
||||||
:material-plus: [cloudflare.zone_token](#zone_token)
|
:material-plus: [cloudflare.zone_token](#zone_token)
|
||||||
|
:material-plus: [acmedns](#acmedns)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
@@ -54,3 +55,19 @@ The Security Token for STS temporary credentials.
|
|||||||
Optional API token with `Zone:Read` permission.
|
Optional API token with `Zone:Read` permission.
|
||||||
|
|
||||||
When provided, allows `api_token` to be scoped to a single zone.
|
When provided, allows `api_token` to be scoped to a single zone.
|
||||||
|
|
||||||
|
#### ACME-DNS
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.13.0"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"provider": "acmedns",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"subdomain": "",
|
||||||
|
"server_url": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See [ACME-DNS](https://github.com/joohoi/acme-dns) for details.
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ icon: material/new-box
|
|||||||
!!! quote "sing-box 1.13.0 中的更改"
|
!!! quote "sing-box 1.13.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [alidns.security_token](#security_token)
|
:material-plus: [alidns.security_token](#security_token)
|
||||||
:material-plus: [cloudflare.zone_token](#zone_token)
|
:material-plus: [cloudflare.zone_token](#zone_token)
|
||||||
|
:material-plus: [acmedns](#acmedns)
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
@@ -54,3 +55,19 @@ icon: material/new-box
|
|||||||
具有 `Zone:Read` 权限的可选 API 令牌。
|
具有 `Zone:Read` 权限的可选 API 令牌。
|
||||||
|
|
||||||
提供后可将 `api_token` 限定到单个区域。
|
提供后可将 `api_token` 限定到单个区域。
|
||||||
|
|
||||||
|
#### ACME-DNS
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"provider": "acmedns",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"subdomain": "",
|
||||||
|
"server_url": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
参阅 [ACME-DNS](https://github.com/joohoi/acme-dns)。
|
||||||
|
|||||||
@@ -418,9 +418,18 @@ Enable kernel TLS receive support.
|
|||||||
|
|
||||||
==Client only==
|
==Client only==
|
||||||
|
|
||||||
!!! failure ""
|
!!! failure "Not Recommended"
|
||||||
|
|
||||||
There is no evidence that GFW detects and blocks servers based on TLS client fingerprinting, and using an imperfect emulation that has not been security reviewed could pose security risks.
|
uTLS has had repeated fingerprinting vulnerabilities discovered by researchers.
|
||||||
|
|
||||||
|
uTLS is a Go library that attempts to imitate browser TLS fingerprints by copying
|
||||||
|
ClientHello structure. However, browsers use completely different TLS stacks
|
||||||
|
(Chrome uses BoringSSL, Firefox uses NSS) with distinct implementation behaviors
|
||||||
|
that cannot be replicated by simply copying the handshake format, making detection possible.
|
||||||
|
Additionally, the library lacks active maintenance and has poor code quality,
|
||||||
|
making it unsuitable for censorship circumvention.
|
||||||
|
|
||||||
|
For TLS fingerprint resistance, use [NaiveProxy](/configuration/inbound/naive/) instead.
|
||||||
|
|
||||||
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance.
|
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance.
|
||||||
|
|
||||||
|
|||||||
@@ -417,9 +417,16 @@ echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/
|
|||||||
|
|
||||||
==仅客户端==
|
==仅客户端==
|
||||||
|
|
||||||
!!! failure ""
|
!!! failure "不推荐"
|
||||||
|
|
||||||
没有证据表明 GFW 根据 TLS 客户端指纹检测并阻止服务器,并且,使用一个未经安全审查的不完美模拟可能带来安全隐患。
|
uTLS 已被研究人员多次发现其指纹可被识别的漏洞。
|
||||||
|
|
||||||
|
uTLS 是一个试图通过复制 ClientHello 结构来模仿浏览器 TLS 指纹的 Go 库。
|
||||||
|
然而,浏览器使用完全不同的 TLS 实现(Chrome 使用 BoringSSL,Firefox 使用 NSS),
|
||||||
|
其实现行为无法通过简单复制握手格式来复现,其行为细节必然存在差异,使得检测成为可能。
|
||||||
|
此外,此库缺乏积极维护,且代码质量较差,不建议用于反审查场景。
|
||||||
|
|
||||||
|
如需 TLS 指纹抵抗,请改用 [NaiveProxy](/configuration/inbound/naive/)。
|
||||||
|
|
||||||
uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻力。
|
uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻力。
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ icon: material/horse
|
|||||||
|
|
||||||
# Trojan
|
# Trojan
|
||||||
|
|
||||||
Torjan is the most commonly used TLS proxy made in China. It can be used in various combinations,
|
Trojan is the most commonly used TLS proxy made in China. It can be used in various combinations.
|
||||||
but only the combination of uTLS and multiplexing is recommended.
|
|
||||||
|
|
||||||
| Protocol and implementation combination | Specification | Resists passive detection | Resists active probes |
|
| Protocol and implementation combination | Specification | Resists passive detection | Resists active probes |
|
||||||
|-----------------------------------------|----------------------------------------------------------------------|---------------------------|-----------------------|
|
|-----------------------------------------|----------------------------------------------------------------------|---------------------------|-----------------------|
|
||||||
@@ -140,11 +139,7 @@ but only the combination of uTLS and multiplexing is recommended.
|
|||||||
"password": "password",
|
"password": "password",
|
||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"server_name": "example.org",
|
"server_name": "example.org"
|
||||||
"utls": {
|
|
||||||
"enabled": true,
|
|
||||||
"fingerprint": "firefox"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"multiplex": {
|
"multiplex": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
@@ -171,11 +166,7 @@ but only the combination of uTLS and multiplexing is recommended.
|
|||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"server_name": "example.org",
|
"server_name": "example.org",
|
||||||
"certificate_path": "/path/to/certificate.pem",
|
"certificate_path": "/path/to/certificate.pem"
|
||||||
"utls": {
|
|
||||||
"enabled": true,
|
|
||||||
"fingerprint": "firefox"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"multiplex": {
|
"multiplex": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
@@ -198,11 +189,7 @@ but only the combination of uTLS and multiplexing is recommended.
|
|||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"server_name": "example.org",
|
"server_name": "example.org",
|
||||||
"insecure": true,
|
"insecure": true
|
||||||
"utls": {
|
|
||||||
"enabled": true,
|
|
||||||
"fingerprint": "firefox"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"multiplex": {
|
"multiplex": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type CacheFile struct {
|
|||||||
storeRDRC bool
|
storeRDRC bool
|
||||||
rdrcTimeout time.Duration
|
rdrcTimeout time.Duration
|
||||||
DB *bbolt.DB
|
DB *bbolt.DB
|
||||||
|
resetAccess sync.Mutex
|
||||||
saveMetadataTimer *time.Timer
|
saveMetadataTimer *time.Timer
|
||||||
saveFakeIPAccess sync.RWMutex
|
saveFakeIPAccess sync.RWMutex
|
||||||
saveDomain map[netip.Addr]string
|
saveDomain map[netip.Addr]string
|
||||||
@@ -169,13 +170,55 @@ func (c *CacheFile) Close() error {
|
|||||||
return c.DB.Close()
|
return c.DB.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) view(fn func(tx *bbolt.Tx) error) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
c.resetDB()
|
||||||
|
err = E.New("database corrupted: ", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return c.DB.View(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) batch(fn func(tx *bbolt.Tx) error) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
c.resetDB()
|
||||||
|
err = E.New("database corrupted: ", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return c.DB.Batch(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) update(fn func(tx *bbolt.Tx) error) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
c.resetDB()
|
||||||
|
err = E.New("database corrupted: ", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return c.DB.Update(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) resetDB() {
|
||||||
|
c.resetAccess.Lock()
|
||||||
|
defer c.resetAccess.Unlock()
|
||||||
|
c.DB.Close()
|
||||||
|
os.Remove(c.path)
|
||||||
|
db, err := bbolt.Open(c.path, 0o666, &bbolt.Options{Timeout: time.Second})
|
||||||
|
if err == nil {
|
||||||
|
_ = filemanager.Chown(c.ctx, c.path)
|
||||||
|
c.DB = db
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CacheFile) StoreFakeIP() bool {
|
func (c *CacheFile) StoreFakeIP() bool {
|
||||||
return c.storeFakeIP
|
return c.storeFakeIP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) LoadMode() string {
|
func (c *CacheFile) LoadMode() string {
|
||||||
var mode string
|
var mode string
|
||||||
c.DB.View(func(t *bbolt.Tx) error {
|
c.view(func(t *bbolt.Tx) error {
|
||||||
bucket := t.Bucket(bucketMode)
|
bucket := t.Bucket(bucketMode)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -193,7 +236,7 @@ func (c *CacheFile) LoadMode() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) StoreMode(mode string) error {
|
func (c *CacheFile) StoreMode(mode string) error {
|
||||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
return c.batch(func(t *bbolt.Tx) error {
|
||||||
bucket, err := t.CreateBucketIfNotExists(bucketMode)
|
bucket, err := t.CreateBucketIfNotExists(bucketMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -230,7 +273,7 @@ func (c *CacheFile) createBucket(t *bbolt.Tx, key []byte) (*bbolt.Bucket, error)
|
|||||||
|
|
||||||
func (c *CacheFile) LoadSelected(group string) string {
|
func (c *CacheFile) LoadSelected(group string) string {
|
||||||
var selected string
|
var selected string
|
||||||
c.DB.View(func(t *bbolt.Tx) error {
|
c.view(func(t *bbolt.Tx) error {
|
||||||
bucket := c.bucket(t, bucketSelected)
|
bucket := c.bucket(t, bucketSelected)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -245,7 +288,7 @@ func (c *CacheFile) LoadSelected(group string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) StoreSelected(group, selected string) error {
|
func (c *CacheFile) StoreSelected(group, selected string) error {
|
||||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
return c.batch(func(t *bbolt.Tx) error {
|
||||||
bucket, err := c.createBucket(t, bucketSelected)
|
bucket, err := c.createBucket(t, bucketSelected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -255,7 +298,7 @@ func (c *CacheFile) StoreSelected(group, selected string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) LoadGroupExpand(group string) (isExpand bool, loaded bool) {
|
func (c *CacheFile) LoadGroupExpand(group string) (isExpand bool, loaded bool) {
|
||||||
c.DB.View(func(t *bbolt.Tx) error {
|
c.view(func(t *bbolt.Tx) error {
|
||||||
bucket := c.bucket(t, bucketExpand)
|
bucket := c.bucket(t, bucketExpand)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -271,7 +314,7 @@ func (c *CacheFile) LoadGroupExpand(group string) (isExpand bool, loaded bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
||||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
return c.batch(func(t *bbolt.Tx) error {
|
||||||
bucket, err := c.createBucket(t, bucketExpand)
|
bucket, err := c.createBucket(t, bucketExpand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -286,7 +329,7 @@ func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
|||||||
|
|
||||||
func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedBinary {
|
func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedBinary {
|
||||||
var savedSet adapter.SavedBinary
|
var savedSet adapter.SavedBinary
|
||||||
err := c.DB.View(func(t *bbolt.Tx) error {
|
err := c.view(func(t *bbolt.Tx) error {
|
||||||
bucket := c.bucket(t, bucketRuleSet)
|
bucket := c.bucket(t, bucketRuleSet)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return os.ErrNotExist
|
return os.ErrNotExist
|
||||||
@@ -304,7 +347,7 @@ func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedBinary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error {
|
func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error {
|
||||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
return c.batch(func(t *bbolt.Tx) error {
|
||||||
bucket, err := c.createBucket(t, bucketRuleSet)
|
bucket, err := c.createBucket(t, bucketRuleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ var (
|
|||||||
|
|
||||||
func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata {
|
func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata {
|
||||||
var metadata adapter.FakeIPMetadata
|
var metadata adapter.FakeIPMetadata
|
||||||
err := c.DB.Batch(func(tx *bbolt.Tx) error {
|
err := c.batch(func(tx *bbolt.Tx) error {
|
||||||
bucket := tx.Bucket(bucketFakeIP)
|
bucket := tx.Bucket(bucketFakeIP)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return os.ErrNotExist
|
return os.ErrNotExist
|
||||||
@@ -45,7 +45,7 @@ func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
|
func (c *CacheFile) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
|
||||||
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
return c.batch(func(tx *bbolt.Tx) error {
|
||||||
bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
|
bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -69,7 +69,7 @@ func (c *CacheFile) FakeIPSaveMetadataAsync(metadata *adapter.FakeIPMetadata) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
|
func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
|
||||||
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
return c.batch(func(tx *bbolt.Tx) error {
|
||||||
bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
|
bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -136,7 +136,7 @@ func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
|||||||
return cachedDomain, true
|
return cachedDomain, true
|
||||||
}
|
}
|
||||||
var domain string
|
var domain string
|
||||||
_ = c.DB.View(func(tx *bbolt.Tx) error {
|
_ = c.view(func(tx *bbolt.Tx) error {
|
||||||
bucket := tx.Bucket(bucketFakeIP)
|
bucket := tx.Bucket(bucketFakeIP)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -163,7 +163,7 @@ func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bo
|
|||||||
return cachedAddress, true
|
return cachedAddress, true
|
||||||
}
|
}
|
||||||
var address netip.Addr
|
var address netip.Addr
|
||||||
_ = c.DB.View(func(tx *bbolt.Tx) error {
|
_ = c.view(func(tx *bbolt.Tx) error {
|
||||||
var bucket *bbolt.Bucket
|
var bucket *bbolt.Bucket
|
||||||
if isIPv6 {
|
if isIPv6 {
|
||||||
bucket = tx.Bucket(bucketFakeIPDomain6)
|
bucket = tx.Bucket(bucketFakeIPDomain6)
|
||||||
@@ -180,7 +180,7 @@ func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) FakeIPReset() error {
|
func (c *CacheFile) FakeIPReset() error {
|
||||||
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
return c.batch(func(tx *bbolt.Tx) error {
|
||||||
err := tx.DeleteBucket(bucketFakeIP)
|
err := tx.DeleteBucket(bucketFakeIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (c *CacheFile) LoadRDRC(transportName string, qName string, qType uint16) (
|
|||||||
copy(key[2:], qName)
|
copy(key[2:], qName)
|
||||||
defer buf.Put(key)
|
defer buf.Put(key)
|
||||||
var deleteCache bool
|
var deleteCache bool
|
||||||
err := c.DB.View(func(tx *bbolt.Tx) error {
|
err := c.view(func(tx *bbolt.Tx) error {
|
||||||
bucket := c.bucket(tx, bucketRDRC)
|
bucket := c.bucket(tx, bucketRDRC)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -56,7 +56,7 @@ func (c *CacheFile) LoadRDRC(transportName string, qName string, qType uint16) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if deleteCache {
|
if deleteCache {
|
||||||
c.DB.Update(func(tx *bbolt.Tx) error {
|
c.update(func(tx *bbolt.Tx) error {
|
||||||
bucket := c.bucket(tx, bucketRDRC)
|
bucket := c.bucket(tx, bucketRDRC)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -72,7 +72,7 @@ func (c *CacheFile) LoadRDRC(transportName string, qName string, qType uint16) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) SaveRDRC(transportName string, qName string, qType uint16) error {
|
func (c *CacheFile) SaveRDRC(transportName string, qName string, qType uint16) error {
|
||||||
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
return c.batch(func(tx *bbolt.Tx) error {
|
||||||
bucket, err := c.createBucket(tx, bucketRDRC)
|
bucket, err := c.createBucket(tx, bucketRDRC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -10,11 +10,31 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/observable"
|
||||||
"github.com/sagernet/sing/common/x/list"
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
|
||||||
"github.com/gofrs/uuid/v5"
|
"github.com/gofrs/uuid/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ConnectionEventType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnectionEventNew ConnectionEventType = iota
|
||||||
|
ConnectionEventUpdate
|
||||||
|
ConnectionEventClosed
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionEvent struct {
|
||||||
|
Type ConnectionEventType
|
||||||
|
ID uuid.UUID
|
||||||
|
Metadata *TrackerMetadata
|
||||||
|
UplinkDelta int64
|
||||||
|
DownlinkDelta int64
|
||||||
|
ClosedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
const closedConnectionsLimit = 1000
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
uploadTotal atomic.Int64
|
uploadTotal atomic.Int64
|
||||||
downloadTotal atomic.Int64
|
downloadTotal atomic.Int64
|
||||||
@@ -22,29 +42,52 @@ type Manager struct {
|
|||||||
connections compatible.Map[uuid.UUID, Tracker]
|
connections compatible.Map[uuid.UUID, Tracker]
|
||||||
closedConnectionsAccess sync.Mutex
|
closedConnectionsAccess sync.Mutex
|
||||||
closedConnections list.List[TrackerMetadata]
|
closedConnections list.List[TrackerMetadata]
|
||||||
// process *process.Process
|
memory uint64
|
||||||
memory uint64
|
|
||||||
|
eventSubscriber *observable.Subscriber[ConnectionEvent]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager() *Manager {
|
func NewManager() *Manager {
|
||||||
return &Manager{}
|
return &Manager{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) SetEventHook(subscriber *observable.Subscriber[ConnectionEvent]) {
|
||||||
|
m.eventSubscriber = subscriber
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) Join(c Tracker) {
|
func (m *Manager) Join(c Tracker) {
|
||||||
m.connections.Store(c.Metadata().ID, c)
|
metadata := c.Metadata()
|
||||||
|
m.connections.Store(metadata.ID, c)
|
||||||
|
if m.eventSubscriber != nil {
|
||||||
|
m.eventSubscriber.Emit(ConnectionEvent{
|
||||||
|
Type: ConnectionEventNew,
|
||||||
|
ID: metadata.ID,
|
||||||
|
Metadata: metadata,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Leave(c Tracker) {
|
func (m *Manager) Leave(c Tracker) {
|
||||||
metadata := c.Metadata()
|
metadata := c.Metadata()
|
||||||
_, loaded := m.connections.LoadAndDelete(metadata.ID)
|
_, loaded := m.connections.LoadAndDelete(metadata.ID)
|
||||||
if loaded {
|
if loaded {
|
||||||
metadata.ClosedAt = time.Now()
|
closedAt := time.Now()
|
||||||
|
metadata.ClosedAt = closedAt
|
||||||
|
metadataCopy := *metadata
|
||||||
m.closedConnectionsAccess.Lock()
|
m.closedConnectionsAccess.Lock()
|
||||||
defer m.closedConnectionsAccess.Unlock()
|
if m.closedConnections.Len() >= closedConnectionsLimit {
|
||||||
if m.closedConnections.Len() >= 1000 {
|
|
||||||
m.closedConnections.PopFront()
|
m.closedConnections.PopFront()
|
||||||
}
|
}
|
||||||
m.closedConnections.PushBack(metadata)
|
m.closedConnections.PushBack(metadataCopy)
|
||||||
|
m.closedConnectionsAccess.Unlock()
|
||||||
|
if m.eventSubscriber != nil {
|
||||||
|
m.eventSubscriber.Emit(ConnectionEvent{
|
||||||
|
Type: ConnectionEventClosed,
|
||||||
|
ID: metadata.ID,
|
||||||
|
Metadata: &metadataCopy,
|
||||||
|
ClosedAt: closedAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +107,8 @@ func (m *Manager) ConnectionsLen() int {
|
|||||||
return m.connections.Len()
|
return m.connections.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Connections() []TrackerMetadata {
|
func (m *Manager) Connections() []*TrackerMetadata {
|
||||||
var connections []TrackerMetadata
|
var connections []*TrackerMetadata
|
||||||
m.connections.Range(func(_ uuid.UUID, value Tracker) bool {
|
m.connections.Range(func(_ uuid.UUID, value Tracker) bool {
|
||||||
connections = append(connections, value.Metadata())
|
connections = append(connections, value.Metadata())
|
||||||
return true
|
return true
|
||||||
@@ -73,10 +116,18 @@ func (m *Manager) Connections() []TrackerMetadata {
|
|||||||
return connections
|
return connections
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) ClosedConnections() []TrackerMetadata {
|
func (m *Manager) ClosedConnections() []*TrackerMetadata {
|
||||||
m.closedConnectionsAccess.Lock()
|
m.closedConnectionsAccess.Lock()
|
||||||
defer m.closedConnectionsAccess.Unlock()
|
values := m.closedConnections.Array()
|
||||||
return m.closedConnections.Array()
|
m.closedConnectionsAccess.Unlock()
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
connections := make([]*TrackerMetadata, len(values))
|
||||||
|
for i := range values {
|
||||||
|
connections[i] = &values[i]
|
||||||
|
}
|
||||||
|
return connections
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) Connection(id uuid.UUID) Tracker {
|
func (m *Manager) Connection(id uuid.UUID) Tracker {
|
||||||
@@ -124,7 +175,7 @@ func (s *Snapshot) MarshalJSON() ([]byte, error) {
|
|||||||
return json.Marshal(map[string]any{
|
return json.Marshal(map[string]any{
|
||||||
"downloadTotal": s.Download,
|
"downloadTotal": s.Download,
|
||||||
"uploadTotal": s.Upload,
|
"uploadTotal": s.Upload,
|
||||||
"connections": common.Map(s.Connections, func(t Tracker) TrackerMetadata { return t.Metadata() }),
|
"connections": common.Map(s.Connections, func(t Tracker) *TrackerMetadata { return t.Metadata() }),
|
||||||
"memory": s.Memory,
|
"memory": s.Memory,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (t TrackerMetadata) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Tracker interface {
|
type Tracker interface {
|
||||||
Metadata() TrackerMetadata
|
Metadata() *TrackerMetadata
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,8 +97,8 @@ type TCPConn struct {
|
|||||||
manager *Manager
|
manager *Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *TCPConn) Metadata() TrackerMetadata {
|
func (tt *TCPConn) Metadata() *TrackerMetadata {
|
||||||
return tt.metadata
|
return &tt.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *TCPConn) Close() error {
|
func (tt *TCPConn) Close() error {
|
||||||
@@ -178,8 +178,8 @@ type UDPConn struct {
|
|||||||
manager *Manager
|
manager *Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ut *UDPConn) Metadata() TrackerMetadata {
|
func (ut *UDPConn) Metadata() *TrackerMetadata {
|
||||||
return ut.metadata
|
return &ut.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ut *UDPConn) Close() error {
|
func (ut *UDPConn) Close() error {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ type CommandClient struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
clientMutex sync.RWMutex
|
clientMutex sync.RWMutex
|
||||||
|
standalone bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandClientOptions struct {
|
type CommandClientOptions struct {
|
||||||
@@ -48,7 +49,7 @@ type CommandClientHandler interface {
|
|||||||
WriteGroups(message OutboundGroupIterator)
|
WriteGroups(message OutboundGroupIterator)
|
||||||
InitializeClashMode(modeList StringIterator, currentMode string)
|
InitializeClashMode(modeList StringIterator, currentMode string)
|
||||||
UpdateClashMode(newMode string)
|
UpdateClashMode(newMode string)
|
||||||
WriteConnections(message *Connections)
|
WriteConnectionEvents(events *ConnectionEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogEntry struct {
|
type LogEntry struct {
|
||||||
@@ -62,8 +63,18 @@ type LogIterator interface {
|
|||||||
Next() *LogEntry
|
Next() *LogEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type XPCDialer interface {
|
||||||
|
DialXPC() (int32, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sXPCDialer XPCDialer
|
||||||
|
|
||||||
|
func SetXPCDialer(dialer XPCDialer) {
|
||||||
|
sXPCDialer = dialer
|
||||||
|
}
|
||||||
|
|
||||||
func NewStandaloneCommandClient() *CommandClient {
|
func NewStandaloneCommandClient() *CommandClient {
|
||||||
return new(CommandClient)
|
return &CommandClient{standalone: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandClient(handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
|
func NewCommandClient(handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
|
||||||
@@ -87,47 +98,135 @@ func streamClientAuthInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc
|
|||||||
return streamer(ctx, desc, cc, method, opts...)
|
return streamer(ctx, desc, cc, method, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) grpcDial() (*grpc.ClientConn, error) {
|
const (
|
||||||
var target string
|
commandClientDialAttempts = 10
|
||||||
if sCommandServerListenPort == 0 {
|
commandClientDialBaseDelay = 100 * time.Millisecond
|
||||||
target = "unix://" + filepath.Join(sBasePath, "command.sock")
|
commandClientDialStepDelay = 50 * time.Millisecond
|
||||||
} else {
|
)
|
||||||
target = net.JoinHostPort("127.0.0.1", strconv.Itoa(int(sCommandServerListenPort)))
|
|
||||||
}
|
func commandClientDialDelay(attempt int) time.Duration {
|
||||||
var (
|
return commandClientDialBaseDelay + time.Duration(attempt)*commandClientDialStepDelay
|
||||||
conn *grpc.ClientConn
|
}
|
||||||
err error
|
|
||||||
)
|
func dialTarget() (string, func(context.Context, string) (net.Conn, error)) {
|
||||||
clientOptions := []grpc.DialOption{
|
if sXPCDialer != nil {
|
||||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
return "passthrough:///xpc", func(ctx context.Context, _ string) (net.Conn, error) {
|
||||||
grpc.WithUnaryInterceptor(unaryClientAuthInterceptor),
|
fileDescriptor, err := sXPCDialer.DialXPC()
|
||||||
grpc.WithStreamInterceptor(streamClientAuthInterceptor),
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
for i := 0; i < 10; i++ {
|
}
|
||||||
conn, err = grpc.NewClient(target, clientOptions...)
|
return networkConnectionFromFileDescriptor(fileDescriptor)
|
||||||
if err == nil {
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(100+i*50) * time.Millisecond)
|
|
||||||
}
|
}
|
||||||
return nil, err
|
if sCommandServerListenPort == 0 {
|
||||||
|
return "unix://" + filepath.Join(sBasePath, "command.sock"), nil
|
||||||
|
}
|
||||||
|
return net.JoinHostPort("127.0.0.1", strconv.Itoa(int(sCommandServerListenPort))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkConnectionFromFileDescriptor(fileDescriptor int32) (net.Conn, error) {
|
||||||
|
file := os.NewFile(uintptr(fileDescriptor), "xpc-command-socket")
|
||||||
|
if file == nil {
|
||||||
|
return nil, E.New("invalid file descriptor")
|
||||||
|
}
|
||||||
|
networkConnection, err := net.FileConn(file)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
return nil, E.Cause(err, "create connection from fd")
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
return networkConnection, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) dialWithRetry(target string, contextDialer func(context.Context, string) (net.Conn, error), retryDial bool) (*grpc.ClientConn, daemon.StartedServiceClient, error) {
|
||||||
|
var connection *grpc.ClientConn
|
||||||
|
var client daemon.StartedServiceClient
|
||||||
|
var lastError error
|
||||||
|
|
||||||
|
for attempt := 0; attempt < commandClientDialAttempts; attempt++ {
|
||||||
|
if connection == nil {
|
||||||
|
options := []grpc.DialOption{
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
grpc.WithUnaryInterceptor(unaryClientAuthInterceptor),
|
||||||
|
grpc.WithStreamInterceptor(streamClientAuthInterceptor),
|
||||||
|
}
|
||||||
|
if contextDialer != nil {
|
||||||
|
options = append(options, grpc.WithContextDialer(contextDialer))
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
connection, err = grpc.NewClient(target, options...)
|
||||||
|
if err != nil {
|
||||||
|
lastError = err
|
||||||
|
if !retryDial {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
time.Sleep(commandClientDialDelay(attempt))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
client = daemon.NewStartedServiceClient(connection)
|
||||||
|
}
|
||||||
|
waitDuration := commandClientDialDelay(attempt)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), waitDuration)
|
||||||
|
_, err := client.GetStartedAt(ctx, &emptypb.Empty{}, grpc.WaitForReady(true))
|
||||||
|
cancel()
|
||||||
|
if err == nil {
|
||||||
|
return connection, client, nil
|
||||||
|
}
|
||||||
|
lastError = err
|
||||||
|
}
|
||||||
|
|
||||||
|
if connection != nil {
|
||||||
|
connection.Close()
|
||||||
|
}
|
||||||
|
return nil, nil, lastError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) Connect() error {
|
func (c *CommandClient) Connect() error {
|
||||||
c.clientMutex.Lock()
|
c.clientMutex.Lock()
|
||||||
common.Close(common.PtrOrNil(c.grpcConn))
|
common.Close(common.PtrOrNil(c.grpcConn))
|
||||||
|
|
||||||
conn, err := c.grpcDial()
|
target, contextDialer := dialTarget()
|
||||||
|
connection, client, err := c.dialWithRetry(target, contextDialer, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.clientMutex.Unlock()
|
c.clientMutex.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.grpcConn = conn
|
c.grpcConn = connection
|
||||||
c.grpcClient = daemon.NewStartedServiceClient(conn)
|
c.grpcClient = client
|
||||||
c.ctx, c.cancel = context.WithCancel(context.Background())
|
c.ctx, c.cancel = context.WithCancel(context.Background())
|
||||||
c.clientMutex.Unlock()
|
c.clientMutex.Unlock()
|
||||||
|
|
||||||
c.handler.Connected()
|
c.handler.Connected()
|
||||||
|
return c.dispatchCommands()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) ConnectWithFD(fd int32) error {
|
||||||
|
c.clientMutex.Lock()
|
||||||
|
common.Close(common.PtrOrNil(c.grpcConn))
|
||||||
|
|
||||||
|
networkConnection, err := networkConnectionFromFileDescriptor(fd)
|
||||||
|
if err != nil {
|
||||||
|
c.clientMutex.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
connection, client, err := c.dialWithRetry("passthrough:///xpc", func(ctx context.Context, _ string) (net.Conn, error) {
|
||||||
|
return networkConnection, nil
|
||||||
|
}, false)
|
||||||
|
if err != nil {
|
||||||
|
networkConnection.Close()
|
||||||
|
c.clientMutex.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.grpcConn = connection
|
||||||
|
c.grpcClient = client
|
||||||
|
c.ctx, c.cancel = context.WithCancel(context.Background())
|
||||||
|
c.clientMutex.Unlock()
|
||||||
|
|
||||||
|
c.handler.Connected()
|
||||||
|
return c.dispatchCommands()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) dispatchCommands() error {
|
||||||
for _, command := range c.options.commands {
|
for _, command := range c.options.commands {
|
||||||
switch command {
|
switch command {
|
||||||
case CommandLog:
|
case CommandLog:
|
||||||
@@ -171,18 +270,41 @@ func (c *CommandClient) getClientForCall() (daemon.StartedServiceClient, error)
|
|||||||
return c.grpcClient, nil
|
return c.grpcClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := c.grpcDial()
|
target, contextDialer := dialTarget()
|
||||||
|
connection, client, err := c.dialWithRetry(target, contextDialer, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.grpcConn = conn
|
c.grpcConn = connection
|
||||||
c.grpcClient = daemon.NewStartedServiceClient(conn)
|
c.grpcClient = client
|
||||||
if c.ctx == nil {
|
if c.ctx == nil {
|
||||||
c.ctx, c.cancel = context.WithCancel(context.Background())
|
c.ctx, c.cancel = context.WithCancel(context.Background())
|
||||||
}
|
}
|
||||||
return c.grpcClient, nil
|
return c.grpcClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) closeConnection() {
|
||||||
|
c.clientMutex.Lock()
|
||||||
|
defer c.clientMutex.Unlock()
|
||||||
|
if c.grpcConn != nil {
|
||||||
|
c.grpcConn.Close()
|
||||||
|
c.grpcConn = nil
|
||||||
|
c.grpcClient = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func callWithResult[T any](c *CommandClient, call func(client daemon.StartedServiceClient) (T, error)) (T, error) {
|
||||||
|
client, err := c.getClientForCall()
|
||||||
|
if err != nil {
|
||||||
|
var zero T
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
if c.standalone {
|
||||||
|
defer c.closeConnection()
|
||||||
|
}
|
||||||
|
return call(client)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CommandClient) getStreamContext() (daemon.StartedServiceClient, context.Context) {
|
func (c *CommandClient) getStreamContext() (daemon.StartedServiceClient, context.Context) {
|
||||||
c.clientMutex.RLock()
|
c.clientMutex.RLock()
|
||||||
defer c.clientMutex.RUnlock()
|
defer c.clientMutex.RUnlock()
|
||||||
@@ -240,7 +362,7 @@ func (c *CommandClient) handleStatusStream() {
|
|||||||
c.handler.Disconnected(err.Error())
|
c.handler.Disconnected(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.handler.WriteStatus(StatusMessageFromGRPC(status))
|
c.handler.WriteStatus(statusMessageFromGRPC(status))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +381,7 @@ func (c *CommandClient) handleGroupStream() {
|
|||||||
c.handler.Disconnected(err.Error())
|
c.handler.Disconnected(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.handler.WriteGroups(OutboundGroupIteratorFromGRPC(groups))
|
c.handler.WriteGroups(outboundGroupIteratorFromGRPC(groups))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,175 +441,134 @@ func (c *CommandClient) handleConnectionsStream() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var connections Connections
|
|
||||||
for {
|
for {
|
||||||
conns, err := stream.Recv()
|
events, err := stream.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.handler.Disconnected(err.Error())
|
c.handler.Disconnected(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
connections.input = ConnectionsFromGRPC(conns)
|
libboxEvents := connectionEventsFromGRPC(events)
|
||||||
c.handler.WriteConnections(&connections)
|
c.handler.WriteConnectionEvents(libboxEvents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error {
|
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.SelectOutbound(context.Background(), &daemon.SelectOutboundRequest{
|
||||||
return err
|
GroupTag: groupTag,
|
||||||
}
|
OutboundTag: outboundTag,
|
||||||
|
})
|
||||||
_, err = client.SelectOutbound(context.Background(), &daemon.SelectOutboundRequest{
|
|
||||||
GroupTag: groupTag,
|
|
||||||
OutboundTag: outboundTag,
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) URLTest(groupTag string) error {
|
func (c *CommandClient) URLTest(groupTag string) error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.URLTest(context.Background(), &daemon.URLTestRequest{
|
||||||
return err
|
OutboundTag: groupTag,
|
||||||
}
|
})
|
||||||
|
|
||||||
_, err = client.URLTest(context.Background(), &daemon.URLTestRequest{
|
|
||||||
OutboundTag: groupTag,
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) SetClashMode(newMode string) error {
|
func (c *CommandClient) SetClashMode(newMode string) error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.SetClashMode(context.Background(), &daemon.ClashMode{
|
||||||
return err
|
Mode: newMode,
|
||||||
}
|
})
|
||||||
|
|
||||||
_, err = client.SetClashMode(context.Background(), &daemon.ClashMode{
|
|
||||||
Mode: newMode,
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) CloseConnection(connId string) error {
|
func (c *CommandClient) CloseConnection(connId string) error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.CloseConnection(context.Background(), &daemon.CloseConnectionRequest{
|
||||||
return err
|
Id: connId,
|
||||||
}
|
})
|
||||||
|
|
||||||
_, err = client.CloseConnection(context.Background(), &daemon.CloseConnectionRequest{
|
|
||||||
Id: connId,
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) CloseConnections() error {
|
func (c *CommandClient) CloseConnections() error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.CloseAllConnections(context.Background(), &emptypb.Empty{})
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.CloseAllConnections(context.Background(), &emptypb.Empty{})
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) ServiceReload() error {
|
func (c *CommandClient) ServiceReload() error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.ReloadService(context.Background(), &emptypb.Empty{})
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.ReloadService(context.Background(), &emptypb.Empty{})
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) ServiceClose() error {
|
func (c *CommandClient) ServiceClose() error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.StopService(context.Background(), &emptypb.Empty{})
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.StopService(context.Background(), &emptypb.Empty{})
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) ClearLogs() error {
|
func (c *CommandClient) ClearLogs() error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.ClearLogs(context.Background(), &emptypb.Empty{})
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.ClearLogs(context.Background(), &emptypb.Empty{})
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
|
func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
|
||||||
client, err := c.getClientForCall()
|
return callWithResult(c, func(client daemon.StartedServiceClient) (*SystemProxyStatus, error) {
|
||||||
if err != nil {
|
status, err := client.GetSystemProxyStatus(context.Background(), &emptypb.Empty{})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
status, err := client.GetSystemProxyStatus(context.Background(), &emptypb.Empty{})
|
return systemProxyStatusFromGRPC(status), nil
|
||||||
if err != nil {
|
})
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return SystemProxyStatusFromGRPC(status), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
|
func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.SetSystemProxyEnabled(context.Background(), &daemon.SetSystemProxyEnabledRequest{
|
||||||
return err
|
Enabled: isEnabled,
|
||||||
}
|
})
|
||||||
|
|
||||||
_, err = client.SetSystemProxyEnabled(context.Background(), &daemon.SetSystemProxyEnabledRequest{
|
|
||||||
Enabled: isEnabled,
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) GetDeprecatedNotes() (DeprecatedNoteIterator, error) {
|
func (c *CommandClient) GetDeprecatedNotes() (DeprecatedNoteIterator, error) {
|
||||||
client, err := c.getClientForCall()
|
return callWithResult(c, func(client daemon.StartedServiceClient) (DeprecatedNoteIterator, error) {
|
||||||
if err != nil {
|
warnings, err := client.GetDeprecatedWarnings(context.Background(), &emptypb.Empty{})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
warnings, err := client.GetDeprecatedWarnings(context.Background(), &emptypb.Empty{})
|
var notes []*DeprecatedNote
|
||||||
if err != nil {
|
for _, warning := range warnings.Warnings {
|
||||||
return nil, err
|
notes = append(notes, &DeprecatedNote{
|
||||||
}
|
Description: warning.Message,
|
||||||
|
MigrationLink: warning.MigrationLink,
|
||||||
var notes []*DeprecatedNote
|
})
|
||||||
for _, warning := range warnings.Warnings {
|
}
|
||||||
notes = append(notes, &DeprecatedNote{
|
return newIterator(notes), nil
|
||||||
Description: warning.Message,
|
})
|
||||||
MigrationLink: warning.MigrationLink,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return newIterator(notes), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) GetStartedAt() (int64, error) {
|
func (c *CommandClient) GetStartedAt() (int64, error) {
|
||||||
client, err := c.getClientForCall()
|
return callWithResult(c, func(client daemon.StartedServiceClient) (int64, error) {
|
||||||
if err != nil {
|
startedAt, err := client.GetStartedAt(context.Background(), &emptypb.Empty{})
|
||||||
return 0, err
|
if err != nil {
|
||||||
}
|
return 0, err
|
||||||
|
}
|
||||||
startedAt, err := client.GetStartedAt(context.Background(), &emptypb.Empty{})
|
return startedAt.StartedAt, nil
|
||||||
if err != nil {
|
})
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return startedAt.StartedAt, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||||
client, err := c.getClientForCall()
|
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
|
||||||
if err != nil {
|
return client.SetGroupExpand(context.Background(), &daemon.SetGroupExpandRequest{
|
||||||
return err
|
GroupTag: groupTag,
|
||||||
}
|
IsExpand: isExpand,
|
||||||
|
})
|
||||||
_, err = client.SetGroupExpand(context.Background(), &daemon.SetGroupExpandRequest{
|
|
||||||
GroupTag: groupTag,
|
|
||||||
IsExpand: isExpand,
|
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ type CommandServerHandler interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandServer(handler CommandServerHandler, platformInterface PlatformInterface) (*CommandServer, error) {
|
func NewCommandServer(handler CommandServerHandler, platformInterface PlatformInterface) (*CommandServer, error) {
|
||||||
ctx := BaseContext(platformInterface)
|
ctx := baseContext(platformInterface)
|
||||||
platformWrapper := &platformInterfaceWrapper{
|
platformWrapper := &platformInterfaceWrapper{
|
||||||
iif: platformInterface,
|
iif: platformInterface,
|
||||||
useProcFS: platformInterface.UseProcFS(),
|
useProcFS: platformInterface.UseProcFS(),
|
||||||
@@ -195,6 +195,14 @@ func (s *CommandServer) NeedWIFIState() bool {
|
|||||||
return instance.Box().Network().NeedWIFIState()
|
return instance.Box().Network().NeedWIFIState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) NeedFindProcess() bool {
|
||||||
|
instance := s.StartedService.Instance()
|
||||||
|
if instance == nil || instance.Box() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return instance.Box().Router().NeedFindProcess()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *CommandServer) Pause() {
|
func (s *CommandServer) Pause() {
|
||||||
instance := s.StartedService.Instance()
|
instance := s.StartedService.Instance()
|
||||||
if instance == nil || instance.PauseManager() == nil {
|
if instance == nil || instance.PauseManager() == nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package libbox
|
|||||||
import (
|
import (
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/daemon"
|
"github.com/sagernet/sing-box/daemon"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -31,11 +32,11 @@ type OutboundGroup struct {
|
|||||||
Selectable bool
|
Selectable bool
|
||||||
Selected string
|
Selected string
|
||||||
IsExpand bool
|
IsExpand bool
|
||||||
ItemList []*OutboundGroupItem
|
itemList []*OutboundGroupItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
|
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
|
||||||
return newIterator(g.ItemList)
|
return newIterator(g.itemList)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OutboundGroupIterator interface {
|
type OutboundGroupIterator interface {
|
||||||
@@ -61,12 +62,119 @@ const (
|
|||||||
ConnectionStateClosed
|
ConnectionStateClosed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnectionEventNew = iota
|
||||||
|
ConnectionEventUpdate
|
||||||
|
ConnectionEventClosed
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
closedConnectionMaxAge = int64((5 * time.Minute) / time.Millisecond)
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionEvent struct {
|
||||||
|
Type int32
|
||||||
|
ID string
|
||||||
|
Connection *Connection
|
||||||
|
UplinkDelta int64
|
||||||
|
DownlinkDelta int64
|
||||||
|
ClosedAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionEvents struct {
|
||||||
|
Reset bool
|
||||||
|
events []*ConnectionEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConnectionEvents) Iterator() ConnectionEventIterator {
|
||||||
|
return newIterator(c.events)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionEventIterator interface {
|
||||||
|
Next() *ConnectionEvent
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
type Connections struct {
|
type Connections struct {
|
||||||
input []Connection
|
connectionMap map[string]*Connection
|
||||||
filtered []Connection
|
input []Connection
|
||||||
|
filtered []Connection
|
||||||
|
filterState int32
|
||||||
|
filterApplied bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnections() *Connections {
|
||||||
|
return &Connections{
|
||||||
|
connectionMap: make(map[string]*Connection),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) ApplyEvents(events *ConnectionEvents) {
|
||||||
|
if events == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if events.Reset {
|
||||||
|
c.connectionMap = make(map[string]*Connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, event := range events.events {
|
||||||
|
switch event.Type {
|
||||||
|
case ConnectionEventNew:
|
||||||
|
if event.Connection != nil {
|
||||||
|
conn := *event.Connection
|
||||||
|
c.connectionMap[event.ID] = &conn
|
||||||
|
}
|
||||||
|
case ConnectionEventUpdate:
|
||||||
|
if conn, ok := c.connectionMap[event.ID]; ok {
|
||||||
|
conn.Uplink = event.UplinkDelta
|
||||||
|
conn.Downlink = event.DownlinkDelta
|
||||||
|
conn.UplinkTotal += event.UplinkDelta
|
||||||
|
conn.DownlinkTotal += event.DownlinkDelta
|
||||||
|
}
|
||||||
|
case ConnectionEventClosed:
|
||||||
|
if event.Connection != nil {
|
||||||
|
conn := *event.Connection
|
||||||
|
conn.ClosedAt = event.ClosedAt
|
||||||
|
conn.Uplink = 0
|
||||||
|
conn.Downlink = 0
|
||||||
|
c.connectionMap[event.ID] = &conn
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if conn, ok := c.connectionMap[event.ID]; ok {
|
||||||
|
conn.ClosedAt = event.ClosedAt
|
||||||
|
conn.Uplink = 0
|
||||||
|
conn.Downlink = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.evictClosedConnections(time.Now().UnixMilli())
|
||||||
|
c.input = c.input[:0]
|
||||||
|
for _, conn := range c.connectionMap {
|
||||||
|
c.input = append(c.input, *conn)
|
||||||
|
}
|
||||||
|
if c.filterApplied {
|
||||||
|
c.FilterState(c.filterState)
|
||||||
|
} else {
|
||||||
|
c.filtered = c.filtered[:0]
|
||||||
|
c.filtered = append(c.filtered, c.input...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) evictClosedConnections(nowMilliseconds int64) {
|
||||||
|
for id, conn := range c.connectionMap {
|
||||||
|
if conn.ClosedAt == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if nowMilliseconds-conn.ClosedAt > closedConnectionMaxAge {
|
||||||
|
delete(c.connectionMap, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connections) FilterState(state int32) {
|
func (c *Connections) FilterState(state int32) {
|
||||||
|
c.filterApplied = true
|
||||||
|
c.filterState = state
|
||||||
c.filtered = c.filtered[:0]
|
c.filtered = c.filtered[:0]
|
||||||
switch state {
|
switch state {
|
||||||
case ConnectionStateAll:
|
case ConnectionStateAll:
|
||||||
@@ -159,12 +267,12 @@ type Connection struct {
|
|||||||
Rule string
|
Rule string
|
||||||
Outbound string
|
Outbound string
|
||||||
OutboundType string
|
OutboundType string
|
||||||
ChainList []string
|
chainList []string
|
||||||
ProcessInfo *ProcessInfo
|
ProcessInfo *ProcessInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connection) Chain() StringIterator {
|
func (c *Connection) Chain() StringIterator {
|
||||||
return newIterator(c.ChainList)
|
return newIterator(c.chainList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connection) DisplayDestination() string {
|
func (c *Connection) DisplayDestination() string {
|
||||||
@@ -184,7 +292,7 @@ type ConnectionIterator interface {
|
|||||||
HasNext() bool
|
HasNext() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func StatusMessageFromGRPC(status *daemon.Status) *StatusMessage {
|
func statusMessageFromGRPC(status *daemon.Status) *StatusMessage {
|
||||||
if status == nil {
|
if status == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -201,7 +309,7 @@ func StatusMessageFromGRPC(status *daemon.Status) *StatusMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OutboundGroupIteratorFromGRPC(groups *daemon.Groups) OutboundGroupIterator {
|
func outboundGroupIteratorFromGRPC(groups *daemon.Groups) OutboundGroupIterator {
|
||||||
if groups == nil || len(groups.Group) == 0 {
|
if groups == nil || len(groups.Group) == 0 {
|
||||||
return newIterator([]*OutboundGroup{})
|
return newIterator([]*OutboundGroup{})
|
||||||
}
|
}
|
||||||
@@ -215,7 +323,7 @@ func OutboundGroupIteratorFromGRPC(groups *daemon.Groups) OutboundGroupIterator
|
|||||||
IsExpand: g.IsExpand,
|
IsExpand: g.IsExpand,
|
||||||
}
|
}
|
||||||
for _, item := range g.Items {
|
for _, item := range g.Items {
|
||||||
libboxGroup.ItemList = append(libboxGroup.ItemList, &OutboundGroupItem{
|
libboxGroup.itemList = append(libboxGroup.itemList, &OutboundGroupItem{
|
||||||
Tag: item.Tag,
|
Tag: item.Tag,
|
||||||
Type: item.Type,
|
Type: item.Type,
|
||||||
URLTestTime: item.UrlTestTime,
|
URLTestTime: item.UrlTestTime,
|
||||||
@@ -227,7 +335,7 @@ func OutboundGroupIteratorFromGRPC(groups *daemon.Groups) OutboundGroupIterator
|
|||||||
return newIterator(libboxGroups)
|
return newIterator(libboxGroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConnectionFromGRPC(conn *daemon.Connection) Connection {
|
func connectionFromGRPC(conn *daemon.Connection) Connection {
|
||||||
var processInfo *ProcessInfo
|
var processInfo *ProcessInfo
|
||||||
if conn.ProcessInfo != nil {
|
if conn.ProcessInfo != nil {
|
||||||
processInfo = &ProcessInfo{
|
processInfo = &ProcessInfo{
|
||||||
@@ -259,23 +367,45 @@ func ConnectionFromGRPC(conn *daemon.Connection) Connection {
|
|||||||
Rule: conn.Rule,
|
Rule: conn.Rule,
|
||||||
Outbound: conn.Outbound,
|
Outbound: conn.Outbound,
|
||||||
OutboundType: conn.OutboundType,
|
OutboundType: conn.OutboundType,
|
||||||
ChainList: conn.ChainList,
|
chainList: conn.ChainList,
|
||||||
ProcessInfo: processInfo,
|
ProcessInfo: processInfo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConnectionsFromGRPC(connections *daemon.Connections) []Connection {
|
func connectionEventFromGRPC(event *daemon.ConnectionEvent) *ConnectionEvent {
|
||||||
if connections == nil || len(connections.Connections) == 0 {
|
if event == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var libboxConnections []Connection
|
libboxEvent := &ConnectionEvent{
|
||||||
for _, conn := range connections.Connections {
|
Type: int32(event.Type),
|
||||||
libboxConnections = append(libboxConnections, ConnectionFromGRPC(conn))
|
ID: event.Id,
|
||||||
|
UplinkDelta: event.UplinkDelta,
|
||||||
|
DownlinkDelta: event.DownlinkDelta,
|
||||||
|
ClosedAt: event.ClosedAt,
|
||||||
}
|
}
|
||||||
return libboxConnections
|
if event.Connection != nil {
|
||||||
|
conn := connectionFromGRPC(event.Connection)
|
||||||
|
libboxEvent.Connection = &conn
|
||||||
|
}
|
||||||
|
return libboxEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
func SystemProxyStatusFromGRPC(status *daemon.SystemProxyStatus) *SystemProxyStatus {
|
func connectionEventsFromGRPC(events *daemon.ConnectionEvents) *ConnectionEvents {
|
||||||
|
if events == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
libboxEvents := &ConnectionEvents{
|
||||||
|
Reset: events.Reset_,
|
||||||
|
}
|
||||||
|
for _, event := range events.Events {
|
||||||
|
if libboxEvent := connectionEventFromGRPC(event); libboxEvent != nil {
|
||||||
|
libboxEvents.events = append(libboxEvents.events, libboxEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return libboxEvents
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemProxyStatusFromGRPC(status *daemon.SystemProxyStatus) *SystemProxyStatus {
|
||||||
if status == nil {
|
if status == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -285,7 +415,7 @@ func SystemProxyStatusFromGRPC(status *daemon.SystemProxyStatus) *SystemProxySta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SystemProxyStatusToGRPC(status *SystemProxyStatus) *daemon.SystemProxyStatus {
|
func systemProxyStatusToGRPC(status *SystemProxyStatus) *daemon.SystemProxyStatus {
|
||||||
if status == nil {
|
if status == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/sagernet/sing/service/filemanager"
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BaseContext(platformInterface PlatformInterface) context.Context {
|
func baseContext(platformInterface PlatformInterface) context.Context {
|
||||||
dnsRegistry := include.DNSTransportRegistry()
|
dnsRegistry := include.DNSTransportRegistry()
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
if localTransport := platformInterface.LocalDNSTransport(); localTransport != nil {
|
if localTransport := platformInterface.LocalDNSTransport(); localTransport != nil {
|
||||||
@@ -45,7 +45,7 @@ func parseConfig(ctx context.Context, configContent string) (option.Options, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckConfig(configContent string) error {
|
func CheckConfig(configContent string) error {
|
||||||
ctx := BaseContext(nil)
|
ctx := baseContext(nil)
|
||||||
options, err := parseConfig(ctx, configContent)
|
options, err := parseConfig(ctx, configContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -189,7 +189,7 @@ func (s *interfaceMonitorStub) MyInterface() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FormatConfig(configContent string) (*StringBox, error) {
|
func FormatConfig(configContent string) (*StringBox, error) {
|
||||||
options, err := parseConfig(BaseContext(nil), configContent)
|
options, err := parseConfig(baseContext(nil), configContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
167
experimental/libbox/ffi.json
Normal file
167
experimental/libbox/ffi.json
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"id": "libbox",
|
||||||
|
"path": ".",
|
||||||
|
"java_package": "io.nekohasekai.libbox",
|
||||||
|
"apple_prefix": "Libbox"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"id": "android-main",
|
||||||
|
"packages": ["libbox"],
|
||||||
|
"default": {
|
||||||
|
"tags": [
|
||||||
|
"with_gvisor",
|
||||||
|
"with_quic",
|
||||||
|
"with_wireguard",
|
||||||
|
"with_utls",
|
||||||
|
"with_naive_outbound",
|
||||||
|
"with_clash_api",
|
||||||
|
"with_conntrack",
|
||||||
|
"badlinkname",
|
||||||
|
"tfogo_checklinkname0",
|
||||||
|
"with_tailscale",
|
||||||
|
"ts_omit_logtail",
|
||||||
|
"ts_omit_ssh",
|
||||||
|
"ts_omit_drive",
|
||||||
|
"ts_omit_taildrop",
|
||||||
|
"ts_omit_webclient",
|
||||||
|
"ts_omit_doctor",
|
||||||
|
"ts_omit_capture",
|
||||||
|
"ts_omit_kube",
|
||||||
|
"ts_omit_aws",
|
||||||
|
"ts_omit_synology",
|
||||||
|
"ts_omit_bird"
|
||||||
|
],
|
||||||
|
"ldflags": "-X github.com/sagernet/sing-box/constant.Version=$(CGO_ENABLED=0 go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest) -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0",
|
||||||
|
"trimpath": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "android-legacy",
|
||||||
|
"packages": ["libbox"],
|
||||||
|
"default": {
|
||||||
|
"tags": [
|
||||||
|
"with_gvisor",
|
||||||
|
"with_quic",
|
||||||
|
"with_wireguard",
|
||||||
|
"with_utls",
|
||||||
|
"with_clash_api",
|
||||||
|
"with_conntrack",
|
||||||
|
"badlinkname",
|
||||||
|
"tfogo_checklinkname0",
|
||||||
|
"with_tailscale",
|
||||||
|
"ts_omit_logtail",
|
||||||
|
"ts_omit_ssh",
|
||||||
|
"ts_omit_drive",
|
||||||
|
"ts_omit_taildrop",
|
||||||
|
"ts_omit_webclient",
|
||||||
|
"ts_omit_doctor",
|
||||||
|
"ts_omit_capture",
|
||||||
|
"ts_omit_kube",
|
||||||
|
"ts_omit_aws",
|
||||||
|
"ts_omit_synology",
|
||||||
|
"ts_omit_bird"
|
||||||
|
],
|
||||||
|
"ldflags": "-X github.com/sagernet/sing-box/constant.Version=$(CGO_ENABLED=0 go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest) -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0",
|
||||||
|
"trimpath": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "apple",
|
||||||
|
"packages": ["libbox"],
|
||||||
|
"default": {
|
||||||
|
"tags": [
|
||||||
|
"with_gvisor",
|
||||||
|
"with_quic",
|
||||||
|
"with_wireguard",
|
||||||
|
"with_utls",
|
||||||
|
"with_naive_outbound",
|
||||||
|
"with_clash_api",
|
||||||
|
"with_conntrack",
|
||||||
|
"badlinkname",
|
||||||
|
"tfogo_checklinkname0",
|
||||||
|
"with_dhcp",
|
||||||
|
"grpcnotrace",
|
||||||
|
"with_tailscale",
|
||||||
|
"ts_omit_logtail",
|
||||||
|
"ts_omit_ssh",
|
||||||
|
"ts_omit_drive",
|
||||||
|
"ts_omit_taildrop",
|
||||||
|
"ts_omit_webclient",
|
||||||
|
"ts_omit_doctor",
|
||||||
|
"ts_omit_capture",
|
||||||
|
"ts_omit_kube",
|
||||||
|
"ts_omit_aws",
|
||||||
|
"ts_omit_synology",
|
||||||
|
"ts_omit_bird"
|
||||||
|
],
|
||||||
|
"ldflags": "-X github.com/sagernet/sing-box/constant.Version=$(CGO_ENABLED=0 go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest) -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0",
|
||||||
|
"trimpath": true
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"match": { "os": "ios" },
|
||||||
|
"tags_append": ["with_low_memory"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": { "os": "tvos" },
|
||||||
|
"tags_append": ["with_low_memory"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"platforms": [
|
||||||
|
{
|
||||||
|
"type": "android",
|
||||||
|
"build": "android-main",
|
||||||
|
"min_sdk": 23,
|
||||||
|
"lib_name": "box",
|
||||||
|
"languages": [{ "type": "java" }],
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"type": "aar",
|
||||||
|
"output_path": "libbox.aar"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "android",
|
||||||
|
"build": "android-legacy",
|
||||||
|
"min_sdk": 21,
|
||||||
|
"lib_name": "box",
|
||||||
|
"languages": [{ "type": "java" }],
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"type": "aar",
|
||||||
|
"output_path": "libbox-legacy.aar"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "apple",
|
||||||
|
"build": "apple",
|
||||||
|
"targets": [
|
||||||
|
"ios/arm64",
|
||||||
|
"ios/simulator/arm64",
|
||||||
|
"ios/simulator/amd64",
|
||||||
|
"tvos/arm64",
|
||||||
|
"tvos/simulator/arm64",
|
||||||
|
"tvos/simulator/amd64",
|
||||||
|
"macos/arm64",
|
||||||
|
"macos/amd64"
|
||||||
|
],
|
||||||
|
"languages": [{ "type": "objc" }],
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"type": "xcframework",
|
||||||
|
"module_name": "Libbox",
|
||||||
|
"output_path": "Libbox.xcframework"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -11,9 +11,7 @@ type PlatformInterface interface {
|
|||||||
AutoDetectInterfaceControl(fd int32) error
|
AutoDetectInterfaceControl(fd int32) error
|
||||||
OpenTun(options TunOptions) (int32, error)
|
OpenTun(options TunOptions) (int32, error)
|
||||||
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) (*ConnectionOwner, error)
|
||||||
PackageNameByUid(uid int32) (string, error)
|
|
||||||
UIDByPackageName(packageName string) (int32, error)
|
|
||||||
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||||
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||||
GetInterfaces() (NetworkInterfaceIterator, error)
|
GetInterfaces() (NetworkInterfaceIterator, error)
|
||||||
@@ -25,6 +23,13 @@ type PlatformInterface interface {
|
|||||||
SendNotification(notification *Notification) error
|
SendNotification(notification *Notification) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConnectionOwner struct {
|
||||||
|
UserId int32
|
||||||
|
UserName string
|
||||||
|
ProcessPath string
|
||||||
|
AndroidPackageName string
|
||||||
|
}
|
||||||
|
|
||||||
type InterfaceUpdateListener interface {
|
type InterfaceUpdateListener interface {
|
||||||
UpdateDefaultInterface(interfaceName string, interfaceIndex int32, isExpensive bool, isConstrained bool)
|
UpdateDefaultInterface(interfaceName string, interfaceIndex int32, isExpensive bool, isConstrained bool)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/varbin"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
@@ -35,7 +36,7 @@ type ErrorMessage struct {
|
|||||||
func (e *ErrorMessage) Encode() []byte {
|
func (e *ErrorMessage) Encode() []byte {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
buffer.WriteByte(MessageTypeError)
|
buffer.WriteByte(MessageTypeError)
|
||||||
varbin.Write(&buffer, binary.BigEndian, e.Message)
|
writeString(&buffer, e.Message)
|
||||||
return buffer.Bytes()
|
return buffer.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
|
|||||||
return nil, E.New("invalid message")
|
return nil, E.New("invalid message")
|
||||||
}
|
}
|
||||||
var message ErrorMessage
|
var message ErrorMessage
|
||||||
message.Message, err = varbin.ReadValue[string](reader, binary.BigEndian)
|
message.Message, err = readString(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,7 @@ func (e *ProfileEncoder) Encode() []byte {
|
|||||||
binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))
|
binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))
|
||||||
for _, preview := range e.profiles {
|
for _, preview := range e.profiles {
|
||||||
binary.Write(&buffer, binary.BigEndian, preview.ProfileID)
|
binary.Write(&buffer, binary.BigEndian, preview.ProfileID)
|
||||||
varbin.Write(&buffer, binary.BigEndian, preview.Name)
|
writeString(&buffer, preview.Name)
|
||||||
binary.Write(&buffer, binary.BigEndian, preview.Type)
|
binary.Write(&buffer, binary.BigEndian, preview.Type)
|
||||||
}
|
}
|
||||||
return buffer.Bytes()
|
return buffer.Bytes()
|
||||||
@@ -117,7 +118,7 @@ func (d *ProfileDecoder) Decode(data []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
profile.Name, err = varbin.ReadValue[string](reader, binary.BigEndian)
|
profile.Name, err = readString(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -178,11 +179,11 @@ func (c *ProfileContent) Encode() []byte {
|
|||||||
buffer.WriteByte(1)
|
buffer.WriteByte(1)
|
||||||
gWriter := gzip.NewWriter(buffer)
|
gWriter := gzip.NewWriter(buffer)
|
||||||
writer := bufio.NewWriter(gWriter)
|
writer := bufio.NewWriter(gWriter)
|
||||||
varbin.Write(writer, binary.BigEndian, c.Name)
|
writeStringBuffered(writer, c.Name)
|
||||||
binary.Write(writer, binary.BigEndian, c.Type)
|
binary.Write(writer, binary.BigEndian, c.Type)
|
||||||
varbin.Write(writer, binary.BigEndian, c.Config)
|
writeStringBuffered(writer, c.Config)
|
||||||
if c.Type != ProfileTypeLocal {
|
if c.Type != ProfileTypeLocal {
|
||||||
varbin.Write(writer, binary.BigEndian, c.RemotePath)
|
writeStringBuffered(writer, c.RemotePath)
|
||||||
}
|
}
|
||||||
if c.Type == ProfileTypeRemote {
|
if c.Type == ProfileTypeRemote {
|
||||||
binary.Write(writer, binary.BigEndian, c.AutoUpdate)
|
binary.Write(writer, binary.BigEndian, c.AutoUpdate)
|
||||||
@@ -214,7 +215,7 @@ func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
|||||||
}
|
}
|
||||||
bReader := varbin.StubReader(gReader)
|
bReader := varbin.StubReader(gReader)
|
||||||
var content ProfileContent
|
var content ProfileContent
|
||||||
content.Name, err = varbin.ReadValue[string](bReader, binary.BigEndian)
|
content.Name, err = readString(bReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -222,12 +223,12 @@ func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
content.Config, err = varbin.ReadValue[string](bReader, binary.BigEndian)
|
content.Config, err = readString(bReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if content.Type != ProfileTypeLocal {
|
if content.Type != ProfileTypeLocal {
|
||||||
content.RemotePath, err = varbin.ReadValue[string](bReader, binary.BigEndian)
|
content.RemotePath, err = readString(bReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -250,3 +251,28 @@ func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
|||||||
}
|
}
|
||||||
return &content, nil
|
return &content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readString(reader io.ByteReader) (string, error) {
|
||||||
|
length, err := binary.ReadUvarint(reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buf := make([]byte, length)
|
||||||
|
for i := range buf {
|
||||||
|
buf[i], err = reader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeString(buffer *bytes.Buffer, value string) {
|
||||||
|
varbin.WriteUvarint(buffer, uint64(len(value)))
|
||||||
|
buffer.WriteString(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeStringBuffered(writer *bufio.Writer, value string) {
|
||||||
|
varbin.WriteUvarint(writer, uint64(len(value)))
|
||||||
|
writer.WriteString(value)
|
||||||
|
}
|
||||||
|
|||||||
@@ -126,6 +126,9 @@ func (w *platformInterfaceWrapper) NetworkInterfaces() ([]adapter.NetworkInterfa
|
|||||||
Constrained: isDefault && w.isConstrained,
|
Constrained: isDefault && w.isConstrained,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
interfaces = common.UniqBy(interfaces, func(it adapter.NetworkInterface) string {
|
||||||
|
return it.Name
|
||||||
|
})
|
||||||
return interfaces, nil
|
return interfaces, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +169,6 @@ func (w *platformInterfaceWrapper) UsePlatformConnectionOwnerFinder() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConnectionOwnerRequest) (*adapter.ConnectionOwner, error) {
|
func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConnectionOwnerRequest) (*adapter.ConnectionOwner, error) {
|
||||||
var uid int32
|
|
||||||
if w.useProcFS {
|
if w.useProcFS {
|
||||||
var source netip.AddrPort
|
var source netip.AddrPort
|
||||||
var destination netip.AddrPort
|
var destination netip.AddrPort
|
||||||
@@ -185,21 +187,24 @@ func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConn
|
|||||||
return nil, E.New("unknown protocol: ", request.IpProtocol)
|
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 {
|
return &adapter.ConnectionOwner{
|
||||||
var err error
|
UserId: uid,
|
||||||
uid, err = w.iif.FindConnectionOwner(request.IpProtocol, request.SourceAddress, request.SourcePort, request.DestinationAddress, request.DestinationPort)
|
}, nil
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
result, err := w.iif.FindConnectionOwner(request.IpProtocol, request.SourceAddress, request.SourcePort, request.DestinationAddress, request.DestinationPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
packageName, _ := w.iif.PackageNameByUid(uid)
|
|
||||||
return &adapter.ConnectionOwner{
|
return &adapter.ConnectionOwner{
|
||||||
UserId: uid,
|
UserId: result.UserId,
|
||||||
AndroidPackageName: packageName,
|
UserName: result.UserName,
|
||||||
|
ProcessPath: result.ProcessPath,
|
||||||
|
AndroidPackageName: result.AndroidPackageName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,11 +71,11 @@ func Version() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FormatBytes(length int64) string {
|
func FormatBytes(length int64) string {
|
||||||
return byteformats.FormatBytes(uint64(length))
|
return byteformats.FormatKBytes(uint64(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatMemoryBytes(length int64) string {
|
func FormatMemoryBytes(length int64) string {
|
||||||
return byteformats.FormatMemoryBytes(uint64(length))
|
return byteformats.FormatMemoryKBytes(uint64(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatDuration(duration int64) string {
|
func FormatDuration(duration int64) string {
|
||||||
|
|||||||
@@ -84,15 +84,15 @@ type StatsServiceServer interface {
|
|||||||
type UnimplementedStatsServiceServer struct{}
|
type UnimplementedStatsServiceServer struct{}
|
||||||
|
|
||||||
func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) {
|
func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GetStats not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) {
|
func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method QueryStats not implemented")
|
return nil, status.Error(codes.Unimplemented, "method QueryStats not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) {
|
func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GetSysStats not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
|
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
|
||||||
func (UnimplementedStatsServiceServer) testEmbeddedByValue() {}
|
func (UnimplementedStatsServiceServer) testEmbeddedByValue() {}
|
||||||
@@ -105,7 +105,7 @@ type UnsafeStatsServiceServer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) {
|
func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) {
|
||||||
// If the following call pancis, it indicates UnimplementedStatsServiceServer was
|
// If the following call panics, it indicates UnimplementedStatsServiceServer was
|
||||||
// embedded by pointer and is nil. This will cause panics if an
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
// unimplemented method is ever invoked, so we test this at initialization
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
// time to prevent it from happening at runtime later due to I/O.
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
|||||||
70
go.mod
70
go.mod
@@ -15,10 +15,11 @@ require (
|
|||||||
github.com/gofrs/uuid/v5 v5.4.0
|
github.com/gofrs/uuid/v5 v5.4.0
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167
|
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167
|
||||||
github.com/keybase/go-keychain v0.0.1
|
github.com/keybase/go-keychain v0.0.1
|
||||||
|
github.com/libdns/acmedns v0.5.0
|
||||||
github.com/libdns/alidns v1.0.6-beta.3
|
github.com/libdns/alidns v1.0.6-beta.3
|
||||||
github.com/libdns/cloudflare v0.2.2
|
github.com/libdns/cloudflare v0.2.2
|
||||||
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.4
|
||||||
github.com/mholt/acmez/v3 v3.1.4
|
github.com/mholt/acmez/v3 v3.1.4
|
||||||
github.com/miekg/dns v1.1.69
|
github.com/miekg/dns v1.1.69
|
||||||
github.com/openai/openai-go/v3 v3.15.0
|
github.com/openai/openai-go/v3 v3.15.0
|
||||||
@@ -26,22 +27,22 @@ require (
|
|||||||
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-20251230094758-5aef2a9e7f0d
|
github.com/sagernet/cronet-go v0.0.0-20260117110918-dc1cda1fe287
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251230094758-5aef2a9e7f0d
|
github.com/sagernet/cronet-go/all v0.0.0-20260117110918-dc1cda1fe287
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.10
|
github.com/sagernet/gomobile v0.1.11
|
||||||
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.58.0-sing-box-mod.1
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.2
|
||||||
github.com/sagernet/sing v0.8.0-beta.8
|
github.com/sagernet/sing v0.8.0-beta.16
|
||||||
github.com/sagernet/sing-mux v0.3.3
|
github.com/sagernet/sing-mux v0.3.4
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.7
|
github.com/sagernet/sing-quic v0.6.0-beta.12
|
||||||
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.0.20251226064455-a850c4f8a1c8
|
github.com/sagernet/sing-tun v0.8.0-beta.17
|
||||||
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.50-sing-box-mod.1
|
||||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6
|
||||||
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.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
@@ -68,6 +69,7 @@ require (
|
|||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
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/database64128/netx-go v0.1.1 // indirect
|
github.com/database64128/netx-go v0.1.1 // 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
|
||||||
@@ -85,6 +87,7 @@ require (
|
|||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||||
@@ -102,28 +105,28 @@ require (
|
|||||||
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.6.0 // 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-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260117110516-f21660bef13f // 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.9 // indirect
|
github.com/spf13/pflag v1.0.9 // indirect
|
||||||
@@ -131,6 +134,7 @@ require (
|
|||||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
||||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
||||||
|
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
|
||||||
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/tidwall/gjson v1.18.0 // indirect
|
github.com/tidwall/gjson v1.18.0 // indirect
|
||||||
|
|||||||
153
go.sum
153
go.sum
@@ -22,6 +22,8 @@ github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
|
|||||||
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
|
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
|
||||||
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
||||||
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||||
|
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
|
||||||
|
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||||
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=
|
||||||
@@ -30,7 +32,6 @@ github.com/database64128/netx-go v0.1.1/go.mod h1:LNlYVipaYkQArRFDNNJ02VkNV+My9A
|
|||||||
github.com/database64128/tfo-go/v2 v2.3.1 h1:EGE+ELd5/AQ0X6YBlQ9RgKs8+kciNhgN3d8lRvfEJQw=
|
github.com/database64128/tfo-go/v2 v2.3.1 h1:EGE+ELd5/AQ0X6YBlQ9RgKs8+kciNhgN3d8lRvfEJQw=
|
||||||
github.com/database64128/tfo-go/v2 v2.3.1/go.mod h1:k9wcpg/8i5zenspBkc9jUEYehpZZccBnCElzOJB++bU=
|
github.com/database64128/tfo-go/v2 v2.3.1/go.mod h1:k9wcpg/8i5zenspBkc9jUEYehpZZccBnCElzOJB++bU=
|
||||||
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.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
||||||
@@ -80,6 +81,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
|
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI=
|
||||||
|
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||||
@@ -99,6 +102,8 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt
|
|||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/libdns/acmedns v0.5.0 h1:5pRtmUj4Lb/QkNJSl1xgOGBUJTWW7RjpNaIhjpDXjPE=
|
||||||
|
github.com/libdns/acmedns v0.5.0/go.mod h1:X7UAFP1Ep9NpTwWpVlrZzJLR7epynAy0wrIxSPFgKjQ=
|
||||||
github.com/libdns/alidns v1.0.6-beta.3 h1:KAmb7FQ1tRzKsaAUGa7ZpGKAMRANwg7+1c7tUbSELq8=
|
github.com/libdns/alidns v1.0.6-beta.3 h1:KAmb7FQ1tRzKsaAUGa7ZpGKAMRANwg7+1c7tUbSELq8=
|
||||||
github.com/libdns/alidns v1.0.6-beta.3/go.mod h1:RECwyQ88e9VqQVtSrvX76o1ux3gQUKGzMgxICi+u7Ec=
|
github.com/libdns/alidns v1.0.6-beta.3/go.mod h1:RECwyQ88e9VqQVtSrvX76o1ux3gQUKGzMgxICi+u7Ec=
|
||||||
github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI=
|
github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI=
|
||||||
@@ -111,8 +116,8 @@ github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0
|
|||||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
|
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
|
||||||
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.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
|
||||||
github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
||||||
github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ=
|
github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ=
|
||||||
github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||||
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
|
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
|
||||||
@@ -145,87 +150,86 @@ 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-20251230094758-5aef2a9e7f0d h1:1oTc5+PjhCjdDro0VYZzwPee0CvTmMbDnyxZAThYvq4=
|
github.com/sagernet/cronet-go v0.0.0-20260117110918-dc1cda1fe287 h1:0BYNmr0ptjsII948U0oBFmrbo4qEaCFcrE2JPRg3Zlk=
|
||||||
github.com/sagernet/cronet-go v0.0.0-20251230094758-5aef2a9e7f0d/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw=
|
github.com/sagernet/cronet-go v0.0.0-20260117110918-dc1cda1fe287/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw=
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251230094758-5aef2a9e7f0d h1:lRwTaavH9XOCf3QPEY9FNkOsy1jLWpo5Lg6Vas9XShs=
|
github.com/sagernet/cronet-go/all v0.0.0-20260117110918-dc1cda1fe287 h1:ghxhYSBQpzkakqWqJDvXr/Zmxe0WjTjKuALEGbjGiGY=
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251230094758-5aef2a9e7f0d/go.mod h1:Wqam5NDbMkewyy+33wu6wsq4uNHGoskHaon1XsTxQMg=
|
github.com/sagernet/cronet-go/all v0.0.0-20260117110918-dc1cda1fe287/go.mod h1:M+4ZjPhLJXIvoxcQsbDofmc19Wrig59hZ+hLvj6S3To=
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251230094225-9a5fe902f561 h1:vjswtG+1CNj4kni3haVLzIn8C7RKa3RnFUXG8OofgSE=
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260117110516-f21660bef13f h1:8jZbZ4KBTdcXDFLwUBNQt5Xci6ZuAKh255S8TwuBCaM=
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260117110516-f21660bef13f/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:ciuXFp02usTUyj9MpzwlB0bEyEjF0VVINMl5QG5uIu4=
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260117110516-f21660bef13f h1:tG0hCx+0u5zca7qQ7AMkcv4DCrBG/DKW1ggs/P+BRRI=
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251230094225-9a5fe902f561 h1:qJVWgBiznBwkfQ9EB0TpjgVVeBVHb/COaItekq/EZ5w=
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260117110516-f21660bef13f h1:ZXp5hKJIA7iJ52ZShJCKMQEPLpp/7dDIVZmPGV9Il40=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251230094225-9a5fe902f561/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260117110516-f21660bef13f/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:FEJJEbbmmOv37Lx+JgHlXIdTXzP0TNYHBvzGgbft4fA=
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260117110516-f21660bef13f h1:gL7H8HS8s38adz4/HZtRHh79qMwsbLTRRPz4GQ9LcWI=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:w9Z3W30bdQrmme2qfJBfkZGUKirPe6N4caCDqp7+ArI=
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260117110516-f21660bef13f h1:Dchgc0pAY5Jwb5lzUlE+1nhHIzqLx+YOurXLHgvWd/0=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:7lJXCswd6nIrw31MJYet0sNcIxD9MVxKW/UoSPynBxw=
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260117110516-f21660bef13f h1:+MOLSQoduuKDxF410i1LcSPaQGaiP0eZb0INvMlmjM4=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:dCten+Kq71YCOGxezyIQqpe5zctf4Oxwhk8tgHUuy0g=
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260117110516-f21660bef13f h1:lIZna05Vn6n8k21p8OpSUnhwGm+E57PrMjiI4ZUfMSg=
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:Q6RE/v/XXaCLOh7w7iIuzCRhwWIjk+ikMcKSqO8vA6c=
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260117110516-f21660bef13f h1:B2aFQ5CRHI20t8YsEizvtguS5W2QfK7D5XV/NzTIxPE=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:bzXuJwzMy/+RLVXYQrNL3cqPYP7pW9OaItF0zyNkBl4=
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260117110516-f21660bef13f h1:qpSwJ1rFGYCfJDenNCZoWYjoG7N+xEa6ke+E7/JO1i4=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251230094225-9a5fe902f561 h1:bzYwMTJ1nenaNkf/TMTj3Cj1KCq2QAE9pvEy8ARdZsY=
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260117110516-f21660bef13f h1:cx7Ipg0tSvTDjS4maMEYz4vuzz93BMPAysmZ1YLrz80=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260117110516-f21660bef13f/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251230094225-9a5fe902f561 h1:lfbECpdqczlAvfkpM6q8CjFWLMjRmkf6eHaFgNMOWZ8=
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260117110516-f21660bef13f h1:4jOHuUiBxD8pJEpBBVQfJqyLmxjpd3t4MLRzU7YLFyg=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:RlA+oS6G9D5i1RNbUyGdGKTY9fAr9sjQs+zZiEghinU=
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260117110516-f21660bef13f h1:OpXBa2WlRU+Mam9oRe9Nn4/zf7gQ+qiBTNK8A5RwbfQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251230094225-9a5fe902f561 h1:MJLgEelOSdhX/3XU7n9dykExmJ5eoA2rZFXRzbiw+vE=
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260117110516-f21660bef13f h1:nJpGFi+6hI85tl4zoyNFEnFEQ5+xEV5gyvsUoMvd8g0=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251230094225-9a5fe902f561 h1:0UIPfoF74+vwCIAYlCq8NGq+em+byaMIJLUIksakdCg=
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260117110516-f21660bef13f h1:SEy2rpmgOJgrqcEryJI/RSnqUWIsEsp0cfYoA8y21jc=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251230094225-9a5fe902f561/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260117110516-f21660bef13f/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:h8zDiRnLpHY8oidZqIE0SOiDqswCBSskHWPRqHGD3F0=
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260117110516-f21660bef13f h1:EW2TuFMLm0iBGqRZtuGwIZdeYmDtDsDmRcRRJQOMxUo=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251230094225-9a5fe902f561 h1:N/svqvPrxxjhPAmZ6OwROQiF3Lg/BoViwGZpnxma3U8=
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260117110516-f21660bef13f h1:3U5woxrNCkzfv1+UX+mVoWh1228AE1qAiMG02F9oFbY=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251230094225-9a5fe902f561 h1:Hoqa5ub53wdkVKBy0rH0pacg5tDdfJq6ZJ4kORdgr+s=
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260117110516-f21660bef13f h1:YwFTfuWG3mmctroeDYtFZ6LHjGsedVO+5wInYbbUuUY=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:w6Y6mcEDFXa8kuLIAlM87TuI4UXK9Nl/CGzYDnroTK8=
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260117110516-f21660bef13f h1:r4V0ddPCRLgGu0VdgR3aUsO9NjpmyjAf+h+3oTD9D6E=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:4GmOCqjTzAqRZky/epW0zRgEYGbNXNcupxDf2WwWS4Y=
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260117110516-f21660bef13f h1:B8yf4gFvEYUnwWmtVK9sdwUsflYZ387MhYmlOP2ohFQ=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:dWnsBlKtGwd9qebvReFAL64CZ/DCMo5eZiqS7cwuP38=
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260117110516-f21660bef13f h1:9YyaMg4rO1/jIgrxmNb0LKH+X7frSYWfX2pFgW5JUVM=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:ri/eIHXgK+PrK/Z05Gv+xwI7PsrsANWEB2EDr6NkBTY=
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260117110516-f21660bef13f h1:B0fnGu0sh9yT/9JDN5u/GqThGoOzNN/daOAuGWFLXEk=
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:JGN++AgXz57mkZ4UmxEuuvmaj32+yF2mZLNnsfvOm08=
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260117110516-f21660bef13f h1:lxPcIXKSSI5JDhc7rx/6yufISWM4vtBS2FY9PavWQTs=
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260117110516-f21660bef13f/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.10 h1:ElqZ0OVDvyQlU91MU0C9cfU0FrILBbc65+NOKzZ1t0c=
|
github.com/sagernet/gomobile v0.1.11 h1:niMQAspvuThup5eRZQpsGcbM76zAvnsGr7RUIpnQMDQ=
|
||||||
github.com/sagernet/gomobile v0.1.10/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
|
github.com/sagernet/gomobile v0.1.11/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
|
||||||
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1 h1:AzCE2RhBjLJ4WIWc/GejpNh+z30d5H1hwaB0nD9eY3o=
|
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1 h1:AzCE2RhBjLJ4WIWc/GejpNh+z30d5H1hwaB0nD9eY3o=
|
||||||
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1/go.mod h1:NJKBtm9nVEK3iyOYWsUlrDQuoGh4zJ4KOPhSYVidvQ4=
|
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1/go.mod h1:NJKBtm9nVEK3iyOYWsUlrDQuoGh4zJ4KOPhSYVidvQ4=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||||
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.58.0-sing-box-mod.1 h1:E9yZrU0ZxSiW5RrGUnFZeI02EIMdAAv0RxdoxXCqZyk=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.2 h1:hJUL+HtxEOjxsa0CsucbBVqI/AMS4k52NwNU637zmdw=
|
||||||
github.com/sagernet/quic-go v0.58.0-sing-box-mod.1/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.2/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
||||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.8.0-beta.16 h1:Fe+6E9VHYky9Mx4cf0ugbZPWDcXRflpAu7JQ5bWXvaA=
|
||||||
github.com/sagernet/sing v0.8.0-beta.8 h1:hUo0wZ2HGTieV1flEIai96HFhF34mMHVnduRqJHQvxg=
|
github.com/sagernet/sing v0.8.0-beta.16/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing v0.8.0-beta.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
|
||||||
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
||||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-quic v0.6.0-beta.12 h1:njyU2NYGBITShAu31wJRmqAtx7hQBcXqBPowDv+W0sk=
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.7 h1:Sh6KltQ6nB69S9ZdDKs5oARqkyY99gOaHe1JPxNV7ag=
|
github.com/sagernet/sing-quic v0.6.0-beta.12/go.mod h1:K5bWvITOm4vE10fwLfrWpw27bCoVJ+tfQ79tOWg+Ko8=
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.7/go.mod h1:0NodMFjlAvfLp87Enpx46fQPrGRvmbUsmy5hRLzomtM=
|
|
||||||
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.0.20251226064455-a850c4f8a1c8 h1:aIgk6YzS/7fNm92CycFWzithdwIc+NAwXGHAJce1dyM=
|
github.com/sagernet/sing-tun v0.8.0-beta.17 h1:6DdbNXeTFYj8Tb4FCh8Mp2boA3rVY6VNqzTOObj7Xis=
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20251226064455-a850c4f8a1c8/go.mod h1:+HAK/y9GZljdT0KYKMYDR8MjjqnqDDQZYp5ZZQoRzS8=
|
github.com/sagernet/sing-tun v0.8.0-beta.17/go.mod h1:+HAK/y9GZljdT0KYKMYDR8MjjqnqDDQZYp5ZZQoRzS8=
|
||||||
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.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
|
||||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4 h1:p+9JllOL5Q2pj6bmP9gu+LdjyRg/XxHLTpMfuhuQsY4=
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6 h1:eYz/OpMqWCvO2++iw3dEuzrlfC2xv78GdlGvprIM6O8=
|
||||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4/go.mod h1:HZxL3asFIkcIJtHdnqsdcXsY6d+1iMtq0SPUlX17TGM=
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6/go.mod h1:m87GAn4UcesHQF3leaPFEINZETO5za1LGn1GJdNDgNc=
|
||||||
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=
|
||||||
@@ -235,14 +239,7 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
|
|||||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
||||||
@@ -253,6 +250,8 @@ github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPx
|
|||||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
|
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
|
||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
||||||
|
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
|
||||||
|
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
||||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
|
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
|
||||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
|
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14=
|
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14=
|
||||||
@@ -271,6 +270,7 @@ 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/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.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
@@ -324,11 +324,12 @@ golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwE
|
|||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,37 +61,40 @@ func checkOptions(options *Options) error {
|
|||||||
|
|
||||||
func checkInbounds(inbounds []Inbound) error {
|
func checkInbounds(inbounds []Inbound) error {
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
for _, inbound := range inbounds {
|
for i, inbound := range inbounds {
|
||||||
if inbound.Tag == "" {
|
tag := inbound.Tag
|
||||||
continue
|
if tag == "" {
|
||||||
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
if seen[inbound.Tag] {
|
if seen[tag] {
|
||||||
return E.New("duplicate inbound tag: ", inbound.Tag)
|
return E.New("duplicate inbound tag: ", tag)
|
||||||
}
|
}
|
||||||
seen[inbound.Tag] = true
|
seen[tag] = true
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkOutbounds(outbounds []Outbound, endpoints []Endpoint) error {
|
func checkOutbounds(outbounds []Outbound, endpoints []Endpoint) error {
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
for _, outbound := range outbounds {
|
for i, outbound := range outbounds {
|
||||||
if outbound.Tag == "" {
|
tag := outbound.Tag
|
||||||
continue
|
if tag == "" {
|
||||||
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
if seen[outbound.Tag] {
|
if seen[tag] {
|
||||||
return E.New("duplicate outbound/endpoint tag: ", outbound.Tag)
|
return E.New("duplicate outbound/endpoint tag: ", tag)
|
||||||
}
|
}
|
||||||
seen[outbound.Tag] = true
|
seen[tag] = true
|
||||||
}
|
}
|
||||||
for _, endpoint := range endpoints {
|
for i, endpoint := range endpoints {
|
||||||
if endpoint.Tag == "" {
|
tag := endpoint.Tag
|
||||||
continue
|
if tag == "" {
|
||||||
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
if seen[endpoint.Tag] {
|
if seen[tag] {
|
||||||
return E.New("duplicate outbound/endpoint tag: ", endpoint.Tag)
|
return E.New("duplicate outbound/endpoint tag: ", tag)
|
||||||
}
|
}
|
||||||
seen[endpoint.Tag] = true
|
seen[tag] = true
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ type DialerOptions struct {
|
|||||||
BindInterface string `json:"bind_interface,omitempty"`
|
BindInterface string `json:"bind_interface,omitempty"`
|
||||||
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
||||||
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
||||||
|
BindAddressNoPort bool `json:"bind_address_no_port,omitempty"`
|
||||||
ProtectPath string `json:"protect_path,omitempty"`
|
ProtectPath string `json:"protect_path,omitempty"`
|
||||||
RoutingMark FwMark `json:"routing_mark,omitempty"`
|
RoutingMark FwMark `json:"routing_mark,omitempty"`
|
||||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||||
|
|||||||
@@ -12,19 +12,22 @@ import (
|
|||||||
|
|
||||||
type TailscaleEndpointOptions struct {
|
type TailscaleEndpointOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
StateDirectory string `json:"state_directory,omitempty"`
|
StateDirectory string `json:"state_directory,omitempty"`
|
||||||
AuthKey string `json:"auth_key,omitempty"`
|
AuthKey string `json:"auth_key,omitempty"`
|
||||||
ControlURL string `json:"control_url,omitempty"`
|
ControlURL string `json:"control_url,omitempty"`
|
||||||
Ephemeral bool `json:"ephemeral,omitempty"`
|
Ephemeral bool `json:"ephemeral,omitempty"`
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
AcceptRoutes bool `json:"accept_routes,omitempty"`
|
AcceptRoutes bool `json:"accept_routes,omitempty"`
|
||||||
ExitNode string `json:"exit_node,omitempty"`
|
ExitNode string `json:"exit_node,omitempty"`
|
||||||
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
|
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
|
||||||
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
|
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
|
||||||
AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"`
|
AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"`
|
||||||
RelayServerPort *uint16 `json:"relay_server_port,omitempty"`
|
RelayServerPort *uint16 `json:"relay_server_port,omitempty"`
|
||||||
RelayServerStaticEndpoints []netip.AddrPort `json:"relay_server_static_endpoints,omitempty"`
|
RelayServerStaticEndpoints []netip.AddrPort `json:"relay_server_static_endpoints,omitempty"`
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
SystemInterface bool `json:"system_interface,omitempty"`
|
||||||
|
SystemInterfaceName string `json:"system_interface_name,omitempty"`
|
||||||
|
SystemInterfaceMTU uint32 `json:"system_interface_mtu,omitempty"`
|
||||||
|
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TailscaleDNSServerOptions struct {
|
type TailscaleDNSServerOptions struct {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type _ACMEDNS01ChallengeOptions struct {
|
|||||||
Provider string `json:"provider,omitempty"`
|
Provider string `json:"provider,omitempty"`
|
||||||
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
|
AliDNSOptions ACMEDNS01AliDNSOptions `json:"-"`
|
||||||
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
|
CloudflareOptions ACMEDNS01CloudflareOptions `json:"-"`
|
||||||
|
ACMEDNSOptions ACMEDNS01ACMEDNSOptions `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ACMEDNS01ChallengeOptions _ACMEDNS01ChallengeOptions
|
type ACMEDNS01ChallengeOptions _ACMEDNS01ChallengeOptions
|
||||||
@@ -42,6 +43,8 @@ func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
|
|||||||
v = o.AliDNSOptions
|
v = o.AliDNSOptions
|
||||||
case C.DNSProviderCloudflare:
|
case C.DNSProviderCloudflare:
|
||||||
v = o.CloudflareOptions
|
v = o.CloudflareOptions
|
||||||
|
case C.DNSProviderACMEDNS:
|
||||||
|
v = o.ACMEDNSOptions
|
||||||
case "":
|
case "":
|
||||||
return nil, E.New("missing provider type")
|
return nil, E.New("missing provider type")
|
||||||
default:
|
default:
|
||||||
@@ -61,6 +64,8 @@ func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {
|
|||||||
v = &o.AliDNSOptions
|
v = &o.AliDNSOptions
|
||||||
case C.DNSProviderCloudflare:
|
case C.DNSProviderCloudflare:
|
||||||
v = &o.CloudflareOptions
|
v = &o.CloudflareOptions
|
||||||
|
case C.DNSProviderACMEDNS:
|
||||||
|
v = &o.ACMEDNSOptions
|
||||||
default:
|
default:
|
||||||
return E.New("unknown provider type: " + o.Provider)
|
return E.New("unknown provider type: " + o.Provider)
|
||||||
}
|
}
|
||||||
@@ -82,3 +87,10 @@ type ACMEDNS01CloudflareOptions struct {
|
|||||||
APIToken string `json:"api_token,omitempty"`
|
APIToken string `json:"api_token,omitempty"`
|
||||||
ZoneToken string `json:"zone_token,omitempty"`
|
ZoneToken string `json:"zone_token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ACMEDNS01ACMEDNSOptions struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Subdomain string `json:"subdomain,omitempty"`
|
||||||
|
ServerURL string `json:"server_url,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,36 +11,37 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TunInboundOptions struct {
|
type TunInboundOptions struct {
|
||||||
InterfaceName string `json:"interface_name,omitempty"`
|
InterfaceName string `json:"interface_name,omitempty"`
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
|
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
|
||||||
AutoRoute bool `json:"auto_route,omitempty"`
|
AutoRoute bool `json:"auto_route,omitempty"`
|
||||||
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
||||||
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
|
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
|
||||||
AutoRedirect bool `json:"auto_redirect,omitempty"`
|
AutoRedirect bool `json:"auto_redirect,omitempty"`
|
||||||
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
|
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
|
||||||
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
|
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
|
||||||
AutoRedirectResetMark FwMark `json:"auto_redirect_reset_mark,omitempty"`
|
AutoRedirectResetMark FwMark `json:"auto_redirect_reset_mark,omitempty"`
|
||||||
AutoRedirectNFQueue uint16 `json:"auto_redirect_nfqueue,omitempty"`
|
AutoRedirectNFQueue uint16 `json:"auto_redirect_nfqueue,omitempty"`
|
||||||
ExcludeMPTCP bool `json:"exclude_mptcp,omitempty"`
|
AutoRedirectFallbackRuleIndex int `json:"auto_redirect_iproute2_fallback_rule_index,omitempty"`
|
||||||
LoopbackAddress badoption.Listable[netip.Addr] `json:"loopback_address,omitempty"`
|
ExcludeMPTCP bool `json:"exclude_mptcp,omitempty"`
|
||||||
StrictRoute bool `json:"strict_route,omitempty"`
|
LoopbackAddress badoption.Listable[netip.Addr] `json:"loopback_address,omitempty"`
|
||||||
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
|
StrictRoute bool `json:"strict_route,omitempty"`
|
||||||
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`
|
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
|
||||||
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
|
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`
|
||||||
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"`
|
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
|
||||||
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"`
|
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"`
|
||||||
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"`
|
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"`
|
||||||
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"`
|
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"`
|
||||||
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"`
|
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"`
|
||||||
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"`
|
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"`
|
||||||
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"`
|
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"`
|
||||||
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
|
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"`
|
||||||
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
|
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
|
||||||
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
|
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
|
||||||
Stack string `json:"stack,omitempty"`
|
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
||||||
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
Stack string `json:"stack,omitempty"`
|
||||||
|
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||||
InboundOptions
|
InboundOptions
|
||||||
|
|
||||||
// Deprecated: removed
|
// Deprecated: removed
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
std_bufio "bufio"
|
std_bufio "bufio"
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/inbound"
|
"github.com/sagernet/sing-box/adapter/inbound"
|
||||||
@@ -36,14 +37,22 @@ type Inbound struct {
|
|||||||
listener *listener.Listener
|
listener *listener.Listener
|
||||||
authenticator *auth.Authenticator
|
authenticator *auth.Authenticator
|
||||||
tlsConfig tls.ServerConfig
|
tlsConfig tls.ServerConfig
|
||||||
|
udpTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (adapter.Inbound, error) {
|
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) (adapter.Inbound, error) {
|
||||||
|
var udpTimeout time.Duration
|
||||||
|
if options.UDPTimeout != 0 {
|
||||||
|
udpTimeout = time.Duration(options.UDPTimeout)
|
||||||
|
} else {
|
||||||
|
udpTimeout = C.UDPTimeout
|
||||||
|
}
|
||||||
inbound := &Inbound{
|
inbound := &Inbound{
|
||||||
Adapter: inbound.NewAdapter(C.TypeMixed, tag),
|
Adapter: inbound.NewAdapter(C.TypeMixed, tag),
|
||||||
router: uot.NewRouter(router, logger),
|
router: uot.NewRouter(router, logger),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
authenticator: auth.NewAuthenticator(options.Users),
|
authenticator: auth.NewAuthenticator(options.Users),
|
||||||
|
udpTimeout: udpTimeout,
|
||||||
}
|
}
|
||||||
if options.TLS != nil {
|
if options.TLS != nil {
|
||||||
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
|
tlsConfig, err := tls.NewServerWithOptions(tls.ServerOptions{
|
||||||
@@ -116,7 +125,7 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
|
|||||||
}
|
}
|
||||||
switch headerBytes[0] {
|
switch headerBytes[0] {
|
||||||
case socks4.Version, socks5.Version:
|
case socks4.Version, socks5.Version:
|
||||||
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose)
|
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, h.udpTimeout, metadata.Source, onClose)
|
||||||
default:
|
default:
|
||||||
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ func (p *paddingConn) writeWithPadding(writer io.Writer, data []byte) (n int, er
|
|||||||
binary.BigEndian.PutUint16(header, uint16(len(data)))
|
binary.BigEndian.PutUint16(header, uint16(len(data)))
|
||||||
header[2] = byte(paddingSize)
|
header[2] = byte(paddingSize)
|
||||||
common.Must1(buffer.Write(data))
|
common.Must1(buffer.Write(data))
|
||||||
|
buffer.Extend(paddingSize)
|
||||||
_, err = writer.Write(buffer.Bytes())
|
_, err = writer.Write(buffer.Bytes())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
n = len(data)
|
n = len(data)
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
var dnsResolver cronet.DNSResolverFunc
|
var dnsResolver cronet.DNSResolverFunc
|
||||||
if dnsRouter != nil {
|
if dnsRouter != nil {
|
||||||
dnsResolver = func(dnsContext context.Context, request *mDNS.Msg) *mDNS.Msg {
|
dnsResolver = func(dnsContext context.Context, request *mDNS.Msg) *mDNS.Msg {
|
||||||
response, err := dnsRouter.Exchange(dnsContext, request, adapter.DNSQueryOptions{})
|
response, err := dnsRouter.Exchange(dnsContext, request, outboundDialer.(dialer.ResolveDialer).QueryOptions())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("DNS exchange failed: ", err)
|
logger.Error("DNS exchange failed: ", err)
|
||||||
return dns.FixedResponseStatus(request, mDNS.RcodeServerFailure)
|
return dns.FixedResponseStatus(request, mDNS.RcodeServerFailure)
|
||||||
@@ -177,6 +177,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
}
|
}
|
||||||
client, err := cronet.NewNaiveClient(cronet.NaiveClientOptions{
|
client, err := cronet.NewNaiveClient(cronet.NaiveClientOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
|
Logger: logger,
|
||||||
ServerAddress: serverAddress,
|
ServerAddress: serverAddress,
|
||||||
ServerName: serverName,
|
ServerName: serverName,
|
||||||
Username: options.Username,
|
Username: options.Username,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
std_bufio "bufio"
|
std_bufio "bufio"
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/inbound"
|
"github.com/sagernet/sing-box/adapter/inbound"
|
||||||
@@ -31,14 +32,22 @@ type Inbound struct {
|
|||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
listener *listener.Listener
|
listener *listener.Listener
|
||||||
authenticator *auth.Authenticator
|
authenticator *auth.Authenticator
|
||||||
|
udpTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) (adapter.Inbound, error) {
|
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) (adapter.Inbound, error) {
|
||||||
|
var udpTimeout time.Duration
|
||||||
|
if options.UDPTimeout != 0 {
|
||||||
|
udpTimeout = time.Duration(options.UDPTimeout)
|
||||||
|
} else {
|
||||||
|
udpTimeout = C.UDPTimeout
|
||||||
|
}
|
||||||
inbound := &Inbound{
|
inbound := &Inbound{
|
||||||
Adapter: inbound.NewAdapter(C.TypeSOCKS, tag),
|
Adapter: inbound.NewAdapter(C.TypeSOCKS, tag),
|
||||||
router: uot.NewRouter(router, logger),
|
router: uot.NewRouter(router, logger),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
authenticator: auth.NewAuthenticator(options.Users),
|
authenticator: auth.NewAuthenticator(options.Users),
|
||||||
|
udpTimeout: udpTimeout,
|
||||||
}
|
}
|
||||||
inbound.listener = listener.New(listener.Options{
|
inbound.listener = listener.New(listener.Options{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
@@ -62,7 +71,7 @@ func (h *Inbound) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose)
|
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, h.udpTimeout, metadata.Source, onClose)
|
||||||
N.CloseOnHandshakeFailure(conn, onClose, err)
|
N.CloseOnHandshakeFailure(conn, onClose, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if E.IsClosedOrCanceled(err) {
|
if E.IsClosedOrCanceled(err) {
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import (
|
|||||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
"github.com/sagernet/sing-box/adapter/endpoint"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
@@ -49,8 +47,10 @@ import (
|
|||||||
tsDNS "github.com/sagernet/tailscale/net/dns"
|
tsDNS "github.com/sagernet/tailscale/net/dns"
|
||||||
"github.com/sagernet/tailscale/net/netmon"
|
"github.com/sagernet/tailscale/net/netmon"
|
||||||
"github.com/sagernet/tailscale/net/tsaddr"
|
"github.com/sagernet/tailscale/net/tsaddr"
|
||||||
|
tsTUN "github.com/sagernet/tailscale/net/tstun"
|
||||||
"github.com/sagernet/tailscale/tsnet"
|
"github.com/sagernet/tailscale/tsnet"
|
||||||
"github.com/sagernet/tailscale/types/ipproto"
|
"github.com/sagernet/tailscale/types/ipproto"
|
||||||
|
"github.com/sagernet/tailscale/types/nettype"
|
||||||
"github.com/sagernet/tailscale/version"
|
"github.com/sagernet/tailscale/version"
|
||||||
"github.com/sagernet/tailscale/wgengine"
|
"github.com/sagernet/tailscale/wgengine"
|
||||||
"github.com/sagernet/tailscale/wgengine/filter"
|
"github.com/sagernet/tailscale/wgengine/filter"
|
||||||
@@ -101,6 +101,51 @@ type Endpoint struct {
|
|||||||
relayServerStaticEndpoints []netip.AddrPort
|
relayServerStaticEndpoints []netip.AddrPort
|
||||||
|
|
||||||
udpTimeout time.Duration
|
udpTimeout time.Duration
|
||||||
|
|
||||||
|
systemInterface bool
|
||||||
|
systemInterfaceName string
|
||||||
|
systemInterfaceMTU uint32
|
||||||
|
systemTun tun.Tun
|
||||||
|
fallbackTCPCloser func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Endpoint) registerNetstackHandlers() {
|
||||||
|
netstack := t.server.ExportNetstack()
|
||||||
|
if netstack == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
previousTCP := netstack.GetTCPHandlerForFlow
|
||||||
|
netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
|
||||||
|
if previousTCP != nil {
|
||||||
|
handler, intercept = previousTCP(src, dst)
|
||||||
|
if handler != nil || !intercept {
|
||||||
|
return handler, intercept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(conn net.Conn) {
|
||||||
|
ctx := log.ContextWithNewID(t.ctx)
|
||||||
|
source := M.SocksaddrFrom(src.Addr(), src.Port())
|
||||||
|
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
|
||||||
|
t.NewConnectionEx(ctx, conn, source, destination, nil)
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
previousUDP := netstack.GetUDPHandlerForFlow
|
||||||
|
netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) {
|
||||||
|
if previousUDP != nil {
|
||||||
|
handler, intercept = previousUDP(src, dst)
|
||||||
|
if handler != nil || !intercept {
|
||||||
|
return handler, intercept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(conn nettype.ConnPacketConn) {
|
||||||
|
ctx := log.ContextWithNewID(t.ctx)
|
||||||
|
source := M.SocksaddrFrom(src.Addr(), src.Port())
|
||||||
|
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
|
||||||
|
packetConn := bufio.NewPacketConn(conn)
|
||||||
|
t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil)
|
||||||
|
}, true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
|
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
|
||||||
@@ -202,6 +247,9 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
relayServerPort: options.RelayServerPort,
|
relayServerPort: options.RelayServerPort,
|
||||||
relayServerStaticEndpoints: options.RelayServerStaticEndpoints,
|
relayServerStaticEndpoints: options.RelayServerStaticEndpoints,
|
||||||
udpTimeout: udpTimeout,
|
udpTimeout: udpTimeout,
|
||||||
|
systemInterface: options.SystemInterface,
|
||||||
|
systemInterfaceName: options.SystemInterfaceName,
|
||||||
|
systemInterfaceMTU: options.SystemInterfaceMTU,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,10 +285,59 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
|||||||
setAndroidProtectFunc(t.platformInterface)
|
setAndroidProtectFunc(t.platformInterface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if t.systemInterface {
|
||||||
|
mtu := t.systemInterfaceMTU
|
||||||
|
if mtu == 0 {
|
||||||
|
mtu = uint32(tsTUN.DefaultTUNMTU())
|
||||||
|
}
|
||||||
|
tunName := t.systemInterfaceName
|
||||||
|
if tunName == "" {
|
||||||
|
tunName = tun.CalculateInterfaceName("tailscale")
|
||||||
|
}
|
||||||
|
tunOptions := tun.Options{
|
||||||
|
Name: tunName,
|
||||||
|
MTU: mtu,
|
||||||
|
GSO: true,
|
||||||
|
InterfaceScope: true,
|
||||||
|
InterfaceMonitor: t.network.InterfaceMonitor(),
|
||||||
|
InterfaceFinder: t.network.InterfaceFinder(),
|
||||||
|
Logger: t.logger,
|
||||||
|
EXP_ExternalConfiguration: true,
|
||||||
|
}
|
||||||
|
systemTun, err := tun.New(tunOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = systemTun.Start()
|
||||||
|
if err != nil {
|
||||||
|
_ = systemTun.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wgTunDevice, err := newTunDeviceAdapter(systemTun, int(mtu), t.logger)
|
||||||
|
if err != nil {
|
||||||
|
_ = systemTun.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.systemTun = systemTun
|
||||||
|
t.server.TunDevice = wgTunDevice
|
||||||
|
}
|
||||||
err := t.server.Start()
|
err := t.server.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if t.systemTun != nil {
|
||||||
|
_ = t.systemTun.Close()
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if t.fallbackTCPCloser == nil {
|
||||||
|
t.fallbackTCPCloser = t.server.RegisterFallbackTCPHandler(func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
|
||||||
|
return func(conn net.Conn) {
|
||||||
|
ctx := log.ContextWithNewID(t.ctx)
|
||||||
|
source := M.SocksaddrFrom(src.Addr(), src.Port())
|
||||||
|
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
|
||||||
|
t.NewConnectionEx(ctx, conn, source, destination, nil)
|
||||||
|
}, true
|
||||||
|
})
|
||||||
|
}
|
||||||
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
|
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
|
||||||
|
|
||||||
ipStack := t.server.ExportNetstack().ExportIPStack()
|
ipStack := t.server.ExportNetstack().ExportIPStack()
|
||||||
@@ -252,13 +349,12 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
|||||||
if gErr != nil {
|
if gErr != nil {
|
||||||
return gonet.TranslateNetstackError(gErr)
|
return gonet.TranslateNetstackError(gErr)
|
||||||
}
|
}
|
||||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
|
|
||||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout).HandlePacket)
|
|
||||||
icmpForwarder := tun.NewICMPForwarder(t.ctx, ipStack, t, t.udpTimeout)
|
icmpForwarder := tun.NewICMPForwarder(t.ctx, ipStack, t, t.udpTimeout)
|
||||||
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
|
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
|
||||||
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
|
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
|
||||||
t.stack = ipStack
|
t.stack = ipStack
|
||||||
t.icmpForwarder = icmpForwarder
|
t.icmpForwarder = icmpForwarder
|
||||||
|
t.registerNetstackHandlers()
|
||||||
|
|
||||||
localBackend := t.server.ExportLocalBackend()
|
localBackend := t.server.ExportLocalBackend()
|
||||||
perfs := &ipn.MaskedPrefs{
|
perfs := &ipn.MaskedPrefs{
|
||||||
@@ -355,6 +451,10 @@ func (t *Endpoint) Close() error {
|
|||||||
if runtime.GOOS == "android" {
|
if runtime.GOOS == "android" {
|
||||||
setAndroidProtectFunc(nil)
|
setAndroidProtectFunc(nil)
|
||||||
}
|
}
|
||||||
|
if t.fallbackTCPCloser != nil {
|
||||||
|
t.fallbackTCPCloser()
|
||||||
|
t.fallbackTCPCloser = nil
|
||||||
|
}
|
||||||
return common.Close(common.PtrOrNil(t.server))
|
return common.Close(common.PtrOrNil(t.server))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,26 +472,42 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
|
|||||||
}
|
}
|
||||||
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
|
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
|
||||||
}
|
}
|
||||||
addr := tcpip.FullAddress{
|
addr4, addr6 := t.server.TailscaleIPs()
|
||||||
|
remoteAddr := tcpip.FullAddress{
|
||||||
NIC: 1,
|
NIC: 1,
|
||||||
Port: destination.Port,
|
Port: destination.Port,
|
||||||
Addr: addressFromAddr(destination.Addr),
|
Addr: addressFromAddr(destination.Addr),
|
||||||
}
|
}
|
||||||
|
var localAddr tcpip.FullAddress
|
||||||
var networkProtocol tcpip.NetworkProtocolNumber
|
var networkProtocol tcpip.NetworkProtocolNumber
|
||||||
if destination.IsIPv4() {
|
if destination.IsIPv4() {
|
||||||
|
if !addr4.IsValid() {
|
||||||
|
return nil, E.New("missing Tailscale IPv4 address")
|
||||||
|
}
|
||||||
networkProtocol = header.IPv4ProtocolNumber
|
networkProtocol = header.IPv4ProtocolNumber
|
||||||
|
localAddr = tcpip.FullAddress{
|
||||||
|
NIC: 1,
|
||||||
|
Addr: addressFromAddr(addr4),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if !addr6.IsValid() {
|
||||||
|
return nil, E.New("missing Tailscale IPv6 address")
|
||||||
|
}
|
||||||
networkProtocol = header.IPv6ProtocolNumber
|
networkProtocol = header.IPv6ProtocolNumber
|
||||||
|
localAddr = tcpip.FullAddress{
|
||||||
|
NIC: 1,
|
||||||
|
Addr: addressFromAddr(addr6),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
tcpConn, err := gonet.DialContextTCP(ctx, t.stack, addr, networkProtocol)
|
tcpConn, err := gonet.DialTCPWithBind(ctx, t.stack, localAddr, remoteAddr, networkProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return tcpConn, nil
|
return tcpConn, nil
|
||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
udpConn, err := gonet.DialUDP(t.stack, nil, &addr, networkProtocol)
|
udpConn, err := gonet.DialUDP(t.stack, &localAddr, &remoteAddr, networkProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
156
protocol/tailscale/tun_device_unix.go
Normal file
156
protocol/tailscale/tun_device_unix.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package tailscale
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
singTun "github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
wgTun "github.com/sagernet/wireguard-go/tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunDeviceAdapter struct {
|
||||||
|
tun singTun.Tun
|
||||||
|
linuxTUN singTun.LinuxTUN
|
||||||
|
events chan wgTun.Event
|
||||||
|
mtu int
|
||||||
|
logger logger.ContextLogger
|
||||||
|
debugTun bool
|
||||||
|
readCount atomic.Uint32
|
||||||
|
writeCount atomic.Uint32
|
||||||
|
closeOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTunDeviceAdapter(tun singTun.Tun, mtu int, logger logger.ContextLogger) (wgTun.Device, error) {
|
||||||
|
if tun == nil {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
if mtu == 0 {
|
||||||
|
mtu = 1500
|
||||||
|
}
|
||||||
|
adapter := &tunDeviceAdapter{
|
||||||
|
tun: tun,
|
||||||
|
events: make(chan wgTun.Event, 1),
|
||||||
|
mtu: mtu,
|
||||||
|
logger: logger,
|
||||||
|
debugTun: os.Getenv("SINGBOX_TS_TUN_DEBUG") != "",
|
||||||
|
}
|
||||||
|
if linuxTUN, ok := tun.(singTun.LinuxTUN); ok {
|
||||||
|
adapter.linuxTUN = linuxTUN
|
||||||
|
}
|
||||||
|
adapter.events <- wgTun.EventUp
|
||||||
|
return adapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) File() *os.File {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
|
||||||
|
if a.linuxTUN != nil {
|
||||||
|
n, err := a.linuxTUN.BatchRead(bufs, offset-singTun.PacketOffset, sizes)
|
||||||
|
if err == nil {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
a.debugPacket("read", bufs[i][offset:offset+sizes[i]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if offset < singTun.PacketOffset {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
readBuf := bufs[0][offset-singTun.PacketOffset:]
|
||||||
|
n, err := a.tun.Read(readBuf)
|
||||||
|
if err == nil {
|
||||||
|
if n < singTun.PacketOffset {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
sizes[0] = n - singTun.PacketOffset
|
||||||
|
a.debugPacket("read", readBuf[singTun.PacketOffset:n])
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
if errors.Is(err, singTun.ErrTooManySegments) {
|
||||||
|
err = wgTun.ErrTooManySegments
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Write(bufs [][]byte, offset int) (count int, err error) {
|
||||||
|
if a.linuxTUN != nil {
|
||||||
|
for i := range bufs {
|
||||||
|
a.debugPacket("write", bufs[i][offset:])
|
||||||
|
}
|
||||||
|
return a.linuxTUN.BatchWrite(bufs, offset)
|
||||||
|
}
|
||||||
|
for _, packet := range bufs {
|
||||||
|
a.debugPacket("write", packet[offset:])
|
||||||
|
if singTun.PacketOffset > 0 {
|
||||||
|
common.ClearArray(packet[offset-singTun.PacketOffset : offset])
|
||||||
|
singTun.PacketFillHeader(packet[offset-singTun.PacketOffset:], singTun.PacketIPVersion(packet[offset:]))
|
||||||
|
}
|
||||||
|
_, err = a.tun.Write(packet[offset-singTun.PacketOffset:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// WireGuard will not read count.
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) MTU() (int, error) {
|
||||||
|
return a.mtu, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Name() (string, error) {
|
||||||
|
return a.tun.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Events() <-chan wgTun.Event {
|
||||||
|
return a.events
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Close() error {
|
||||||
|
var err error
|
||||||
|
a.closeOnce.Do(func() {
|
||||||
|
close(a.events)
|
||||||
|
err = a.tun.Close()
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) BatchSize() int {
|
||||||
|
if a.linuxTUN != nil {
|
||||||
|
return a.linuxTUN.BatchSize()
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) debugPacket(direction string, packet []byte) {
|
||||||
|
if !a.debugTun || a.logger == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var counter *atomic.Uint32
|
||||||
|
switch direction {
|
||||||
|
case "read":
|
||||||
|
counter = &a.readCount
|
||||||
|
case "write":
|
||||||
|
counter = &a.writeCount
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if counter.Add(1) > 8 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sample := packet
|
||||||
|
if len(sample) > 64 {
|
||||||
|
sample = sample[:64]
|
||||||
|
}
|
||||||
|
a.logger.Trace("tailscale tun ", direction, " len=", len(packet), " head=", hex.EncodeToString(sample))
|
||||||
|
}
|
||||||
117
protocol/tailscale/tun_device_windows.go
Normal file
117
protocol/tailscale/tun_device_windows.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package tailscale
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
singTun "github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
wgTun "github.com/sagernet/wireguard-go/tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunDeviceAdapter struct {
|
||||||
|
tun singTun.WinTun
|
||||||
|
nativeTun *singTun.NativeTun
|
||||||
|
events chan wgTun.Event
|
||||||
|
mtu atomic.Int64
|
||||||
|
closeOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTunDeviceAdapter(tun singTun.Tun, mtu int, _ logger.ContextLogger) (wgTun.Device, error) {
|
||||||
|
winTun, ok := tun.(singTun.WinTun)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("not a windows tun device")
|
||||||
|
}
|
||||||
|
nativeTun, ok := winTun.(*singTun.NativeTun)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("unsupported windows tun device")
|
||||||
|
}
|
||||||
|
if mtu == 0 {
|
||||||
|
mtu = 1500
|
||||||
|
}
|
||||||
|
adapter := &tunDeviceAdapter{
|
||||||
|
tun: winTun,
|
||||||
|
nativeTun: nativeTun,
|
||||||
|
events: make(chan wgTun.Event, 1),
|
||||||
|
}
|
||||||
|
adapter.mtu.Store(int64(mtu))
|
||||||
|
adapter.events <- wgTun.EventUp
|
||||||
|
return adapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) File() *os.File {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
|
||||||
|
packet, release, err := a.tun.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer release()
|
||||||
|
sizes[0] = copy(bufs[0][offset-singTun.PacketOffset:], packet)
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Write(bufs [][]byte, offset int) (count int, err error) {
|
||||||
|
for _, packet := range bufs {
|
||||||
|
if singTun.PacketOffset > 0 {
|
||||||
|
singTun.PacketFillHeader(packet[offset-singTun.PacketOffset:], singTun.PacketIPVersion(packet[offset:]))
|
||||||
|
}
|
||||||
|
_, err = a.tun.Write(packet[offset-singTun.PacketOffset:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) MTU() (int, error) {
|
||||||
|
return int(a.mtu.Load()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) ForceMTU(mtu int) {
|
||||||
|
if mtu <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
update := int(a.mtu.Load()) != mtu
|
||||||
|
a.mtu.Store(int64(mtu))
|
||||||
|
if update {
|
||||||
|
select {
|
||||||
|
case a.events <- wgTun.EventMTUUpdate:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) LUID() uint64 {
|
||||||
|
if a.nativeTun == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return a.nativeTun.LUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Name() (string, error) {
|
||||||
|
return a.tun.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Events() <-chan wgTun.Event {
|
||||||
|
return a.events
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) Close() error {
|
||||||
|
var err error
|
||||||
|
a.closeOnce.Do(func() {
|
||||||
|
close(a.events)
|
||||||
|
err = a.tun.Close()
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *tunDeviceAdapter) BatchSize() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@ func (l *ProxyListener) acceptLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
|
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
|
||||||
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, nil, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
|
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, nil, 0, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if ruleIndex == 0 {
|
if ruleIndex == 0 {
|
||||||
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
||||||
}
|
}
|
||||||
|
autoRedirectFallbackRuleIndex := options.AutoRedirectFallbackRuleIndex
|
||||||
|
if autoRedirectFallbackRuleIndex == 0 {
|
||||||
|
autoRedirectFallbackRuleIndex = tun.DefaultIPRoute2AutoRedirectFallbackRuleIndex
|
||||||
|
}
|
||||||
inputMark := uint32(options.AutoRedirectInputMark)
|
inputMark := uint32(options.AutoRedirectInputMark)
|
||||||
if inputMark == 0 {
|
if inputMark == 0 {
|
||||||
inputMark = tun.DefaultAutoRedirectInputMark
|
inputMark = tun.DefaultAutoRedirectInputMark
|
||||||
@@ -200,35 +204,36 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
inboundOptions: options.InboundOptions,
|
inboundOptions: options.InboundOptions,
|
||||||
tunOptions: tun.Options{
|
tunOptions: tun.Options{
|
||||||
Name: options.InterfaceName,
|
Name: options.InterfaceName,
|
||||||
MTU: tunMTU,
|
MTU: tunMTU,
|
||||||
GSO: enableGSO,
|
GSO: enableGSO,
|
||||||
Inet4Address: inet4Address,
|
Inet4Address: inet4Address,
|
||||||
Inet6Address: inet6Address,
|
Inet6Address: inet6Address,
|
||||||
AutoRoute: options.AutoRoute,
|
AutoRoute: options.AutoRoute,
|
||||||
IPRoute2TableIndex: tableIndex,
|
IPRoute2TableIndex: tableIndex,
|
||||||
IPRoute2RuleIndex: ruleIndex,
|
IPRoute2RuleIndex: ruleIndex,
|
||||||
AutoRedirectInputMark: inputMark,
|
IPRoute2AutoRedirectFallbackRuleIndex: autoRedirectFallbackRuleIndex,
|
||||||
AutoRedirectOutputMark: outputMark,
|
AutoRedirectInputMark: inputMark,
|
||||||
AutoRedirectResetMark: resetMark,
|
AutoRedirectOutputMark: outputMark,
|
||||||
AutoRedirectNFQueue: nfQueue,
|
AutoRedirectResetMark: resetMark,
|
||||||
ExcludeMPTCP: options.ExcludeMPTCP,
|
AutoRedirectNFQueue: nfQueue,
|
||||||
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
|
ExcludeMPTCP: options.ExcludeMPTCP,
|
||||||
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
|
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
|
||||||
StrictRoute: options.StrictRoute,
|
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
|
||||||
IncludeInterface: options.IncludeInterface,
|
StrictRoute: options.StrictRoute,
|
||||||
ExcludeInterface: options.ExcludeInterface,
|
IncludeInterface: options.IncludeInterface,
|
||||||
Inet4RouteAddress: inet4RouteAddress,
|
ExcludeInterface: options.ExcludeInterface,
|
||||||
Inet6RouteAddress: inet6RouteAddress,
|
Inet4RouteAddress: inet4RouteAddress,
|
||||||
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
Inet6RouteAddress: inet6RouteAddress,
|
||||||
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
||||||
IncludeUID: includeUID,
|
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
||||||
ExcludeUID: excludeUID,
|
IncludeUID: includeUID,
|
||||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
ExcludeUID: excludeUID,
|
||||||
IncludePackage: options.IncludePackage,
|
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||||
ExcludePackage: options.ExcludePackage,
|
IncludePackage: options.IncludePackage,
|
||||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
ExcludePackage: options.ExcludePackage,
|
||||||
EXP_MultiPendingPackets: multiPendingPackets,
|
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||||
|
EXP_MultiPendingPackets: multiPendingPackets,
|
||||||
},
|
},
|
||||||
udpTimeout: udpTimeout,
|
udpTimeout: udpTimeout,
|
||||||
stack: options.Stack,
|
stack: options.Stack,
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
if outbound.tlsConfig != nil {
|
||||||
|
outbound.tlsDialer = tls.NewDialer(outboundDialer, outbound.tlsConfig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if options.Transport != nil {
|
if options.Transport != nil {
|
||||||
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
outbound.transport, err = v2ray.NewClientTransport(ctx, outbound.dialer, outbound.serverAddr, common.PtrValueOrDefault(options.Transport), outbound.tlsConfig)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ get_version() {
|
|||||||
get_ldflags() {
|
get_ldflags() {
|
||||||
local version
|
local version
|
||||||
version=$(get_version)
|
version=$(get_version)
|
||||||
echo "-X 'github.com/sagernet/sing-box/constant.Version=${version}' -s -w -buildid= -checklinkname=0"
|
echo "-X 'github.com/sagernet/sing-box/constant.Version=${version}' -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0"
|
||||||
}
|
}
|
||||||
|
|
||||||
build_sing_box() {
|
build_sing_box() {
|
||||||
|
|||||||
151
route/conn.go
151
route/conn.go
@@ -2,7 +2,6 @@ package route
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -102,8 +101,12 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co
|
|||||||
m.connections.Remove(element)
|
m.connections.Remove(element)
|
||||||
})
|
})
|
||||||
var done atomic.Bool
|
var done atomic.Bool
|
||||||
m.preConnectionCopy(ctx, conn, remoteConn, false, &done, onClose)
|
if m.kickWriteHandshake(ctx, conn, remoteConn, false, &done, onClose) {
|
||||||
m.preConnectionCopy(ctx, remoteConn, conn, true, &done, onClose)
|
return
|
||||||
|
}
|
||||||
|
if m.kickWriteHandshake(ctx, remoteConn, conn, true, &done, onClose) {
|
||||||
|
return
|
||||||
|
}
|
||||||
go m.connectionCopy(ctx, conn, remoteConn, false, &done, onClose)
|
go m.connectionCopy(ctx, conn, remoteConn, false, &done, onClose)
|
||||||
go m.connectionCopy(ctx, remoteConn, conn, true, &done, onClose)
|
go m.connectionCopy(ctx, remoteConn, conn, true, &done, onClose)
|
||||||
}
|
}
|
||||||
@@ -226,75 +229,8 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
|||||||
go m.packetConnectionCopy(ctx, destination, conn, true, &done, onClose)
|
go m.packetConnectionCopy(ctx, destination, conn, true, &done, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConnectionManager) preConnectionCopy(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
|
||||||
readHandshake := N.NeedHandshakeForRead(source)
|
|
||||||
writeHandshake := N.NeedHandshakeForWrite(destination)
|
|
||||||
if readHandshake || writeHandshake {
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
err = m.connectionCopyEarlyWrite(source, destination, readHandshake, writeHandshake)
|
|
||||||
if err == nil && N.NeedHandshakeForRead(source) {
|
|
||||||
continue
|
|
||||||
} else if E.IsMulti(err, os.ErrInvalid, context.DeadlineExceeded, io.EOF) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if done.Swap(true) {
|
|
||||||
onClose(err)
|
|
||||||
}
|
|
||||||
common.Close(source, destination)
|
|
||||||
if !direction {
|
|
||||||
m.logger.ErrorContext(ctx, "connection upload handshake: ", err)
|
|
||||||
} else {
|
|
||||||
m.logger.ErrorContext(ctx, "connection download handshake: ", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
||||||
var (
|
_, err := bufio.CopyWithIncreateBuffer(destination, source, bufio.DefaultIncreaseBufferAfter, bufio.DefaultBatchSize)
|
||||||
sourceReader io.Reader = source
|
|
||||||
destinationWriter io.Writer = destination
|
|
||||||
)
|
|
||||||
var readCounters, writeCounters []N.CountFunc
|
|
||||||
for {
|
|
||||||
sourceReader, readCounters = N.UnwrapCountReader(sourceReader, readCounters)
|
|
||||||
destinationWriter, writeCounters = N.UnwrapCountWriter(destinationWriter, writeCounters)
|
|
||||||
if cachedSrc, isCached := sourceReader.(N.CachedReader); isCached {
|
|
||||||
cachedBuffer := cachedSrc.ReadCached()
|
|
||||||
if cachedBuffer != nil {
|
|
||||||
dataLen := cachedBuffer.Len()
|
|
||||||
_, err := destination.Write(cachedBuffer.Bytes())
|
|
||||||
cachedBuffer.Release()
|
|
||||||
if err != nil {
|
|
||||||
if done.Swap(true) {
|
|
||||||
onClose(err)
|
|
||||||
}
|
|
||||||
common.Close(source, destination)
|
|
||||||
if !direction {
|
|
||||||
m.logger.ErrorContext(ctx, "connection upload payload: ", err)
|
|
||||||
} else {
|
|
||||||
m.logger.ErrorContext(ctx, "connection download payload: ", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, counter := range readCounters {
|
|
||||||
counter(int64(dataLen))
|
|
||||||
}
|
|
||||||
for _, counter := range writeCounters {
|
|
||||||
counter(int64(dataLen))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := bufio.CopyWithCounters(destinationWriter, sourceReader, source, readCounters, writeCounters, bufio.DefaultIncreaseBufferAfter, bufio.DefaultBatchSize)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Close(source, destination)
|
common.Close(source, destination)
|
||||||
} else if duplexDst, isDuplex := destination.(N.WriteCloser); isDuplex {
|
} else if duplexDst, isDuplex := destination.(N.WriteCloser); isDuplex {
|
||||||
@@ -328,45 +264,54 @@ func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConnectionManager) connectionCopyEarlyWrite(source net.Conn, destination io.Writer, readHandshake bool, writeHandshake bool) error {
|
func (m *ConnectionManager) kickWriteHandshake(ctx context.Context, source net.Conn, destination net.Conn, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) bool {
|
||||||
payload := buf.NewPacket()
|
if !N.NeedHandshakeForWrite(destination) {
|
||||||
defer payload.Release()
|
return false
|
||||||
err := source.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
|
||||||
if err != nil {
|
|
||||||
if err == os.ErrInvalid {
|
|
||||||
if writeHandshake {
|
|
||||||
return common.Error(destination.Write(nil))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
isTimeout bool
|
cachedBuffer *buf.Buffer
|
||||||
isEOF bool
|
wrotePayload bool
|
||||||
)
|
)
|
||||||
_, err = payload.ReadOnceFrom(source)
|
sourceReader, readCounters := N.UnwrapCountReader(source, nil)
|
||||||
if err != nil {
|
destinationWriter, writeCounters := N.UnwrapCountWriter(destination, nil)
|
||||||
if E.IsTimeout(err) {
|
if cachedReader, ok := sourceReader.(N.CachedReader); ok {
|
||||||
isTimeout = true
|
cachedBuffer = cachedReader.ReadCached()
|
||||||
} else if errors.Is(err, io.EOF) {
|
}
|
||||||
isEOF = true
|
var err error
|
||||||
} else {
|
if cachedBuffer != nil {
|
||||||
return E.Cause(err, "read payload")
|
wrotePayload = true
|
||||||
|
dataLen := cachedBuffer.Len()
|
||||||
|
_, err = destinationWriter.Write(cachedBuffer.Bytes())
|
||||||
|
cachedBuffer.Release()
|
||||||
|
if err == nil {
|
||||||
|
for _, counter := range readCounters {
|
||||||
|
counter(int64(dataLen))
|
||||||
|
}
|
||||||
|
for _, counter := range writeCounters {
|
||||||
|
counter(int64(dataLen))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_ = destination.SetWriteDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
||||||
|
_, err = destinationWriter.Write(nil)
|
||||||
|
_ = destination.SetWriteDeadline(time.Time{})
|
||||||
}
|
}
|
||||||
_ = source.SetReadDeadline(time.Time{})
|
if err == nil {
|
||||||
if !payload.IsEmpty() || writeHandshake {
|
return false
|
||||||
_, err = destination.Write(payload.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "write payload")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if isTimeout {
|
if !wrotePayload && (E.IsMulti(err, os.ErrInvalid, context.DeadlineExceeded, io.EOF) || E.IsTimeout(err)) {
|
||||||
return context.DeadlineExceeded
|
return false
|
||||||
} else if isEOF {
|
|
||||||
return io.EOF
|
|
||||||
}
|
}
|
||||||
return nil
|
if !done.Swap(true) {
|
||||||
|
onClose(err)
|
||||||
|
}
|
||||||
|
common.Close(source, destination)
|
||||||
|
if !direction {
|
||||||
|
m.logger.ErrorContext(ctx, "connection upload handshake: ", err)
|
||||||
|
} else {
|
||||||
|
m.logger.ErrorContext(ctx, "connection download handshake: ", err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConnectionManager) packetConnectionCopy(ctx context.Context, source N.PacketReader, destination N.PacketWriter, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
func (m *ConnectionManager) packetConnectionCopy(ctx context.Context, source N.PacketReader, destination N.PacketWriter, direction bool, done *atomic.Bool, onClose N.CloseHandlerFunc) {
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ func (r *Router) Start(stage adapter.StartStage) error {
|
|||||||
if C.IsAndroid && r.platformInterface != nil {
|
if C.IsAndroid && r.platformInterface != nil {
|
||||||
needFindProcess = true
|
needFindProcess = true
|
||||||
}
|
}
|
||||||
|
r.needFindProcess = needFindProcess
|
||||||
if needFindProcess {
|
if needFindProcess {
|
||||||
if r.platformInterface != nil && r.platformInterface.UsePlatformConnectionOwnerFinder() {
|
if r.platformInterface != nil && r.platformInterface.UsePlatformConnectionOwnerFinder() {
|
||||||
r.processSearcher = newPlatformSearcher(r.platformInterface)
|
r.processSearcher = newPlatformSearcher(r.platformInterface)
|
||||||
@@ -201,6 +202,10 @@ func (r *Router) AppendTracker(tracker adapter.ConnectionTracker) {
|
|||||||
r.trackers = append(r.trackers, tracker)
|
r.trackers = append(r.trackers, tracker)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) NeedFindProcess() bool {
|
||||||
|
return r.needFindProcess
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) ResetNetwork() {
|
func (r *Router) ResetNetwork() {
|
||||||
r.network.ResetNetwork()
|
r.network.ResetNetwork()
|
||||||
r.dns.ResetNetwork()
|
r.dns.ResetNetwork()
|
||||||
|
|||||||
79
test/go.mod
79
test/go.mod
@@ -10,9 +10,9 @@ require (
|
|||||||
github.com/docker/docker v27.3.1+incompatible
|
github.com/docker/docker v27.3.1+incompatible
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/gofrs/uuid/v5 v5.4.0
|
github.com/gofrs/uuid/v5 v5.4.0
|
||||||
github.com/sagernet/quic-go v0.58.0-sing-box-mod.1
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.2
|
||||||
github.com/sagernet/sing v0.8.0-beta.6.0.20251207063731-56fd482ce1c6
|
github.com/sagernet/sing v0.8.0-beta.16
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.6
|
github.com/sagernet/sing-quic v0.6.0-beta.11
|
||||||
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/spyzhov/ajson v0.9.4
|
github.com/spyzhov/ajson v0.9.4
|
||||||
@@ -40,17 +40,17 @@ require (
|
|||||||
github.com/database64128/tfo-go/v2 v2.3.1 // indirect
|
github.com/database64128/tfo-go/v2 v2.3.1 // 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/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
|
|
||||||
github.com/distribution/reference v0.5.0 // indirect
|
github.com/distribution/reference v0.5.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/ebitengine/purego v0.9.1 // indirect
|
github.com/ebitengine/purego v0.9.1 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/florianl/go-nfqueue/v2 v2.0.2 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/gaissmai/bart v0.18.0 // indirect
|
github.com/gaissmai/bart v0.18.0 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.2.3 // indirect
|
github.com/go-chi/chi/v5 v5.2.3 // indirect
|
||||||
github.com/go-chi/render v1.0.3 // indirect
|
github.com/go-chi/render v1.0.3 // indirect
|
||||||
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 // indirect
|
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced // indirect
|
||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
@@ -65,21 +65,19 @@ require (
|
|||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||||
github.com/illarion/gonotify/v3 v3.0.2 // indirect
|
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167 // indirect
|
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167 // indirect
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
||||||
github.com/keybase/go-keychain v0.0.1 // indirect
|
github.com/keybase/go-keychain v0.0.1 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/libdns/acmedns v0.5.0 // indirect
|
||||||
github.com/libdns/alidns v1.0.6-beta.3 // indirect
|
github.com/libdns/alidns v1.0.6-beta.3 // indirect
|
||||||
github.com/libdns/cloudflare v0.2.2 // indirect
|
github.com/libdns/cloudflare v0.2.2 // indirect
|
||||||
github.com/libdns/libdns v1.1.1 // indirect
|
github.com/libdns/libdns v1.1.1 // indirect
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
|
||||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
|
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
|
||||||
github.com/mdlayher/sdnotify v1.0.0 // indirect
|
|
||||||
github.com/mdlayher/socket v0.5.1 // indirect
|
github.com/mdlayher/socket v0.5.1 // indirect
|
||||||
github.com/metacubex/utls v1.8.3 // indirect
|
github.com/metacubex/utls v1.8.4 // indirect
|
||||||
github.com/mholt/acmez/v3 v3.1.4 // indirect
|
github.com/mholt/acmez/v3 v3.1.4 // indirect
|
||||||
github.com/miekg/dns v1.1.69 // indirect
|
github.com/miekg/dns v1.1.69 // indirect
|
||||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||||
@@ -88,8 +86,9 @@ require (
|
|||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/openai/openai-go/v3 v3.15.0 // indirect
|
github.com/openai/openai-go/v3 v3.15.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
|
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // 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
|
||||||
@@ -97,40 +96,40 @@ require (
|
|||||||
github.com/safchain/ethtool v0.3.0 // indirect
|
github.com/safchain/ethtool v0.3.0 // indirect
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
|
||||||
github.com/sagernet/cors v1.2.1 // indirect
|
github.com/sagernet/cors v1.2.1 // indirect
|
||||||
github.com/sagernet/cronet-go v0.0.0-20251220122645-b05b5c41614a // indirect
|
github.com/sagernet/cronet-go v0.0.0-20260117110918-dc1cda1fe287 // indirect
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251220122645-b05b5c41614a // indirect
|
github.com/sagernet/cronet-go/all v0.0.0-20260117110918-dc1cda1fe287 // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251220122226-25b6d00c5b7e // indirect
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260117110516-f21660bef13f // indirect
|
||||||
github.com/sagernet/fswatch v0.1.1 // indirect
|
github.com/sagernet/fswatch v0.1.1 // indirect
|
||||||
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237 // indirect
|
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237 // 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/sagernet/sing-mux v0.3.3 // indirect
|
github.com/sagernet/sing-mux v0.3.4 // indirect
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20251201004738-e9e3fbf0c15e // indirect
|
github.com/sagernet/sing-tun v0.8.0-beta.17 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 // indirect
|
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 // indirect
|
||||||
github.com/sagernet/smux v1.5.34-mod.2 // indirect
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1 // indirect
|
||||||
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.4 // indirect
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6 // indirect
|
||||||
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288 // indirect
|
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288 // indirect
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
|
||||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
|
||||||
@@ -140,7 +139,6 @@ require (
|
|||||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
|
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
|
||||||
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/tidwall/gjson v1.18.0 // indirect
|
github.com/tidwall/gjson v1.18.0 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
@@ -163,6 +161,7 @@ require (
|
|||||||
golang.org/x/crypto v0.46.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
|
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
|
||||||
golang.org/x/mod v0.31.0 // indirect
|
golang.org/x/mod v0.31.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.32.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/term v0.38.0 // indirect
|
golang.org/x/term v0.38.0 // indirect
|
||||||
|
|||||||
168
test/go.sum
168
test/go.sum
@@ -37,13 +37,10 @@ github.com/database64128/netx-go v0.1.1/go.mod h1:LNlYVipaYkQArRFDNNJ02VkNV+My9A
|
|||||||
github.com/database64128/tfo-go/v2 v2.3.1 h1:EGE+ELd5/AQ0X6YBlQ9RgKs8+kciNhgN3d8lRvfEJQw=
|
github.com/database64128/tfo-go/v2 v2.3.1 h1:EGE+ELd5/AQ0X6YBlQ9RgKs8+kciNhgN3d8lRvfEJQw=
|
||||||
github.com/database64128/tfo-go/v2 v2.3.1/go.mod h1:k9wcpg/8i5zenspBkc9jUEYehpZZccBnCElzOJB++bU=
|
github.com/database64128/tfo-go/v2 v2.3.1/go.mod h1:k9wcpg/8i5zenspBkc9jUEYehpZZccBnCElzOJB++bU=
|
||||||
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.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
||||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
|
|
||||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
|
|
||||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||||
@@ -56,6 +53,8 @@ github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s
|
|||||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/florianl/go-nfqueue/v2 v2.0.2 h1:FL5lQTeetgpCvac1TRwSfgaXUn0YSO7WzGvWNIp3JPE=
|
||||||
|
github.com/florianl/go-nfqueue/v2 v2.0.2/go.mod h1:VA09+iPOT43OMoCKNfXHyzujQUty2xmzyCRkBOlmabc=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||||
@@ -68,8 +67,8 @@ github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
|||||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 h1:F8d1AJ6M9UQCavhwmO6ZsrYLfG8zVFWfEfMS2MXPkSY=
|
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced h1:Q311OHjMh/u5E2TITc++WlTP5We0xNseRMkHDyvhW7I=
|
||||||
github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
github.com/go-json-experiment/json v0.0.0-20250813024750-ebf49471dced/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
@@ -105,8 +104,6 @@ github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8
|
|||||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||||
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
||||||
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||||
github.com/illarion/gonotify/v3 v3.0.2 h1:O7S6vcopHexutmpObkeWsnzMJt/r1hONIEogeVNmJMk=
|
|
||||||
github.com/illarion/gonotify/v3 v3.0.2/go.mod h1:HWGPdPe817GfvY3w7cx6zkbzNZfi3QjcBm/wgVvEL1U=
|
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167 h1:MEufgJohwIjFi2n3eJv4c/8UdRLQVUwPwSWQPoER+eU=
|
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167 h1:MEufgJohwIjFi2n3eJv4c/8UdRLQVUwPwSWQPoER+eU=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167/go.mod h1:qfvBmyDNp+/liLEYWRvqny/PEz9hGe2Dz833eXILSmo=
|
github.com/insomniacslk/dhcp v0.0.0-20251020182700-175e84fbb167/go.mod h1:qfvBmyDNp+/liLEYWRvqny/PEz9hGe2Dz833eXILSmo=
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
||||||
@@ -115,14 +112,16 @@ github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRt
|
|||||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/libdns/acmedns v0.5.0 h1:5pRtmUj4Lb/QkNJSl1xgOGBUJTWW7RjpNaIhjpDXjPE=
|
||||||
|
github.com/libdns/acmedns v0.5.0/go.mod h1:X7UAFP1Ep9NpTwWpVlrZzJLR7epynAy0wrIxSPFgKjQ=
|
||||||
github.com/libdns/alidns v1.0.6-beta.3 h1:KAmb7FQ1tRzKsaAUGa7ZpGKAMRANwg7+1c7tUbSELq8=
|
github.com/libdns/alidns v1.0.6-beta.3 h1:KAmb7FQ1tRzKsaAUGa7ZpGKAMRANwg7+1c7tUbSELq8=
|
||||||
github.com/libdns/alidns v1.0.6-beta.3/go.mod h1:RECwyQ88e9VqQVtSrvX76o1ux3gQUKGzMgxICi+u7Ec=
|
github.com/libdns/alidns v1.0.6-beta.3/go.mod h1:RECwyQ88e9VqQVtSrvX76o1ux3gQUKGzMgxICi+u7Ec=
|
||||||
github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI=
|
github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI=
|
||||||
@@ -131,16 +130,12 @@ github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
|
|||||||
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
|
||||||
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
|
||||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg=
|
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0tpKMbdCXCIfBclan4oHk1Jb+Hrejirg=
|
||||||
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
|
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
|
||||||
github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c=
|
|
||||||
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.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
|
||||||
github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
||||||
github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ=
|
github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ=
|
||||||
github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||||
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
|
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
|
||||||
@@ -159,10 +154,12 @@ github.com/openai/openai-go/v3 v3.15.0 h1:hk99rM7YPz+M99/5B/zOQcVwFRLLMdprVGx1va
|
|||||||
github.com/openai/openai-go/v3 v3.15.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo=
|
github.com/openai/openai-go/v3 v3.15.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||||
|
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@@ -180,54 +177,54 @@ 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-20251220122645-b05b5c41614a h1:EdIrRa0yH3ZaLOfX95RYYiN0etpX3b9+jcsW/D8jCyQ=
|
github.com/sagernet/cronet-go v0.0.0-20260117110918-dc1cda1fe287 h1:0BYNmr0ptjsII948U0oBFmrbo4qEaCFcrE2JPRg3Zlk=
|
||||||
github.com/sagernet/cronet-go v0.0.0-20251220122645-b05b5c41614a/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw=
|
github.com/sagernet/cronet-go v0.0.0-20260117110918-dc1cda1fe287/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw=
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251220122645-b05b5c41614a h1:GU/oBPVjyrCVb2l0SI5qtF6aUlLsnE4u4ehXbS/NF+0=
|
github.com/sagernet/cronet-go/all v0.0.0-20260117110918-dc1cda1fe287 h1:ghxhYSBQpzkakqWqJDvXr/Zmxe0WjTjKuALEGbjGiGY=
|
||||||
github.com/sagernet/cronet-go/all v0.0.0-20251220122645-b05b5c41614a/go.mod h1:4MT0juyKK0lIVwa6+6xLbRMFJBUIqRZOx70iMvq5vf0=
|
github.com/sagernet/cronet-go/all v0.0.0-20260117110918-dc1cda1fe287/go.mod h1:M+4ZjPhLJXIvoxcQsbDofmc19Wrig59hZ+hLvj6S3To=
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251220122226-25b6d00c5b7e h1:EAcY0ZK8DWTNUdVCKdBQfXRsfGsohsh2ae9jIjlri2o=
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260117110516-f21660bef13f h1:8jZbZ4KBTdcXDFLwUBNQt5Xci6ZuAKh255S8TwuBCaM=
|
||||||
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260117110516-f21660bef13f/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251220122226-25b6d00c5b7e h1:9luoNSy9Ej8rC/nZejzrP0uxX+BxiNxjLJ7XPYlApQg=
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260117110516-f21660bef13f h1:tG0hCx+0u5zca7qQ7AMkcv4DCrBG/DKW1ggs/P+BRRI=
|
||||||
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251220122226-25b6d00c5b7e h1:CSWZTuMlkO/98RSQpqC1EHtc9eSBzVmvMdPTnaFSDCM=
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260117110516-f21660bef13f h1:ZXp5hKJIA7iJ52ZShJCKMQEPLpp/7dDIVZmPGV9Il40=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260117110516-f21660bef13f/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251220122226-25b6d00c5b7e h1:vkf3GDA11NtGm6XQHyP4tgWK3GD426ObmshdKdxGMhA=
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260117110516-f21660bef13f h1:gL7H8HS8s38adz4/HZtRHh79qMwsbLTRRPz4GQ9LcWI=
|
||||||
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251220122226-25b6d00c5b7e h1:pNyNjDC5XPC6+2yNqiDA8QL8IYSsBJpaSLwPi/jY4JQ=
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260117110516-f21660bef13f h1:Dchgc0pAY5Jwb5lzUlE+1nhHIzqLx+YOurXLHgvWd/0=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251220122226-25b6d00c5b7e h1:2yDmTzppvxWuwxo4oDjxRzjlXBzZ6O3OCPWYeQcJRrw=
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260117110516-f21660bef13f h1:+MOLSQoduuKDxF410i1LcSPaQGaiP0eZb0INvMlmjM4=
|
||||||
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251220122226-25b6d00c5b7e h1:+lyrRlxlKBYT/3LcYMaFHilUnRlKn3OQrImlWCey+qM=
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260117110516-f21660bef13f h1:lIZna05Vn6n8k21p8OpSUnhwGm+E57PrMjiI4ZUfMSg=
|
||||||
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
|
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251220122226-25b6d00c5b7e h1:dBeeUaCPG0Y26FfCPO1o3v72yw2pSjqopryAW0uV354=
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260117110516-f21660bef13f h1:B2aFQ5CRHI20t8YsEizvtguS5W2QfK7D5XV/NzTIxPE=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251220122226-25b6d00c5b7e h1:JkGEqkfeglehN9tu+a0gHTq9Dmm+pY2wtjY+NiUdjgw=
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260117110516-f21660bef13f h1:qpSwJ1rFGYCfJDenNCZoWYjoG7N+xEa6ke+E7/JO1i4=
|
||||||
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
|
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251220122226-25b6d00c5b7e h1:rd/AbuuEMCHcA6ib5JBkQmWvonnoGwVc0/P0sHcWfAQ=
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260117110516-f21660bef13f h1:cx7Ipg0tSvTDjS4maMEYz4vuzz93BMPAysmZ1YLrz80=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260117110516-f21660bef13f/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251220122226-25b6d00c5b7e h1:4XcwU6KOCkVOAr2EKKY8geYm7ctKCLlb1SsmFWMMUzE=
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260117110516-f21660bef13f h1:4jOHuUiBxD8pJEpBBVQfJqyLmxjpd3t4MLRzU7YLFyg=
|
||||||
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251220122226-25b6d00c5b7e h1:fYDhPtxvMtZxBUM0k6GcaxbynLxMG2iTmQL3XcLtjS8=
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260117110516-f21660bef13f h1:OpXBa2WlRU+Mam9oRe9Nn4/zf7gQ+qiBTNK8A5RwbfQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251220122226-25b6d00c5b7e h1:F5Vzd50H/AaoSHvt81yXZeUwDRKTcFjO/+KVW6VqbUs=
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260117110516-f21660bef13f h1:nJpGFi+6hI85tl4zoyNFEnFEQ5+xEV5gyvsUoMvd8g0=
|
||||||
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251220122226-25b6d00c5b7e h1:Ok/Eh8ajcvxlu7FAWk+lHAPLcSRDKrKkgq30o6gCR6M=
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260117110516-f21660bef13f h1:SEy2rpmgOJgrqcEryJI/RSnqUWIsEsp0cfYoA8y21jc=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260117110516-f21660bef13f/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251220122226-25b6d00c5b7e h1:JoohVAfcwE9HjpwAaTULtEOFZ1Mz0Zz5K4pOcrRjqwo=
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260117110516-f21660bef13f h1:EW2TuFMLm0iBGqRZtuGwIZdeYmDtDsDmRcRRJQOMxUo=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251220122226-25b6d00c5b7e h1:ULxfoxcNRA96QDDRLmObH4adbeUGtudH/2HlL4TgaNY=
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260117110516-f21660bef13f h1:3U5woxrNCkzfv1+UX+mVoWh1228AE1qAiMG02F9oFbY=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251220122226-25b6d00c5b7e h1:NnhScXJyx4Fg+pV/WtkC1mD6+VR7D/Q+PMU4xGRvdRQ=
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260117110516-f21660bef13f h1:YwFTfuWG3mmctroeDYtFZ6LHjGsedVO+5wInYbbUuUY=
|
||||||
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260117110516-f21660bef13f/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251220122226-25b6d00c5b7e h1:INcIfjzUl0qgtZkt8OcVV537GoApcBsdegDl8yNJEdQ=
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260117110516-f21660bef13f h1:r4V0ddPCRLgGu0VdgR3aUsO9NjpmyjAf+h+3oTD9D6E=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
|
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251220122226-25b6d00c5b7e h1:jrialJ4U7BW9xyGI/JdUiigZ9hgyF1EayFKCH4pZxdw=
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260117110516-f21660bef13f h1:B8yf4gFvEYUnwWmtVK9sdwUsflYZ387MhYmlOP2ohFQ=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
|
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251220122226-25b6d00c5b7e h1:XefHhnALVHU4ZgICNq6ZJl78bWDQO4nHwoWn+Ar7e0g=
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260117110516-f21660bef13f h1:9YyaMg4rO1/jIgrxmNb0LKH+X7frSYWfX2pFgW5JUVM=
|
||||||
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
|
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260117110516-f21660bef13f/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251220122226-25b6d00c5b7e h1:djShWO5vN0CamyboNDyNRDxL0/9oMtKjCDesqolIAsg=
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260117110516-f21660bef13f h1:B0fnGu0sh9yT/9JDN5u/GqThGoOzNN/daOAuGWFLXEk=
|
||||||
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260117110516-f21660bef13f/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251220122226-25b6d00c5b7e h1:AgKqyS5zfRgDc8uprxUh+XCwwzJCj31KAFPjzyLRWs0=
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260117110516-f21660bef13f h1:lxPcIXKSSI5JDhc7rx/6yufISWM4vtBS2FY9PavWQTs=
|
||||||
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251220122226-25b6d00c5b7e/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
|
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260117110516-f21660bef13f/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/gvisor v0.0.0-20250822052253-5558536cf237 h1:SUPFNB+vSP4RBPrSEgNII+HkfqC8hKMpYLodom4o4EU=
|
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237 h1:SUPFNB+vSP4RBPrSEgNII+HkfqC8hKMpYLodom4o4EU=
|
||||||
@@ -236,27 +233,28 @@ 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.58.0-sing-box-mod.1 h1:E9yZrU0ZxSiW5RrGUnFZeI02EIMdAAv0RxdoxXCqZyk=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.2 h1:hJUL+HtxEOjxsa0CsucbBVqI/AMS4k52NwNU637zmdw=
|
||||||
github.com/sagernet/quic-go v0.58.0-sing-box-mod.1/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.2/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
||||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.8.0-beta.16 h1:Fe+6E9VHYky9Mx4cf0ugbZPWDcXRflpAu7JQ5bWXvaA=
|
||||||
github.com/sagernet/sing v0.8.0-beta.6.0.20251207063731-56fd482ce1c6 h1:EYaDzllFzNYnzQ9xH/ieSAXct4wQ8pD45kgNMo7RPZc=
|
github.com/sagernet/sing v0.8.0-beta.16/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing v0.8.0-beta.6.0.20251207063731-56fd482ce1c6/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
|
||||||
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
|
github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
||||||
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-quic v0.6.0-beta.11 h1:eUusxITKKRedhWC2ScUYFUvD96h/QfbKLaS3N6/7in4=
|
||||||
|
github.com/sagernet/sing-quic v0.6.0-beta.11/go.mod h1:K5bWvITOm4vE10fwLfrWpw27bCoVJ+tfQ79tOWg+Ko8=
|
||||||
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.0.20251201004738-e9e3fbf0c15e h1:ZEv+9vy7vC1vbr3LfwZGx3JAOkl/w4+hnGamHw4W36M=
|
github.com/sagernet/sing-tun v0.8.0-beta.17 h1:6DdbNXeTFYj8Tb4FCh8Mp2boA3rVY6VNqzTOObj7Xis=
|
||||||
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20251201004738-e9e3fbf0c15e/go.mod h1:eWETzl4AwaxGKiZTpDIDVJLTBz9cfIdoZwaZY1jlSjg=
|
github.com/sagernet/sing-tun v0.8.0-beta.17/go.mod h1:+HAK/y9GZljdT0KYKMYDR8MjjqnqDDQZYp5ZZQoRzS8=
|
||||||
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.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
|
||||||
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.4 h1:Ceg+9Ug+qAFgEchGodlHmMOY2h7KktQQDAyuoIsPbos=
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6 h1:eYz/OpMqWCvO2++iw3dEuzrlfC2xv78GdlGvprIM6O8=
|
||||||
github.com/sagernet/tailscale v1.86.5-sing-box-1.13-mod.4/go.mod h1:YdN/avjce8sqPFLT9E1uEh8gPewNSnC41U4ZhBJ+ACw=
|
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6/go.mod h1:m87GAn4UcesHQF3leaPFEINZETO5za1LGn1GJdNDgNc=
|
||||||
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=
|
||||||
@@ -266,14 +264,7 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
|
|||||||
github.com/spyzhov/ajson v0.9.4 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84=
|
github.com/spyzhov/ajson v0.9.4 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84=
|
||||||
github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
|
github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
||||||
@@ -290,8 +281,6 @@ github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+y
|
|||||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
|
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14=
|
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14=
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da h1:jVRUZPRs9sqyKlYHHzHjAqKN+6e/Vog6NpHYeNPJqOw=
|
|
||||||
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.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
@@ -373,6 +362,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
|
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||||
|
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -388,7 +379,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
@@ -433,8 +423,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633 h1:2gap+Kh/3F47cO6hAu3idFvsJ0ue6TRcEi2IUkv/F8k=
|
|
||||||
gvisor.dev/gvisor v0.0.0-20250205023644-9414b50a5633/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM=
|
|
||||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build with_naive_outbound
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
134
test/socks_test.go
Normal file
134
test/socks_test.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSOCKSUDPTimeout(t *testing.T) {
|
||||||
|
const testTimeout = 2 * time.Second
|
||||||
|
udpTimeout := option.UDPTimeoutCompat(testTimeout)
|
||||||
|
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeSOCKS,
|
||||||
|
Tag: "socks-in",
|
||||||
|
Options: &option.SocksInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
UDPTimeout: udpTimeout,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
testUDPSessionIdleTimeout(t, clientPort, testPort, testTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMixedUDPTimeout(t *testing.T) {
|
||||||
|
const testTimeout = 2 * time.Second
|
||||||
|
udpTimeout := option.UDPTimeoutCompat(testTimeout)
|
||||||
|
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeMixed,
|
||||||
|
Tag: "mixed-in",
|
||||||
|
Options: &option.HTTPMixedInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: common.Ptr(badoption.Addr(netip.IPv4Unspecified())),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
UDPTimeout: udpTimeout,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
testUDPSessionIdleTimeout(t, clientPort, testPort, testTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUDPSessionIdleTimeout(t *testing.T, proxyPort uint16, echoPort uint16, expectedTimeout time.Duration) {
|
||||||
|
echoServer, err := listenPacket("udp", ":"+F.ToString(echoPort))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer echoServer.Close()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
buffer := make([]byte, 1024)
|
||||||
|
for {
|
||||||
|
n, address, err := echoServer.ReadFrom(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = echoServer.WriteTo(buffer[:n], address)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", proxyPort), socks.Version5, "", "")
|
||||||
|
|
||||||
|
packetConn, err := dialer.ListenPacket(context.Background(), M.ParseSocksaddrHostPort("127.0.0.1", echoPort))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer packetConn.Close()
|
||||||
|
|
||||||
|
remoteAddress := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: int(echoPort)}
|
||||||
|
|
||||||
|
_, err = packetConn.WriteTo([]byte("hello"), remoteAddress)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buffer := make([]byte, 1024)
|
||||||
|
packetConn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||||
|
n, _, err := packetConn.ReadFrom(buffer)
|
||||||
|
require.NoError(t, err, "failed to receive echo response")
|
||||||
|
require.Equal(t, "hello", string(buffer[:n]))
|
||||||
|
t.Log("UDP echo successful, session established")
|
||||||
|
|
||||||
|
packetConn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
waitTime := expectedTimeout + time.Second
|
||||||
|
t.Logf("Waiting %v for UDP session to timeout...", waitTime)
|
||||||
|
time.Sleep(waitTime)
|
||||||
|
|
||||||
|
_, err = packetConn.WriteTo([]byte("after-timeout"), remoteAddress)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Write after timeout correctly failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
packetConn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||||
|
n, _, err = packetConn.ReadFrom(buffer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Read after timeout correctly failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatalf("UDP session should have timed out after %v, but received response: %s",
|
||||||
|
expectedTimeout, string(buffer[:n]))
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ type GunServiceServer interface {
|
|||||||
type UnimplementedGunServiceServer struct{}
|
type UnimplementedGunServiceServer struct{}
|
||||||
|
|
||||||
func (UnimplementedGunServiceServer) Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error {
|
func (UnimplementedGunServiceServer) Tun(grpc.BidiStreamingServer[Hunk, Hunk]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method Tun not implemented")
|
return status.Error(codes.Unimplemented, "method Tun not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGunServiceServer) mustEmbedUnimplementedGunServiceServer() {}
|
func (UnimplementedGunServiceServer) mustEmbedUnimplementedGunServiceServer() {}
|
||||||
func (UnimplementedGunServiceServer) testEmbeddedByValue() {}
|
func (UnimplementedGunServiceServer) testEmbeddedByValue() {}
|
||||||
@@ -74,7 +74,7 @@ type UnsafeGunServiceServer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RegisterGunServiceServer(s grpc.ServiceRegistrar, srv GunServiceServer) {
|
func RegisterGunServiceServer(s grpc.ServiceRegistrar, srv GunServiceServer) {
|
||||||
// If the following call pancis, it indicates UnimplementedGunServiceServer was
|
// If the following call panics, it indicates UnimplementedGunServiceServer was
|
||||||
// embedded by pointer and is nil. This will cause panics if an
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
// unimplemented method is ever invoked, so we test this at initialization
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
// time to prevent it from happening at runtime later due to I/O.
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
|||||||
Reference in New Issue
Block a user