mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79d3649a8b | ||
|
|
e483c909b4 | ||
|
|
d9579c26ee | ||
|
|
48d3021b2c | ||
|
|
ce0fcd5c8b | ||
|
|
d9d0a2373a | ||
|
|
0c754505f7 | ||
|
|
606ff668da | ||
|
|
f43703801b | ||
|
|
1ed8f3a8d3 | ||
|
|
60fc913dc3 | ||
|
|
be8ee370ac | ||
|
|
0a9bf97438 | ||
|
|
74de437bfb | ||
|
|
c385e7c137 | ||
|
|
01291d16e0 | ||
|
|
11a448b52d | ||
|
|
22bda86bbf | ||
|
|
04cc343b2e | ||
|
|
093e539d3d | ||
|
|
5821b974bd | ||
|
|
8ce40f77b4 | ||
|
|
b45cb0763e | ||
|
|
48d102a0ab | ||
|
|
6a5943f4ce | ||
|
|
c9f9d9ee1c | ||
|
|
f3bf440c91 | ||
|
|
292fcde876 | ||
|
|
53e227a318 | ||
|
|
65cb225a2c | ||
|
|
7819f13489 | ||
|
|
f2780d0713 | ||
|
|
72239dcbd3 | ||
|
|
daf38a84e1 | ||
|
|
f2ddc5883b | ||
|
|
0437ac512b | ||
|
|
d297ad4c56 | ||
|
|
6823670f3d | ||
|
|
4a611eddf4 | ||
|
|
f12a294fb7 | ||
|
|
040a188c66 | ||
|
|
7ed10b35d0 | ||
|
|
afd341adfd | ||
|
|
7d26bac5ac | ||
|
|
63d8f6dc1c | ||
|
|
15cc3b85eb | ||
|
|
5a1c59ca88 | ||
|
|
7686503df8 | ||
|
|
96a8de9548 |
30
.fpm_openwrt
30
.fpm_openwrt
@@ -1,30 +0,0 @@
|
|||||||
-s dir
|
|
||||||
--name sing-box
|
|
||||||
--category net
|
|
||||||
--license GPL-3.0-or-later
|
|
||||||
--description "The universal proxy platform."
|
|
||||||
--url "https://sing-box.sagernet.org/"
|
|
||||||
--maintainer "nekohasekai <contact-git@sekai.icu>"
|
|
||||||
--no-deb-generate-changes
|
|
||||||
|
|
||||||
--config-files /etc/config/sing-box
|
|
||||||
--config-files /etc/sing-box/config.json
|
|
||||||
|
|
||||||
--depends ca-bundle
|
|
||||||
--depends kmod-inet-diag
|
|
||||||
--depends kmod-tun
|
|
||||||
--depends firewall4
|
|
||||||
|
|
||||||
--before-remove release/config/openwrt.prerm
|
|
||||||
|
|
||||||
release/config/config.json=/etc/sing-box/config.json
|
|
||||||
|
|
||||||
release/config/openwrt.conf=/etc/config/sing-box
|
|
||||||
release/config/openwrt.init=/etc/init.d/sing-box
|
|
||||||
release/config/openwrt.keep=/lib/upgrade/keep.d/sing-box
|
|
||||||
|
|
||||||
release/completions/sing-box.bash=/usr/share/bash-completion/completions/sing-box.bash
|
|
||||||
release/completions/sing-box.fish=/usr/share/fish/vendor_completions.d/sing-box.fish
|
|
||||||
release/completions/sing-box.zsh=/usr/share/zsh/site-functions/_sing-box
|
|
||||||
|
|
||||||
LICENSE=/usr/share/licenses/sing-box/LICENSE
|
|
||||||
21
.fpm_systemd
21
.fpm_systemd
@@ -1,21 +0,0 @@
|
|||||||
-s dir
|
|
||||||
--name sing-box
|
|
||||||
--category net
|
|
||||||
--license GPL-3.0-or-later
|
|
||||||
--description "The universal proxy platform."
|
|
||||||
--url "https://sing-box.sagernet.org/"
|
|
||||||
--maintainer "nekohasekai <contact-git@sekai.icu>"
|
|
||||||
--deb-field "Bug: https://github.com/SagerNet/sing-box/issues"
|
|
||||||
--no-deb-generate-changes
|
|
||||||
--config-files /etc/sing-box/config.json
|
|
||||||
|
|
||||||
release/config/config.json=/etc/sing-box/config.json
|
|
||||||
|
|
||||||
release/config/sing-box.service=/usr/lib/systemd/system/sing-box.service
|
|
||||||
release/config/sing-box@.service=/usr/lib/systemd/system/sing-box@.service
|
|
||||||
|
|
||||||
release/completions/sing-box.bash=/usr/share/bash-completion/completions/sing-box.bash
|
|
||||||
release/completions/sing-box.fish=/usr/share/fish/vendor_completions.d/sing-box.fish
|
|
||||||
release/completions/sing-box.zsh=/usr/share/zsh/site-functions/_sing-box
|
|
||||||
|
|
||||||
LICENSE=/usr/share/licenses/sing-box/LICENSE
|
|
||||||
28
.github/deb2ipk.sh
vendored
28
.github/deb2ipk.sh
vendored
@@ -1,28 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# mod from https://gist.github.com/pldubouilh/c5703052986bfdd404005951dee54683
|
|
||||||
|
|
||||||
set -e -o pipefail
|
|
||||||
|
|
||||||
PROJECT=$(dirname "$0")/../..
|
|
||||||
TMP_PATH=`mktemp -d`
|
|
||||||
cp $2 $TMP_PATH
|
|
||||||
pushd $TMP_PATH
|
|
||||||
|
|
||||||
DEB_NAME=`ls *.deb`
|
|
||||||
ar x $DEB_NAME
|
|
||||||
|
|
||||||
mkdir control
|
|
||||||
pushd control
|
|
||||||
tar xf ../control.tar.gz
|
|
||||||
rm md5sums
|
|
||||||
sed "s/Architecture:\\ \w*/Architecture:\\ $1/g" ./control -i
|
|
||||||
cat control
|
|
||||||
tar czf ../control.tar.gz ./*
|
|
||||||
popd
|
|
||||||
|
|
||||||
DEB_NAME=${DEB_NAME%.deb}
|
|
||||||
tar czf $DEB_NAME.ipk control.tar.gz data.tar.gz debian-binary
|
|
||||||
popd
|
|
||||||
|
|
||||||
cp $TMP_PATH/$DEB_NAME.ipk $3
|
|
||||||
rm -r $TMP_PATH
|
|
||||||
379
.github/workflows/build.yml
vendored
379
.github/workflows/build.yml
vendored
@@ -46,16 +46,16 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
echo "version=${{ inputs.version }}"
|
echo "version=${{ inputs.version }}"
|
||||||
echo "version=${{ inputs.version }}" >> "$GITHUB_ENV"
|
echo "version=${{ inputs.version }}" >> "$GITHUB_ENV"
|
||||||
- name: Calculate version
|
- name: Calculate version
|
||||||
if: github.event_name != 'workflow_dispatch'
|
if: github.event_name != 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
go run -v ./cmd/internal/read_tag --ci --nightly
|
go run -v ./cmd/internal/read_tag --nightly
|
||||||
- name: Set outputs
|
- name: Set outputs
|
||||||
id: outputs
|
id: outputs
|
||||||
run: |-
|
run: |-
|
||||||
@@ -69,208 +69,141 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
- name: linux_386
|
||||||
- { os: linux, arch: "386", go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
goos: linux
|
||||||
- { os: linux, arch: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
|
goarch: 386
|
||||||
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
- name: linux_amd64
|
||||||
- { os: linux, arch: arm, goarm: "5", openwrt: "arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" }
|
goos: linux
|
||||||
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl, openwrt: "arm_arm1176jzf-s_vfp" }
|
goarch: amd64
|
||||||
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
- name: linux_arm64
|
||||||
- { os: linux, arch: mips, gomips: softfloat, openwrt: "mips_24kc mips_4kec mips_mips32" }
|
goos: linux
|
||||||
- { os: linux, arch: mipsle, gomips: hardfloat, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc_24kf" }
|
goarch: arm64
|
||||||
- { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
|
- name: linux_arm
|
||||||
- { os: linux, arch: mips64, gomips: softfloat, openwrt: "mips64_mips64r2 mips64_octeonplus" }
|
goos: linux
|
||||||
- { os: linux, arch: mips64le, gomips: hardfloat, debian: mips64el, rpm: mips64el }
|
goarch: arm
|
||||||
- { os: linux, arch: mips64le, gomips: softfloat, openwrt: "mips64el_mips64r2" }
|
goarm: 6
|
||||||
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
- name: linux_arm_v7
|
||||||
- { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le }
|
goos: linux
|
||||||
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
|
goarch: arm
|
||||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
goarm: 7
|
||||||
|
- name: linux_s390x
|
||||||
- { os: windows, arch: amd64 }
|
goos: linux
|
||||||
- { os: windows, arch: amd64, legacy_go: true }
|
goarch: s390x
|
||||||
- { os: windows, arch: "386" }
|
- name: linux_riscv64
|
||||||
- { os: windows, arch: "386", legacy_go: true }
|
goos: linux
|
||||||
- { os: windows, arch: arm64 }
|
goarch: riscv64
|
||||||
|
- name: linux_mips64le
|
||||||
- { os: darwin, arch: amd64 }
|
goos: linux
|
||||||
- { os: darwin, arch: amd64, legacy_go: true }
|
goarch: mips64le
|
||||||
- { os: darwin, arch: arm64 }
|
- name: windows_amd64
|
||||||
|
goos: windows
|
||||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
goarch: amd64
|
||||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
require_legacy_go: true
|
||||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
|
- name: windows_386
|
||||||
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
goos: windows
|
||||||
|
goarch: 386
|
||||||
|
require_legacy_go: true
|
||||||
|
- name: windows_arm64
|
||||||
|
goos: windows
|
||||||
|
goarch: arm64
|
||||||
|
- name: darwin_arm64
|
||||||
|
goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
- name: darwin_amd64
|
||||||
|
goos: darwin
|
||||||
|
goarch: amd64
|
||||||
|
require_legacy_go: true
|
||||||
|
- name: android_arm64
|
||||||
|
goos: android
|
||||||
|
goarch: arm64
|
||||||
|
- name: android_arm
|
||||||
|
goos: android
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
- name: android_amd64
|
||||||
|
goos: android
|
||||||
|
goarch: amd64
|
||||||
|
- name: android_386
|
||||||
|
goos: android
|
||||||
|
goarch: 386
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
if: matrix.legacy_go
|
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.20
|
go-version: ^1.23
|
||||||
- name: Setup Go
|
- name: Cache legacy Go
|
||||||
if: ${{ ! matrix.legacy_go }}
|
if: matrix.require_legacy_go
|
||||||
uses: actions/setup-go@v5
|
id: cache-legacy-go
|
||||||
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
path: |
|
||||||
|
~/go/go1.20.14
|
||||||
|
key: go120
|
||||||
|
- name: Setup legacy Go
|
||||||
|
if: matrix.require_legacy_go == 'true' && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||||
|
run: |-
|
||||||
|
wget https://dl.google.com/go/go1.20.14.linux-amd64.tar.gz
|
||||||
|
tar -xzf go1.20.14.linux-amd64.tar.gz
|
||||||
|
mv go $HOME/go/go1.20.14
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
if: matrix.os == 'android'
|
if: matrix.goos == 'android'
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r28
|
ndk-version: r28-beta2
|
||||||
local-cache: true
|
local-cache: true
|
||||||
|
- name: Setup Goreleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser-pro
|
||||||
|
version: latest
|
||||||
|
install-only: true
|
||||||
|
- name: Extract signing key
|
||||||
|
run: |-
|
||||||
|
mkdir -p $HOME/.gnupg
|
||||||
|
cat > $HOME/.gnupg/sagernet.key <<EOF
|
||||||
|
${{ secrets.GPG_KEY }}
|
||||||
|
EOF
|
||||||
|
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
run: |-
|
run: |-
|
||||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
||||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
git tag v${{ needs.calculate_version.outputs.version }} -f
|
||||||
- name: Set build tags
|
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api'
|
|
||||||
if [ ! '${{ matrix.legacy_go }}' = 'true' ]; then
|
|
||||||
TAGS="${TAGS},with_ech"
|
|
||||||
fi
|
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
|
||||||
- name: Build
|
- name: Build
|
||||||
if: matrix.os != 'android'
|
if: matrix.goos != 'android'
|
||||||
run: |
|
run: |-
|
||||||
set -xeuo pipefail
|
goreleaser release --clean --split
|
||||||
mkdir -p dist
|
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
|
||||||
./cmd/sing-box
|
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
GOOS: ${{ matrix.goos }}
|
||||||
GOOS: ${{ matrix.os }}
|
GOARCH: ${{ matrix.goarch }}
|
||||||
GOARCH: ${{ matrix.arch }}
|
GOPATH: ${{ env.HOME }}/go
|
||||||
GO386: ${{ matrix.go386 }}
|
|
||||||
GOARM: ${{ matrix.goarm }}
|
GOARM: ${{ matrix.goarm }}
|
||||||
GOMIPS: ${{ matrix.gomips }}
|
|
||||||
GOMIPS64: ${{ matrix.gomips }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key
|
||||||
|
NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
- name: Build Android
|
- name: Build Android
|
||||||
if: matrix.os == 'android'
|
if: matrix.goos == 'android'
|
||||||
run: |
|
run: |-
|
||||||
set -xeuo pipefail
|
|
||||||
go install -v ./cmd/internal/build
|
go install -v ./cmd/internal/build
|
||||||
export CC='${{ matrix.ndk }}-clang'
|
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build goreleaser release --clean --split
|
||||||
export CXX="${CC}++"
|
|
||||||
mkdir -p dist
|
|
||||||
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
|
||||||
./cmd/sing-box
|
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
BUILD_GOOS: ${{ matrix.goos }}
|
||||||
BUILD_GOOS: ${{ matrix.os }}
|
BUILD_GOARCH: ${{ matrix.goarch }}
|
||||||
BUILD_GOARCH: ${{ matrix.arch }}
|
GOARM: ${{ matrix.goarm }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Set name
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
run: |-
|
NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key
|
||||||
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}"
|
NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
if [[ -n "${{ matrix.goarm }}" ]]; then
|
|
||||||
DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
|
|
||||||
elif [[ -n "${{ matrix.go386 }}" && "${{ matrix.go386 }}" != 'sse2' ]]; then
|
|
||||||
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
|
|
||||||
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
|
|
||||||
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
|
|
||||||
elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then
|
|
||||||
DIR_NAME="${DIR_NAME}-legacy"
|
|
||||||
fi
|
|
||||||
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
|
||||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
|
||||||
PKG_VERSION="${PKG_VERSION//-/\~}"
|
|
||||||
echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
|
|
||||||
- name: Package DEB
|
|
||||||
if: matrix.debian != ''
|
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
sudo gem install fpm
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y debsigs
|
|
||||||
cp .fpm_systemd .fpm
|
|
||||||
fpm -t deb \
|
|
||||||
-v "$PKG_VERSION" \
|
|
||||||
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.debian }}.deb" \
|
|
||||||
--architecture ${{ matrix.debian }} \
|
|
||||||
dist/sing-box=/usr/bin/sing-box
|
|
||||||
curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
|
|
||||||
sudo patch /usr/bin/debsigs < '/tmp/debsigs.diff'
|
|
||||||
rm -rf $HOME/.gnupg
|
|
||||||
gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
|
|
||||||
${{ secrets.GPG_KEY }}
|
|
||||||
EOF
|
|
||||||
debsigs --sign=origin -k ${{ secrets.GPG_KEY_ID }} --gpgopts '--pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}"' dist/*.deb
|
|
||||||
- name: Package RPM
|
|
||||||
if: matrix.rpm != ''
|
|
||||||
run: |-
|
|
||||||
set -xeuo pipefail
|
|
||||||
sudo gem install fpm
|
|
||||||
cp .fpm_systemd .fpm
|
|
||||||
fpm -t rpm \
|
|
||||||
-v "$PKG_VERSION" \
|
|
||||||
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.rpm }}.rpm" \
|
|
||||||
--architecture ${{ matrix.rpm }} \
|
|
||||||
dist/sing-box=/usr/bin/sing-box
|
|
||||||
cat > $HOME/.rpmmacros <<EOF
|
|
||||||
%_gpg_name ${{ secrets.GPG_KEY_ID }}
|
|
||||||
%_gpg_sign_cmd_extra_args --pinentry-mode loopback --passphrase ${{ secrets.GPG_PASSPHRASE }}
|
|
||||||
EOF
|
|
||||||
gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
|
|
||||||
${{ secrets.GPG_KEY }}
|
|
||||||
EOF
|
|
||||||
rpmsign --addsign dist/*.rpm
|
|
||||||
- name: Package Pacman
|
|
||||||
if: matrix.pacman != ''
|
|
||||||
run: |-
|
|
||||||
set -xeuo pipefail
|
|
||||||
sudo gem install fpm
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libarchive-tools
|
|
||||||
cp .fpm_systemd .fpm
|
|
||||||
fpm -t pacman \
|
|
||||||
-v "$PKG_VERSION" \
|
|
||||||
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.pacman }}.pkg.tar.zst" \
|
|
||||||
--architecture ${{ matrix.pacman }} \
|
|
||||||
dist/sing-box=/usr/bin/sing-box
|
|
||||||
- name: Package OpenWrt
|
|
||||||
if: matrix.openwrt != ''
|
|
||||||
run: |-
|
|
||||||
set -xeuo pipefail
|
|
||||||
sudo gem install fpm
|
|
||||||
cp .fpm_openwrt .fpm
|
|
||||||
fpm -t deb \
|
|
||||||
-v "$PKG_VERSION" \
|
|
||||||
-p "dist/openwrt.deb" \
|
|
||||||
--architecture all \
|
|
||||||
dist/sing-box=/usr/bin/sing-box
|
|
||||||
for architecture in ${{ matrix.openwrt }}; do
|
|
||||||
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
|
|
||||||
done
|
|
||||||
rm "dist/openwrt.deb"
|
|
||||||
- name: Archive
|
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
cd dist
|
|
||||||
mkdir -p "${DIR_NAME}"
|
|
||||||
cp ../LICENSE "${DIR_NAME}"
|
|
||||||
if [ '${{ matrix.os }}' = 'windows' ]; then
|
|
||||||
cp sing-box "${DIR_NAME}/sing-box.exe"
|
|
||||||
zip -r "${DIR_NAME}.zip" "${DIR_NAME}"
|
|
||||||
else
|
|
||||||
cp sing-box "${DIR_NAME}"
|
|
||||||
tar -czvf "${DIR_NAME}.tar.gz" "${DIR_NAME}"
|
|
||||||
fi
|
|
||||||
rm -r "${DIR_NAME}"
|
|
||||||
- name: Cleanup
|
|
||||||
run: rm dist/sing-box
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_go && '-legacy' || '' }}
|
name: binary-${{ matrix.name }}
|
||||||
path: "dist"
|
path: 'dist'
|
||||||
build_android:
|
build_android:
|
||||||
name: Build Android
|
name: Build Android
|
||||||
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
|
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Android'
|
||||||
@@ -286,12 +219,12 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r28
|
ndk-version: r28-beta2
|
||||||
- name: Setup OpenJDK
|
- name: Setup OpenJDK
|
||||||
run: |-
|
run: |-
|
||||||
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
||||||
@@ -323,16 +256,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ~/.gradle
|
path: ~/.gradle
|
||||||
key: gradle-${{ hashFiles('**/*.gradle') }}
|
key: gradle-${{ hashFiles('**/*.gradle') }}
|
||||||
- name: Update version
|
- name: Build release
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
go run -v ./cmd/internal/update_android_version --ci
|
go run -v ./cmd/internal/update_android_version --ci
|
||||||
- name: Update nightly version
|
|
||||||
if: github.event_name != 'workflow_dispatch'
|
|
||||||
run: |-
|
|
||||||
go run -v ./cmd/internal/update_android_version --ci --nightly
|
|
||||||
- name: Build
|
|
||||||
run: |-
|
|
||||||
mkdir clients/android/app/libs
|
mkdir clients/android/app/libs
|
||||||
cp libbox.aar clients/android/app/libs
|
cp libbox.aar clients/android/app/libs
|
||||||
cd clients/android
|
cd clients/android
|
||||||
@@ -341,16 +268,47 @@ jobs:
|
|||||||
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
|
LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
|
||||||
- name: Prepare upload
|
- name: Build debug
|
||||||
|
if: github.event_name != 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
mkdir -p dist
|
go run -v ./cmd/internal/update_android_version --ci
|
||||||
cp clients/android/app/build/outputs/apk/play/release/*.apk dist
|
mkdir clients/android/app/libs
|
||||||
cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist
|
cp libbox.aar clients/android/app/libs
|
||||||
|
cd clients/android
|
||||||
|
./gradlew :app:assemblePlayRelease
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
|
||||||
|
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||||
|
LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }}
|
||||||
|
- name: Prepare release upload
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
run: |-
|
||||||
|
mkdir -p dist/release
|
||||||
|
cp clients/android/app/build/outputs/apk/play/release/*.apk dist/release
|
||||||
|
cp clients/android/app/build/outputs/apk/other/release/*-universal.apk dist/release
|
||||||
|
- name: Prepare debug upload
|
||||||
|
if: github.event_name != 'workflow_dispatch'
|
||||||
|
run: |-
|
||||||
|
mkdir -p dist/release
|
||||||
|
cp clients/android/app/build/outputs/apk/play/release/*.apk dist/release
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: binary-android-apks
|
name: binary-android-apks
|
||||||
path: 'dist'
|
path: 'dist'
|
||||||
|
- name: Upload debug apk (arm64-v8a)
|
||||||
|
if: github.event_name != 'workflow_dispatch'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: "SFA-${{ needs.calculate_version.outputs.version }}-arm64-v8a.apk"
|
||||||
|
path: 'dist/release/*-arm64-v8a.apk'
|
||||||
|
- name: Upload debug apk (universal)
|
||||||
|
if: github.event_name != 'workflow_dispatch'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: "SFA-${{ needs.calculate_version.outputs.version }}-universal.apk"
|
||||||
|
path: 'dist/release/*-universal.apk'
|
||||||
publish_android:
|
publish_android:
|
||||||
name: Publish Android
|
name: Publish Android
|
||||||
if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android'
|
if: github.event_name == 'workflow_dispatch' && inputs.build == 'publish-android'
|
||||||
@@ -366,12 +324,12 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r28
|
ndk-version: r28-beta2
|
||||||
- name: Setup OpenJDK
|
- name: Setup OpenJDK
|
||||||
run: |-
|
run: |-
|
||||||
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
||||||
@@ -464,7 +422,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Setup Xcode stable
|
- name: Setup Xcode stable
|
||||||
if: matrix.if && github.ref == 'refs/heads/main-next'
|
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -504,19 +462,19 @@ jobs:
|
|||||||
|
|
||||||
PROFILES_ZIP_PATH=$RUNNER_TEMP/Profiles.zip
|
PROFILES_ZIP_PATH=$RUNNER_TEMP/Profiles.zip
|
||||||
echo -n "$PROVISIONING_PROFILES" | base64 --decode -o $PROFILES_ZIP_PATH
|
echo -n "$PROVISIONING_PROFILES" | base64 --decode -o $PROFILES_ZIP_PATH
|
||||||
|
|
||||||
PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"
|
PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||||
mkdir -p "$PROFILES_PATH"
|
mkdir -p "$PROFILES_PATH"
|
||||||
unzip $PROFILES_ZIP_PATH -d "$PROFILES_PATH"
|
unzip $PROFILES_ZIP_PATH -d "$PROFILES_PATH"
|
||||||
|
|
||||||
ASC_KEY_PATH=$RUNNER_TEMP/Key.p12
|
ASC_KEY_PATH=$RUNNER_TEMP/Key.p12
|
||||||
echo -n "$ASC_KEY" | base64 --decode -o $ASC_KEY_PATH
|
echo -n "$ASC_KEY" | base64 --decode -o $ASC_KEY_PATH
|
||||||
|
|
||||||
xcrun notarytool store-credentials "notarytool-password" \
|
xcrun notarytool store-credentials "notarytool-password" \
|
||||||
--key $ASC_KEY_PATH \
|
--key $ASC_KEY_PATH \
|
||||||
--key-id $ASC_KEY_ID \
|
--key-id $ASC_KEY_ID \
|
||||||
--issuer $ASC_KEY_ISSUER_ID
|
--issuer $ASC_KEY_ISSUER_ID
|
||||||
|
|
||||||
echo "ASC_KEY_PATH=$ASC_KEY_PATH" >> "$GITHUB_ENV"
|
echo "ASC_KEY_PATH=$ASC_KEY_PATH" >> "$GITHUB_ENV"
|
||||||
echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV"
|
echo "ASC_KEY_ID=$ASC_KEY_ID" >> "$GITHUB_ENV"
|
||||||
echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV"
|
echo "ASC_KEY_ISSUER_ID=$ASC_KEY_ISSUER_ID" >> "$GITHUB_ENV"
|
||||||
@@ -592,10 +550,10 @@ jobs:
|
|||||||
cd "${{ matrix.archive }}"
|
cd "${{ matrix.archive }}"
|
||||||
zip -r SFM.dSYMs.zip dSYMs
|
zip -r SFM.dSYMs.zip dSYMs
|
||||||
popd
|
popd
|
||||||
|
|
||||||
mkdir -p dist
|
mkdir -p dist/release
|
||||||
cp clients/apple/SFM.dmg "dist/SFM-${VERSION}-universal.dmg"
|
cp clients/apple/SFM.dmg "dist/release/SFM-${VERSION}-universal.dmg"
|
||||||
cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/SFM-${VERSION}-universal.dSYMs.zip"
|
cp "clients/apple/${{ matrix.archive }}/SFM.dSYMs.zip" "dist/release/SFM-${VERSION}-universal.dSYMs.zip"
|
||||||
- name: Upload image
|
- name: Upload image
|
||||||
if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
|
if: matrix.if && matrix.name == 'macOS-standalone' && github.event_name == 'workflow_dispatch'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -616,6 +574,12 @@ jobs:
|
|||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
- name: Setup Goreleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser-pro
|
||||||
|
version: latest
|
||||||
|
install-only: true
|
||||||
- name: Cache ghr
|
- name: Cache ghr
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
id: cache-ghr
|
id: cache-ghr
|
||||||
@@ -640,17 +604,26 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
- name: Merge builds
|
||||||
|
if: github.event_name != 'workflow_dispatch' || inputs.build == 'All' || inputs.build == 'Binary'
|
||||||
|
run: |-
|
||||||
|
goreleaser continue --merge --skip publish
|
||||||
|
mkdir -p dist/release
|
||||||
|
mv dist/*/sing-box*{tar.gz,zip,deb,rpm,_amd64.pkg.tar.zst,_arm64.pkg.tar.zst} dist/release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
- name: Upload builds
|
- name: Upload builds
|
||||||
if: ${{ env.PUBLISHED == 'false' }}
|
if: ${{ env.PUBLISHED == 'false' }}
|
||||||
run: |-
|
run: |-
|
||||||
export PATH="$PATH:$HOME/go/bin"
|
export PATH="$PATH:$HOME/go/bin"
|
||||||
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist
|
ghr --replace --draft --prerelease -p 5 "v${VERSION}" dist/release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Replace builds
|
- name: Replace builds
|
||||||
if: ${{ env.PUBLISHED != 'false' }}
|
if: ${{ env.PUBLISHED != 'false' }}
|
||||||
run: |-
|
run: |-
|
||||||
export PATH="$PATH:$HOME/go/bin"
|
export PATH="$PATH:$HOME/go/bin"
|
||||||
ghr --replace -p 5 "v${VERSION}" dist
|
ghr --replace -p 5 "v${VERSION}" dist/release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
5
.github/workflows/lint.yml
vendored
5
.github/workflows/lint.yml
vendored
@@ -28,11 +28,10 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
install-mode: binary
|
install-mode: binary
|
||||||
verify: false
|
|
||||||
185
.github/workflows/linux.yml
vendored
185
.github/workflows/linux.yml
vendored
@@ -1,63 +1,13 @@
|
|||||||
name: Build Linux Packages
|
name: Release to Linux repository
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: "Version name"
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
- published
|
- published
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
calculate_version:
|
|
||||||
name: Calculate version
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.outputs.outputs.version }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ^1.24
|
|
||||||
- name: Check input version
|
|
||||||
if: github.event_name == 'workflow_dispatch'
|
|
||||||
run: |-
|
|
||||||
echo "version=${{ inputs.version }}"
|
|
||||||
echo "version=${{ inputs.version }}" >> "$GITHUB_ENV"
|
|
||||||
- name: Calculate version
|
|
||||||
if: github.event_name != 'workflow_dispatch'
|
|
||||||
run: |-
|
|
||||||
go run -v ./cmd/internal/read_tag --ci --nightly
|
|
||||||
- name: Set outputs
|
|
||||||
id: outputs
|
|
||||||
run: |-
|
|
||||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
||||||
build:
|
build:
|
||||||
name: Build binary
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
|
||||||
- calculate_version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
|
||||||
- { os: linux, arch: "386", debian: i386, rpm: i386 }
|
|
||||||
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl }
|
|
||||||
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl }
|
|
||||||
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64 }
|
|
||||||
- { os: linux, arch: mips64le, debian: mips64el, rpm: mips64el }
|
|
||||||
- { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel }
|
|
||||||
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
|
||||||
- { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le }
|
|
||||||
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64 }
|
|
||||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
@@ -66,122 +16,23 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Setup Android NDK
|
- name: Extract signing key
|
||||||
if: matrix.os == 'android'
|
|
||||||
uses: nttld/setup-ndk@v1
|
|
||||||
with:
|
|
||||||
ndk-version: r28
|
|
||||||
local-cache: true
|
|
||||||
- name: Set tag
|
|
||||||
run: |-
|
run: |-
|
||||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
mkdir -p $HOME/.gnupg
|
||||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
cat > $HOME/.gnupg/sagernet.key <<EOF
|
||||||
- name: Set build tags
|
${{ secrets.GPG_KEY }}
|
||||||
run: |
|
EOF
|
||||||
set -xeuo pipefail
|
echo "HOME=$HOME" >> "$GITHUB_ENV"
|
||||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_acme,with_clash_api'
|
- name: Publish release
|
||||||
if [ ! '${{ matrix.legacy_go }}' = 'true' ]; then
|
uses: goreleaser/goreleaser-action@v6
|
||||||
TAGS="${TAGS},with_ech"
|
with:
|
||||||
fi
|
distribution: goreleaser-pro
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
version: latest
|
||||||
- name: Build
|
args: release -f .goreleaser.fury.yaml --clean
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
mkdir -p dist
|
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
|
||||||
./cmd/sing-box
|
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
|
||||||
GOOS: ${{ matrix.os }}
|
|
||||||
GOARCH: ${{ matrix.arch }}
|
|
||||||
GOARM: ${{ matrix.goarm }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Set mtime
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
run: |-
|
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
|
||||||
TZ=UTC touch -t '197001010000' dist/sing-box
|
NFPM_KEY_PATH: ${{ env.HOME }}/.gnupg/sagernet.key
|
||||||
- name: Set name
|
NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
if: ${{ ! contains(needs.calculate_version.outputs.version, '-') }}
|
|
||||||
run: |-
|
|
||||||
echo "NAME=sing-box" >> "$GITHUB_ENV"
|
|
||||||
- name: Set beta name
|
|
||||||
if: contains(needs.calculate_version.outputs.version, '-')
|
|
||||||
run: |-
|
|
||||||
echo "NAME=sing-box-beta" >> "$GITHUB_ENV"
|
|
||||||
- name: Set version
|
|
||||||
run: |-
|
|
||||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
|
||||||
PKG_VERSION="${PKG_VERSION//-/\~}"
|
|
||||||
echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
|
|
||||||
- name: Package DEB
|
|
||||||
if: matrix.debian != ''
|
|
||||||
run: |
|
|
||||||
set -xeuo pipefail
|
|
||||||
sudo gem install fpm
|
|
||||||
sudo apt-get install -y debsigs
|
|
||||||
cp .fpm_systemd .fpm
|
|
||||||
fpm -t deb \
|
|
||||||
--name "${NAME}" \
|
|
||||||
-v "$PKG_VERSION" \
|
|
||||||
-p "dist/${NAME}_${{ needs.calculate_version.outputs.version }}_linux_${{ matrix.debian }}.deb" \
|
|
||||||
--architecture ${{ matrix.debian }} \
|
|
||||||
dist/sing-box=/usr/bin/sing-box
|
|
||||||
curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
|
|
||||||
sudo patch /usr/bin/debsigs < '/tmp/debsigs.diff'
|
|
||||||
rm -rf $HOME/.gnupg
|
|
||||||
gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
|
|
||||||
${{ secrets.GPG_KEY }}
|
|
||||||
EOF
|
|
||||||
debsigs --sign=origin -k ${{ secrets.GPG_KEY_ID }} --gpgopts '--pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}"' dist/*.deb
|
|
||||||
- name: Package RPM
|
|
||||||
if: matrix.rpm != ''
|
|
||||||
run: |-
|
|
||||||
set -xeuo pipefail
|
|
||||||
sudo gem install fpm
|
|
||||||
cp .fpm_systemd .fpm
|
|
||||||
fpm -t rpm \
|
|
||||||
--name "${NAME}" \
|
|
||||||
-v "$PKG_VERSION" \
|
|
||||||
-p "dist/${NAME}_${{ needs.calculate_version.outputs.version }}_linux_${{ matrix.rpm }}.rpm" \
|
|
||||||
--architecture ${{ matrix.rpm }} \
|
|
||||||
dist/sing-box=/usr/bin/sing-box
|
|
||||||
cat > $HOME/.rpmmacros <<EOF
|
|
||||||
%_gpg_name ${{ secrets.GPG_KEY_ID }}
|
|
||||||
%_gpg_sign_cmd_extra_args --pinentry-mode loopback --passphrase ${{ secrets.GPG_PASSPHRASE }}
|
|
||||||
EOF
|
|
||||||
gpg --pinentry-mode loopback --passphrase "${{ secrets.GPG_PASSPHRASE }}" --import <<EOF
|
|
||||||
${{ secrets.GPG_KEY }}
|
|
||||||
EOF
|
|
||||||
rpmsign --addsign dist/*.rpm
|
|
||||||
- name: Cleanup
|
|
||||||
run: rm dist/sing-box
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.legacy_go && '-legacy' || '' }}
|
|
||||||
path: "dist"
|
|
||||||
upload:
|
|
||||||
name: Upload builds
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- calculate_version
|
|
||||||
- build
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Set tag
|
|
||||||
run: |-
|
|
||||||
git ls-remote --exit-code --tags origin v${{ needs.calculate_version.outputs.version }} || echo "PUBLISHED=false" >> "$GITHUB_ENV"
|
|
||||||
git tag v${{ needs.calculate_version.outputs.version }} -f
|
|
||||||
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
|
|
||||||
- name: Download builds
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
- name: Publish packages
|
|
||||||
run: |-
|
|
||||||
ls dist | xargs -I {} curl -F "package=@dist/{}" https://${{ secrets.FURY_TOKEN }}@push.fury.io/sagernet/
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ nfpms:
|
|||||||
contents:
|
contents:
|
||||||
- src: release/config/config.json
|
- src: release/config/config.json
|
||||||
dst: /etc/sing-box/config.json
|
dst: /etc/sing-box/config.json
|
||||||
type: "config|noreplace"
|
type: config
|
||||||
|
|
||||||
- src: release/config/sing-box.service
|
- src: release/config/sing-box.service
|
||||||
dst: /usr/lib/systemd/system/sing-box.service
|
dst: /usr/lib/systemd/system/sing-box.service
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ builds:
|
|||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
- GOROOT={{ .Env.GOPATH }}/go1.20.14
|
- GOROOT={{ .Env.GOPATH }}/go1.20.14
|
||||||
tool: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
|
gobinary: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
|
||||||
targets:
|
targets:
|
||||||
- windows_amd64_v1
|
- windows_amd64_v1
|
||||||
- windows_386
|
- windows_386
|
||||||
@@ -95,12 +95,10 @@ archives:
|
|||||||
builds:
|
builds:
|
||||||
- main
|
- main
|
||||||
- android
|
- android
|
||||||
formats:
|
format: tar.gz
|
||||||
- tar.gz
|
|
||||||
format_overrides:
|
format_overrides:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
formats:
|
format: zip
|
||||||
- zip
|
|
||||||
wrap_in_directory: true
|
wrap_in_directory: true
|
||||||
files:
|
files:
|
||||||
- LICENSE
|
- LICENSE
|
||||||
@@ -130,7 +128,7 @@ nfpms:
|
|||||||
contents:
|
contents:
|
||||||
- src: release/config/config.json
|
- src: release/config/config.json
|
||||||
dst: /etc/sing-box/config.json
|
dst: /etc/sing-box/config.json
|
||||||
type: "config|noreplace"
|
type: config
|
||||||
|
|
||||||
- src: release/config/sing-box.service
|
- src: release/config/sing-box.service
|
||||||
dst: /usr/lib/systemd/system/sing-box.service
|
dst: /usr/lib/systemd/system/sing-box.service
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
|
FROM --platform=$BUILDPLATFORM golang:1.23-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
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -10,7 +10,7 @@ GOHOSTARCH = $(shell go env GOHOSTARCH)
|
|||||||
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run ./cmd/internal/read_tag)
|
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run ./cmd/internal/read_tag)
|
||||||
|
|
||||||
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid="
|
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid="
|
||||||
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)
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ ci_build:
|
|||||||
go build $(MAIN_PARAMS) $(MAIN)
|
go build $(MAIN_PARAMS) $(MAIN)
|
||||||
|
|
||||||
generate_completions:
|
generate_completions:
|
||||||
go run -v --tags "$(TAGS),generate,generate_completions" $(MAIN)
|
go run -v --tags $(TAGS),generate,generate_completions $(MAIN)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
go build -o $(PREFIX)/bin/$(NAME) $(MAIN_PARAMS) $(MAIN)
|
go build -o $(PREFIX)/bin/$(NAME) $(MAIN_PARAMS) $(MAIN)
|
||||||
@@ -247,4 +247,4 @@ clean:
|
|||||||
update:
|
update:
|
||||||
git fetch
|
git fetch
|
||||||
git reset FETCH_HEAD --hard
|
git reset FETCH_HEAD --hard
|
||||||
git clean -fdx
|
git clean -fdx
|
||||||
@@ -39,17 +39,17 @@ type CacheFile interface {
|
|||||||
StoreSelected(group string, selected string) error
|
StoreSelected(group string, selected string) error
|
||||||
LoadGroupExpand(group string) (isExpand bool, loaded bool)
|
LoadGroupExpand(group string) (isExpand bool, loaded bool)
|
||||||
StoreGroupExpand(group string, expand bool) error
|
StoreGroupExpand(group string, expand bool) error
|
||||||
LoadRuleSet(tag string) *SavedBinary
|
LoadRuleSet(tag string) *SavedRuleSet
|
||||||
SaveRuleSet(tag string, set *SavedBinary) error
|
SaveRuleSet(tag string, set *SavedRuleSet) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type SavedBinary struct {
|
type SavedRuleSet struct {
|
||||||
Content []byte
|
Content []byte
|
||||||
LastUpdated time.Time
|
LastUpdated time.Time
|
||||||
LastEtag string
|
LastEtag string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SavedBinary) MarshalBinary() ([]byte, error) {
|
func (s *SavedRuleSet) MarshalBinary() ([]byte, error) {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
err := binary.Write(&buffer, binary.BigEndian, uint8(1))
|
err := binary.Write(&buffer, binary.BigEndian, uint8(1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,7 +70,7 @@ func (s *SavedBinary) MarshalBinary() ([]byte, error) {
|
|||||||
return buffer.Bytes(), nil
|
return buffer.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SavedBinary) UnmarshalBinary(data []byte) error {
|
func (s *SavedRuleSet) UnmarshalBinary(data []byte) error {
|
||||||
reader := bytes.NewReader(data)
|
reader := bytes.NewReader(data)
|
||||||
var version uint8
|
var version uint8
|
||||||
err := binary.Read(reader, binary.BigEndian, &version)
|
err := binary.Read(reader, binary.BigEndian, &version)
|
||||||
|
|||||||
@@ -53,11 +53,10 @@ type InboundContext struct {
|
|||||||
|
|
||||||
// sniffer
|
// sniffer
|
||||||
|
|
||||||
Protocol string
|
Protocol string
|
||||||
Domain string
|
Domain string
|
||||||
Client string
|
Client string
|
||||||
SniffContext any
|
SniffContext any
|
||||||
PacketSniffError error
|
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ type NetworkManager interface {
|
|||||||
PackageManager() tun.PackageManager
|
PackageManager() tun.PackageManager
|
||||||
WIFIState() WIFIState
|
WIFIState() WIFIState
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
UpdateWIFIState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkOptions struct {
|
type NetworkOptions struct {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ type Router interface {
|
|||||||
ClearDNSCache()
|
ClearDNSCache()
|
||||||
Rules() []Rule
|
Rules() []Rule
|
||||||
|
|
||||||
AppendTracker(tracker ConnectionTracker)
|
SetTracker(tracker ConnectionTracker)
|
||||||
|
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
}
|
}
|
||||||
|
|||||||
26
box.go
26
box.go
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
"github.com/sagernet/sing-box/adapter/endpoint"
|
||||||
"github.com/sagernet/sing-box/adapter/inbound"
|
"github.com/sagernet/sing-box/adapter/inbound"
|
||||||
"github.com/sagernet/sing-box/adapter/outbound"
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
@@ -84,7 +85,6 @@ func New(options Options) (*Box, error) {
|
|||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
}
|
}
|
||||||
ctx = service.ContextWithDefaultRegistry(ctx)
|
ctx = service.ContextWithDefaultRegistry(ctx)
|
||||||
|
|
||||||
endpointRegistry := service.FromContext[adapter.EndpointRegistry](ctx)
|
endpointRegistry := service.FromContext[adapter.EndpointRegistry](ctx)
|
||||||
inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
|
inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
|
||||||
outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
|
outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
|
||||||
@@ -101,7 +101,10 @@ func New(options Options) (*Box, error) {
|
|||||||
|
|
||||||
ctx = pause.WithDefaultManager(ctx)
|
ctx = pause.WithDefaultManager(ctx)
|
||||||
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
||||||
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
|
debugOptions := common.PtrValueOrDefault(experimentalOptions.Debug)
|
||||||
|
applyDebugOptions(debugOptions)
|
||||||
|
ctx = conntrack.ContextWithDefaultTracker(ctx, debugOptions.OOMKiller, uint64(debugOptions.MemoryLimit))
|
||||||
|
|
||||||
var needCacheFile bool
|
var needCacheFile bool
|
||||||
var needClashAPI bool
|
var needClashAPI bool
|
||||||
var needV2RayAPI bool
|
var needV2RayAPI bool
|
||||||
@@ -165,15 +168,7 @@ func New(options Options) (*Box, error) {
|
|||||||
} else {
|
} else {
|
||||||
tag = F.ToString(i)
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
endpointCtx := ctx
|
err = endpointManager.Create(ctx,
|
||||||
if tag != "" {
|
|
||||||
// TODO: remove this
|
|
||||||
endpointCtx = adapter.WithContext(endpointCtx, &adapter.InboundContext{
|
|
||||||
Outbound: tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
err = endpointManager.Create(
|
|
||||||
endpointCtx,
|
|
||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("endpoint/", endpointOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("endpoint/", endpointOptions.Type, "[", tag, "]")),
|
||||||
tag,
|
tag,
|
||||||
@@ -191,8 +186,7 @@ func New(options Options) (*Box, error) {
|
|||||||
} else {
|
} else {
|
||||||
tag = F.ToString(i)
|
tag = F.ToString(i)
|
||||||
}
|
}
|
||||||
err = inboundManager.Create(
|
err = inboundManager.Create(ctx,
|
||||||
ctx,
|
|
||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||||
tag,
|
tag,
|
||||||
@@ -257,7 +251,7 @@ func New(options Options) (*Box, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "create clash-server")
|
return nil, E.Cause(err, "create clash-server")
|
||||||
}
|
}
|
||||||
router.AppendTracker(clashServer)
|
router.SetTracker(clashServer)
|
||||||
service.MustRegister[adapter.ClashServer](ctx, clashServer)
|
service.MustRegister[adapter.ClashServer](ctx, clashServer)
|
||||||
services = append(services, clashServer)
|
services = append(services, clashServer)
|
||||||
}
|
}
|
||||||
@@ -267,7 +261,7 @@ func New(options Options) (*Box, error) {
|
|||||||
return nil, E.Cause(err, "create v2ray-server")
|
return nil, E.Cause(err, "create v2ray-server")
|
||||||
}
|
}
|
||||||
if v2rayServer.StatsService() != nil {
|
if v2rayServer.StatsService() != nil {
|
||||||
router.AppendTracker(v2rayServer.StatsService())
|
router.SetTracker(v2rayServer.StatsService())
|
||||||
services = append(services, v2rayServer)
|
services = append(services, v2rayServer)
|
||||||
service.MustRegister[adapter.V2RayServer](ctx, v2rayServer)
|
service.MustRegister[adapter.V2RayServer](ctx, v2rayServer)
|
||||||
}
|
}
|
||||||
@@ -408,7 +402,7 @@ func (s *Box) Close() error {
|
|||||||
close(s.done)
|
close(s.done)
|
||||||
}
|
}
|
||||||
err := common.Close(
|
err := common.Close(
|
||||||
s.inbound, s.outbound, s.endpoint, s.router, s.connection, s.network,
|
s.inbound, s.outbound, s.router, s.connection, s.network,
|
||||||
)
|
)
|
||||||
for _, lifecycleService := range s.services {
|
for _, lifecycleService := range s.services {
|
||||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||||
|
|||||||
Submodule clients/android updated: 9dd336679d...e1049099a0
Submodule clients/apple updated: ae5818ee5a...3d889ae017
@@ -5,7 +5,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/asc-go/asc"
|
"github.com/sagernet/asc-go/asc"
|
||||||
@@ -195,10 +194,6 @@ func publishTestflight(ctx context.Context) error {
|
|||||||
log.Info(string(platform), " ", tag, " create submission")
|
log.Info(string(platform), " ", tag, " create submission")
|
||||||
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
_, _, err = client.TestFlight.CreateBetaAppReviewSubmission(ctx, build.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "ANOTHER_BUILD_IN_REVIEW") {
|
|
||||||
log.Error(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func FindSDK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findNDK() bool {
|
func findNDK() bool {
|
||||||
const fixedVersion = "28.0.13004108"
|
const fixedVersion = "28.0.12674087"
|
||||||
const versionFile = "source.properties"
|
const versionFile = "source.properties"
|
||||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
||||||
androidNDKPath = fixedPath
|
androidNDKPath = fixedPath
|
||||||
|
|||||||
@@ -5,49 +5,40 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
"github.com/sagernet/sing-box/common/badversion"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var nightly bool
|
||||||
flagRunInCI bool
|
|
||||||
flagRunNightly bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
flag.BoolVar(&nightly, "nightly", false, "Print nightly tag")
|
||||||
flag.BoolVar(&flagRunNightly, "nightly", false, "Run nightly")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
var (
|
if nightly {
|
||||||
versionStr string
|
version, err := build_shared.ReadTagVersionRev()
|
||||||
err error
|
|
||||||
)
|
|
||||||
if flagRunNightly {
|
|
||||||
var version badversion.Version
|
|
||||||
version, err = build_shared.ReadTagVersion()
|
|
||||||
if err == nil {
|
|
||||||
versionStr = version.String()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
versionStr, err = build_shared.ReadTag()
|
|
||||||
}
|
|
||||||
if flagRunInCI {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
var versionStr string
|
||||||
|
if version.PreReleaseIdentifier != "" {
|
||||||
|
versionStr = version.VersionString() + "-nightly"
|
||||||
|
} else {
|
||||||
|
version.Patch++
|
||||||
|
versionStr = version.VersionString() + "-nightly"
|
||||||
|
}
|
||||||
err = setGitHubEnv("version", versionStr)
|
err = setGitHubEnv("version", versionStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
tag, err := build_shared.ReadTag()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
os.Stdout.WriteString("unknown\n")
|
os.Stdout.WriteString("unknown\n")
|
||||||
} else {
|
} else {
|
||||||
os.Stdout.WriteString(versionStr + "\n")
|
os.Stdout.WriteString(tag + "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,10 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var flagRunInCI bool
|
||||||
flagRunInCI bool
|
|
||||||
flagRunNightly bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
||||||
flag.BoolVar(&flagRunNightly, "nightly", false, "Run nightly")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -50,23 +46,21 @@ func main() {
|
|||||||
switch propPair[0] {
|
switch propPair[0] {
|
||||||
case "VERSION_NAME":
|
case "VERSION_NAME":
|
||||||
if propPair[1] != newVersion {
|
if propPair[1] != newVersion {
|
||||||
log.Info("updated version from ", propPair[1], " to ", newVersion)
|
|
||||||
versionUpdated = true
|
versionUpdated = true
|
||||||
propPair[1] = newVersion
|
propPair[1] = newVersion
|
||||||
|
log.Info("updated version to ", newVersion)
|
||||||
}
|
}
|
||||||
case "GO_VERSION":
|
case "GO_VERSION":
|
||||||
if propPair[1] != runtime.Version() {
|
if propPair[1] != runtime.Version() {
|
||||||
log.Info("updated Go version from ", propPair[1], " to ", runtime.Version())
|
|
||||||
goVersionUpdated = true
|
goVersionUpdated = true
|
||||||
propPair[1] = runtime.Version()
|
propPair[1] = runtime.Version()
|
||||||
|
log.Info("updated Go version to ", runtime.Version())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !(versionUpdated || goVersionUpdated) {
|
if !(versionUpdated || goVersionUpdated) {
|
||||||
log.Info("version not changed")
|
log.Info("version not changed")
|
||||||
return
|
return
|
||||||
} else if flagRunInCI && !flagRunNightly {
|
|
||||||
log.Fatal("version changed, commit changes first.")
|
|
||||||
}
|
}
|
||||||
for _, propPair := range propsList {
|
for _, propPair := range propsList {
|
||||||
switch propPair[0] {
|
switch propPair[0] {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateTLSKeyPair(serverName string) error {
|
func generateTLSKeyPair(serverName string) error {
|
||||||
privateKeyPem, publicKeyPem, err := tls.GenerateCertificate(nil, nil, time.Now, serverName, time.Now().AddDate(0, flagGenerateTLSKeyPairMonths, 0))
|
privateKeyPem, publicKeyPem, err := tls.GenerateKeyPair(time.Now, serverName, time.Now().AddDate(0, flagGenerateTLSKeyPairMonths, 0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/srs"
|
"github.com/sagernet/sing-box/common/srs"
|
||||||
@@ -57,14 +56,6 @@ func ruleSetMatch(sourcePath string, domain string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "read rule-set")
|
return E.Cause(err, "read rule-set")
|
||||||
}
|
}
|
||||||
if flagRuleSetMatchFormat == "" {
|
|
||||||
switch filepath.Ext(sourcePath) {
|
|
||||||
case ".json":
|
|
||||||
flagRuleSetMatchFormat = C.RuleSetFormatSource
|
|
||||||
case ".srs":
|
|
||||||
flagRuleSetMatchFormat = C.RuleSetFormatBinary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var ruleSet option.PlainRuleSetCompat
|
var ruleSet option.PlainRuleSetCompat
|
||||||
switch flagRuleSetMatchFormat {
|
switch flagRuleSetMatchFormat {
|
||||||
case C.RuleSetFormatSource:
|
case C.RuleSetFormatSource:
|
||||||
|
|||||||
@@ -61,15 +61,14 @@ func upgradeRuleSet(sourcePath string) error {
|
|||||||
log.Info("already up-to-date")
|
log.Info("already up-to-date")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
plainRuleSetCompat.Options, err = plainRuleSetCompat.Upgrade()
|
plainRuleSet, err := plainRuleSetCompat.Upgrade()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
plainRuleSetCompat.Version = C.RuleSetVersionCurrent
|
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
encoder := json.NewEncoder(buffer)
|
encoder := json.NewEncoder(buffer)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
err = encoder.Encode(plainRuleSetCompat)
|
err = encoder.Encode(plainRuleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "encode config")
|
return E.Cause(err, "encode config")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/settings"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -57,7 +58,7 @@ func syncTime() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if commandSyncTimeWrite {
|
if commandSyncTimeWrite {
|
||||||
err = ntp.SetSystemTime(response.Time)
|
err = settings.SetSystemTime(response.Time)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "write time to system")
|
return E.Cause(err, "write time to system")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
package conntrack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/x/list"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Conn struct {
|
|
||||||
net.Conn
|
|
||||||
element *list.Element[io.Closer]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConn(conn net.Conn) (net.Conn, error) {
|
|
||||||
connAccess.Lock()
|
|
||||||
element := openConnection.PushBack(conn)
|
|
||||||
connAccess.Unlock()
|
|
||||||
if KillerEnabled {
|
|
||||||
err := KillerCheck()
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Conn{
|
|
||||||
Conn: conn,
|
|
||||||
element: element,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Close() error {
|
|
||||||
if c.element.Value != nil {
|
|
||||||
connAccess.Lock()
|
|
||||||
if c.element.Value != nil {
|
|
||||||
openConnection.Remove(c.element)
|
|
||||||
c.element.Value = nil
|
|
||||||
}
|
|
||||||
connAccess.Unlock()
|
|
||||||
}
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Upstream() any {
|
|
||||||
return c.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) ReaderReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) WriterReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
14
common/conntrack/context.go
Normal file
14
common/conntrack/context.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ContextWithDefaultTracker(ctx context.Context, killerEnabled bool, memoryLimit uint64) context.Context {
|
||||||
|
if service.FromContext[Tracker](ctx) != nil {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
return service.ContextWith[Tracker](ctx, NewDefaultTracker(killerEnabled, memoryLimit))
|
||||||
|
}
|
||||||
245
common/conntrack/default.go
Normal file
245
common/conntrack/default.go
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
runtimeDebug "runtime/debug"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/memory"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Tracker = (*DefaultTracker)(nil)
|
||||||
|
|
||||||
|
type DefaultTracker struct {
|
||||||
|
connAccess sync.RWMutex
|
||||||
|
connList list.List[net.Conn]
|
||||||
|
connAddress map[netip.AddrPort]netip.AddrPort
|
||||||
|
|
||||||
|
packetConnAccess sync.RWMutex
|
||||||
|
packetConnList list.List[AbstractPacketConn]
|
||||||
|
packetConnAddress map[netip.AddrPort]bool
|
||||||
|
|
||||||
|
pendingAccess sync.RWMutex
|
||||||
|
pendingList list.List[netip.AddrPort]
|
||||||
|
|
||||||
|
killerEnabled bool
|
||||||
|
memoryLimit uint64
|
||||||
|
killerLastCheck time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultTracker(killerEnabled bool, memoryLimit uint64) *DefaultTracker {
|
||||||
|
return &DefaultTracker{
|
||||||
|
connAddress: make(map[netip.AddrPort]netip.AddrPort),
|
||||||
|
packetConnAddress: make(map[netip.AddrPort]bool),
|
||||||
|
killerEnabled: killerEnabled,
|
||||||
|
memoryLimit: memoryLimit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) NewConn(conn net.Conn) (net.Conn, error) {
|
||||||
|
err := t.KillerCheck()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.connAccess.Lock()
|
||||||
|
element := t.connList.PushBack(conn)
|
||||||
|
t.connAddress[M.AddrPortFromNet(conn.LocalAddr())] = M.AddrPortFromNet(conn.RemoteAddr())
|
||||||
|
t.connAccess.Unlock()
|
||||||
|
return &Conn{
|
||||||
|
Conn: conn,
|
||||||
|
closeFunc: common.OnceFunc(func() {
|
||||||
|
t.removeConn(element)
|
||||||
|
}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) NewConnEx(conn net.Conn) (N.CloseHandlerFunc, error) {
|
||||||
|
err := t.KillerCheck()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.connAccess.Lock()
|
||||||
|
element := t.connList.PushBack(conn)
|
||||||
|
t.connAddress[M.AddrPortFromNet(conn.LocalAddr())] = M.AddrPortFromNet(conn.RemoteAddr())
|
||||||
|
t.connAccess.Unlock()
|
||||||
|
return N.OnceClose(func(it error) {
|
||||||
|
t.removeConn(element)
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) NewPacketConn(conn net.PacketConn) (net.PacketConn, error) {
|
||||||
|
err := t.KillerCheck()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.packetConnAccess.Lock()
|
||||||
|
element := t.packetConnList.PushBack(conn)
|
||||||
|
t.packetConnAddress[M.AddrPortFromNet(conn.LocalAddr())] = true
|
||||||
|
t.packetConnAccess.Unlock()
|
||||||
|
return &PacketConn{
|
||||||
|
PacketConn: conn,
|
||||||
|
closeFunc: common.OnceFunc(func() {
|
||||||
|
t.removePacketConn(element)
|
||||||
|
}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) NewPacketConnEx(conn AbstractPacketConn) (N.CloseHandlerFunc, error) {
|
||||||
|
err := t.KillerCheck()
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.packetConnAccess.Lock()
|
||||||
|
element := t.packetConnList.PushBack(conn)
|
||||||
|
t.packetConnAddress[M.AddrPortFromNet(conn.LocalAddr())] = true
|
||||||
|
t.packetConnAccess.Unlock()
|
||||||
|
return N.OnceClose(func(it error) {
|
||||||
|
t.removePacketConn(element)
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) CheckConn(source netip.AddrPort, destination netip.AddrPort) bool {
|
||||||
|
t.connAccess.RLock()
|
||||||
|
defer t.connAccess.RUnlock()
|
||||||
|
return t.connAddress[source] == destination
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) CheckPacketConn(source netip.AddrPort) bool {
|
||||||
|
t.packetConnAccess.RLock()
|
||||||
|
defer t.packetConnAccess.RUnlock()
|
||||||
|
return t.packetConnAddress[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) AddPendingDestination(destination netip.AddrPort) func() {
|
||||||
|
t.pendingAccess.Lock()
|
||||||
|
defer t.pendingAccess.Unlock()
|
||||||
|
element := t.pendingList.PushBack(destination)
|
||||||
|
return func() {
|
||||||
|
t.pendingAccess.Lock()
|
||||||
|
defer t.pendingAccess.Unlock()
|
||||||
|
t.pendingList.Remove(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) CheckDestination(destination netip.AddrPort) bool {
|
||||||
|
t.pendingAccess.RLock()
|
||||||
|
defer t.pendingAccess.RUnlock()
|
||||||
|
for element := t.pendingList.Front(); element != nil; element = element.Next() {
|
||||||
|
if element.Value == destination {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) KillerCheck() error {
|
||||||
|
if !t.killerEnabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
nowTime := time.Now()
|
||||||
|
if nowTime.Sub(t.killerLastCheck) < 3*time.Second {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t.killerLastCheck = nowTime
|
||||||
|
if memory.Total() > t.memoryLimit {
|
||||||
|
t.Close()
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
runtimeDebug.FreeOSMemory()
|
||||||
|
}()
|
||||||
|
return E.New("out of memory")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) Count() int {
|
||||||
|
t.connAccess.RLock()
|
||||||
|
defer t.connAccess.RUnlock()
|
||||||
|
t.packetConnAccess.RLock()
|
||||||
|
defer t.packetConnAccess.RUnlock()
|
||||||
|
return t.connList.Len() + t.packetConnList.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) Close() {
|
||||||
|
t.connAccess.Lock()
|
||||||
|
for element := t.connList.Front(); element != nil; element = element.Next() {
|
||||||
|
element.Value.Close()
|
||||||
|
}
|
||||||
|
t.connList.Init()
|
||||||
|
t.connAccess.Unlock()
|
||||||
|
t.packetConnAccess.Lock()
|
||||||
|
for element := t.packetConnList.Front(); element != nil; element = element.Next() {
|
||||||
|
element.Value.Close()
|
||||||
|
}
|
||||||
|
t.packetConnList.Init()
|
||||||
|
t.packetConnAccess.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) removeConn(element *list.Element[net.Conn]) {
|
||||||
|
t.connAccess.Lock()
|
||||||
|
defer t.connAccess.Unlock()
|
||||||
|
delete(t.connAddress, M.AddrPortFromNet(element.Value.LocalAddr()))
|
||||||
|
t.connList.Remove(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTracker) removePacketConn(element *list.Element[AbstractPacketConn]) {
|
||||||
|
t.packetConnAccess.Lock()
|
||||||
|
defer t.packetConnAccess.Unlock()
|
||||||
|
delete(t.packetConnAddress, M.AddrPortFromNet(element.Value.LocalAddr()))
|
||||||
|
t.packetConnList.Remove(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Conn struct {
|
||||||
|
net.Conn
|
||||||
|
closeFunc func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Close() error {
|
||||||
|
c.closeFunc()
|
||||||
|
return c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Upstream() any {
|
||||||
|
return c.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) ReaderReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) WriterReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketConn struct {
|
||||||
|
net.PacketConn
|
||||||
|
closeFunc func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketConn) Close() error {
|
||||||
|
c.closeFunc()
|
||||||
|
return c.PacketConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketConn) Upstream() any {
|
||||||
|
return c.PacketConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketConn) ReaderReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketConn) WriterReplaceable() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package conntrack
|
|
||||||
|
|
||||||
import (
|
|
||||||
runtimeDebug "runtime/debug"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/memory"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
KillerEnabled bool
|
|
||||||
MemoryLimit uint64
|
|
||||||
killerLastCheck time.Time
|
|
||||||
)
|
|
||||||
|
|
||||||
func KillerCheck() error {
|
|
||||||
if !KillerEnabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
nowTime := time.Now()
|
|
||||||
if nowTime.Sub(killerLastCheck) < 3*time.Second {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
killerLastCheck = nowTime
|
|
||||||
if memory.Total() > MemoryLimit {
|
|
||||||
Close()
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
runtimeDebug.FreeOSMemory()
|
|
||||||
}()
|
|
||||||
return E.New("out of memory")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package conntrack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
"github.com/sagernet/sing/common/x/list"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PacketConn struct {
|
|
||||||
net.PacketConn
|
|
||||||
element *list.Element[io.Closer]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPacketConn(conn net.PacketConn) (net.PacketConn, error) {
|
|
||||||
connAccess.Lock()
|
|
||||||
element := openConnection.PushBack(conn)
|
|
||||||
connAccess.Unlock()
|
|
||||||
if KillerEnabled {
|
|
||||||
err := KillerCheck()
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &PacketConn{
|
|
||||||
PacketConn: conn,
|
|
||||||
element: element,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) Close() error {
|
|
||||||
if c.element.Value != nil {
|
|
||||||
connAccess.Lock()
|
|
||||||
if c.element.Value != nil {
|
|
||||||
openConnection.Remove(c.element)
|
|
||||||
c.element.Value = nil
|
|
||||||
}
|
|
||||||
connAccess.Unlock()
|
|
||||||
}
|
|
||||||
return c.PacketConn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) Upstream() any {
|
|
||||||
return bufio.NewPacketConn(c.PacketConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) ReaderReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) WriterReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package conntrack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/x/list"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
connAccess sync.RWMutex
|
|
||||||
openConnection list.List[io.Closer]
|
|
||||||
)
|
|
||||||
|
|
||||||
func Count() int {
|
|
||||||
if !Enabled {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return openConnection.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func List() []io.Closer {
|
|
||||||
if !Enabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
connAccess.RLock()
|
|
||||||
defer connAccess.RUnlock()
|
|
||||||
connList := make([]io.Closer, 0, openConnection.Len())
|
|
||||||
for element := openConnection.Front(); element != nil; element = element.Next() {
|
|
||||||
connList = append(connList, element.Value)
|
|
||||||
}
|
|
||||||
return connList
|
|
||||||
}
|
|
||||||
|
|
||||||
func Close() {
|
|
||||||
if !Enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
connAccess.Lock()
|
|
||||||
defer connAccess.Unlock()
|
|
||||||
for element := openConnection.Front(); element != nil; element = element.Next() {
|
|
||||||
common.Close(element.Value)
|
|
||||||
element.Value = nil
|
|
||||||
}
|
|
||||||
openConnection.Init()
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
//go:build !with_conntrack
|
|
||||||
|
|
||||||
package conntrack
|
|
||||||
|
|
||||||
const Enabled = false
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
//go:build with_conntrack
|
|
||||||
|
|
||||||
package conntrack
|
|
||||||
|
|
||||||
const Enabled = true
|
|
||||||
32
common/conntrack/tracker.go
Normal file
32
common/conntrack/tracker.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: add to N
|
||||||
|
type AbstractPacketConn interface {
|
||||||
|
Close() error
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
SetDeadline(t time.Time) error
|
||||||
|
SetReadDeadline(t time.Time) error
|
||||||
|
SetWriteDeadline(t time.Time) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tracker interface {
|
||||||
|
NewConn(conn net.Conn) (net.Conn, error)
|
||||||
|
NewPacketConn(conn net.PacketConn) (net.PacketConn, error)
|
||||||
|
NewConnEx(conn net.Conn) (N.CloseHandlerFunc, error)
|
||||||
|
NewPacketConnEx(conn AbstractPacketConn) (N.CloseHandlerFunc, error)
|
||||||
|
CheckConn(source netip.AddrPort, destination netip.AddrPort) bool
|
||||||
|
CheckPacketConn(source netip.AddrPort) bool
|
||||||
|
AddPendingDestination(destination netip.AddrPort) func()
|
||||||
|
CheckDestination(destination netip.AddrPort) bool
|
||||||
|
KillerCheck() error
|
||||||
|
Count() int
|
||||||
|
Close()
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DefaultDialer struct {
|
type DefaultDialer struct {
|
||||||
|
tracker conntrack.Tracker
|
||||||
dialer4 tcpDialer
|
dialer4 tcpDialer
|
||||||
dialer6 tcpDialer
|
dialer6 tcpDialer
|
||||||
udpDialer4 net.Dialer
|
udpDialer4 net.Dialer
|
||||||
@@ -46,6 +47,7 @@ type DefaultDialer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
||||||
|
tracker := service.FromContext[conntrack.Tracker](ctx)
|
||||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
|
|
||||||
@@ -197,6 +199,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &DefaultDialer{
|
return &DefaultDialer{
|
||||||
|
tracker: tracker,
|
||||||
dialer4: tcpDialer4,
|
dialer4: tcpDialer4,
|
||||||
dialer6: tcpDialer6,
|
dialer6: tcpDialer6,
|
||||||
udpDialer4: udpDialer4,
|
udpDialer4: udpDialer4,
|
||||||
@@ -219,18 +222,26 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
|||||||
return nil, E.New("invalid address")
|
return nil, E.New("invalid address")
|
||||||
}
|
}
|
||||||
if d.networkStrategy == nil {
|
if d.networkStrategy == nil {
|
||||||
|
if address.IsFqdn() {
|
||||||
|
return nil, E.New("unexpected domain destination")
|
||||||
|
}
|
||||||
|
// Since pending check is only used by ndis, it is not performed for non-windows connections which are only supported on platform clients
|
||||||
|
if d.tracker != nil {
|
||||||
|
done := d.tracker.AddPendingDestination(address.AddrPort())
|
||||||
|
defer done()
|
||||||
|
}
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
if !address.IsIPv6() {
|
if !address.IsIPv6() {
|
||||||
return trackConn(d.udpDialer4.DialContext(ctx, network, address.String()))
|
return d.trackConn(d.udpDialer4.DialContext(ctx, network, address.String()))
|
||||||
} else {
|
} else {
|
||||||
return trackConn(d.udpDialer6.DialContext(ctx, network, address.String()))
|
return d.trackConn(d.udpDialer6.DialContext(ctx, network, address.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !address.IsIPv6() {
|
if !address.IsIPv6() {
|
||||||
return trackConn(DialSlowContext(&d.dialer4, ctx, network, address))
|
return d.trackConn(DialSlowContext(&d.dialer4, ctx, network, address))
|
||||||
} else {
|
} else {
|
||||||
return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
|
return d.trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return d.DialParallelInterface(ctx, network, address, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
return d.DialParallelInterface(ctx, network, address, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
||||||
@@ -282,17 +293,17 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
|||||||
if !fastFallback && !isPrimary {
|
if !fastFallback && !isPrimary {
|
||||||
d.networkLastFallback.Store(time.Now())
|
d.networkLastFallback.Store(time.Now())
|
||||||
}
|
}
|
||||||
return trackConn(conn, nil)
|
return d.trackConn(conn, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
if d.networkStrategy == nil {
|
if d.networkStrategy == nil {
|
||||||
if destination.IsIPv6() {
|
if destination.IsIPv6() {
|
||||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
|
return d.trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
|
||||||
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4))
|
return d.trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4))
|
||||||
} else {
|
} else {
|
||||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
return d.trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return d.ListenSerialInterfacePacket(ctx, destination, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
return d.ListenSerialInterfacePacket(ctx, destination, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
||||||
@@ -329,33 +340,23 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return trackPacketConn(packetConn, nil)
|
return d.trackPacketConn(packetConn, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
||||||
udpListener := d.udpListener
|
return d.udpListener.ListenPacket(context.Background(), network, address)
|
||||||
udpListener.Control = control.Append(udpListener.Control, func(network, address string, conn syscall.RawConn) error {
|
|
||||||
for _, wgControlFn := range WgControlFns {
|
|
||||||
err := wgControlFn(network, address, conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return udpListener.ListenPacket(context.Background(), network, address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
func (d *DefaultDialer) trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||||
if !conntrack.Enabled || err != nil {
|
if d.tracker == nil || err != nil {
|
||||||
return conn, err
|
return conn, err
|
||||||
}
|
}
|
||||||
return conntrack.NewConn(conn)
|
return d.tracker.NewConn(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackPacketConn(conn net.PacketConn, err error) (net.PacketConn, error) {
|
func (d *DefaultDialer) trackPacketConn(conn net.PacketConn, err error) (net.PacketConn, error) {
|
||||||
if !conntrack.Enabled || err != nil {
|
if err != nil {
|
||||||
return conn, err
|
return conn, err
|
||||||
}
|
}
|
||||||
return conntrack.NewPacketConn(conn)
|
return d.tracker.NewPacketConn(conn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
|||||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||||
return nil, false, E.New("no available network interface")
|
return nil, false, E.New("no available network interface")
|
||||||
}
|
}
|
||||||
defaultInterface := d.networkManager.InterfaceMonitor().DefaultInterface()
|
|
||||||
if fallbackDelay == 0 {
|
if fallbackDelay == 0 {
|
||||||
fallbackDelay = N.DefaultFallbackDelay
|
fallbackDelay = N.DefaultFallbackDelay
|
||||||
}
|
}
|
||||||
@@ -32,9 +31,7 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
|||||||
results := make(chan dialResult) // unbuffered
|
results := make(chan dialResult) // unbuffered
|
||||||
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
||||||
perNetDialer := dialer
|
perNetDialer := dialer
|
||||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
|
||||||
}
|
|
||||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
@@ -92,7 +89,6 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
|||||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||||
return nil, false, E.New("no available network interface")
|
return nil, false, E.New("no available network interface")
|
||||||
}
|
}
|
||||||
defaultInterface := d.networkManager.InterfaceMonitor().DefaultInterface()
|
|
||||||
if fallbackDelay == 0 {
|
if fallbackDelay == 0 {
|
||||||
fallbackDelay = N.DefaultFallbackDelay
|
fallbackDelay = N.DefaultFallbackDelay
|
||||||
}
|
}
|
||||||
@@ -107,9 +103,7 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
|||||||
results := make(chan dialResult) // unbuffered
|
results := make(chan dialResult) // unbuffered
|
||||||
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
startRacer := func(ctx context.Context, primary bool, iif adapter.NetworkInterface) {
|
||||||
perNetDialer := dialer
|
perNetDialer := dialer
|
||||||
if defaultInterface == nil || iif.Index != defaultInterface.Index {
|
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
||||||
perNetDialer.Control = control.Append(perNetDialer.Control, control.BindToInterface(nil, iif.Name, iif.Index))
|
|
||||||
}
|
|
||||||
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
conn, err := perNetDialer.DialContext(ctx, network, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
@@ -155,13 +149,10 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
|
|||||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||||
return nil, E.New("no available network interface")
|
return nil, E.New("no available network interface")
|
||||||
}
|
}
|
||||||
defaultInterface := d.networkManager.InterfaceMonitor().DefaultInterface()
|
|
||||||
var errors []error
|
var errors []error
|
||||||
for _, primaryInterface := range primaryInterfaces {
|
for _, primaryInterface := range primaryInterfaces {
|
||||||
perNetListener := listener
|
perNetListener := listener
|
||||||
if defaultInterface == nil || primaryInterface.Index != defaultInterface.Index {
|
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, primaryInterface.Name, primaryInterface.Index))
|
||||||
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, primaryInterface.Name, primaryInterface.Index))
|
|
||||||
}
|
|
||||||
conn, err := perNetListener.ListenPacket(ctx, network, addr)
|
conn, err := perNetListener.ListenPacket(ctx, network, addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
@@ -170,9 +161,7 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
|
|||||||
}
|
}
|
||||||
for _, fallbackInterface := range fallbackInterfaces {
|
for _, fallbackInterface := range fallbackInterfaces {
|
||||||
perNetListener := listener
|
perNetListener := listener
|
||||||
if defaultInterface == nil || fallbackInterface.Index != defaultInterface.Index {
|
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, fallbackInterface.Name, fallbackInterface.Index))
|
||||||
perNetListener.Control = control.Append(perNetListener.Control, control.BindToInterface(nil, fallbackInterface.Name, fallbackInterface.Index))
|
|
||||||
}
|
|
||||||
conn, err := perNetListener.ListenPacket(ctx, network, addr)
|
conn, err := perNetListener.ListenPacket(ctx, network, addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
|
|||||||
158
common/humanize/bytes.go
Normal file
158
common/humanize/bytes.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package humanize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IEC Sizes.
|
||||||
|
// kibis of bits
|
||||||
|
const (
|
||||||
|
Byte = 1 << (iota * 10)
|
||||||
|
KiByte
|
||||||
|
MiByte
|
||||||
|
GiByte
|
||||||
|
TiByte
|
||||||
|
PiByte
|
||||||
|
EiByte
|
||||||
|
)
|
||||||
|
|
||||||
|
// SI Sizes.
|
||||||
|
const (
|
||||||
|
IByte = 1
|
||||||
|
KByte = IByte * 1000
|
||||||
|
MByte = KByte * 1000
|
||||||
|
GByte = MByte * 1000
|
||||||
|
TByte = GByte * 1000
|
||||||
|
PByte = TByte * 1000
|
||||||
|
EByte = PByte * 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultSizeTable = map[string]uint64{
|
||||||
|
"b": Byte,
|
||||||
|
"kib": KiByte,
|
||||||
|
"kb": KByte,
|
||||||
|
"mib": MiByte,
|
||||||
|
"mb": MByte,
|
||||||
|
"gib": GiByte,
|
||||||
|
"gb": GByte,
|
||||||
|
"tib": TiByte,
|
||||||
|
"tb": TByte,
|
||||||
|
"pib": PiByte,
|
||||||
|
"pb": PByte,
|
||||||
|
"eib": EiByte,
|
||||||
|
"eb": EByte,
|
||||||
|
// Without suffix
|
||||||
|
"": Byte,
|
||||||
|
"ki": KiByte,
|
||||||
|
"k": KByte,
|
||||||
|
"mi": MiByte,
|
||||||
|
"m": MByte,
|
||||||
|
"gi": GiByte,
|
||||||
|
"g": GByte,
|
||||||
|
"ti": TiByte,
|
||||||
|
"t": TByte,
|
||||||
|
"pi": PiByte,
|
||||||
|
"p": PByte,
|
||||||
|
"ei": EiByte,
|
||||||
|
"e": EByte,
|
||||||
|
}
|
||||||
|
|
||||||
|
var memorysSizeTable = map[string]uint64{
|
||||||
|
"b": Byte,
|
||||||
|
"kb": KiByte,
|
||||||
|
"mb": MiByte,
|
||||||
|
"gb": GiByte,
|
||||||
|
"tb": TiByte,
|
||||||
|
"pb": PiByte,
|
||||||
|
"eb": EiByte,
|
||||||
|
"": Byte,
|
||||||
|
"k": KiByte,
|
||||||
|
"m": MiByte,
|
||||||
|
"g": GiByte,
|
||||||
|
"t": TiByte,
|
||||||
|
"p": PiByte,
|
||||||
|
"e": EiByte,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
|
||||||
|
iSizes = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Bytes(s uint64) string {
|
||||||
|
return humanateBytes(s, 1000, defaultSizes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MemoryBytes(s uint64) string {
|
||||||
|
return humanateBytes(s, 1024, defaultSizes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IBytes(s uint64) string {
|
||||||
|
return humanateBytes(s, 1024, iSizes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logn(n, b float64) float64 {
|
||||||
|
return math.Log(n) / math.Log(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func humanateBytes(s uint64, base float64, sizes []string) string {
|
||||||
|
if s < 10 {
|
||||||
|
return fmt.Sprintf("%d B", s)
|
||||||
|
}
|
||||||
|
e := math.Floor(logn(float64(s), base))
|
||||||
|
suffix := sizes[int(e)]
|
||||||
|
val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
|
||||||
|
f := "%.0f %s"
|
||||||
|
if val < 10 {
|
||||||
|
f = "%.1f %s"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(f, val, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseBytes(s string) (uint64, error) {
|
||||||
|
return parseBytes0(s, defaultSizeTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseMemoryBytes(s string) (uint64, error) {
|
||||||
|
return parseBytes0(s, memorysSizeTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBytes0(s string, sizeTable map[string]uint64) (uint64, error) {
|
||||||
|
lastDigit := 0
|
||||||
|
hasComma := false
|
||||||
|
for _, r := range s {
|
||||||
|
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if r == ',' {
|
||||||
|
hasComma = true
|
||||||
|
}
|
||||||
|
lastDigit++
|
||||||
|
}
|
||||||
|
|
||||||
|
num := s[:lastDigit]
|
||||||
|
if hasComma {
|
||||||
|
num = strings.Replace(num, ",", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := strconv.ParseFloat(num, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
||||||
|
if m, ok := sizeTable[extra]; ok {
|
||||||
|
f *= float64(m)
|
||||||
|
if f >= math.MaxUint64 {
|
||||||
|
return 0, fmt.Errorf("too large: %v", s)
|
||||||
|
}
|
||||||
|
return uint64(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("unhandled size name: %v", extra)
|
||||||
|
}
|
||||||
@@ -124,6 +124,14 @@ func (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error)
|
|||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
row := b[4+itemSize*i : 4+itemSize*(i+1)]
|
row := b[4+itemSize*i : 4+itemSize*(i+1)]
|
||||||
|
|
||||||
|
if s.tcpState >= 0 {
|
||||||
|
tcpState := readNativeUint32(row[s.tcpState : s.tcpState+4])
|
||||||
|
// MIB_TCP_STATE_ESTAB, only check established connections for TCP
|
||||||
|
if tcpState != 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// according to MSDN, only the lower 16 bits of dwLocalPort are used and the port number is in network endian.
|
// according to MSDN, only the lower 16 bits of dwLocalPort are used and the port number is in network endian.
|
||||||
// this field can be illustrated as follows depends on different machine endianess:
|
// this field can be illustrated as follows depends on different machine endianess:
|
||||||
// little endian: [ MSB LSB 0 0 ] interpret as native uint32 is ((LSB<<8)|MSB)
|
// little endian: [ MSB LSB 0 0 ] interpret as native uint32 is ((LSB<<8)|MSB)
|
||||||
@@ -136,7 +144,7 @@ func (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error)
|
|||||||
|
|
||||||
srcIP, _ := netip.AddrFromSlice(row[s.ip : s.ip+s.ipSize])
|
srcIP, _ := netip.AddrFromSlice(row[s.ip : s.ip+s.ipSize])
|
||||||
// windows binds an unbound udp socket to 0.0.0.0/[::] while first sendto
|
// windows binds an unbound udp socket to 0.0.0.0/[::] while first sendto
|
||||||
if ip != srcIP && (!srcIP.IsUnspecified()) {
|
if ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
common/settings/time_stub.go
Normal file
12
common/settings/time_stub.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build !(windows || linux || darwin)
|
||||||
|
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetSystemTime(nowTime time.Time) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
14
common/settings/time_unix.go
Normal file
14
common/settings/time_unix.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//go:build linux || darwin
|
||||||
|
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetSystemTime(nowTime time.Time) error {
|
||||||
|
timeVal := unix.NsecToTimeval(nowTime.UnixNano())
|
||||||
|
return unix.Settimeofday(&timeVal)
|
||||||
|
}
|
||||||
32
common/settings/time_windows.go
Normal file
32
common/settings/time_windows.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetSystemTime(nowTime time.Time) error {
|
||||||
|
var systemTime windows.Systemtime
|
||||||
|
systemTime.Year = uint16(nowTime.Year())
|
||||||
|
systemTime.Month = uint16(nowTime.Month())
|
||||||
|
systemTime.Day = uint16(nowTime.Day())
|
||||||
|
systemTime.Hour = uint16(nowTime.Hour())
|
||||||
|
systemTime.Minute = uint16(nowTime.Minute())
|
||||||
|
systemTime.Second = uint16(nowTime.Second())
|
||||||
|
systemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000)
|
||||||
|
|
||||||
|
dllKernel32 := windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
proc := dllKernel32.NewProc("SetSystemTime")
|
||||||
|
|
||||||
|
_, _, err := proc.Call(
|
||||||
|
uintptr(unsafe.Pointer(&systemTime)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil && err.Error() != "The operation completed successfully." {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -24,25 +23,20 @@ func BitTorrent(_ context.Context, metadata *adapter.InboundContext, reader io.R
|
|||||||
var first byte
|
var first byte
|
||||||
err := binary.Read(reader, binary.BigEndian, &first)
|
err := binary.Read(reader, binary.BigEndian, &first)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if first != 19 {
|
if first != 19 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
const header = "BitTorrent protocol"
|
|
||||||
var protocol [19]byte
|
var protocol [19]byte
|
||||||
var n int
|
_, err = reader.Read(protocol[:])
|
||||||
n, err = reader.Read(protocol[:])
|
|
||||||
if string(protocol[:n]) != header[:n] {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
if n < 19 {
|
if string(protocol[:]) != "BitTorrent protocol" {
|
||||||
return ErrNeedMoreData
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.Protocol = C.ProtocolBitTorrent
|
metadata.Protocol = C.ProtocolBitTorrent
|
||||||
@@ -73,9 +67,7 @@ func UTP(_ context.Context, metadata *adapter.InboundContext, packet []byte) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if extension > 0x04 {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
var length byte
|
var length byte
|
||||||
err = binary.Read(reader, binary.BigEndian, &length)
|
err = binary.Read(reader, binary.BigEndian, &length)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -32,27 +32,6 @@ func TestSniffBittorrent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSniffIncompleteBittorrent(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
pkt, err := hex.DecodeString("13426974546f7272656e74")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.BitTorrent(context.TODO(), &metadata, bytes.NewReader(pkt))
|
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffNotBittorrent(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
pkt, err := hex.DecodeString("13426974546f7272656e75")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.BitTorrent(context.TODO(), &metadata, bytes.NewReader(pkt))
|
|
||||||
require.NotEmpty(t, err)
|
|
||||||
require.NotErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffUTP(t *testing.T) {
|
func TestSniffUTP(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -92,19 +71,3 @@ func TestSniffUDPTracker(t *testing.T) {
|
|||||||
require.Equal(t, C.ProtocolBitTorrent, metadata.Protocol)
|
require.Equal(t, C.ProtocolBitTorrent, metadata.Protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSniffNotUTP(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
packets := []string{
|
|
||||||
"0102736470696e674958d580121500000000000079aaed6717a39c27b07c0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
}
|
|
||||||
for _, pkt := range packets {
|
|
||||||
pkt, err := hex.DecodeString(pkt)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.UTP(context.TODO(), &metadata, pkt)
|
|
||||||
require.Error(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
"github.com/sagernet/sing/common/task"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
@@ -18,40 +21,35 @@ func StreamDomainNameQuery(readCtx context.Context, metadata *adapter.InboundCon
|
|||||||
var length uint16
|
var length uint16
|
||||||
err := binary.Read(reader, binary.BigEndian, &length)
|
err := binary.Read(reader, binary.BigEndian, &length)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
if length < 12 {
|
if length == 0 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
buffer := buf.NewSize(int(length))
|
buffer := buf.NewSize(int(length))
|
||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
var n int
|
readCtx, cancel := context.WithTimeout(readCtx, time.Millisecond*100)
|
||||||
n, err = buffer.ReadFullFrom(reader, buffer.FreeLen())
|
var readTask task.Group
|
||||||
packet := buffer.Bytes()
|
readTask.Append0(func(ctx context.Context) error {
|
||||||
if n > 2 && packet[2]&0x80 != 0 { // QR
|
return common.Error(buffer.ReadFullFrom(reader, buffer.FreeLen()))
|
||||||
return os.ErrInvalid
|
})
|
||||||
}
|
err = readTask.Run(readCtx)
|
||||||
if n > 5 && packet[4] == 0 && packet[5] == 0 { // QDCOUNT
|
cancel()
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
for i := 6; i < 10; i++ {
|
|
||||||
// ANCOUNT, NSCOUNT
|
|
||||||
if n > i && packet[i] != 0 {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
return DomainNameQuery(readCtx, metadata, packet)
|
return DomainNameQuery(readCtx, metadata, buffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func DomainNameQuery(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error {
|
func DomainNameQuery(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error {
|
||||||
var msg mDNS.Msg
|
var msg mDNS.Msg
|
||||||
err := msg.Unpack(packet)
|
err := msg.Unpack(packet)
|
||||||
if err != nil || msg.Response || len(msg.Question) == 0 || len(msg.Answer) > 0 || len(msg.Ns) > 0 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(msg.Question) == 0 || msg.Question[0].Qclass != mDNS.ClassINET || !M.IsDomainName(msg.Question[0].Name) {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
metadata.Protocol = C.ProtocolDNS
|
metadata.Protocol = C.ProtocolDNS
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package sniff_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/sniff"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSniffDNS(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
query, err := hex.DecodeString("740701000001000000000000012a06676f6f676c6503636f6d0000010001")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.DomainNameQuery(context.TODO(), &metadata, query)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, C.ProtocolDNS, metadata.Protocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffStreamDNS(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
query, err := hex.DecodeString("001e740701000001000000000000012a06676f6f676c6503636f6d0000010001")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query))
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, C.ProtocolDNS, metadata.Protocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffIncompleteStreamDNS(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
query, err := hex.DecodeString("001e740701000001000000000000")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query))
|
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffNotStreamDNS(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
query, err := hex.DecodeString("001e740701000000000000000000")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query))
|
|
||||||
require.NotEmpty(t, err)
|
|
||||||
require.NotErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
}
|
|
||||||
@@ -3,12 +3,10 @@ package sniff
|
|||||||
import (
|
import (
|
||||||
std_bufio "bufio"
|
std_bufio "bufio"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
"github.com/sagernet/sing/protocol/http"
|
"github.com/sagernet/sing/protocol/http"
|
||||||
)
|
)
|
||||||
@@ -16,11 +14,7 @@ import (
|
|||||||
func HTTPHost(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
|
func HTTPHost(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
|
||||||
request, err := http.ReadRequest(std_bufio.NewReader(reader))
|
request, err := http.ReadRequest(std_bufio.NewReader(reader))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.ErrUnexpectedEOF) {
|
return err
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
metadata.Protocol = C.ProtocolHTTP
|
metadata.Protocol = C.ProtocolHTTP
|
||||||
metadata.Domain = M.ParseSocksaddr(request.Host).AddrString()
|
metadata.Domain = M.ParseSocksaddr(request.Host).AddrString()
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import (
|
|||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrClientHelloFragmented = E.New("need more packet for chromium QUIC connection")
|
||||||
|
|
||||||
func QUICClientHello(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error {
|
func QUICClientHello(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error {
|
||||||
reader := bytes.NewReader(packet)
|
reader := bytes.NewReader(packet)
|
||||||
typeByte, err := reader.ReadByte()
|
typeByte, err := reader.ReadByte()
|
||||||
@@ -306,7 +308,7 @@ find:
|
|||||||
metadata.Protocol = C.ProtocolQUIC
|
metadata.Protocol = C.ProtocolQUIC
|
||||||
metadata.Client = C.ClientChromium
|
metadata.Client = C.ClientChromium
|
||||||
metadata.SniffContext = fragments
|
metadata.SniffContext = fragments
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return ErrClientHelloFragmented
|
||||||
}
|
}
|
||||||
metadata.Domain = fingerprint.ServerName
|
metadata.Domain = fingerprint.ServerName
|
||||||
for metadata.Client == "" {
|
for metadata.Client == "" {
|
||||||
|
|||||||
@@ -12,26 +12,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSniffQUICChromeNew(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
pkt, err := hex.DecodeString("ca0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea4489ad89c322f75f9a383c90d126a0b21104cb519c2bb32e6a134e86896452e942b26c519b8c7ac9e4c99fae5e1f65cf08fb98443b30e4567932e8fb0789820d8f33037b59ac8113530258c9467dfb52489396dae01f099d28b234efa107fa411f2a1ffa2abe74988e03d662d4296024e95ce0fe1671724937157f77b84990478a2d4060676cf0827b4e8c600654111750414dafa0cccb332f3020c2922a015f445df5edc9c7d2d1ceea9fddcc9ff821c9183aa39a70da20fcc057579e1051c1c899148d6cf9d08b4919822082d040d1ce03ca4f216be6cb7ef03db6df0993ef1ccce5c8c648980554f41704526e1809d2545739f5872e75ec797db1c99f5682e2eda9363cb32aa367b7b363c782ddbacf874183cc15c8a2db068dd4093eebdd096ad33832a7939deb0a872279744f5a56dc001ba62fac973bf680f3b362bdd336add4dd102f462b773bf70bfce1921070a802a92025273a177186d1a643081b42175eb789ccddadb71033ef4feacbf6fd282ab622cf61669d73cda559e411c6ccdd8f003443b6933b7729b7a357aa4aa2fba0f365f829a4d497afb5dc2648a53bc9f3e786d955069d0a4781088a5463747dfe9958ea19ea444eae947ec6a67640955f710f93640084f3fbb8ad259b68dbc0ee0b7fab2d81bffd83ed8a6d33522dbfef43bec0a0fb4bdf1cb712dc4ced0680c0687fa240fd157baa232b1c84e14adce6421cf9270f9b3972f98fc67b344b8a4f1fb551e26f7f76d484ed9f8197f231dc5d9a44cc0ddce73d7f810a620851f4e97eb5037ab5135d7c3be5b80cc32d19910b8387aca64c93c02dc3e35238b78e6aff470722078982e58802844932b6041446bfdcc97ba640cbb86721bcd0f40f27b77aa6287ce5674ec1720134b9302875482c3269787e004b9edb483d44f326eef38c0e83cb46af96488c2e696bc2524567fb29c1e8edcd5a73615496d172d46a9d29e0505c0018b7bbb00165eca0389e09c4b1d73b6cc4a2f735a720650134a2e98e8105e20695cf231b92586237dfe0f99c897414e51c21627496276535f07abb53fb2b554376fe520fa45a3e944fd91dfe7a72aead08842b6b63d8edf861fb911954c83bd9a896eb9da4af5eff646455069d747facd4e77c254096843bff7c3e9031dbdf8dc37ea45f1122922fcbc322ec1378f3c7c1af0da62e1052e6210f1b23073f93a82d90e14cb20bc4501d487a1c848674d57a7c269b13590b3a99d8b8b4f6d0dfbd1d2cbbe7a32c0d5c84ae7ec438b0b19f3862d8fabaa828d06c7e3c6967405cd56a1ae90f38633e2ee0e3ecfca3df399fe12f029e0860a1a30da010300d0c94f0bf56091d00011488c1429928b21c739ebf50ba8be91116315d3173f6d2c56735722478c4d74392ba84d1727036b3d64e8c2263b0f33cb8086be587ca6b3940259c06afa2683868856529303ae12e91d7ca874568be7f2bfaa0656dfab0ed31ed90eaea10fb7f3433ec59a334abe6211d547fa0c825ac45d3691e749d15432008de83e9f6d98f368359137ae803d9189b3386f800c7c0cf4b615d1983cf82d9981a8105b60a80fe66c9b0d439b5ba153dd19e9e7483a01cf3b02b4597540b38e658d4eb8455e030b2bf2690bdd78c23f16fe5")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
pkt, err = hex.DecodeString("c20000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea4489e2ff30c43a5f63beb2e4501ce7754085bcbe838003a0b4bccb53863c0766df7eac073c2bdc170772b157997945acdc2ab2e84750cc9aa0ffa0fdc023da7fc565a14f87f7c563dbc9183dd226aab79957d263f66e64b85a1b15a24516bd2c7c04eea4fa0a34ef9849c21585db2e4adb7c05e265c4f38d8ffe4cbed0f3b0e68f3693bf1f726c3fb135b8e32a5d22931d7c55fc2ff4b9a354933ab14544df3cdaf3e3217dfb8d7feb3465dc34df6320ea486f12e5b2d609aaa5f4515c20c86fc440f8087be0ee3d339835746ae2573c2afdee6bb6ef7e9eb541feae9209391b2902cfb0bdaccd9da8d290714638b7da588d4a656ca6eabba78b7363922d6037cf060b161a42019d4feb4156459103cffdeefd0e63114af2b0e0c39e70ebc7fecb8dd1ebb8d60b2137f509bb7dcef5f1d3e06ab1d391466652d57440a410fb4f58a6ce1fb62feb453241f64e110709f59a3d9ebdac94f811337d0e4a80fd6b56b2a70cd6eebbf98e1661291da6bf5beb8b8afc376dfd20eb76afe709e8e8f28e0ef82105954e346546ad25973df43f4acddbec0ffd9b215f62abebebf71305b5ea993560316f69430bf5afe50420340622f802b5830f3bcebffff04980c75a59d28902879e5d51a4fb21062a4ae13c42297075b21d54ee04303879c1157e7470c1451673c98a2f3921f2f3e8f6acfe85b01caaca66b59e5ebffbfe68e5e9ab17e9a1b857eb409df91cb76767fc1814fd3c522a9b117edd0b02526e469cb4afb291a4dcc74c79b47ec6e7ce558c597129366f83ec306b11d2598c705fd4ee9ee99df6b7039bef13b08fc6f26853ad213829d24f895747d45a47414f931c583fb6c3e4f6c27d0c2b81a5f3cee390ec6314e1fec637e8d28b675e97caafdfbf8c25d34a635083a7553d219dd80dbb39087d74c6ad6192ca6f48a3ff8d47db41b2a492c63fcd780012780931dae0a325f9dcbd772d09a700f132c4bc1d9809b25b9751b694eb72a8ba4db7208d2b1bab63e1845208e4f841ea30218a559db98751589716b6d059ca673378f5fe7c7d8a1c82e14a561c47313bbcc278412ba86ffb2b87ec308eab9df696f5b4b54f8e361731bf232820a02a35fda7e5d4bf01b8f005ad299a055116e7b23c181f15a66442cf6032ca477bccc55b79d424eb4f245847bd81a581dc369dd20b1a4892733bde3c38e492c0039f69f2b947a4dc251a49ee7ccc0f36b3b75a555fa1d126db75f94dab60f52f6b15a877a0c380b59f82d35c570bc5f8051e9ef87db51f52383d47b50829b7f9e947ccc67aa280566aa48b4a85c1c7eca6f542789d8abcc050f1aa3cc221b6859656a21454aa21c7bfb9d12115f61c3ed46263ade68a8d3679fa62a659a5da7817406bd16618fccf33ed208ada1b03584e8b485d3cb6ed80a0774e60b6cd55aff64169ea998cf8235997049515abac58e0169ca07fb1c8c4c8b2803ba9d27b44c045d0a1cac86e5e188195c68001f53eb44851b6d821fc01ccbb41e27f38e6ddd66540c2d62ed6e0d551e22c0f26b60078c74a6302a1ed3d9e8fc0861257a63f6ac4e759fd54bff088becd28e30944a6c15db4fc8ae6244346869add946d9d92c430d737e042fa18b28a8ed64d1e8987ad9061cdc1335f")
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "www.google.com", metadata.Domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffQUICChromium(t *testing.T) {
|
func TestSniffQUICChromium(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
pkt, err := hex.DecodeString("c30000000108f40d654cc09b27f5000044d08a94548e57e43cc5483f129986187c432d58d46674830442988f869566a6e31e2ae37c9f7acbf61cc81621594fab0b3dfdc1635460b32389563dc8e74006315661cd22694114612973c1c45910621713a48b375854f095e8a77ccf3afa64e972f0f7f7002f50e0b014b1b146ea47c07fb20b73ad5587872b51a0b3fafdf1c4cf4fe6f8b112142392efa25d993abe2f42582be145148bdfe12edcd96c3655b65a4781b093e5594ba8e3ae5320f12e8314fc3ca374128cc43381046c322b964681ed4395c813b28534505118201459665a44b8f0abead877de322e9040631d20b05f15b81fa7ff785d4041aecc37c7e2ccdc5d1532787ce566517e8985fd5c200dbfd1e67bc255efaba94cfc07bb52fea4a90887413b134f2715b5643542aa897c6116486f428d82da64d2a2c1e1bdd40bd592558901a554b003d6966ac5a7b8b9413eddbf6ef21f28386c74981e3ce1d724c341e95494907626659692720c81114ca4acea35a14c402cfa3dc2228446e78dc1b81fa4325cf7e314a9cad6a6bdff33b3351dcba74eb15fae67f1227283aa4cdd64bcadf8f19358333f8549b596f4350297b5c65274565869d497398339947b9d3d064e5b06d39d34b436d8a41c1a3880de10bd26c3b1c5b4e2a49b0d4d07b8d90cd9e92bc611564d19ea8ec33099e92033caf21f5307dbeaa4708b99eb313bff99e2081ac25fd12d6a72e8335e0724f6718fe023cd0ad0d6e6a6309f09c9c391eec2bc08e9c3210a043c08e1759f354c121f6517fff4d6e20711a871e41285d48d930352fddffb92c96ba57df045ce99f8bfdfa8edc0969ce68a51e9fbb4f54b956d9df74a9e4af27ed2b27839bce1cffeca8333c0aaee81a570217442f9029ba8fedb84a2cf4be4d910982d891ea00e816c7fb98e8020e896a9c6fdd9106611da0a99dde18df1b7a8f6327acb1eed9ad93314451e48cb0dfb9571728521ca3db2ac0968159d5622556a55d51a422d11995b650949aaefc5d24c16080446dfc4fbc10353f9f93ce161ab513367bb89ab83988e0630b689e174e27bcfcc31996ee7b0bca909e251b82d69a28fee5a5d662e127508cd19dbbe5097b7d5b62a49203d66764197a527e472e2627e44a93d44177dace9d60e7d0e03305ddf4cfe47cdf2362e14de79ef46a6763ce696cd7854a48d9419a0817507a4713ffd4977b906d4f2b5fb6dbe1bd15bc505d5fea582190bf531a45d5ee026da8918547fd5105f15e5d061c7b0cf80a34990366ed8e91e13c2f0d85e5dad537298808d193cf54b7eaac33f10051f74cb6b75e52f81618c36f03d86aef613ba237a1a793ba1539938a38f62ccaf7bd5f6c5e0ce53cde4012fcf2b758214a0422d2faaa798e86e19d7481b42df2b36a73d287ff28c20cce01ce598771fec16a8f1f00305c06010126013a6c1de9f589b4e79d693717cd88ad1c42a2d99fa96617ba0bc6365b68e21a70ebc447904aa27979e1514433cfd83bfec09f137c747d47582cb63eb28f873fb94cf7a59ff764ddfbb687d79a58bb10f85949269f7f72c611a5e0fbb52adfa298ff060ec2eb7216fd7302ea8fb07798cbb3be25cb53ac8161aac2b5bbcfbcfb01c113d28bd1cb0333fb89ac82a95930f7abded0a2f5a623cc6a1f62bf3f38ef1b81c1e50a634f657dbb6770e4af45879e2fb1e00c742e7b52205c8015b5c0f5b1e40186ff9aa7288ab3e01a51fb87761f9bc6837082af109b39cc9f620")
|
pkt, err := hex.DecodeString("c30000000108f40d654cc09b27f5000044d08a94548e57e43cc5483f129986187c432d58d46674830442988f869566a6e31e2ae37c9f7acbf61cc81621594fab0b3dfdc1635460b32389563dc8e74006315661cd22694114612973c1c45910621713a48b375854f095e8a77ccf3afa64e972f0f7f7002f50e0b014b1b146ea47c07fb20b73ad5587872b51a0b3fafdf1c4cf4fe6f8b112142392efa25d993abe2f42582be145148bdfe12edcd96c3655b65a4781b093e5594ba8e3ae5320f12e8314fc3ca374128cc43381046c322b964681ed4395c813b28534505118201459665a44b8f0abead877de322e9040631d20b05f15b81fa7ff785d4041aecc37c7e2ccdc5d1532787ce566517e8985fd5c200dbfd1e67bc255efaba94cfc07bb52fea4a90887413b134f2715b5643542aa897c6116486f428d82da64d2a2c1e1bdd40bd592558901a554b003d6966ac5a7b8b9413eddbf6ef21f28386c74981e3ce1d724c341e95494907626659692720c81114ca4acea35a14c402cfa3dc2228446e78dc1b81fa4325cf7e314a9cad6a6bdff33b3351dcba74eb15fae67f1227283aa4cdd64bcadf8f19358333f8549b596f4350297b5c65274565869d497398339947b9d3d064e5b06d39d34b436d8a41c1a3880de10bd26c3b1c5b4e2a49b0d4d07b8d90cd9e92bc611564d19ea8ec33099e92033caf21f5307dbeaa4708b99eb313bff99e2081ac25fd12d6a72e8335e0724f6718fe023cd0ad0d6e6a6309f09c9c391eec2bc08e9c3210a043c08e1759f354c121f6517fff4d6e20711a871e41285d48d930352fddffb92c96ba57df045ce99f8bfdfa8edc0969ce68a51e9fbb4f54b956d9df74a9e4af27ed2b27839bce1cffeca8333c0aaee81a570217442f9029ba8fedb84a2cf4be4d910982d891ea00e816c7fb98e8020e896a9c6fdd9106611da0a99dde18df1b7a8f6327acb1eed9ad93314451e48cb0dfb9571728521ca3db2ac0968159d5622556a55d51a422d11995b650949aaefc5d24c16080446dfc4fbc10353f9f93ce161ab513367bb89ab83988e0630b689e174e27bcfcc31996ee7b0bca909e251b82d69a28fee5a5d662e127508cd19dbbe5097b7d5b62a49203d66764197a527e472e2627e44a93d44177dace9d60e7d0e03305ddf4cfe47cdf2362e14de79ef46a6763ce696cd7854a48d9419a0817507a4713ffd4977b906d4f2b5fb6dbe1bd15bc505d5fea582190bf531a45d5ee026da8918547fd5105f15e5d061c7b0cf80a34990366ed8e91e13c2f0d85e5dad537298808d193cf54b7eaac33f10051f74cb6b75e52f81618c36f03d86aef613ba237a1a793ba1539938a38f62ccaf7bd5f6c5e0ce53cde4012fcf2b758214a0422d2faaa798e86e19d7481b42df2b36a73d287ff28c20cce01ce598771fec16a8f1f00305c06010126013a6c1de9f589b4e79d693717cd88ad1c42a2d99fa96617ba0bc6365b68e21a70ebc447904aa27979e1514433cfd83bfec09f137c747d47582cb63eb28f873fb94cf7a59ff764ddfbb687d79a58bb10f85949269f7f72c611a5e0fbb52adfa298ff060ec2eb7216fd7302ea8fb07798cbb3be25cb53ac8161aac2b5bbcfbcfb01c113d28bd1cb0333fb89ac82a95930f7abded0a2f5a623cc6a1f62bf3f38ef1b81c1e50a634f657dbb6770e4af45879e2fb1e00c742e7b52205c8015b5c0f5b1e40186ff9aa7288ab3e01a51fb87761f9bc6837082af109b39cc9f620")
|
||||||
@@ -40,7 +20,7 @@ func TestSniffQUICChromium(t *testing.T) {
|
|||||||
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.Equal(t, metadata.Client, C.ClientChromium)
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
require.ErrorIs(t, err, sniff.ErrClientHelloFragmented)
|
||||||
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
|
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
var tpktVersion uint8
|
var tpktVersion uint8
|
||||||
err := binary.Read(reader, binary.BigEndian, &tpktVersion)
|
err := binary.Read(reader, binary.BigEndian, &tpktVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
if tpktVersion != 0x03 {
|
if tpktVersion != 0x03 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
@@ -25,7 +24,7 @@ func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
var tpktReserved uint8
|
var tpktReserved uint8
|
||||||
err = binary.Read(reader, binary.BigEndian, &tpktReserved)
|
err = binary.Read(reader, binary.BigEndian, &tpktReserved)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
if tpktReserved != 0x00 {
|
if tpktReserved != 0x00 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
@@ -34,7 +33,7 @@ func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
var tpktLength uint16
|
var tpktLength uint16
|
||||||
err = binary.Read(reader, binary.BigEndian, &tpktLength)
|
err = binary.Read(reader, binary.BigEndian, &tpktLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if tpktLength != 19 {
|
if tpktLength != 19 {
|
||||||
@@ -44,7 +43,7 @@ func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
var cotpLength uint8
|
var cotpLength uint8
|
||||||
err = binary.Read(reader, binary.BigEndian, &cotpLength)
|
err = binary.Read(reader, binary.BigEndian, &cotpLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cotpLength != 14 {
|
if cotpLength != 14 {
|
||||||
@@ -54,7 +53,7 @@ func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
var cotpTpduType uint8
|
var cotpTpduType uint8
|
||||||
err = binary.Read(reader, binary.BigEndian, &cotpTpduType)
|
err = binary.Read(reader, binary.BigEndian, &cotpTpduType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
if cotpTpduType != 0xE0 {
|
if cotpTpduType != 0xE0 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
@@ -62,13 +61,13 @@ func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
|
|
||||||
err = rw.SkipN(reader, 5)
|
err = rw.SkipN(reader, 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var rdpType uint8
|
var rdpType uint8
|
||||||
err = binary.Read(reader, binary.BigEndian, &rdpType)
|
err = binary.Read(reader, binary.BigEndian, &rdpType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
if rdpType != 0x01 {
|
if rdpType != 0x01 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
@@ -76,12 +75,12 @@ func RDP(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
var rdpFlags uint8
|
var rdpFlags uint8
|
||||||
err = binary.Read(reader, binary.BigEndian, &rdpFlags)
|
err = binary.Read(reader, binary.BigEndian, &rdpFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
var rdpLength uint8
|
var rdpLength uint8
|
||||||
err = binary.Read(reader, binary.BigEndian, &rdpLength)
|
err = binary.Read(reader, binary.BigEndian, &rdpLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return err
|
||||||
}
|
}
|
||||||
if rdpLength != 8 {
|
if rdpLength != 8 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ package sniff
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
@@ -20,8 +18,6 @@ type (
|
|||||||
PacketSniffer = func(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error
|
PacketSniffer = func(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNeedMoreData = E.New("need more data")
|
|
||||||
|
|
||||||
func Skip(metadata *adapter.InboundContext) bool {
|
func Skip(metadata *adapter.InboundContext) bool {
|
||||||
// skip server first protocols
|
// skip server first protocols
|
||||||
switch metadata.Destination.Port {
|
switch metadata.Destination.Port {
|
||||||
@@ -38,12 +34,12 @@ func Skip(metadata *adapter.InboundContext) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.Conn, buffers []*buf.Buffer, buffer *buf.Buffer, timeout time.Duration, sniffers ...StreamSniffer) error {
|
func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.Conn, buffer *buf.Buffer, timeout time.Duration, sniffers ...StreamSniffer) error {
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
timeout = C.ReadPayloadTimeout
|
timeout = C.ReadPayloadTimeout
|
||||||
}
|
}
|
||||||
deadline := time.Now().Add(timeout)
|
deadline := time.Now().Add(timeout)
|
||||||
var sniffError error
|
var errors []error
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
err := conn.SetReadDeadline(deadline)
|
err := conn.SetReadDeadline(deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,32 +53,26 @@ func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.
|
|||||||
}
|
}
|
||||||
return E.Cause(err, "read payload")
|
return E.Cause(err, "read payload")
|
||||||
}
|
}
|
||||||
sniffError = nil
|
errors = nil
|
||||||
for _, sniffer := range sniffers {
|
for _, sniffer := range sniffers {
|
||||||
reader := io.MultiReader(common.Map(append(buffers, buffer), func(it *buf.Buffer) io.Reader {
|
err = sniffer(ctx, metadata, bytes.NewReader(buffer.Bytes()))
|
||||||
return bytes.NewReader(it.Bytes())
|
|
||||||
})...)
|
|
||||||
err = sniffer(ctx, metadata, reader)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sniffError = E.Errors(sniffError, err)
|
errors = append(errors, err)
|
||||||
}
|
|
||||||
if !errors.Is(sniffError, ErrNeedMoreData) {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sniffError
|
return E.Errors(errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PeekPacket(ctx context.Context, metadata *adapter.InboundContext, packet []byte, sniffers ...PacketSniffer) error {
|
func PeekPacket(ctx context.Context, metadata *adapter.InboundContext, packet []byte, sniffers ...PacketSniffer) error {
|
||||||
var sniffError []error
|
var errors []error
|
||||||
for _, sniffer := range sniffers {
|
for _, sniffer := range sniffers {
|
||||||
err := sniffer(ctx, metadata, packet)
|
err := sniffer(ctx, metadata, packet)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sniffError = append(sniffError, err)
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
return E.Errors(sniffError...)
|
return E.Errors(errors...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,27 +5,22 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func SSH(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
|
func SSH(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
|
||||||
const sshPrefix = "SSH-2.0-"
|
scanner := bufio.NewScanner(reader)
|
||||||
bReader := bufio.NewReader(reader)
|
if !scanner.Scan() {
|
||||||
prefix, err := bReader.Peek(len(sshPrefix))
|
|
||||||
if string(prefix[:]) != sshPrefix[:len(prefix)] {
|
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
if err != nil {
|
fistLine := scanner.Text()
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
if !strings.HasPrefix(fistLine, "SSH-2.0-") {
|
||||||
}
|
return os.ErrInvalid
|
||||||
fistLine, _, err := bReader.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
metadata.Protocol = C.ProtocolSSH
|
metadata.Protocol = C.ProtocolSSH
|
||||||
metadata.Client = string(fistLine)[8:]
|
metadata.Client = fistLine[8:]
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,24 +24,3 @@ func TestSniffSSH(t *testing.T) {
|
|||||||
require.Equal(t, C.ProtocolSSH, metadata.Protocol)
|
require.Equal(t, C.ProtocolSSH, metadata.Protocol)
|
||||||
require.Equal(t, "dropbear", metadata.Client)
|
require.Equal(t, "dropbear", metadata.Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSniffIncompleteSSH(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
pkt, err := hex.DecodeString("5353482d322e30")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.SSH(context.TODO(), &metadata, bytes.NewReader(pkt))
|
|
||||||
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffNotSSH(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
pkt, err := hex.DecodeString("5353482d322e31")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.SSH(context.TODO(), &metadata, bytes.NewReader(pkt))
|
|
||||||
require.NotEmpty(t, err)
|
|
||||||
require.NotErrorIs(t, err, sniff.ErrNeedMoreData)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ package sniff
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TLSClientHello(ctx context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
|
func TLSClientHello(ctx context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
|
||||||
@@ -25,9 +23,5 @@ func TLSClientHello(ctx context.Context, metadata *adapter.InboundContext, reade
|
|||||||
metadata.Domain = clientHello.ServerName
|
metadata.Domain = clientHello.ServerName
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if errors.Is(err, io.ErrUnexpectedEOF) {
|
return err
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
func GenerateCertificate(timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||||
privateKeyPem, publicKeyPem, err := GenerateCertificate(parent, parentKey, timeFunc, serverName, timeFunc().Add(time.Hour))
|
privateKeyPem, publicKeyPem, err := GenerateKeyPair(timeFunc, serverName, timeFunc().Add(time.Hour))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() ti
|
|||||||
return &certificate, err
|
return &certificate, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateCertificate(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string, expire time.Time) (privateKeyPem []byte, publicKeyPem []byte, err error) {
|
func GenerateKeyPair(timeFunc func() time.Time, serverName string, expire time.Time) (privateKeyPem []byte, publicKeyPem []byte, err error) {
|
||||||
if timeFunc == nil {
|
if timeFunc == nil {
|
||||||
timeFunc = time.Now
|
timeFunc = time.Now
|
||||||
}
|
}
|
||||||
@@ -47,11 +47,7 @@ func GenerateCertificate(parent *x509.Certificate, parentKey any, timeFunc func(
|
|||||||
},
|
},
|
||||||
DNSNames: []string{serverName},
|
DNSNames: []string{serverName},
|
||||||
}
|
}
|
||||||
if parent == nil {
|
publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
||||||
parent = template
|
|
||||||
parentKey = key
|
|
||||||
}
|
|
||||||
publicDer, err := x509.CreateCertificate(rand.Reader, template, parent, key.Public(), parentKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
|
|||||||
return nil, E.New("reality verification failed")
|
return nil, E.New("reality verification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &realityClientConnWrapper{uConn}, nil
|
return &utlsConnWrapper{uConn}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) {
|
func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) {
|
||||||
@@ -249,36 +249,3 @@ func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChain
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type realityClientConnWrapper struct {
|
|
||||||
*utls.UConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *realityClientConnWrapper) ConnectionState() tls.ConnectionState {
|
|
||||||
state := c.Conn.ConnectionState()
|
|
||||||
//nolint:staticcheck
|
|
||||||
return tls.ConnectionState{
|
|
||||||
Version: state.Version,
|
|
||||||
HandshakeComplete: state.HandshakeComplete,
|
|
||||||
DidResume: state.DidResume,
|
|
||||||
CipherSuite: state.CipherSuite,
|
|
||||||
NegotiatedProtocol: state.NegotiatedProtocol,
|
|
||||||
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
|
|
||||||
ServerName: state.ServerName,
|
|
||||||
PeerCertificates: state.PeerCertificates,
|
|
||||||
VerifiedChains: state.VerifiedChains,
|
|
||||||
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
|
|
||||||
OCSPResponse: state.OCSPResponse,
|
|
||||||
TLSUnique: state.TLSUnique,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *realityClientConnWrapper) Upstream() any {
|
|
||||||
return c.UConn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Due to low implementation quality, the reality server intercepted half close and caused memory leaks.
|
|
||||||
// We fixed it by calling Close() directly.
|
|
||||||
func (c *realityClientConnWrapper) CloseWrite() error {
|
|
||||||
return c.Close()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -89,20 +89,16 @@ func NewRealityServer(ctx context.Context, logger log.Logger, options option.Inb
|
|||||||
tlsConfig.MaxTimeDiff = time.Duration(options.Reality.MaxTimeDifference)
|
tlsConfig.MaxTimeDiff = time.Duration(options.Reality.MaxTimeDifference)
|
||||||
|
|
||||||
tlsConfig.ShortIds = make(map[[8]byte]bool)
|
tlsConfig.ShortIds = make(map[[8]byte]bool)
|
||||||
if len(options.Reality.ShortID) == 0 {
|
for i, shortIDString := range options.Reality.ShortID {
|
||||||
tlsConfig.ShortIds[[8]byte{0}] = true
|
var shortID [8]byte
|
||||||
} else {
|
decodedLen, err := hex.Decode(shortID[:], []byte(shortIDString))
|
||||||
for i, shortIDString := range options.Reality.ShortID {
|
if err != nil {
|
||||||
var shortID [8]byte
|
return nil, E.Cause(err, "decode short_id[", i, "]: ", shortIDString)
|
||||||
decodedLen, err := hex.Decode(shortID[:], []byte(shortIDString))
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "decode short_id[", i, "]: ", shortIDString)
|
|
||||||
}
|
|
||||||
if decodedLen > 8 {
|
|
||||||
return nil, E.New("invalid short_id[", i, "]: ", shortIDString)
|
|
||||||
}
|
|
||||||
tlsConfig.ShortIds[shortID] = true
|
|
||||||
}
|
}
|
||||||
|
if decodedLen > 8 {
|
||||||
|
return nil, E.New("invalid short_id[", i, "]: ", shortIDString)
|
||||||
|
}
|
||||||
|
tlsConfig.ShortIds[shortID] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
handshakeDialer, err := dialer.New(ctx, options.Reality.Handshake.DialerOptions)
|
handshakeDialer, err := dialer.New(ctx, options.Reality.Handshake.DialerOptions)
|
||||||
@@ -198,9 +194,3 @@ func (c *realityConnWrapper) ConnectionState() ConnectionState {
|
|||||||
func (c *realityConnWrapper) Upstream() any {
|
func (c *realityConnWrapper) Upstream() any {
|
||||||
return c.Conn
|
return c.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due to low implementation quality, the reality server intercepted half close and caused memory leaks.
|
|
||||||
// We fixed it by calling Close() directly.
|
|
||||||
func (c *realityConnWrapper) CloseWrite() error {
|
|
||||||
return c.Close()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/fswatch"
|
"github.com/sagernet/fswatch"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@@ -222,12 +221,8 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
key = content
|
key = content
|
||||||
}
|
}
|
||||||
if certificate == nil && key == nil && options.Insecure {
|
if certificate == nil && key == nil && options.Insecure {
|
||||||
timeFunc := ntp.TimeFuncFromContext(ctx)
|
|
||||||
if timeFunc == nil {
|
|
||||||
timeFunc = time.Now
|
|
||||||
}
|
|
||||||
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
return GenerateKeyPair(nil, nil, timeFunc, info.ServerName)
|
return GenerateCertificate(ntp.TimeFuncFromContext(ctx), info.ServerName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if certificate == nil {
|
if certificate == nil {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const (
|
|||||||
TypeVLESS = "vless"
|
TypeVLESS = "vless"
|
||||||
TypeTUIC = "tuic"
|
TypeTUIC = "tuic"
|
||||||
TypeHysteria2 = "hysteria2"
|
TypeHysteria2 = "hysteria2"
|
||||||
|
TypeNDIS = "ndis"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -80,6 +81,8 @@ func ProxyDisplayName(proxyType string) string {
|
|||||||
return "Selector"
|
return "Selector"
|
||||||
case TypeURLTest:
|
case TypeURLTest:
|
||||||
return "URLTest"
|
return "URLTest"
|
||||||
|
case TypeNDIS:
|
||||||
|
return "NDIS"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
|
|||||||
9
debug.go
9
debug.go
@@ -3,7 +3,6 @@ package box
|
|||||||
import (
|
import (
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/conntrack"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,11 +23,7 @@ func applyDebugOptions(options option.DebugOptions) {
|
|||||||
if options.TraceBack != "" {
|
if options.TraceBack != "" {
|
||||||
debug.SetTraceback(options.TraceBack)
|
debug.SetTraceback(options.TraceBack)
|
||||||
}
|
}
|
||||||
if options.MemoryLimit.Value() != 0 {
|
if options.MemoryLimit != 0 {
|
||||||
debug.SetMemoryLimit(int64(float64(options.MemoryLimit.Value()) / 1.5))
|
debug.SetMemoryLimit(int64(float64(options.MemoryLimit) / 1.5))
|
||||||
conntrack.MemoryLimit = options.MemoryLimit.Value()
|
|
||||||
}
|
|
||||||
if options.OOMKiller != nil {
|
|
||||||
conntrack.KillerEnabled = *options.OOMKiller
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/humanize"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common/byteformats"
|
|
||||||
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"
|
||||||
"github.com/sagernet/sing/common/json/badjson"
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
@@ -38,9 +38,9 @@ func applyDebugListenOption(options option.DebugOptions) {
|
|||||||
runtime.ReadMemStats(&memStats)
|
runtime.ReadMemStats(&memStats)
|
||||||
|
|
||||||
var memObject badjson.JSONObject
|
var memObject badjson.JSONObject
|
||||||
memObject.Put("heap", byteformats.FormatMemoryBytes(memStats.HeapInuse))
|
memObject.Put("heap", humanize.MemoryBytes(memStats.HeapInuse))
|
||||||
memObject.Put("stack", byteformats.FormatMemoryBytes(memStats.StackInuse))
|
memObject.Put("stack", humanize.MemoryBytes(memStats.StackInuse))
|
||||||
memObject.Put("idle", byteformats.FormatMemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
|
memObject.Put("idle", humanize.MemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
|
||||||
memObject.Put("goroutines", runtime.NumGoroutine())
|
memObject.Put("goroutines", runtime.NumGoroutine())
|
||||||
memObject.Put("rss", rusageMaxRSS())
|
memObject.Put("rss", rusageMaxRSS())
|
||||||
|
|
||||||
|
|||||||
@@ -2,199 +2,6 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
### 1.11.10
|
|
||||||
|
|
||||||
* Undeprecate the `block` outbound **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
Since we don’t have a replacement for using the `block` outbound in selectors yet,
|
|
||||||
we decided to temporarily undeprecate the `block` outbound until a replacement is available in the future.
|
|
||||||
|
|
||||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
|
|
||||||
violated the rules (TestFlight users are not affected)._
|
|
||||||
|
|
||||||
### 1.11.9
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
|
|
||||||
violated the rules (TestFlight users are not affected)._
|
|
||||||
|
|
||||||
### 1.11.8
|
|
||||||
|
|
||||||
* Improve `auto_redirect` **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
Now `auto_redirect` fixes compatibility issues between TUN and Docker bridge networks,
|
|
||||||
see [Tun](/configuration/inbound/tun/#auto_redirect).
|
|
||||||
|
|
||||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
|
|
||||||
violated the rules (TestFlight users are not affected)._
|
|
||||||
|
|
||||||
### 1.11.7
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
|
|
||||||
violated the rules (TestFlight users are not affected)._
|
|
||||||
|
|
||||||
### 1.11.6
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
|
|
||||||
violated the rules (TestFlight users are not affected)._
|
|
||||||
|
|
||||||
### 1.11.5
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
|
|
||||||
violated the rules (TestFlight users are not affected)._
|
|
||||||
|
|
||||||
### 1.11.4
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
### 1.11.3
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
_This version overwrites 1.11.2, as incorrect binaries were released due to a bug in the continuous integration
|
|
||||||
process._
|
|
||||||
|
|
||||||
### 1.11.1
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
### 1.11.0
|
|
||||||
|
|
||||||
Important changes since 1.10:
|
|
||||||
|
|
||||||
* Introducing rule actions **1**
|
|
||||||
* Improve tun compatibility **3**
|
|
||||||
* Merge route options to route actions **4**
|
|
||||||
* Add `network_type`, `network_is_expensive` and `network_is_constrainted` rule items **5**
|
|
||||||
* Add multi network dialing **6**
|
|
||||||
* Add `cache_capacity` DNS option **7**
|
|
||||||
* Add `override_address` and `override_port` route options **8**
|
|
||||||
* Upgrade WireGuard outbound to endpoint **9**
|
|
||||||
* Add UDP GSO support for WireGuard
|
|
||||||
* Make GSO adaptive **10**
|
|
||||||
* Add UDP timeout route option **11**
|
|
||||||
* Add more masquerade options for hysteria2 **12**
|
|
||||||
* Add `rule-set merge` command
|
|
||||||
* Add port hopping support for Hysteria2 **13**
|
|
||||||
* Hysteria2 `ignore_client_bandwidth` behavior update **14**
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
New rule actions replace legacy inbound fields and special outbound fields,
|
|
||||||
and can be used for pre-matching **2**.
|
|
||||||
|
|
||||||
See [Rule](/configuration/route/rule/),
|
|
||||||
[Rule Action](/configuration/route/rule_action/),
|
|
||||||
[DNS Rule](/configuration/dns/rule/) and
|
|
||||||
[DNS Rule Action](/configuration/dns/rule_action/).
|
|
||||||
|
|
||||||
For migration, see
|
|
||||||
[Migrate legacy special outbounds to rule actions](/migration/#migrate-legacy-special-outbounds-to-rule-actions),
|
|
||||||
[Migrate legacy inbound fields to rule actions](/migration/#migrate-legacy-inbound-fields-to-rule-actions)
|
|
||||||
and [Migrate legacy DNS route options to rule actions](/migration/#migrate-legacy-dns-route-options-to-rule-actions).
|
|
||||||
|
|
||||||
**2**:
|
|
||||||
|
|
||||||
Similar to Surge's pre-matching.
|
|
||||||
|
|
||||||
Specifically, new rule actions allow you to reject connections with
|
|
||||||
TCP RST (for TCP connections) and ICMP port unreachable (for UDP packets)
|
|
||||||
before connection established to improve tun's compatibility.
|
|
||||||
|
|
||||||
See [Rule Action](/configuration/route/rule_action/).
|
|
||||||
|
|
||||||
**3**:
|
|
||||||
|
|
||||||
When `gvisor` tun stack is enabled, even if the request passes routing,
|
|
||||||
if the outbound connection establishment fails,
|
|
||||||
the connection still does not need to be established and a TCP RST is replied.
|
|
||||||
|
|
||||||
**4**:
|
|
||||||
|
|
||||||
Route options in DNS route actions will no longer be considered deprecated,
|
|
||||||
see [DNS Route Action](/configuration/dns/rule_action/).
|
|
||||||
|
|
||||||
Also, now `udp_disable_domain_unmapping` and `udp_connect` can also be configured in route action,
|
|
||||||
see [Route Action](/configuration/route/rule_action/).
|
|
||||||
|
|
||||||
**5**:
|
|
||||||
|
|
||||||
When using in graphical clients, new routing rule items allow you to match on
|
|
||||||
network type (WIFI, cellular, etc.), whether the network is expensive, and whether Low Data Mode is enabled.
|
|
||||||
|
|
||||||
See [Route Rule](/configuration/route/rule/), [DNS Route Rule](/configuration/dns/rule/)
|
|
||||||
and [Headless Rule](/configuration/rule-set/headless-rule/).
|
|
||||||
|
|
||||||
**6**:
|
|
||||||
|
|
||||||
Similar to Surge's strategy.
|
|
||||||
|
|
||||||
New options allow you to connect using multiple network interfaces,
|
|
||||||
prefer or only use one type of interface,
|
|
||||||
and configure a timeout to fallback to other interfaces.
|
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial/#network_strategy),
|
|
||||||
[Rule Action](/configuration/route/rule_action/#network_strategy)
|
|
||||||
and [Route](/configuration/route/#default_network_strategy).
|
|
||||||
|
|
||||||
**7**:
|
|
||||||
|
|
||||||
See [DNS](/configuration/dns/#cache_capacity).
|
|
||||||
|
|
||||||
**8**:
|
|
||||||
|
|
||||||
See [Rule Action](/configuration/route/#override_address) and
|
|
||||||
[Migrate destination override fields to route options](/migration/#migrate-destination-override-fields-to-route-options).
|
|
||||||
|
|
||||||
**9**:
|
|
||||||
|
|
||||||
The new WireGuard endpoint combines inbound and outbound capabilities,
|
|
||||||
and the old outbound will be removed in sing-box 1.13.0.
|
|
||||||
|
|
||||||
See [Endpoint](/configuration/endpoint/), [WireGuard Endpoint](/configuration/endpoint/wireguard/)
|
|
||||||
and [Migrate WireGuard outbound fields to route options](/migration/#migrate-wireguard-outbound-to-endpoint).
|
|
||||||
|
|
||||||
**10**:
|
|
||||||
|
|
||||||
For WireGuard outbound and endpoint, GSO will be automatically enabled when available,
|
|
||||||
see [WireGuard Outbound](/configuration/outbound/wireguard/#gso).
|
|
||||||
|
|
||||||
For TUN, GSO has been removed,
|
|
||||||
see [Deprecated](/deprecated/#gso-option-in-tun).
|
|
||||||
|
|
||||||
**11**:
|
|
||||||
|
|
||||||
See [Rule Action](/configuration/route/rule_action/#udp_timeout).
|
|
||||||
|
|
||||||
**12**:
|
|
||||||
|
|
||||||
See [Hysteria2](/configuration/inbound/hysteria2/#masquerade).
|
|
||||||
|
|
||||||
**13**:
|
|
||||||
|
|
||||||
See [Hysteria2](/configuration/outbound/hysteria2/).
|
|
||||||
|
|
||||||
**14**:
|
|
||||||
|
|
||||||
When `up_mbps` and `down_mbps` are set, `ignore_client_bandwidth` instead denies clients from using BBR CC.
|
|
||||||
|
|
||||||
### 1.10.7
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
#### 1.11.0-beta.20
|
#### 1.11.0-beta.20
|
||||||
|
|
||||||
* Hysteria2 `ignore_client_bandwidth` behavior update **1**
|
* Hysteria2 `ignore_client_bandwidth` behavior update **1**
|
||||||
|
|||||||
@@ -7,10 +7,6 @@ icon: material/apple
|
|||||||
SFI/SFM/SFT allows users to manage and run local or remote sing-box configuration files, and provides
|
SFI/SFM/SFT allows users to manage and run local or remote sing-box configuration files, and provides
|
||||||
platform-specific function implementation, such as TUN transparent proxy implementation.
|
platform-specific function implementation, such as TUN transparent proxy implementation.
|
||||||
|
|
||||||
!!! failure ""
|
|
||||||
|
|
||||||
We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected).
|
|
||||||
|
|
||||||
## :material-graph: Requirements
|
## :material-graph: Requirements
|
||||||
|
|
||||||
* iOS 15.0+ / macOS 13.0+ / Apple tvOS 17.0+
|
* iOS 15.0+ / macOS 13.0+ / Apple tvOS 17.0+
|
||||||
|
|||||||
@@ -211,10 +211,6 @@ Set the default route to the Tun.
|
|||||||
|
|
||||||
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
|
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
|
||||||
|
|
||||||
!!! note "Also enable `auto_redirect`"
|
|
||||||
|
|
||||||
`auto_redirect` is always recommended on Linux, it provides better routing, higher performance (better than tproxy), and avoids conflicts between TUN and Docker bridge networks.
|
|
||||||
|
|
||||||
#### iproute2_table_index
|
#### iproute2_table_index
|
||||||
|
|
||||||
!!! question "Since sing-box 1.10.0"
|
!!! question "Since sing-box 1.10.0"
|
||||||
@@ -239,29 +235,22 @@ Linux iproute2 rule start index generated by `auto_route`.
|
|||||||
|
|
||||||
Only supported on Linux with `auto_route` enabled.
|
Only supported on Linux with `auto_route` enabled.
|
||||||
|
|
||||||
Improve TUN routing and performance using nftables.
|
Automatically configure iptables/nftables to redirect connections.
|
||||||
|
|
||||||
`auto_redirect` is always recommended on Linux, it provides better routing,
|
*In Android*:
|
||||||
higher performance (better than tproxy),
|
|
||||||
and avoids conflicts between TUN and Docker bridge networks.
|
|
||||||
|
|
||||||
Note that `auto_redirect` also works on Android,
|
Only local IPv4 connections are forwarded. To share your VPN connection over hotspot or repeater,
|
||||||
but due to the lack of `nftables` and `ip6tables`,
|
|
||||||
only simple IPv4 TCP forwarding is performed.
|
|
||||||
To share your VPN connection over hotspot or repeater on Android,
|
|
||||||
use [VPNHotspot](https://github.com/Mygod/VPNHotspot).
|
use [VPNHotspot](https://github.com/Mygod/VPNHotspot).
|
||||||
|
|
||||||
`auto_redirect` also automatically inserts compatibility rules
|
*In Linux*:
|
||||||
into the OpenWrt fw4 table, i.e.
|
|
||||||
it will work on routers without any extra configuration.
|
|
||||||
|
|
||||||
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
|
`auto_route` with `auto_redirect` works as expected on routers **without intervention**.
|
||||||
|
|
||||||
#### auto_redirect_input_mark
|
#### auto_redirect_input_mark
|
||||||
|
|
||||||
!!! question "Since sing-box 1.10.0"
|
!!! question "Since sing-box 1.10.0"
|
||||||
|
|
||||||
Connection input mark used by `auto_redirect`.
|
Connection input mark used by `route[_exclude]_address_set` with `auto_redirect`.
|
||||||
|
|
||||||
`0x2023` is used by default.
|
`0x2023` is used by default.
|
||||||
|
|
||||||
@@ -269,7 +258,7 @@ Connection input mark used by `auto_redirect`.
|
|||||||
|
|
||||||
!!! question "Since sing-box 1.10.0"
|
!!! question "Since sing-box 1.10.0"
|
||||||
|
|
||||||
Connection output mark used by `auto_redirect`.
|
Connection input mark used by `route[_exclude]_address_set` with `auto_redirect`.
|
||||||
|
|
||||||
`0x2024` is used by default.
|
`0x2024` is used by default.
|
||||||
|
|
||||||
@@ -280,15 +269,17 @@ Enforce strict routing rules when `auto_route` is enabled:
|
|||||||
*In Linux*:
|
*In Linux*:
|
||||||
|
|
||||||
* Let unsupported network unreachable
|
* Let unsupported network unreachable
|
||||||
* For legacy reasons, when neither `strict_route` nor `auto_redirect` are enabled, all ICMP traffic will not go through TUN.
|
* Make ICMP traffic route to tun instead of upstream interfaces
|
||||||
|
* Route all connections to tun
|
||||||
|
|
||||||
|
It prevents IP address leaks and makes DNS hijacking work on Android.
|
||||||
|
|
||||||
*In Windows*:
|
*In Windows*:
|
||||||
|
|
||||||
* Let unsupported network unreachable
|
* Add firewall rules to prevent DNS leak caused by
|
||||||
* prevent DNS leak caused by
|
|
||||||
Windows' [ordinary multihomed DNS resolution behavior](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29)
|
Windows' [ordinary multihomed DNS resolution behavior](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29)
|
||||||
|
|
||||||
It may prevent some Windows applications (such as VirtualBox) from working properly in certain situations.
|
It may prevent some applications (such as VirtualBox) from working properly in certain situations.
|
||||||
|
|
||||||
#### route_address
|
#### route_address
|
||||||
|
|
||||||
@@ -376,6 +367,8 @@ Exclude custom routes when `auto_route` is enabled.
|
|||||||
|
|
||||||
Add the destination IP CIDR rules in the specified rule-sets to the firewall.
|
Add the destination IP CIDR rules in the specified rule-sets to the firewall.
|
||||||
Matched traffic will bypass the sing-box routes.
|
Matched traffic will bypass the sing-box routes.
|
||||||
|
|
||||||
|
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
|
||||||
|
|
||||||
=== "Without `auto_redirect` enabled"
|
=== "Without `auto_redirect` enabled"
|
||||||
|
|
||||||
|
|||||||
@@ -215,10 +215,6 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
VPN 默认优先于 tun。要使 tun 经过 VPN,启用 `route.override_android_vpn`。
|
VPN 默认优先于 tun。要使 tun 经过 VPN,启用 `route.override_android_vpn`。
|
||||||
|
|
||||||
!!! note "也启用 `auto_redirect`"
|
|
||||||
|
|
||||||
在 Linux 上始终推荐使用 `auto_redirect`,它提供更好的路由, 更高的性能(优于 tproxy), 并避免 TUN 与 Docker 桥接网络冲突。
|
|
||||||
|
|
||||||
#### iproute2_table_index
|
#### iproute2_table_index
|
||||||
|
|
||||||
!!! question "自 sing-box 1.10.0 起"
|
!!! question "自 sing-box 1.10.0 起"
|
||||||
@@ -241,24 +237,23 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅支持 Linux,且需要 `auto_route` 已启用。
|
仅支持 Linux,且需要 `auto_route` 已启用。
|
||||||
|
|
||||||
通过使用 nftables 改善 TUN 路由和性能。
|
自动配置 iptables/nftables 以重定向连接。
|
||||||
|
|
||||||
在 Linux 上始终推荐使用 `auto_redirect`,它提供更好的路由、更高的性能(优于 tproxy),并避免了 TUN 和 Docker 桥接网络之间的冲突。
|
*在 Android 中*:
|
||||||
|
|
||||||
请注意,`auto_redirect` 也适用于 Android,但由于缺少 `nftables` 和 `ip6tables`,仅执行简单的 IPv4 TCP 转发。
|
仅转发本地 IPv4 连接。 要通过热点或中继共享您的 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
|
||||||
若要在 Android 上通过热点或中继器共享 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
|
|
||||||
|
|
||||||
`auto_redirect` 还会自动将兼容性规则插入 OpenWrt 的 fw4 表中,即无需额外配置即可在路由器上工作。
|
*在 Linux 中*:
|
||||||
|
|
||||||
与 `route.default_mark` 和 `[dialOptions].routing_mark` 冲突。
|
带有 `auto_redirect `的 `auto_route` 可以在路由器上按预期工作,**无需干预**。
|
||||||
|
|
||||||
#### auto_redirect_input_mark
|
#### auto_redirect_input_mark
|
||||||
|
|
||||||
!!! question "自 sing-box 1.10.0 起"
|
!!! question "自 sing-box 1.10.0 起"
|
||||||
|
|
||||||
`auto_redirect` 使用的连接输入标记。
|
`route_address_set` 和 `route_exclude_address_set` 使用的连接输入标记。
|
||||||
|
|
||||||
默认使用 `0x2023`。
|
默认使用 `0x2023`。
|
||||||
|
|
||||||
@@ -266,25 +261,29 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
!!! question "自 sing-box 1.10.0 起"
|
!!! question "自 sing-box 1.10.0 起"
|
||||||
|
|
||||||
`auto_redirect` 使用的连接输出标记。
|
`route_address_set` 和 `route_exclude_address_set` 使用的连接输出标记。
|
||||||
|
|
||||||
默认使用 `0x2024`。
|
默认使用 `0x2024`。
|
||||||
|
|
||||||
#### strict_route
|
#### strict_route
|
||||||
|
|
||||||
当启用 `auto_route` 时,强制执行严格的路由规则:
|
启用 `auto_route` 时执行严格的路由规则。
|
||||||
|
|
||||||
*在 Linux 中*:
|
*在 Linux 中*:
|
||||||
|
|
||||||
* 使不支持的网络不可达。
|
* 让不支持的网络无法到达
|
||||||
* 出于历史遗留原因,当未启用 `strict_route` 或 `auto_redirect` 时,所有 ICMP 流量将不会通过 TUN。
|
* 使 ICMP 流量路由到 tun 而不是上游接口
|
||||||
|
* 将所有连接路由到 tun
|
||||||
|
|
||||||
*在 Windows 中*:
|
它可以防止 IP 地址泄漏,并使 DNS 劫持在 Android 上工作。
|
||||||
|
|
||||||
* 使不支持的网络不可达。
|
*在 Windows 中*:
|
||||||
* 阻止 Windows 的 [普通多宿主 DNS 解析行为](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29) 造成的 DNS 泄露
|
|
||||||
|
|
||||||
它可能会使某些 Windows 应用程序(如 VirtualBox)在某些情况下无法正常工作。
|
* 添加防火墙规则以阻止 Windows
|
||||||
|
的 [普通多宿主 DNS 解析行为](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29)
|
||||||
|
造成的 DNS 泄露
|
||||||
|
|
||||||
|
它可能会使某些应用程序(如 VirtualBox)在某些情况下无法正常工作。
|
||||||
|
|
||||||
#### route_address
|
#### route_address
|
||||||
|
|
||||||
@@ -342,6 +341,8 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
将指定规则集中的目标 IP CIDR 规则添加到防火墙。
|
将指定规则集中的目标 IP CIDR 规则添加到防火墙。
|
||||||
不匹配的流量将绕过 sing-box 路由。
|
不匹配的流量将绕过 sing-box 路由。
|
||||||
|
|
||||||
|
与 `route.default_mark` 和 `[dialOptions].routing_mark` 冲突。
|
||||||
|
|
||||||
=== "`auto_redirect` 未启用"
|
=== "`auto_redirect` 未启用"
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ sing-box 使用 JSON 作为配置文件格式。
|
|||||||
{
|
{
|
||||||
"log": {},
|
"log": {},
|
||||||
"dns": {},
|
"dns": {},
|
||||||
"ntp": {},
|
|
||||||
"endpoints": [],
|
"endpoints": [],
|
||||||
"inbounds": [],
|
"inbounds": [],
|
||||||
"outbounds": [],
|
"outbounds": [],
|
||||||
@@ -23,7 +22,6 @@ sing-box 使用 JSON 作为配置文件格式。
|
|||||||
|----------------|------------------------|
|
|----------------|------------------------|
|
||||||
| `log` | [日志](./log/) |
|
| `log` | [日志](./log/) |
|
||||||
| `dns` | [DNS](./dns/) |
|
| `dns` | [DNS](./dns/) |
|
||||||
| `ntp` | [NTP](./ntp/) |
|
|
||||||
| `endpoints` | [端点](./endpoint/) |
|
| `endpoints` | [端点](./endpoint/) |
|
||||||
| `inbounds` | [入站](./inbound/) |
|
| `inbounds` | [入站](./inbound/) |
|
||||||
| `outbounds` | [出站](./outbound/) |
|
| `outbounds` | [出站](./outbound/) |
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
icon: material/delete-clock
|
icon: material/delete-clock
|
||||||
---
|
---
|
||||||
|
|
||||||
|
!!! failure "Deprecated in sing-box 1.11.0"
|
||||||
|
|
||||||
|
Legacy special outbounds are deprecated and will be removed in sing-box 1.13.0, check [Migration](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json F
|
||||||
{
|
{
|
||||||
"type": "block",
|
"type": "block",
|
||||||
"tag": "block"
|
"tag": "block"
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
icon: material/delete-clock
|
icon: material/delete-clock
|
||||||
---
|
---
|
||||||
|
|
||||||
|
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||||
|
|
||||||
|
旧的特殊出站已被弃用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-legacy-special-outbounds-to-rule-actions).
|
||||||
|
|
||||||
`block` 出站关闭所有传入请求。
|
`block` 出站关闭所有传入请求。
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ icon: material/delete-clock
|
|||||||
|
|
||||||
!!! failure "已在 sing-box 1.11.0 废弃"
|
!!! failure "已在 sing-box 1.11.0 废弃"
|
||||||
|
|
||||||
WireGuard 出站已被弃用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-wireguard-outbound-to-endpoint)。
|
WireGuard 出站已被启用,且将在 sing-box 1.13.0 中被移除,参阅 [迁移指南](/migration/#migrate-wireguard-outbound-to-endpoint)。
|
||||||
|
|
||||||
!!! quote "sing-box 1.11.0 中的更改"
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
|
|||||||
@@ -31,45 +31,6 @@ Tag of target outbound.
|
|||||||
|
|
||||||
See `route-options` fields below.
|
See `route-options` fields below.
|
||||||
|
|
||||||
### reject
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"action": "reject",
|
|
||||||
"method": "default", // default
|
|
||||||
"no_drop": false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`reject` reject connections
|
|
||||||
|
|
||||||
The specified method is used for reject tun connections if `sniff` action has not been performed yet.
|
|
||||||
|
|
||||||
For non-tun connections and already established connections, will just be closed.
|
|
||||||
|
|
||||||
#### method
|
|
||||||
|
|
||||||
- `default`: Reply with TCP RST for TCP connections, and ICMP port unreachable for UDP packets.
|
|
||||||
- `drop`: Drop packets.
|
|
||||||
|
|
||||||
#### no_drop
|
|
||||||
|
|
||||||
If not enabled, `method` will be temporarily overwritten to `drop` after 50 triggers in 30s.
|
|
||||||
|
|
||||||
Not available when `method` is set to drop.
|
|
||||||
|
|
||||||
### hijack-dns
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"action": "hijack-dns"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`hijack-dns` hijack DNS requests to the sing-box DNS module.
|
|
||||||
|
|
||||||
## Non-final actions
|
|
||||||
|
|
||||||
### route-options
|
### route-options
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -148,6 +109,45 @@ If no protocol is sniffed, the following ports will be recognized as protocols b
|
|||||||
| 443 | `quic` |
|
| 443 | `quic` |
|
||||||
| 3478 | `stun` |
|
| 3478 | `stun` |
|
||||||
|
|
||||||
|
### reject
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "reject",
|
||||||
|
"method": "default", // default
|
||||||
|
"no_drop": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`reject` reject connections
|
||||||
|
|
||||||
|
The specified method is used for reject tun connections if `sniff` action has not been performed yet.
|
||||||
|
|
||||||
|
For non-tun connections and already established connections, will just be closed.
|
||||||
|
|
||||||
|
#### method
|
||||||
|
|
||||||
|
- `default`: Reply with TCP RST for TCP connections, and ICMP port unreachable for UDP packets.
|
||||||
|
- `drop`: Drop packets.
|
||||||
|
|
||||||
|
#### no_drop
|
||||||
|
|
||||||
|
If not enabled, `method` will be temporarily overwritten to `drop` after 50 triggers in 30s.
|
||||||
|
|
||||||
|
Not available when `method` is set to drop.
|
||||||
|
|
||||||
|
### hijack-dns
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "hijack-dns"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`hijack-dns` hijack DNS requests to the sing-box DNS module.
|
||||||
|
|
||||||
|
## Non-final actions
|
||||||
|
|
||||||
### sniff
|
### sniff
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -27,45 +27,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
参阅下方的 `route-options` 字段。
|
参阅下方的 `route-options` 字段。
|
||||||
|
|
||||||
### reject
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"action": "reject",
|
|
||||||
"method": "default", // 默认
|
|
||||||
"no_drop": false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`reject` 拒绝连接。
|
|
||||||
|
|
||||||
如果尚未执行 `sniff` 操作,则将使用指定方法拒绝 tun 连接。
|
|
||||||
|
|
||||||
对于非 tun 连接和已建立的连接,将直接关闭。
|
|
||||||
|
|
||||||
#### method
|
|
||||||
|
|
||||||
- `default`: 对于 TCP 连接回复 RST,对于 UDP 包回复 ICMP 端口不可达。
|
|
||||||
- `drop`: 丢弃数据包。
|
|
||||||
|
|
||||||
#### no_drop
|
|
||||||
|
|
||||||
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`。
|
|
||||||
|
|
||||||
当 `method` 设为 `drop` 时不可用。
|
|
||||||
|
|
||||||
### hijack-dns
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"action": "hijack-dns"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`hijack-dns` 劫持 DNS 请求至 sing-box DNS 模块。
|
|
||||||
|
|
||||||
## 非最终动作
|
|
||||||
|
|
||||||
### route-options
|
### route-options
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -146,6 +107,45 @@ UDP 连接超时时间。
|
|||||||
| 443 | `quic` |
|
| 443 | `quic` |
|
||||||
| 3478 | `stun` |
|
| 3478 | `stun` |
|
||||||
|
|
||||||
|
### reject
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "reject",
|
||||||
|
"method": "default", // 默认
|
||||||
|
"no_drop": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`reject` 拒绝连接。
|
||||||
|
|
||||||
|
如果尚未执行 `sniff` 操作,则将使用指定方法拒绝 tun 连接。
|
||||||
|
|
||||||
|
对于非 tun 连接和已建立的连接,将直接关闭。
|
||||||
|
|
||||||
|
#### method
|
||||||
|
|
||||||
|
- `default`: 对于 TCP 连接回复 RST,对于 UDP 包回复 ICMP 端口不可达。
|
||||||
|
- `drop`: 丢弃数据包。
|
||||||
|
|
||||||
|
#### no_drop
|
||||||
|
|
||||||
|
如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`。
|
||||||
|
|
||||||
|
当 `method` 设为 `drop` 时不可用。
|
||||||
|
|
||||||
|
### hijack-dns
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "hijack-dns"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`hijack-dns` 劫持 DNS 请求至 sing-box DNS 模块。
|
||||||
|
|
||||||
|
## 非最终动作
|
||||||
|
|
||||||
### sniff
|
### sniff
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c
|
|||||||
|
|
||||||
| QUIC Client | Type |
|
| QUIC Client | Type |
|
||||||
|:------------------------:|:----------:|
|
|:------------------------:|:----------:|
|
||||||
| Chromium/Cronet | `chromium` |
|
| Chromium/Cronet | `chrimium` |
|
||||||
| Safari/Apple Network API | `safari` |
|
| Safari/Apple Network API | `safari` |
|
||||||
| Firefox / uquic firefox | `firefox` |
|
| Firefox / uquic firefox | `firefox` |
|
||||||
| quic-go / uquic chrome | `quic-go` |
|
| quic-go / uquic chrome | `quic-go` |
|
||||||
@@ -74,7 +74,7 @@ Tag of rule-set.
|
|||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
List of [Headless Rule](./headless-rule/).
|
List of [Headless Rule](../headless-rule/).
|
||||||
|
|
||||||
### Local or Remote Fields
|
### Local or Remote Fields
|
||||||
|
|
||||||
@@ -84,8 +84,6 @@ List of [Headless Rule](./headless-rule/).
|
|||||||
|
|
||||||
Format of rule-set file, `source` or `binary`.
|
Format of rule-set file, `source` or `binary`.
|
||||||
|
|
||||||
Optional when `path` or `url` uses `json` or `srs` as extension.
|
|
||||||
|
|
||||||
### Local Fields
|
### Local Fields
|
||||||
|
|
||||||
#### path
|
#### path
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ icon: material/new-box
|
|||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|
||||||
一组 [无头规则](./headless-rule/).
|
一组 [无头规则](../headless-rule/).
|
||||||
|
|
||||||
### 本地或远程字段
|
### 本地或远程字段
|
||||||
|
|
||||||
@@ -84,8 +84,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
规则集格式, `source` 或 `binary`。
|
规则集格式, `source` 或 `binary`。
|
||||||
|
|
||||||
当 `path` 或 `url` 使用 `json` 或 `srs` 作为扩展名时可选。
|
|
||||||
|
|
||||||
### 本地字段
|
### 本地字段
|
||||||
|
|
||||||
#### path
|
#### path
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ The protocol version, `1` or `2`.
|
|||||||
|
|
||||||
### Application support
|
### Application support
|
||||||
|
|
||||||
| Project | UoT v1 | UoT v2 |
|
| Project | UoT v1 | UoT v2 |
|
||||||
|--------------|----------------------|----------------------|
|
|--------------|----------------------|-------------------------------------------------------------------------------------------------------------------|
|
||||||
| sing-box | v0 (2022/08/11) | v1.2-beta9 |
|
| sing-box | v0 (2022/08/11) | v1.2-beta9 |
|
||||||
| Clash.Meta | v1.12.0 (2022/07/02) | v1.14.3 (2023/03/31) |
|
| Xray-core | v1.5.7 (2022/06/05) | [f57ec13](https://github.com/XTLS/Xray-core/commit/f57ec1388084df041a2289bacab14e446bf1b357) (Not released) |
|
||||||
| Shadowrocket | v2.2.12 (2022/08/13) | / |
|
| Clash.Meta | v1.12.0 (2022/07/02) | [8cb67b6](https://github.com/MetaCubeX/Clash.Meta/commit/8cb67b6480649edfa45dcc9ac89ce0789651e8b3) (Not released) |
|
||||||
|
| Shadowrocket | v2.2.12 (2022/08/13) | / |
|
||||||
|
|
||||||
### Protocol details
|
### Protocol details
|
||||||
|
|
||||||
@@ -49,13 +50,7 @@ The client requests the magic address to the upper layer proxy protocol to indic
|
|||||||
|------|----------|-------|--------|----------|
|
|------|----------|-------|--------|----------|
|
||||||
| u8 | variable | u16be | u16be | variable |
|
| u8 | variable | u16be | u16be | variable |
|
||||||
|
|
||||||
**ATYP / address / port**: Uses the SOCKS address format, but with different address types:
|
**ATYP / address / port**: Uses the SOCKS address format.
|
||||||
|
|
||||||
| ATYP | Address type |
|
|
||||||
|--------|--------------|
|
|
||||||
| `0x00` | IPv4 Address |
|
|
||||||
| `0x01` | IPv6 Address |
|
|
||||||
| `0x02` | Domain Name |
|
|
||||||
|
|
||||||
#### Protocol version 2
|
#### Protocol version 2
|
||||||
|
|
||||||
|
|||||||
@@ -9,56 +9,43 @@ icon: material/package
|
|||||||
=== ":material-debian: Debian / APT"
|
=== ":material-debian: Debian / APT"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /etc/apt/keyrings &&
|
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
||||||
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc &&
|
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
||||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc &&
|
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
||||||
echo '
|
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
||||||
Types: deb
|
sudo apt-get update
|
||||||
URIs: https://deb.sagernet.org/
|
sudo apt-get install sing-box # or sing-box-beta
|
||||||
Suites: *
|
|
||||||
Components: *
|
|
||||||
Enabled: yes
|
|
||||||
Signed-By: /etc/apt/keyrings/sagernet.asc
|
|
||||||
' | sudo tee /etc/apt/sources.list.d/sagernet.sources &&
|
|
||||||
sudo apt-get update &&
|
|
||||||
sudo apt-get install sing-box # or sing-box-beta
|
|
||||||
```
|
```
|
||||||
|
|
||||||
=== ":material-redhat: Redhat / DNF 5"
|
=== ":material-redhat: Redhat / DNF"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo &&
|
sudo dnf -y install dnf-plugins-core
|
||||||
sudo dnf install sing-box # or sing-box-beta
|
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
|
||||||
```
|
|
||||||
|
|
||||||
=== ":material-redhat: Redhat / DNF 4"
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo &&
|
|
||||||
sudo dnf -y install dnf-plugins-core &&
|
|
||||||
sudo dnf install sing-box # or sing-box-beta
|
sudo dnf install sing-box # or sing-box-beta
|
||||||
```
|
```
|
||||||
|
(This applies to any distribution that uses `dnf` as the package manager: Fedora, CentOS, even OpenSUSE with DNF installed.)
|
||||||
|
|
||||||
## :material-download-box: Manual Installation
|
## :material-download-box: Manual Installation
|
||||||
|
|
||||||
The script download and install the latest package from GitHub releases
|
=== ":material-debian: Debian / DEB"
|
||||||
for deb or rpm based Linux distributions, ArchLinux and OpenWrt.
|
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
curl -fsSL https://sing-box.app/install.sh | sh
|
bash <(curl -fsSL https://sing-box.app/deb-install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
or latest beta:
|
=== ":material-redhat: Redhat / RPM"
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta
|
bash <(curl -fsSL https://sing-box.app/rpm-install.sh)
|
||||||
```
|
```
|
||||||
|
(This applies to any distribution that uses `rpm` and `systemd`. Because of how `rpm` defines dependencies, if it installs, it probably works.)
|
||||||
|
|
||||||
or specific version:
|
=== ":simple-archlinux: Archlinux / PKG"
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version>
|
bash <(curl -fsSL https://sing-box.app/arch-install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
## :material-book-lock-open: Managed Installation
|
## :material-book-lock-open: Managed Installation
|
||||||
|
|
||||||
|
|||||||
@@ -9,55 +9,43 @@ icon: material/package
|
|||||||
=== ":material-debian: Debian / APT"
|
=== ":material-debian: Debian / APT"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /etc/apt/keyrings &&
|
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
||||||
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc &&
|
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
||||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc &&
|
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
||||||
echo '
|
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
||||||
Types: deb
|
sudo apt-get update
|
||||||
URIs: https://deb.sagernet.org/
|
sudo apt-get install sing-box # or sing-box-beta
|
||||||
Suites: *
|
|
||||||
Components: *
|
|
||||||
Enabled: yes
|
|
||||||
Signed-By: /etc/apt/keyrings/sagernet.asc
|
|
||||||
' | sudo tee /etc/apt/sources.list.d/sagernet.sources &&
|
|
||||||
sudo apt-get update &&
|
|
||||||
sudo apt-get install sing-box # or sing-box-beta
|
|
||||||
```
|
```
|
||||||
|
|
||||||
=== ":material-redhat: Redhat / DNF 5"
|
=== ":material-redhat: Redhat / DNF"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo &&
|
sudo dnf -y install dnf-plugins-core
|
||||||
sudo dnf install sing-box # or sing-box-beta
|
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
|
||||||
```
|
|
||||||
|
|
||||||
=== ":material-redhat: Redhat / DNF 4"
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo &&
|
|
||||||
sudo dnf -y install dnf-plugins-core &&
|
|
||||||
sudo dnf install sing-box # or sing-box-beta
|
sudo dnf install sing-box # or sing-box-beta
|
||||||
```
|
```
|
||||||
|
(这适用于任何使用 `dnf` 作为包管理器的发行版:Fedora、CentOS,甚至安装了 DNF 的 OpenSUSE。)
|
||||||
|
|
||||||
## :material-download-box: 手动安装
|
## :material-download-box: 手动安装
|
||||||
|
|
||||||
该脚本从 GitHub 发布中下载并安装最新的软件包,适用于基于 deb 或 rpm 的 Linux 发行版、ArchLinux 和 OpenWrt。
|
=== ":material-debian: Debian / DEB"
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
curl -fsSL https://sing-box.app/install.sh | sh
|
bash <(curl -fsSL https://sing-box.app/deb-install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
或最新测试版:
|
=== ":material-redhat: Redhat / RPM"
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta
|
bash <(curl -fsSL https://sing-box.app/rpm-install.sh)
|
||||||
```
|
```
|
||||||
|
(这适用于任何使用 `rpm` 和 `systemd` 的发行版。由于 `rpm` 定义依赖关系的方式,如果安装成功,就多半能用。)
|
||||||
|
|
||||||
或指定版本:
|
=== ":simple-archlinux: Archlinux / PKG"
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version>
|
bash <(curl -fsSL https://sing-box.app/arch-install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
## :material-book-lock-open: 托管安装
|
## :material-book-lock-open: 托管安装
|
||||||
|
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
download_beta=false
|
|
||||||
download_version=""
|
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--beta)
|
|
||||||
download_beta=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--version)
|
|
||||||
shift
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
echo "Missing argument for --version"
|
|
||||||
echo "Usage: $0 [--beta] [--version <version>]"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
download_version="$1"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown argument: $1"
|
|
||||||
echo "Usage: $0 [--beta] [--version <version>]"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if command -v pacman >/dev/null 2>&1; then
|
|
||||||
os="linux"
|
|
||||||
arch=$(uname -m)
|
|
||||||
package_suffix=".pkg.tar.zst"
|
|
||||||
package_install="pacman -U --noconfirm"
|
|
||||||
elif command -v dpkg >/dev/null 2>&1; then
|
|
||||||
os="linux"
|
|
||||||
arch=$(dpkg --print-architecture)
|
|
||||||
package_suffix=".deb"
|
|
||||||
package_install="dpkg -i"
|
|
||||||
elif command -v dnf >/dev/null 2>&1; then
|
|
||||||
os="linux"
|
|
||||||
arch=$(uname -m)
|
|
||||||
package_suffix=".rpm"
|
|
||||||
package_install="dnf install -y"
|
|
||||||
elif command -v rpm >/dev/null 2>&1; then
|
|
||||||
os="linux"
|
|
||||||
arch=$(uname -m)
|
|
||||||
package_suffix=".rpm"
|
|
||||||
package_install="rpm -i"
|
|
||||||
elif command -v opkg >/dev/null 2>&1; then
|
|
||||||
os="openwrt"
|
|
||||||
. /etc/os-release
|
|
||||||
arch="$OPENWRT_ARCH"
|
|
||||||
package_suffix=".ipk"
|
|
||||||
package_install="opkg update && opkg install"
|
|
||||||
else
|
|
||||||
echo "Missing supported package manager."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$download_version" ]; then
|
|
||||||
if [ "$download_beta" != "true" ]; then
|
|
||||||
if [ -n "$GITHUB_TOKEN" ]; then
|
|
||||||
latest_release=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases/latest)
|
|
||||||
else
|
|
||||||
latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest)
|
|
||||||
fi
|
|
||||||
curl_exit_status=$?
|
|
||||||
if [ $curl_exit_status -ne 0 ]; then
|
|
||||||
exit $curl_exit_status
|
|
||||||
fi
|
|
||||||
if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then
|
|
||||||
echo "$latest_release"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g')
|
|
||||||
else
|
|
||||||
if [ -n "$GITHUB_TOKEN" ]; then
|
|
||||||
latest_release=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases)
|
|
||||||
else
|
|
||||||
latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases)
|
|
||||||
fi
|
|
||||||
curl_exit_status=$?
|
|
||||||
if [ $curl_exit_status -ne 0 ]; then
|
|
||||||
exit $curl_exit_status
|
|
||||||
fi
|
|
||||||
if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then
|
|
||||||
echo "$latest_release"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g')
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
package_name="sing-box_${download_version}_${os}_${arch}${package_suffix}"
|
|
||||||
package_url="https://github.com/SagerNet/sing-box/releases/download/v${download_version}/${package_name}"
|
|
||||||
|
|
||||||
echo "Downloading $package_url"
|
|
||||||
if [ -n "$GITHUB_TOKEN" ]; then
|
|
||||||
curl --fail -Lo "$package_name" -H "Authorization: token ${GITHUB_TOKEN}" "$package_url"
|
|
||||||
else
|
|
||||||
curl --fail -Lo "$package_name" "$package_url"
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl_exit_status=$?
|
|
||||||
if [ $curl_exit_status -ne 0 ]; then
|
|
||||||
exit $curl_exit_status
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v sudo >/dev/null 2>&1; then
|
|
||||||
package_install="sudo $package_install"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$package_install $package_name"
|
|
||||||
sh -c "$package_install \"$package_name\""
|
|
||||||
rm -f "$package_name"
|
|
||||||
@@ -108,7 +108,7 @@ Inbound fields are deprecated and can be replaced by rule actions.
|
|||||||
|
|
||||||
!!! info "References"
|
!!! info "References"
|
||||||
|
|
||||||
[Listen Fields](/configuration/shared/listen/) /
|
[Listen Fields](/configuration/inbound/listen/) /
|
||||||
[Rule](/configuration/route/rule/) /
|
[Rule](/configuration/route/rule/) /
|
||||||
[Rule Action](/configuration/route/rule_action/) /
|
[Rule Action](/configuration/route/rule_action/) /
|
||||||
[DNS Rule](/configuration/dns/rule/) /
|
[DNS Rule](/configuration/dns/rule/) /
|
||||||
|
|||||||
@@ -284,8 +284,8 @@ func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedBinary {
|
func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedRuleSet {
|
||||||
var savedSet adapter.SavedBinary
|
var savedSet adapter.SavedRuleSet
|
||||||
err := c.DB.View(func(t *bbolt.Tx) error {
|
err := c.DB.View(func(t *bbolt.Tx) error {
|
||||||
bucket := c.bucket(t, bucketRuleSet)
|
bucket := c.bucket(t, bucketRuleSet)
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
@@ -303,7 +303,7 @@ func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedBinary {
|
|||||||
return &savedSet
|
return &savedSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error {
|
func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedRuleSet) error {
|
||||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||||
bucket, err := c.createBucket(t, bucketRuleSet)
|
bucket, err := c.createBucket(t, bucketRuleSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
|||||||
s.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
s.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
||||||
chiRouter.Group(func(r chi.Router) {
|
chiRouter.Group(func(r chi.Router) {
|
||||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
||||||
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(Dir(s.externalUI))))
|
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(http.Dir(s.externalUI))))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package clashapi
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type Dir http.Dir
|
|
||||||
|
|
||||||
func (d Dir) Open(name string) (http.File, error) {
|
|
||||||
file, err := http.Dir(d).Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &fileWrapper{file}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// workaround for #2345 #2596
|
|
||||||
type fileWrapper struct {
|
|
||||||
http.File
|
|
||||||
}
|
|
||||||
@@ -41,6 +41,7 @@ func (s *Server) downloadExternalUI() error {
|
|||||||
} else {
|
} else {
|
||||||
downloadURL = "https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip"
|
downloadURL = "https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip"
|
||||||
}
|
}
|
||||||
|
s.logger.Info("downloading external ui")
|
||||||
var detour adapter.Outbound
|
var detour adapter.Outbound
|
||||||
if s.externalUIDownloadDetour != "" {
|
if s.externalUIDownloadDetour != "" {
|
||||||
outbound, loaded := s.outbound.Outbound(s.externalUIDownloadDetour)
|
outbound, loaded := s.outbound.Outbound(s.externalUIDownloadDetour)
|
||||||
@@ -52,7 +53,6 @@ func (s *Server) downloadExternalUI() error {
|
|||||||
outbound := s.outbound.Default()
|
outbound := s.outbound.Default()
|
||||||
detour = outbound
|
detour = outbound
|
||||||
}
|
}
|
||||||
s.logger.Info("downloading external ui using outbound/", detour.Type(), "[", detour.Tag(), "]")
|
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
@@ -71,15 +71,15 @@ func (s *Server) downloadExternalUI() error {
|
|||||||
if response.StatusCode != http.StatusOK {
|
if response.StatusCode != http.StatusOK {
|
||||||
return E.New("download external ui failed: ", response.Status)
|
return E.New("download external ui failed: ", response.Status)
|
||||||
}
|
}
|
||||||
err = s.downloadZIP(response.Body, s.externalUI)
|
err = s.downloadZIP(filepath.Base(downloadURL), response.Body, s.externalUI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
removeAllInDirectory(s.externalUI)
|
removeAllInDirectory(s.externalUI)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) downloadZIP(body io.Reader, output string) error {
|
func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
|
||||||
tempFile, err := filemanager.CreateTemp(s.ctx, "external-ui.zip")
|
tempFile, err := filemanager.CreateTemp(s.ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
runtimeDebug "runtime/debug"
|
runtimeDebug "runtime/debug"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/conntrack"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CommandClient) CloseConnections() error {
|
func (c *CommandClient) CloseConnections() error {
|
||||||
@@ -19,7 +17,7 @@ func (c *CommandClient) CloseConnections() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleCloseConnections(conn net.Conn) error {
|
func (s *CommandServer) handleCloseConnections(conn net.Conn) error {
|
||||||
conntrack.Close()
|
tracker.Close()
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
runtimeDebug.FreeOSMemory()
|
runtimeDebug.FreeOSMemory()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/varbin"
|
"github.com/sagernet/sing/common/varbin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +18,19 @@ func (c *CommandClient) ServiceReload() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return readError(conn)
|
var hasError bool
|
||||||
|
err = binary.Read(conn, binary.BigEndian, &hasError)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if hasError {
|
||||||
|
errorMessage, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return E.New(errorMessage)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleServiceReload(conn net.Conn) error {
|
func (s *CommandServer) handleServiceReload(conn net.Conn) error {
|
||||||
@@ -42,7 +55,19 @@ func (c *CommandClient) ServiceClose() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return readError(conn)
|
var hasError bool
|
||||||
|
err = binary.Read(conn, binary.BigEndian, &hasError)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if hasError {
|
||||||
|
errorMessage, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return E.New(errorMessage)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleServiceClose(conn net.Conn) error {
|
func (s *CommandServer) handleServiceClose(conn net.Conn) error {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/conntrack"
|
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/memory"
|
"github.com/sagernet/sing/common/memory"
|
||||||
@@ -28,7 +27,7 @@ func (s *CommandServer) readStatus() StatusMessage {
|
|||||||
var message StatusMessage
|
var message StatusMessage
|
||||||
message.Memory = int64(memory.Inuse())
|
message.Memory = int64(memory.Inuse())
|
||||||
message.Goroutines = int32(runtime.NumGoroutine())
|
message.Goroutines = int32(runtime.NumGoroutine())
|
||||||
message.ConnectionsOut = int32(conntrack.Count())
|
message.ConnectionsOut = int32(tracker.Count())
|
||||||
|
|
||||||
if s.service != nil {
|
if s.service != nil {
|
||||||
message.TrafficAvailable = true
|
message.TrafficAvailable = true
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ func (s *platformInterfaceStub) OpenTun(options *tun.Options, platformOptions op
|
|||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *platformInterfaceStub) UpdateRouteOptions(options *tun.Options, platformInterface option.TunPlatformOptions) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) UsePlatformDefaultInterfaceMonitor() bool {
|
func (s *platformInterfaceStub) UsePlatformDefaultInterfaceMonitor() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,21 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/conntrack"
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var tracker *conntrack.DefaultTracker
|
||||||
|
|
||||||
func SetMemoryLimit(enabled bool) {
|
func SetMemoryLimit(enabled bool) {
|
||||||
|
if tracker != nil {
|
||||||
|
tracker.Close()
|
||||||
|
}
|
||||||
const memoryLimit = 45 * 1024 * 1024
|
const memoryLimit = 45 * 1024 * 1024
|
||||||
const memoryLimitGo = memoryLimit / 1.5
|
const memoryLimitGo = memoryLimit / 1.5
|
||||||
if enabled {
|
if enabled {
|
||||||
runtimeDebug.SetGCPercent(10)
|
runtimeDebug.SetGCPercent(10)
|
||||||
runtimeDebug.SetMemoryLimit(memoryLimitGo)
|
runtimeDebug.SetMemoryLimit(memoryLimitGo)
|
||||||
conntrack.KillerEnabled = true
|
tracker = conntrack.NewDefaultTracker(true, memoryLimit)
|
||||||
conntrack.MemoryLimit = memoryLimit
|
|
||||||
} else {
|
} else {
|
||||||
runtimeDebug.SetGCPercent(100)
|
runtimeDebug.SetGCPercent(100)
|
||||||
runtimeDebug.SetMemoryLimit(math.MaxInt64)
|
runtimeDebug.SetMemoryLimit(math.MaxInt64)
|
||||||
conntrack.KillerEnabled = false
|
tracker = conntrack.NewDefaultTracker(false, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,12 +56,7 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
|
|||||||
|
|
||||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
||||||
if sFixAndroidStack {
|
if sFixAndroidStack {
|
||||||
done := make(chan struct{})
|
go m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||||
go func() {
|
|
||||||
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
<-done
|
|
||||||
} else {
|
} else {
|
||||||
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type PlatformInterface interface {
|
|||||||
UsePlatformAutoDetectInterfaceControl() bool
|
UsePlatformAutoDetectInterfaceControl() bool
|
||||||
AutoDetectInterfaceControl(fd int32) error
|
AutoDetectInterfaceControl(fd int32) error
|
||||||
OpenTun(options TunOptions) (int32, error)
|
OpenTun(options TunOptions) (int32, error)
|
||||||
|
UpdateRouteOptions(options TunOptions) error
|
||||||
WriteLog(message string)
|
WriteLog(message string)
|
||||||
UseProcFS() bool
|
UseProcFS() bool
|
||||||
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type Interface interface {
|
|||||||
UsePlatformAutoDetectInterfaceControl() bool
|
UsePlatformAutoDetectInterfaceControl() bool
|
||||||
AutoDetectInterfaceControl(fd int) error
|
AutoDetectInterfaceControl(fd int) error
|
||||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||||
|
UpdateRouteOptions(options *tun.Options, platformOptions option.TunPlatformOptions) error
|
||||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
||||||
Interfaces() ([]adapter.NetworkInterface, error)
|
Interfaces() ([]adapter.NetworkInterface, error)
|
||||||
UnderNetworkExtension() bool
|
UnderNetworkExtension() bool
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box"
|
"github.com/sagernet/sing-box"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@@ -40,7 +41,7 @@ type BoxService struct {
|
|||||||
clashServer adapter.ClashServer
|
clashServer adapter.ClashServer
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
|
|
||||||
iOSPauseFields
|
servicePauseFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||||
@@ -60,6 +61,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
|||||||
useProcFS: platformInterface.UseProcFS(),
|
useProcFS: platformInterface.UseProcFS(),
|
||||||
}
|
}
|
||||||
service.MustRegister[platform.Interface](ctx, platformWrapper)
|
service.MustRegister[platform.Interface](ctx, platformWrapper)
|
||||||
|
service.MustRegister[conntrack.Tracker](ctx, tracker)
|
||||||
instance, err := box.New(box.Options{
|
instance, err := box.New(box.Options{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Options: options,
|
Options: options,
|
||||||
@@ -174,6 +176,20 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
|
|||||||
return tun.New(*options)
|
return tun.New(*options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) UpdateRouteOptions(options *tun.Options, platformOptions option.TunPlatformOptions) error {
|
||||||
|
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
||||||
|
return E.New("android: unsupported uid options")
|
||||||
|
}
|
||||||
|
if len(options.IncludeAndroidUser) > 0 {
|
||||||
|
return E.New("android: unsupported android_user option")
|
||||||
|
}
|
||||||
|
routeRanges, err := options.BuildAutoRouteRanges(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w.iif.UpdateRouteOptions(&tunOptions{options, routeRanges, platformOptions})
|
||||||
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
|
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
|
||||||
return &platformDefaultInterfaceMonitor{
|
return &platformDefaultInterfaceMonitor{
|
||||||
platformInterfaceWrapper: w,
|
platformInterfaceWrapper: w,
|
||||||
|
|||||||
@@ -1,39 +1,33 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type iOSPauseFields struct {
|
type servicePauseFields struct {
|
||||||
endPauseTimer *time.Timer
|
pauseAccess sync.Mutex
|
||||||
|
pauseTimer *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) Pause() {
|
func (s *BoxService) Pause() {
|
||||||
s.pauseManager.DevicePause()
|
s.pauseAccess.Lock()
|
||||||
if !C.IsIos {
|
defer s.pauseAccess.Unlock()
|
||||||
s.instance.Router().ResetNetwork()
|
if s.pauseTimer != nil {
|
||||||
} else {
|
s.pauseTimer.Stop()
|
||||||
if s.endPauseTimer == nil {
|
|
||||||
s.endPauseTimer = time.AfterFunc(time.Minute, s.pauseManager.DeviceWake)
|
|
||||||
} else {
|
|
||||||
s.endPauseTimer.Reset(time.Minute)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
s.pauseTimer = time.AfterFunc(3*time.Second, s.ResetNetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) Wake() {
|
func (s *BoxService) Wake() {
|
||||||
if !C.IsIos {
|
s.pauseAccess.Lock()
|
||||||
s.pauseManager.DeviceWake()
|
defer s.pauseAccess.Unlock()
|
||||||
s.instance.Router().ResetNetwork()
|
if s.pauseTimer != nil {
|
||||||
|
s.pauseTimer.Stop()
|
||||||
}
|
}
|
||||||
|
s.pauseTimer = time.AfterFunc(3*time.Minute, s.ResetNetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) ResetNetwork() {
|
func (s *BoxService) ResetNetwork() {
|
||||||
s.instance.Router().ResetNetwork()
|
s.instance.Router().ResetNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) UpdateWIFIState() {
|
|
||||||
s.instance.Network().UpdateWIFIState()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/humanize"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/locale"
|
"github.com/sagernet/sing-box/experimental/locale"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common/byteformats"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -75,11 +75,11 @@ func Version() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FormatBytes(length int64) string {
|
func FormatBytes(length int64) string {
|
||||||
return byteformats.FormatBytes(uint64(length))
|
return humanize.Bytes(uint64(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatMemoryBytes(length int64) string {
|
func FormatMemoryBytes(length int64) string {
|
||||||
return byteformats.FormatMemoryBytes(uint64(length))
|
return humanize.MemoryBytes(uint64(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatDuration(duration int64) string {
|
func FormatDuration(duration int64) string {
|
||||||
|
|||||||
@@ -7,13 +7,11 @@ var (
|
|||||||
|
|
||||||
type Locale struct {
|
type Locale struct {
|
||||||
// deprecated messages for graphical clients
|
// deprecated messages for graphical clients
|
||||||
Locale string
|
|
||||||
DeprecatedMessage string
|
DeprecatedMessage string
|
||||||
DeprecatedMessageNoLink string
|
DeprecatedMessageNoLink string
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultLocal = &Locale{
|
var defaultLocal = &Locale{
|
||||||
Locale: "en_US",
|
|
||||||
DeprecatedMessage: "%s is deprecated in sing-box %s and will be removed in sing-box %s please checkout documentation for migration.",
|
DeprecatedMessage: "%s is deprecated in sing-box %s and will be removed in sing-box %s please checkout documentation for migration.",
|
||||||
DeprecatedMessageNoLink: "%s is deprecated in sing-box %s and will be removed in sing-box %s.",
|
DeprecatedMessageNoLink: "%s is deprecated in sing-box %s and will be removed in sing-box %s.",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ var warningMessageForEndUsers = "\n\n如果您不明白此消息意味着什么
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
localeRegistry["zh_CN"] = &Locale{
|
localeRegistry["zh_CN"] = &Locale{
|
||||||
Locale: "zh_CN",
|
|
||||||
DeprecatedMessage: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除,请参阅迁移指南。" + warningMessageForEndUsers,
|
DeprecatedMessage: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除,请参阅迁移指南。" + warningMessageForEndUsers,
|
||||||
DeprecatedMessageNoLink: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除。" + warningMessageForEndUsers,
|
DeprecatedMessageNoLink: "%s 已在 sing-box %s 中被弃用,且将在 sing-box %s 中被移除。" + warningMessageForEndUsers,
|
||||||
}
|
}
|
||||||
|
|||||||
209
go.mod
209
go.mod
@@ -1,104 +1,105 @@
|
|||||||
module github.com/sagernet/sing-box
|
module github.com/sagernet/sing-box
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caddyserver/certmagic v0.20.0
|
github.com/caddyserver/certmagic v0.20.0
|
||||||
github.com/cloudflare/circl v1.3.7
|
github.com/cloudflare/circl v1.3.7
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/go-chi/chi/v5 v5.2.1
|
github.com/go-chi/chi/v5 v5.1.0
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/gofrs/uuid/v5 v5.3.2
|
github.com/gofrs/uuid/v5 v5.3.0
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
|
||||||
github.com/libdns/alidns v1.0.3
|
github.com/libdns/alidns v1.0.3
|
||||||
github.com/libdns/cloudflare v0.1.1
|
github.com/libdns/cloudflare v0.1.1
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa
|
||||||
github.com/mholt/acmez v1.2.0
|
github.com/mholt/acmez v1.2.0
|
||||||
github.com/miekg/dns v1.1.63
|
github.com/miekg/dns v1.1.62
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0
|
github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
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/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.4
|
github.com/sagernet/gomobile v0.1.4
|
||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
||||||
github.com/sagernet/quic-go v0.49.0-beta.1
|
github.com/sagernet/quic-go v0.48.2-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.6.9
|
github.com/sagernet/sing v0.6.0-beta.9
|
||||||
github.com/sagernet/sing-dns v0.4.2
|
github.com/sagernet/sing-dns v0.4.0-beta.1
|
||||||
github.com/sagernet/sing-mux v0.3.2
|
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||||
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76
|
github.com/sagernet/sing-quic v0.4.0-beta.3
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
|
||||||
github.com/sagernet/sing-tun v0.6.5
|
github.com/sagernet/sing-tun v0.6.0-beta.7
|
||||||
github.com/sagernet/sing-vmess v0.2.1
|
github.com/sagernet/sing-vmess v0.2.0-beta.2
|
||||||
github.com/sagernet/smux v1.5.34-mod.2
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||||
github.com/sagernet/utls v1.6.7
|
github.com/sagernet/utls v1.6.7
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
github.com/sagernet/wireguard-go v0.0.1-beta.5
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.9.0
|
||||||
go.uber.org/zap v1.27.0
|
github.com/wiresock/ndisapi-go v0.0.0-20241230094942-3299a7566e08
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/crypto v0.32.0
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
golang.org/x/crypto v0.31.0
|
||||||
golang.org/x/mod v0.20.0
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||||
golang.org/x/net v0.34.0
|
golang.org/x/mod v0.20.0
|
||||||
golang.org/x/sys v0.30.0
|
golang.org/x/net v0.31.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.org/x/sys v0.28.0
|
||||||
google.golang.org/grpc v1.63.2
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/grpc v1.63.2
|
||||||
howett.net/plist v1.0.1
|
google.golang.org/protobuf v1.33.0
|
||||||
)
|
howett.net/plist v1.0.1
|
||||||
|
)
|
||||||
//replace github.com/sagernet/sing => ../sing
|
|
||||||
|
//replace github.com/sagernet/sing => ../sing
|
||||||
require (
|
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/httphead v0.1.0 // indirect
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||||
github.com/josharian/native v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
github.com/libdns/libdns v0.2.2 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
github.com/libdns/libdns v0.2.2 // indirect
|
||||||
github.com/mdlayher/socket v0.4.1 // indirect
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/mdlayher/socket v0.4.1 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
|
)
|
||||||
|
|||||||
467
go.sum
467
go.sum
@@ -1,235 +1,232 @@
|
|||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||||
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||||
github.com/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||||
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/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
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-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
|
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
||||||
github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
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/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
||||||
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
||||||
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
||||||
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
|
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
|
||||||
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
github.com/libdns/libdns v0.2.2/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/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw=
|
||||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
||||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
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=
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4=
|
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4=
|
||||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU=
|
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU=
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
||||||
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/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
|
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
|
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
|
||||||
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/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.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
||||||
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
||||||
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.49.0-beta.1 h1:3LdoCzVVfYRibZns1tYWSIoB65fpTmrwy+yfK8DQ8Jk=
|
github.com/sagernet/quic-go v0.48.2-beta.1 h1:W0plrLWa1XtOWDTdX3CJwxmQuxkya12nN5BRGZ87kEg=
|
||||||
github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8WHNsRs71b3Lt1+p/U=
|
github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.6.9 h1:y/XJH17oyBd6hxgQtKnIdLXu7TsOHxO5i1JeVfVmjXw=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-beta.9 h1:P8lKa5hN53fRNAVCIKy5cWd6/kLO5c4slhdsfehSmHs=
|
||||||
github.com/sagernet/sing-dns v0.4.2 h1:cWe2XPUBFLep2j9kJV4Epg3bctGhMvrrl/sWi9Wszfg=
|
github.com/sagernet/sing v0.6.0-beta.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-dns v0.4.2/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
|
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
|
||||||
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
|
||||||
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||||
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 h1:iwpCX6H3nZEOGUGwx0q5azcgYOA9f6v9YssihXoRKHk=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
|
||||||
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY=
|
github.com/sagernet/sing-quic v0.4.0-beta.3 h1:cOBjlhVdRZmBm6hIw1GleERpnTSFdBB2htgx5kQ5uqg=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-quic v0.4.0-beta.3/go.mod h1:1UNObFodd8CnS3aCT53x9cigjPSCl3P//8dfBMCwBDM=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0=
|
||||||
github.com/sagernet/sing-tun v0.6.5 h1:nGfD6GNq/r0tEjdZHOV3BS6fydSmd4kBAokU5rffssg=
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA=
|
||||||
github.com/sagernet/sing-tun v0.6.5/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
github.com/sagernet/sing-tun v0.6.0-beta.7 h1:FCSX8oGBqb0H57AAvfGeeH/jMGYWCOg6XWkN/oeES+0=
|
||||||
github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug=
|
github.com/sagernet/sing-tun v0.6.0-beta.7/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||||
github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
github.com/sagernet/sing-vmess v0.2.0-beta.2 h1:obAkAL35X7ql4RnGzDg4dBYIRpGXRKqcN4LyLZpZGSs=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
github.com/sagernet/sing-vmess v0.2.0-beta.2/go.mod h1:HGhf9XUdeE2iOWrX0hQNFgXPbKyGlzpeYFyX0c/pykk=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||||
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
|
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
github.com/sagernet/wireguard-go v0.0.1-beta.5/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/wiresock/ndisapi-go v0.0.0-20241230094942-3299a7566e08 h1:is+7xN6CAKtgxt3mDSl9OQNvjfi6LggugSP07QhDtws=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/wiresock/ndisapi-go v0.0.0-20241230094942-3299a7566e08/go.mod h1:lFE7JYt3LC2UYJ31mRDwl/K35pbtxDnkSDlXrYzgyqg=
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
||||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||||
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||||
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||||
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
|
||||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
|
||||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
|
||||||
|
|||||||
12
include/ndis.go
Normal file
12
include/ndis.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build windows && with_gvisor
|
||||||
|
|
||||||
|
package include
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/adapter/inbound"
|
||||||
|
"github.com/sagernet/sing-box/protocol/ndis"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerNDISInbound(registry *inbound.Registry) {
|
||||||
|
ndis.RegisterInbound(registry)
|
||||||
|
}
|
||||||
20
include/ndis_nongvisor_stub.go
Normal file
20
include/ndis_nongvisor_stub.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build windows && !with_gvisor
|
||||||
|
|
||||||
|
package include
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/inbound"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerNDISInbound(registry *inbound.Registry) {
|
||||||
|
inbound.Register[option.NDISInboundOptions](registry, C.TypeNDIS, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.NDISInboundOptions) (adapter.Inbound, error) {
|
||||||
|
return nil, tun.ErrGVisorNotIncluded
|
||||||
|
})
|
||||||
|
}
|
||||||
20
include/ndis_nonwindows_stub.go
Normal file
20
include/ndis_nonwindows_stub.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package include
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/adapter/inbound"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerNDISInbound(registry *inbound.Registry) {
|
||||||
|
inbound.Register[option.NDISInboundOptions](registry, C.TypeNDIS, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.NDISInboundOptions) (adapter.Inbound, error) {
|
||||||
|
return nil, E.New("NDIS is only supported in windows")
|
||||||
|
})
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user