mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-29 13:22:06 +03:00
Compare commits
39 Commits
v1.12.0-be
...
fix-endpoi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2cfc8092ad | ||
|
|
26bd698462 | ||
|
|
c5ecca3938 | ||
|
|
900888731c | ||
|
|
13e648e4b1 | ||
|
|
aff12ff671 | ||
|
|
101fb88255 | ||
|
|
8b489354e4 | ||
|
|
7dea6eb7a6 | ||
|
|
af1bfe4e3e | ||
|
|
d574e9eb52 | ||
|
|
2d7df1e1f2 | ||
|
|
1c0ffcf5b1 | ||
|
|
348cc39975 | ||
|
|
987899f94a | ||
|
|
d8b2d5142f | ||
|
|
134802d1ee | ||
|
|
e5e81b4de1 | ||
|
|
300c961efa | ||
|
|
7c7f512405 | ||
|
|
03e8d029c2 | ||
|
|
787b5f1931 | ||
|
|
56a7624618 | ||
|
|
3a84acf122 | ||
|
|
f600e02e47 | ||
|
|
e6d19de58a | ||
|
|
f2bbf6b2aa | ||
|
|
c54d50fd36 | ||
|
|
6a051054db | ||
|
|
49498f6439 | ||
|
|
144a890c71 | ||
|
|
afb4993445 | ||
|
|
4c9455b944 | ||
|
|
5fdc051a08 | ||
|
|
cb68a40c43 | ||
|
|
023218e6e7 | ||
|
|
2a24b94b8d | ||
|
|
c6531cf184 | ||
|
|
d4fa0ed349 |
30
.fpm_openwrt
Normal file
30
.fpm_openwrt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-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
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
-s dir
|
-s dir
|
||||||
--name sing-box
|
--name sing-box
|
||||||
--category net
|
--category net
|
||||||
--license GPLv3-or-later
|
--license GPL-3.0-or-later
|
||||||
--description "The universal proxy platform."
|
--description "The universal proxy platform."
|
||||||
--url "https://sing-box.sagernet.org/"
|
--url "https://sing-box.sagernet.org/"
|
||||||
--maintainer "nekohasekai <contact-git@sekai.icu>"
|
--maintainer "nekohasekai <contact-git@sekai.icu>"
|
||||||
--deb-field "Bug: https://github.com/SagerNet/sing-box/issues"
|
--deb-field "Bug: https://github.com/SagerNet/sing-box/issues"
|
||||||
|
--no-deb-generate-changes
|
||||||
--config-files /etc/sing-box/config.json
|
--config-files /etc/sing-box/config.json
|
||||||
|
|
||||||
release/config/config.json=/etc/sing-box/config.json
|
release/config/config.json=/etc/sing-box/config.json
|
||||||
28
.github/deb2ipk.sh
vendored
Executable file
28
.github/deb2ipk.sh
vendored
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/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
|
||||||
89
.github/workflows/build.yml
vendored
89
.github/workflows/build.yml
vendored
@@ -68,32 +68,39 @@ jobs:
|
|||||||
- calculate_version
|
- calculate_version
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ linux, windows, darwin, android ]
|
|
||||||
arch: [ "386", amd64, arm64 ]
|
|
||||||
legacy_go: [ false ]
|
|
||||||
include:
|
include:
|
||||||
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 }
|
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
|
||||||
- { os: linux, arch: "386", debian: i386, rpm: i386 }
|
- { os: linux, arch: "386", go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
|
||||||
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl }
|
- { os: linux, arch: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
|
||||||
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl }
|
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
|
||||||
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64 }
|
- { os: linux, arch: arm, goarm: "5", openwrt: "arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" }
|
||||||
- { os: linux, arch: mips64le, debian: mips64el, rpm: mips64el }
|
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl, openwrt: "arm_arm1176jzf-s_vfp" }
|
||||||
- { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel }
|
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
|
||||||
|
- { os: linux, arch: mips, gomips: softfloat, openwrt: "mips_24kc mips_4kec mips_mips32" }
|
||||||
|
- { os: linux, arch: mipsle, gomips: hardfloat, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc_24kf" }
|
||||||
|
- { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
|
||||||
|
- { os: linux, arch: mips64, gomips: softfloat, openwrt: "mips64_mips64r2 mips64_octeonplus" }
|
||||||
|
- { os: linux, arch: mips64le, gomips: hardfloat, debian: mips64el, rpm: mips64el }
|
||||||
|
- { os: linux, arch: mips64le, gomips: softfloat, openwrt: "mips64el_mips64r2" }
|
||||||
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
|
||||||
- { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le }
|
- { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le }
|
||||||
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64 }
|
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
|
||||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
|
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||||
|
|
||||||
- { os: windows, arch: "386", legacy_go: true }
|
- { os: windows, arch: amd64 }
|
||||||
- { os: windows, arch: amd64, legacy_go: true }
|
- { os: windows, arch: amd64, legacy_go: true }
|
||||||
- { os: darwin, arch: amd64, legacy_go: true }
|
- { os: windows, arch: "386" }
|
||||||
|
- { os: windows, arch: "386", legacy_go: true }
|
||||||
|
- { os: windows, arch: arm64 }
|
||||||
|
|
||||||
|
- { os: darwin, arch: amd64 }
|
||||||
|
- { os: darwin, arch: amd64, legacy_go: true }
|
||||||
|
- { os: darwin, arch: arm64 }
|
||||||
|
|
||||||
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
|
||||||
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
|
|
||||||
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
|
||||||
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
|
||||||
exclude:
|
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
|
||||||
- { os: darwin, arch: "386" }
|
- { os: android, arch: "386", ndk: "i686-linux-android21" }
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||||
@@ -139,7 +146,10 @@ jobs:
|
|||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
GOOS: ${{ matrix.os }}
|
GOOS: ${{ matrix.os }}
|
||||||
GOARCH: ${{ matrix.arch }}
|
GOARCH: ${{ matrix.arch }}
|
||||||
|
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 }}
|
||||||
- name: Build Android
|
- name: Build Android
|
||||||
if: matrix.os == 'android'
|
if: matrix.os == 'android'
|
||||||
@@ -159,12 +169,17 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Set name
|
- name: Set name
|
||||||
run: |-
|
run: |-
|
||||||
ARM_VERSION=$([ -n '${{ matrix.goarm}}' ] && echo 'v${{ matrix.goarm}}' || true)
|
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}"
|
||||||
LEGACY=$([ '${{ matrix.legacy_go }}' = 'true' ] && echo "-legacy" || true)
|
if [[ -n "${{ matrix.goarm }}" ]]; then
|
||||||
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}${ARM_VERSION}${LEGACY}"
|
DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
|
||||||
PKG_NAME="sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.arch }}${ARM_VERSION}"
|
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}"
|
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
|
||||||
echo "PKG_NAME=${PKG_NAME}" >> "${GITHUB_ENV}"
|
|
||||||
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
|
||||||
PKG_VERSION="${PKG_VERSION//-/\~}"
|
PKG_VERSION="${PKG_VERSION//-/\~}"
|
||||||
echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
|
echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
|
||||||
@@ -173,10 +188,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
sudo gem install fpm
|
sudo gem install fpm
|
||||||
|
sudo apt-get update
|
||||||
sudo apt-get install -y debsigs
|
sudo apt-get install -y debsigs
|
||||||
|
cp .fpm_systemd .fpm
|
||||||
fpm -t deb \
|
fpm -t deb \
|
||||||
-v "$PKG_VERSION" \
|
-v "$PKG_VERSION" \
|
||||||
-p "dist/${PKG_NAME}.deb" \
|
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.debian }}.deb" \
|
||||||
--architecture ${{ matrix.debian }} \
|
--architecture ${{ matrix.debian }} \
|
||||||
dist/sing-box=/usr/bin/sing-box
|
dist/sing-box=/usr/bin/sing-box
|
||||||
curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
|
curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
|
||||||
@@ -191,9 +208,10 @@ jobs:
|
|||||||
run: |-
|
run: |-
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
sudo gem install fpm
|
sudo gem install fpm
|
||||||
|
cp .fpm_systemd .fpm
|
||||||
fpm -t rpm \
|
fpm -t rpm \
|
||||||
-v "$PKG_VERSION" \
|
-v "$PKG_VERSION" \
|
||||||
-p "dist/${PKG_NAME}.rpm" \
|
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.rpm }}.rpm" \
|
||||||
--architecture ${{ matrix.rpm }} \
|
--architecture ${{ matrix.rpm }} \
|
||||||
dist/sing-box=/usr/bin/sing-box
|
dist/sing-box=/usr/bin/sing-box
|
||||||
cat > $HOME/.rpmmacros <<EOF
|
cat > $HOME/.rpmmacros <<EOF
|
||||||
@@ -209,12 +227,29 @@ jobs:
|
|||||||
run: |-
|
run: |-
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
sudo gem install fpm
|
sudo gem install fpm
|
||||||
|
sudo apt-get update
|
||||||
sudo apt-get install -y libarchive-tools
|
sudo apt-get install -y libarchive-tools
|
||||||
|
cp .fpm_systemd .fpm
|
||||||
fpm -t pacman \
|
fpm -t pacman \
|
||||||
-v "$PKG_VERSION" \
|
-v "$PKG_VERSION" \
|
||||||
-p "dist/${PKG_NAME}.pkg.tar.zst" \
|
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.pacman }}.pkg.tar.zst" \
|
||||||
--architecture ${{ matrix.pacman }} \
|
--architecture ${{ matrix.pacman }} \
|
||||||
dist/sing-box=/usr/bin/sing-box
|
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
|
- name: Archive
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
@@ -234,7 +269,7 @@ jobs:
|
|||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
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.legacy_go && '-legacy' || '' }}
|
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' || '' }}
|
||||||
path: "dist"
|
path: "dist"
|
||||||
build_android:
|
build_android:
|
||||||
name: Build Android
|
name: Build Android
|
||||||
|
|||||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -120,6 +120,7 @@ jobs:
|
|||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
sudo gem install fpm
|
sudo gem install fpm
|
||||||
sudo apt-get install -y debsigs
|
sudo apt-get install -y debsigs
|
||||||
|
cp .fpm_systemd .fpm
|
||||||
fpm -t deb \
|
fpm -t deb \
|
||||||
--name "${NAME}" \
|
--name "${NAME}" \
|
||||||
-v "$PKG_VERSION" \
|
-v "$PKG_VERSION" \
|
||||||
@@ -138,6 +139,7 @@ jobs:
|
|||||||
run: |-
|
run: |-
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
sudo gem install fpm
|
sudo gem install fpm
|
||||||
|
cp .fpm_systemd .fpm
|
||||||
fpm -t rpm \
|
fpm -t rpm \
|
||||||
--name "${NAME}" \
|
--name "${NAME}" \
|
||||||
-v "$PKG_VERSION" \
|
-v "$PKG_VERSION" \
|
||||||
|
|||||||
4
Makefile
4
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)
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ func NewManager(logger log.ContextLogger, registry adapter.EndpointRegistry) *Ma
|
|||||||
|
|
||||||
func (m *Manager) Start(stage adapter.StartStage) error {
|
func (m *Manager) Start(stage adapter.StartStage) error {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
|
||||||
if m.started && m.stage >= stage {
|
if m.started && m.stage >= stage {
|
||||||
panic("already started")
|
panic("already started")
|
||||||
}
|
}
|
||||||
@@ -43,9 +42,12 @@ func (m *Manager) Start(stage adapter.StartStage) error {
|
|||||||
m.stage = stage
|
m.stage = stage
|
||||||
if stage == adapter.StartStateStart {
|
if stage == adapter.StartStateStart {
|
||||||
// started with outbound manager
|
// started with outbound manager
|
||||||
|
m.access.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, endpoint := range m.endpoints {
|
endpoints := m.endpoints
|
||||||
|
m.access.Unlock()
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
err := adapter.LegacyStart(endpoint, stage)
|
err := adapter.LegacyStart(endpoint, stage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, stage, " endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
return E.Cause(err, stage, " endpoint/", endpoint.Type(), "[", endpoint.Tag(), "]")
|
||||||
|
|||||||
Submodule clients/android updated: 8354b78e5d...cec05bf693
@@ -5,6 +5,7 @@ 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"
|
||||||
@@ -56,6 +57,14 @@ 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:
|
||||||
|
|||||||
@@ -333,7 +333,17 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
||||||
return d.udpListener.ListenPacket(context.Background(), network, address)
|
udpListener := d.udpListener
|
||||||
|
udpListener.Control = control.Append(udpListener.Control, func(network, address string, conn syscall.RawConn) error {
|
||||||
|
for _, wgControlFn := range WgControlFns {
|
||||||
|
err := wgControlFn(network, address, conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return udpListener.ListenPacket(context.Background(), network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -31,13 +31,18 @@ func BitTorrent(_ context.Context, metadata *adapter.InboundContext, reader io.R
|
|||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const header = "BitTorrent protocol"
|
||||||
var protocol [19]byte
|
var protocol [19]byte
|
||||||
_, err = reader.Read(protocol[:])
|
var n int
|
||||||
|
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 E.Cause1(ErrNeedMoreData, err)
|
||||||
}
|
}
|
||||||
if string(protocol[:]) != "BitTorrent protocol" {
|
if n < 19 {
|
||||||
return os.ErrInvalid
|
return ErrNeedMoreData
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.Protocol = C.ProtocolBitTorrent
|
metadata.Protocol = C.ProtocolBitTorrent
|
||||||
|
|||||||
@@ -32,6 +32,27 @@ 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()
|
||||||
|
|
||||||
|
|||||||
@@ -20,22 +20,36 @@ func StreamDomainNameQuery(readCtx context.Context, metadata *adapter.InboundCon
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return E.Cause1(ErrNeedMoreData, err)
|
||||||
}
|
}
|
||||||
if length == 0 {
|
if length < 12 {
|
||||||
return os.ErrInvalid
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
buffer := buf.NewSize(int(length))
|
buffer := buf.NewSize(int(length))
|
||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
_, err = buffer.ReadFullFrom(reader, buffer.FreeLen())
|
var n int
|
||||||
|
n, err = buffer.ReadFullFrom(reader, buffer.FreeLen())
|
||||||
|
packet := buffer.Bytes()
|
||||||
|
if n > 2 && packet[2]&0x80 != 0 { // QR
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
if n > 5 && packet[4] == 0 && packet[5] == 0 { // QDCOUNT
|
||||||
|
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 E.Cause1(ErrNeedMoreData, err)
|
||||||
}
|
}
|
||||||
return DomainNameQuery(readCtx, metadata, buffer.Bytes())
|
return DomainNameQuery(readCtx, metadata, packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
if err != nil || msg.Response || len(msg.Question) == 0 || len(msg.Answer) > 0 || len(msg.Ns) > 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
metadata.Protocol = C.ProtocolDNS
|
metadata.Protocol = C.ProtocolDNS
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sniff_test
|
package sniff_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -21,3 +22,32 @@ func TestSniffDNS(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, C.ProtocolDNS, metadata.Protocol)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.
|
|||||||
}
|
}
|
||||||
sniffError = E.Errors(sniffError, err)
|
sniffError = E.Errors(sniffError, err)
|
||||||
}
|
}
|
||||||
if !errors.Is(err, ErrNeedMoreData) {
|
if !errors.Is(sniffError, ErrNeedMoreData) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ func SSH(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
|
|||||||
const sshPrefix = "SSH-2.0-"
|
const sshPrefix = "SSH-2.0-"
|
||||||
bReader := bufio.NewReader(reader)
|
bReader := bufio.NewReader(reader)
|
||||||
prefix, err := bReader.Peek(len(sshPrefix))
|
prefix, err := bReader.Peek(len(sshPrefix))
|
||||||
|
if string(prefix[:]) != sshPrefix[:len(prefix)] {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause1(ErrNeedMoreData, err)
|
return E.Cause1(ErrNeedMoreData, err)
|
||||||
} else if string(prefix) != sshPrefix {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
}
|
||||||
fistLine, _, err := bReader.ReadLine()
|
fistLine, _, err := bReader.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -24,3 +24,24 @@ 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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,16 +89,20 @@ 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)
|
||||||
for i, shortIDString := range options.Reality.ShortID {
|
if len(options.Reality.ShortID) == 0 {
|
||||||
var shortID [8]byte
|
tlsConfig.ShortIds[[8]byte{0}] = true
|
||||||
decodedLen, err := hex.Decode(shortID[:], []byte(shortIDString))
|
} else {
|
||||||
if err != nil {
|
for i, shortIDString := range options.Reality.ShortID {
|
||||||
return nil, E.Cause(err, "decode short_id[", i, "]: ", shortIDString)
|
var shortID [8]byte
|
||||||
|
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)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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"
|
||||||
@@ -221,8 +222,12 @@ 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, ntp.TimeFuncFromContext(ctx), info.ServerName)
|
return GenerateKeyPair(nil, nil, timeFunc, info.ServerName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if certificate == nil {
|
if certificate == nil {
|
||||||
|
|||||||
6
debug.go
6
debug.go
@@ -24,9 +24,9 @@ func applyDebugOptions(options option.DebugOptions) {
|
|||||||
if options.TraceBack != "" {
|
if options.TraceBack != "" {
|
||||||
debug.SetTraceback(options.TraceBack)
|
debug.SetTraceback(options.TraceBack)
|
||||||
}
|
}
|
||||||
if options.MemoryLimit != 0 {
|
if options.MemoryLimit.Value() != 0 {
|
||||||
debug.SetMemoryLimit(int64(float64(options.MemoryLimit) / 1.5))
|
debug.SetMemoryLimit(int64(float64(options.MemoryLimit.Value()) / 1.5))
|
||||||
conntrack.MemoryLimit = uint64(options.MemoryLimit)
|
conntrack.MemoryLimit = options.MemoryLimit.Value()
|
||||||
}
|
}
|
||||||
if options.OOMKiller != nil {
|
if options.OOMKiller != nil {
|
||||||
conntrack.KillerEnabled = *options.OOMKiller
|
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", humanize.MemoryBytes(memStats.HeapInuse))
|
memObject.Put("heap", byteformats.FormatMemoryBytes(memStats.HeapInuse))
|
||||||
memObject.Put("stack", humanize.MemoryBytes(memStats.StackInuse))
|
memObject.Put("stack", byteformats.FormatMemoryBytes(memStats.StackInuse))
|
||||||
memObject.Put("idle", humanize.MemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
|
memObject.Put("idle", byteformats.FormatMemoryBytes(memStats.HeapIdle-memStats.HeapReleased))
|
||||||
memObject.Put("goroutines", runtime.NumGoroutine())
|
memObject.Put("goroutines", runtime.NumGoroutine())
|
||||||
memObject.Put("rss", rusageMaxRSS())
|
memObject.Put("rss", rusageMaxRSS())
|
||||||
|
|
||||||
|
|||||||
@@ -2,23 +2,66 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 1.11.11
|
||||||
|
|
||||||
|
* 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.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
|
### 1.11.7
|
||||||
|
|
||||||
* Fixes and improvements
|
* 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)._
|
_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
|
### 1.11.6
|
||||||
|
|
||||||
* Fixes and improvements
|
* 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)._
|
_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
|
### 1.11.5
|
||||||
|
|
||||||
* Fixes and improvements
|
* 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)._
|
_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
|
### 1.11.4
|
||||||
|
|
||||||
@@ -28,7 +71,8 @@ _We are temporarily unable to update sing-box apps on the App Store because the
|
|||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
_This version overwrites 1.11.2, as incorrect binaries were released due to a bug in the continuous integration process._
|
_This version overwrites 1.11.2, as incorrect binaries were released due to a bug in the continuous integration
|
||||||
|
process._
|
||||||
|
|
||||||
### 1.11.1
|
### 1.11.1
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,10 @@ 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"
|
||||||
@@ -235,22 +239,29 @@ 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.
|
||||||
|
|
||||||
Automatically configure iptables/nftables to redirect connections.
|
Improve TUN routing and performance using nftables.
|
||||||
|
|
||||||
*In Android*:
|
`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.
|
||||||
|
|
||||||
Only local IPv4 connections are forwarded. To share your VPN connection over hotspot or repeater,
|
Note that `auto_redirect` also works on Android,
|
||||||
|
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).
|
||||||
|
|
||||||
*In Linux*:
|
`auto_redirect` also automatically inserts compatibility rules
|
||||||
|
into the OpenWrt fw4 table, i.e.
|
||||||
|
it will work on routers without any extra configuration.
|
||||||
|
|
||||||
`auto_route` with `auto_redirect` works as expected on routers **without intervention**.
|
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
|
||||||
|
|
||||||
#### 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 `route[_exclude]_address_set` with `auto_redirect`.
|
Connection input mark used by `auto_redirect`.
|
||||||
|
|
||||||
`0x2023` is used by default.
|
`0x2023` is used by default.
|
||||||
|
|
||||||
@@ -258,7 +269,7 @@ Connection input mark used by `route[_exclude]_address_set` with `auto_redirect`
|
|||||||
|
|
||||||
!!! question "Since sing-box 1.10.0"
|
!!! question "Since sing-box 1.10.0"
|
||||||
|
|
||||||
Connection input mark used by `route[_exclude]_address_set` with `auto_redirect`.
|
Connection output mark used by `auto_redirect`.
|
||||||
|
|
||||||
`0x2024` is used by default.
|
`0x2024` is used by default.
|
||||||
|
|
||||||
@@ -269,17 +280,15 @@ Enforce strict routing rules when `auto_route` is enabled:
|
|||||||
*In Linux*:
|
*In Linux*:
|
||||||
|
|
||||||
* Let unsupported network unreachable
|
* Let unsupported network unreachable
|
||||||
* Make ICMP traffic route to tun instead of upstream interfaces
|
* For legacy reasons, when neither `strict_route` nor `auto_redirect` are enabled, all ICMP traffic will not go through TUN.
|
||||||
* Route all connections to tun
|
|
||||||
|
|
||||||
It prevents IP address leaks and makes DNS hijacking work on Android.
|
|
||||||
|
|
||||||
*In Windows*:
|
*In Windows*:
|
||||||
|
|
||||||
* Add firewall rules to prevent DNS leak caused by
|
* Let unsupported network unreachable
|
||||||
|
* 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 applications (such as VirtualBox) from working properly in certain situations.
|
It may prevent some Windows applications (such as VirtualBox) from working properly in certain situations.
|
||||||
|
|
||||||
#### route_address
|
#### route_address
|
||||||
|
|
||||||
@@ -368,8 +377,6 @@ 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"
|
||||||
|
|
||||||
!!! question "Since sing-box 1.11.0"
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|||||||
@@ -215,6 +215,10 @@ 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 起"
|
||||||
@@ -239,21 +243,22 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
仅支持 Linux,且需要 `auto_route` 已启用。
|
仅支持 Linux,且需要 `auto_route` 已启用。
|
||||||
|
|
||||||
自动配置 iptables/nftables 以重定向连接。
|
通过使用 nftables 改善 TUN 路由和性能。
|
||||||
|
|
||||||
*在 Android 中*:
|
在 Linux 上始终推荐使用 `auto_redirect`,它提供更好的路由、更高的性能(优于 tproxy),并避免了 TUN 和 Docker 桥接网络之间的冲突。
|
||||||
|
|
||||||
仅转发本地 IPv4 连接。 要通过热点或中继共享您的 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
|
请注意,`auto_redirect` 也适用于 Android,但由于缺少 `nftables` 和 `ip6tables`,仅执行简单的 IPv4 TCP 转发。
|
||||||
|
若要在 Android 上通过热点或中继器共享 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
|
||||||
|
|
||||||
*在 Linux 中*:
|
`auto_redirect` 还会自动将兼容性规则插入 OpenWrt 的 fw4 表中,即无需额外配置即可在路由器上工作。
|
||||||
|
|
||||||
带有 `auto_redirect `的 `auto_route` 可以在路由器上按预期工作,**无需干预**。
|
与 `route.default_mark` 和 `[dialOptions].routing_mark` 冲突。
|
||||||
|
|
||||||
#### auto_redirect_input_mark
|
#### auto_redirect_input_mark
|
||||||
|
|
||||||
!!! question "自 sing-box 1.10.0 起"
|
!!! question "自 sing-box 1.10.0 起"
|
||||||
|
|
||||||
`route_address_set` 和 `route_exclude_address_set` 使用的连接输入标记。
|
`auto_redirect` 使用的连接输入标记。
|
||||||
|
|
||||||
默认使用 `0x2023`。
|
默认使用 `0x2023`。
|
||||||
|
|
||||||
@@ -261,29 +266,25 @@ tun 接口的 IPv6 前缀。
|
|||||||
|
|
||||||
!!! question "自 sing-box 1.10.0 起"
|
!!! question "自 sing-box 1.10.0 起"
|
||||||
|
|
||||||
`route_address_set` 和 `route_exclude_address_set` 使用的连接输出标记。
|
`auto_redirect` 使用的连接输出标记。
|
||||||
|
|
||||||
默认使用 `0x2024`。
|
默认使用 `0x2024`。
|
||||||
|
|
||||||
#### strict_route
|
#### strict_route
|
||||||
|
|
||||||
启用 `auto_route` 时执行严格的路由规则。
|
当启用 `auto_route` 时,强制执行严格的路由规则:
|
||||||
|
|
||||||
*在 Linux 中*:
|
*在 Linux 中*:
|
||||||
|
|
||||||
* 让不支持的网络无法到达
|
* 使不支持的网络不可达。
|
||||||
* 使 ICMP 流量路由到 tun 而不是上游接口
|
* 出于历史遗留原因,当未启用 `strict_route` 或 `auto_redirect` 时,所有 ICMP 流量将不会通过 TUN。
|
||||||
* 将所有连接路由到 tun
|
|
||||||
|
|
||||||
它可以防止 IP 地址泄漏,并使 DNS 劫持在 Android 上工作。
|
*在 Windows 中*:
|
||||||
|
|
||||||
*在 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
|
它可能会使某些 Windows 应用程序(如 VirtualBox)在某些情况下无法正常工作。
|
||||||
的 [普通多宿主 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,8 +343,6 @@ tun 接口的 IPv6 前缀。
|
|||||||
将指定规则集中的目标 IP CIDR 规则添加到防火墙。
|
将指定规则集中的目标 IP CIDR 规则添加到防火墙。
|
||||||
不匹配的流量将绕过 sing-box 路由。
|
不匹配的流量将绕过 sing-box 路由。
|
||||||
|
|
||||||
与 `route.default_mark` 和 `[dialOptions].routing_mark` 冲突。
|
|
||||||
|
|
||||||
=== "`auto_redirect` 未启用"
|
=== "`auto_redirect` 未启用"
|
||||||
|
|
||||||
!!! question "自 sing-box 1.11.0 起"
|
!!! question "自 sing-box 1.11.0 起"
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
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
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
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` 出站关闭所有传入请求。
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ 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
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ icon: material/new-box
|
|||||||
|
|
||||||
规则集格式, `source` 或 `binary`。
|
规则集格式, `source` 或 `binary`。
|
||||||
|
|
||||||
|
当 `path` 或 `url` 使用 `json` 或 `srs` 作为扩展名时可选。
|
||||||
|
|
||||||
### 本地字段
|
### 本地字段
|
||||||
|
|
||||||
#### path
|
#### path
|
||||||
|
|||||||
@@ -9,43 +9,56 @@ icon: material/package
|
|||||||
=== ":material-debian: Debian / APT"
|
=== ":material-debian: Debian / APT"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
sudo mkdir -p /etc/apt/keyrings &&
|
||||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc &&
|
||||||
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
sudo chmod a+r /etc/apt/keyrings/sagernet.asc &&
|
||||||
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
echo '
|
||||||
sudo apt-get update
|
Types: deb
|
||||||
sudo apt-get install sing-box # or sing-box-beta
|
URIs: https://deb.sagernet.org/
|
||||||
|
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"
|
=== ":material-redhat: Redhat / DNF 5"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf -y install dnf-plugins-core
|
sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo &&
|
||||||
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
|
sudo dnf install sing-box # or sing-box-beta
|
||||||
|
```
|
||||||
|
|
||||||
|
=== ":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
|
||||||
|
|
||||||
=== ":material-debian: Debian / DEB"
|
The script download and install the latest package from GitHub releases
|
||||||
|
for deb or rpm based Linux distributions, ArchLinux and OpenWrt.
|
||||||
|
|
||||||
```bash
|
```shell
|
||||||
bash <(curl -fsSL https://sing-box.app/deb-install.sh)
|
curl -fsSL https://sing-box.app/install.sh | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
=== ":material-redhat: Redhat / RPM"
|
or latest beta:
|
||||||
|
|
||||||
```bash
|
```shell
|
||||||
bash <(curl -fsSL https://sing-box.app/rpm-install.sh)
|
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta
|
||||||
```
|
```
|
||||||
(This applies to any distribution that uses `rpm` and `systemd`. Because of how `rpm` defines dependencies, if it installs, it probably works.)
|
|
||||||
|
|
||||||
=== ":simple-archlinux: Archlinux / PKG"
|
or specific version:
|
||||||
|
|
||||||
```bash
|
```shell
|
||||||
bash <(curl -fsSL https://sing-box.app/arch-install.sh)
|
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version>
|
||||||
```
|
```
|
||||||
|
|
||||||
## :material-book-lock-open: Managed Installation
|
## :material-book-lock-open: Managed Installation
|
||||||
|
|
||||||
|
|||||||
@@ -9,43 +9,55 @@ icon: material/package
|
|||||||
=== ":material-debian: Debian / APT"
|
=== ":material-debian: Debian / APT"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
sudo mkdir -p /etc/apt/keyrings &&
|
||||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc &&
|
||||||
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
sudo chmod a+r /etc/apt/keyrings/sagernet.asc &&
|
||||||
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
echo '
|
||||||
sudo apt-get update
|
Types: deb
|
||||||
sudo apt-get install sing-box # or sing-box-beta
|
URIs: https://deb.sagernet.org/
|
||||||
|
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"
|
=== ":material-redhat: Redhat / DNF 5"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf -y install dnf-plugins-core
|
sudo dnf config-manager addrepo --from-repofile=https://sing-box.app/sing-box.repo &&
|
||||||
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
|
sudo dnf install sing-box # or sing-box-beta
|
||||||
|
```
|
||||||
|
|
||||||
|
=== ":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: 手动安装
|
||||||
|
|
||||||
=== ":material-debian: Debian / DEB"
|
该脚本从 GitHub 发布中下载并安装最新的软件包,适用于基于 deb 或 rpm 的 Linux 发行版、ArchLinux 和 OpenWrt。
|
||||||
|
|
||||||
```bash
|
```shell
|
||||||
bash <(curl -fsSL https://sing-box.app/deb-install.sh)
|
curl -fsSL https://sing-box.app/install.sh | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
=== ":material-redhat: Redhat / RPM"
|
或最新测试版:
|
||||||
|
|
||||||
```bash
|
```shell
|
||||||
bash <(curl -fsSL https://sing-box.app/rpm-install.sh)
|
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta
|
||||||
```
|
```
|
||||||
(这适用于任何使用 `rpm` 和 `systemd` 的发行版。由于 `rpm` 定义依赖关系的方式,如果安装成功,就多半能用。)
|
|
||||||
|
|
||||||
=== ":simple-archlinux: Archlinux / PKG"
|
或指定版本:
|
||||||
|
|
||||||
```bash
|
```shell
|
||||||
bash <(curl -fsSL https://sing-box.app/arch-install.sh)
|
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version>
|
||||||
```
|
```
|
||||||
|
|
||||||
## :material-book-lock-open: 托管安装
|
## :material-book-lock-open: 托管安装
|
||||||
|
|
||||||
|
|||||||
116
docs/installation/tools/install.sh
Executable file
116
docs/installation/tools/install.sh
Executable file
@@ -0,0 +1,116 @@
|
|||||||
|
#!/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"
|
||||||
@@ -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 humanize.Bytes(uint64(length))
|
return byteformats.FormatBytes(uint64(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatMemoryBytes(length int64) string {
|
func FormatMemoryBytes(length int64) string {
|
||||||
return humanize.MemoryBytes(uint64(length))
|
return byteformats.FormatMemoryBytes(uint64(length))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatDuration(duration int64) string {
|
func FormatDuration(duration int64) string {
|
||||||
|
|||||||
23
go.mod
23
go.mod
@@ -1,6 +1,8 @@
|
|||||||
module github.com/sagernet/sing-box
|
module github.com/sagernet/sing-box
|
||||||
|
|
||||||
go 1.20
|
go 1.23.6
|
||||||
|
|
||||||
|
toolchain go1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caddyserver/certmagic v0.20.0
|
github.com/caddyserver/certmagic v0.20.0
|
||||||
@@ -26,18 +28,18 @@ require (
|
|||||||
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.49.0-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.6-0.20250406121928-926a5a1e8bb7
|
github.com/sagernet/sing v0.6.9
|
||||||
github.com/sagernet/sing-dns v0.4.1
|
github.com/sagernet/sing-dns v0.4.3
|
||||||
github.com/sagernet/sing-mux v0.3.1
|
github.com/sagernet/sing-mux v0.3.2
|
||||||
github.com/sagernet/sing-quic v0.4.1
|
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76
|
||||||
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
|
||||||
github.com/sagernet/sing-tun v0.6.1
|
github.com/sagernet/sing-tun v0.6.5
|
||||||
github.com/sagernet/sing-vmess v0.2.0
|
github.com/sagernet/sing-vmess v0.2.1
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
github.com/sagernet/smux v1.5.34-mod.2
|
||||||
github.com/sagernet/utls v1.6.7
|
github.com/sagernet/utls v1.6.7
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.5
|
github.com/sagernet/wireguard-go v0.0.1-beta.7
|
||||||
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.10.0
|
||||||
@@ -88,6 +90,7 @@ require (
|
|||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/tevino/abool/v2 v2.1.0 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
@@ -102,3 +105,5 @@ require (
|
|||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/sagernet/wireguard-go => github.com/getlantern/wireguard-go v0.0.1-beta.5.0.20250303165430-793006c422ec
|
||||||
|
|||||||
50
go.sum
50
go.sum
@@ -18,11 +18,14 @@ github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbY
|
|||||||
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/getlantern/wireguard-go v0.0.1-beta.5.0.20250303165430-793006c422ec h1:jXekDkSozctYj5JQlV1mCsIW+qHpIkgSFhOIpgRSB1I=
|
||||||
|
github.com/getlantern/wireguard-go v0.0.1-beta.5.0.20250303165430-793006c422ec/go.mod h1:akc2Wh+rX9bFFNnHJGsQ8VIV3eJI1LXJYgx2Y+8lcW8=
|
||||||
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
github.com/go-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-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
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=
|
||||||
@@ -34,6 +37,7 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
|
|||||||
github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
|
github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
|
||||||
github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
github.com/gofrs/uuid/v5 v5.3.2/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/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
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=
|
||||||
@@ -61,6 +65,7 @@ github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZY
|
|||||||
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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
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=
|
||||||
@@ -85,6 +90,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||||||
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/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
|
||||||
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=
|
||||||
@@ -118,31 +124,28 @@ github.com/sagernet/quic-go v0.49.0-beta.1 h1:3LdoCzVVfYRibZns1tYWSIoB65fpTmrwy+
|
|||||||
github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8WHNsRs71b3Lt1+p/U=
|
github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8WHNsRs71b3Lt1+p/U=
|
||||||
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.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.6.9 h1:y/XJH17oyBd6hxgQtKnIdLXu7TsOHxO5i1JeVfVmjXw=
|
||||||
github.com/sagernet/sing v0.6.6-0.20250406121928-926a5a1e8bb7 h1:ZJauxLmH12Gzv3nucfjsSBQw9UA8t7Sxu8pYHBSP2TU=
|
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing v0.6.6-0.20250406121928-926a5a1e8bb7/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing-dns v0.4.3 h1:R6X9oWYbdZ0Mm+8PkdqrBkqx3JwiAbnETUZGOpEdY4E=
|
||||||
github.com/sagernet/sing-dns v0.4.1 h1:nozS7iqpxZ7aV73oHbkD/8haOvf3XXDCgT//8NdYirk=
|
github.com/sagernet/sing-dns v0.4.3/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
|
||||||
github.com/sagernet/sing-dns v0.4.1/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
|
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
||||||
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
|
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||||
github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
|
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76 h1:iwpCX6H3nZEOGUGwx0q5azcgYOA9f6v9YssihXoRKHk=
|
||||||
github.com/sagernet/sing-quic v0.4.1 h1:pxlMa4efZu/M07RgGagNNDDyl6ZUwpmNUjRTpgHOWK4=
|
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY=
|
||||||
github.com/sagernet/sing-quic v0.4.1/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY=
|
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
|
github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
|
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
|
||||||
github.com/sagernet/sing-tun v0.6.1 h1:4l0+gnEKcGjlWfUVTD+W0BRApqIny/lU2ZliurE+VMo=
|
github.com/sagernet/sing-tun v0.6.5 h1:nGfD6GNq/r0tEjdZHOV3BS6fydSmd4kBAokU5rffssg=
|
||||||
github.com/sagernet/sing-tun v0.6.1/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
github.com/sagernet/sing-tun v0.6.5/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||||
github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
|
github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug=
|
||||||
github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
||||||
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
||||||
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.5/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
|
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
@@ -150,10 +153,19 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k
|
|||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
|
||||||
|
github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
@@ -165,6 +177,7 @@ github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvv
|
|||||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
@@ -190,11 +203,12 @@ golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||||
|
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
|||||||
@@ -1,43 +1,14 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
import (
|
import "github.com/sagernet/sing/common/byteformats"
|
||||||
"github.com/sagernet/sing-box/common/humanize"
|
|
||||||
"github.com/sagernet/sing/common/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DebugOptions struct {
|
type DebugOptions struct {
|
||||||
Listen string `json:"listen,omitempty"`
|
Listen string `json:"listen,omitempty"`
|
||||||
GCPercent *int `json:"gc_percent,omitempty"`
|
GCPercent *int `json:"gc_percent,omitempty"`
|
||||||
MaxStack *int `json:"max_stack,omitempty"`
|
MaxStack *int `json:"max_stack,omitempty"`
|
||||||
MaxThreads *int `json:"max_threads,omitempty"`
|
MaxThreads *int `json:"max_threads,omitempty"`
|
||||||
PanicOnFault *bool `json:"panic_on_fault,omitempty"`
|
PanicOnFault *bool `json:"panic_on_fault,omitempty"`
|
||||||
TraceBack string `json:"trace_back,omitempty"`
|
TraceBack string `json:"trace_back,omitempty"`
|
||||||
MemoryLimit MemoryBytes `json:"memory_limit,omitempty"`
|
MemoryLimit *byteformats.MemoryBytes `json:"memory_limit,omitempty"`
|
||||||
OOMKiller *bool `json:"oom_killer,omitempty"`
|
OOMKiller *bool `json:"oom_killer,omitempty"`
|
||||||
}
|
|
||||||
|
|
||||||
type MemoryBytes uint64
|
|
||||||
|
|
||||||
func (l MemoryBytes) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(humanize.MemoryBytes(uint64(l)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *MemoryBytes) UnmarshalJSON(bytes []byte) error {
|
|
||||||
var valueInteger int64
|
|
||||||
err := json.Unmarshal(bytes, &valueInteger)
|
|
||||||
if err == nil {
|
|
||||||
*l = MemoryBytes(valueInteger)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var valueString string
|
|
||||||
err = json.Unmarshal(bytes, &valueString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
parsedValue, err := humanize.ParseMemoryBytes(valueString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*l = MemoryBytes(parsedValue)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
|
import "github.com/sagernet/sing/common/byteformats"
|
||||||
|
|
||||||
type HysteriaInboundOptions struct {
|
type HysteriaInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Up string `json:"up,omitempty"`
|
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
Down string `json:"down,omitempty"`
|
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||||
DownMbps int `json:"down_mbps,omitempty"`
|
DownMbps int `json:"down_mbps,omitempty"`
|
||||||
Obfs string `json:"obfs,omitempty"`
|
Obfs string `json:"obfs,omitempty"`
|
||||||
Users []HysteriaUser `json:"users,omitempty"`
|
Users []HysteriaUser `json:"users,omitempty"`
|
||||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||||
ReceiveWindowClient uint64 `json:"recv_window_client,omitempty"`
|
ReceiveWindowClient uint64 `json:"recv_window_client,omitempty"`
|
||||||
MaxConnClient int `json:"max_conn_client,omitempty"`
|
MaxConnClient int `json:"max_conn_client,omitempty"`
|
||||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||||
InboundTLSOptionsContainer
|
InboundTLSOptionsContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,16 +26,16 @@ type HysteriaUser struct {
|
|||||||
type HysteriaOutboundOptions struct {
|
type HysteriaOutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
Up string `json:"up,omitempty"`
|
Up *byteformats.NetworkBytesCompat `json:"up,omitempty"`
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
Down string `json:"down,omitempty"`
|
Down *byteformats.NetworkBytesCompat `json:"down,omitempty"`
|
||||||
DownMbps int `json:"down_mbps,omitempty"`
|
DownMbps int `json:"down_mbps,omitempty"`
|
||||||
Obfs string `json:"obfs,omitempty"`
|
Obfs string `json:"obfs,omitempty"`
|
||||||
Auth []byte `json:"auth,omitempty"`
|
Auth []byte `json:"auth,omitempty"`
|
||||||
AuthString string `json:"auth_str,omitempty"`
|
AuthString string `json:"auth_str,omitempty"`
|
||||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||||
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
||||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
OutboundTLSOptionsContainer
|
OutboundTLSOptionsContainer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func (h *Outbound) UnmarshalJSONContext(ctx context.Context, content []byte) err
|
|||||||
return E.New("missing outbound options registry in context")
|
return E.New("missing outbound options registry in context")
|
||||||
}
|
}
|
||||||
switch h.Type {
|
switch h.Type {
|
||||||
case C.TypeBlock, C.TypeDNS:
|
case C.TypeDNS:
|
||||||
deprecated.Report(ctx, deprecated.OptionSpecialOutbounds)
|
deprecated.Report(ctx, deprecated.OptionSpecialOutbounds)
|
||||||
}
|
}
|
||||||
options, loaded := registry.CreateOptions(h.Type)
|
options, loaded := registry.CreateOptions(h.Type)
|
||||||
|
|||||||
@@ -252,6 +252,14 @@ type _RejectActionOptions struct {
|
|||||||
|
|
||||||
type RejectActionOptions _RejectActionOptions
|
type RejectActionOptions _RejectActionOptions
|
||||||
|
|
||||||
|
func (r RejectActionOptions) MarshalJSON() ([]byte, error) {
|
||||||
|
switch r.Method {
|
||||||
|
case C.RuleActionRejectMethodDefault:
|
||||||
|
r.Method = ""
|
||||||
|
}
|
||||||
|
return json.Marshal((_RejectActionOptions)(r))
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
|
func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
|
err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@@ -27,6 +29,18 @@ type _RuleSet struct {
|
|||||||
type RuleSet _RuleSet
|
type RuleSet _RuleSet
|
||||||
|
|
||||||
func (r RuleSet) MarshalJSON() ([]byte, error) {
|
func (r RuleSet) MarshalJSON() ([]byte, error) {
|
||||||
|
if r.Type != C.RuleSetTypeInline {
|
||||||
|
var defaultFormat string
|
||||||
|
switch r.Type {
|
||||||
|
case C.RuleSetTypeLocal:
|
||||||
|
defaultFormat = ruleSetDefaultFormat(r.LocalOptions.Path)
|
||||||
|
case C.RuleSetTypeRemote:
|
||||||
|
defaultFormat = ruleSetDefaultFormat(r.RemoteOptions.URL)
|
||||||
|
}
|
||||||
|
if r.Format == defaultFormat {
|
||||||
|
r.Format = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
var v any
|
var v any
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case "", C.RuleSetTypeInline:
|
case "", C.RuleSetTypeInline:
|
||||||
@@ -62,7 +76,19 @@ func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
|||||||
default:
|
default:
|
||||||
return E.New("unknown rule-set type: " + r.Type)
|
return E.New("unknown rule-set type: " + r.Type)
|
||||||
}
|
}
|
||||||
|
err = badjson.UnmarshallExcluded(bytes, (*_RuleSet)(r), v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if r.Type != C.RuleSetTypeInline {
|
if r.Type != C.RuleSetTypeInline {
|
||||||
|
if r.Format == "" {
|
||||||
|
switch r.Type {
|
||||||
|
case C.RuleSetTypeLocal:
|
||||||
|
r.Format = ruleSetDefaultFormat(r.LocalOptions.Path)
|
||||||
|
case C.RuleSetTypeRemote:
|
||||||
|
r.Format = ruleSetDefaultFormat(r.RemoteOptions.URL)
|
||||||
|
}
|
||||||
|
}
|
||||||
switch r.Format {
|
switch r.Format {
|
||||||
case "":
|
case "":
|
||||||
return E.New("missing format")
|
return E.New("missing format")
|
||||||
@@ -73,13 +99,23 @@ func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
|||||||
} else {
|
} else {
|
||||||
r.Format = ""
|
r.Format = ""
|
||||||
}
|
}
|
||||||
err = badjson.UnmarshallExcluded(bytes, (*_RuleSet)(r), v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ruleSetDefaultFormat(path string) string {
|
||||||
|
if pathURL, err := url.Parse(path); err == nil {
|
||||||
|
path = pathURL.Path
|
||||||
|
}
|
||||||
|
switch filepath.Ext(path) {
|
||||||
|
case ".json":
|
||||||
|
return C.RuleSetFormatSource
|
||||||
|
case ".srs":
|
||||||
|
return C.RuleSetFormatBinary
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type LocalRuleSet struct {
|
type LocalRuleSet struct {
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type WireGuardEndpointOptions struct {
|
|||||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||||
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||||
Workers int `json:"workers,omitempty"`
|
Workers int `json:"workers,omitempty"`
|
||||||
|
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||||
DialerOptions
|
DialerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +39,13 @@ type LegacyWireGuardOutboundOptions struct {
|
|||||||
PrivateKey string `json:"private_key"`
|
PrivateKey string `json:"private_key"`
|
||||||
Peers []LegacyWireGuardPeer `json:"peers,omitempty"`
|
Peers []LegacyWireGuardPeer `json:"peers,omitempty"`
|
||||||
ServerOptions
|
ServerOptions
|
||||||
PeerPublicKey string `json:"peer_public_key"`
|
PeerPublicKey string `json:"peer_public_key"`
|
||||||
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
PreSharedKey string `json:"pre_shared_key,omitempty"`
|
||||||
Reserved []uint8 `json:"reserved,omitempty"`
|
Reserved []uint8 `json:"reserved,omitempty"`
|
||||||
Workers int `json:"workers,omitempty"`
|
Workers int `json:"workers,omitempty"`
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
|
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LegacyWireGuardPeer struct {
|
type LegacyWireGuardPeer struct {
|
||||||
@@ -53,3 +55,15 @@ type LegacyWireGuardPeer struct {
|
|||||||
AllowedIPs badoption.Listable[netip.Prefix] `json:"allowed_ips,omitempty"`
|
AllowedIPs badoption.Listable[netip.Prefix] `json:"allowed_ips,omitempty"`
|
||||||
Reserved []uint8 `json:"reserved,omitempty"`
|
Reserved []uint8 `json:"reserved,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WireGuardAmnezia struct {
|
||||||
|
JC int `json:"jc,omitempty"`
|
||||||
|
JMin int `json:"jmin,omitempty"`
|
||||||
|
JMax int `json:"jmax,omitempty"`
|
||||||
|
S1 int `json:"s1,omitempty"`
|
||||||
|
S2 int `json:"s2,omitempty"`
|
||||||
|
H1 uint32 `json:"h1,omitempty"`
|
||||||
|
H2 uint32 `json:"h2,omitempty"`
|
||||||
|
H3 uint32 `json:"h3,omitempty"`
|
||||||
|
H4 uint32 `json:"h4,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
|
|||||||
case 2:
|
case 2:
|
||||||
destination.Addr = i.overrideDestination.Addr
|
destination.Addr = i.overrideDestination.Addr
|
||||||
case 3:
|
case 3:
|
||||||
destination.Port = metadata.Destination.Port
|
destination.Port = i.overrideDestination.Port
|
||||||
}
|
}
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
if i.overrideOption != 0 {
|
if i.overrideOption != 0 {
|
||||||
|
|||||||
@@ -395,12 +395,16 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
|||||||
func (g *URLTestGroup) performUpdateCheck() {
|
func (g *URLTestGroup) performUpdateCheck() {
|
||||||
var updated bool
|
var updated bool
|
||||||
if outbound, exists := g.Select(N.NetworkTCP); outbound != nil && (g.selectedOutboundTCP == nil || (exists && outbound != g.selectedOutboundTCP)) {
|
if outbound, exists := g.Select(N.NetworkTCP); outbound != nil && (g.selectedOutboundTCP == nil || (exists && outbound != g.selectedOutboundTCP)) {
|
||||||
|
if g.selectedOutboundTCP != nil {
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
g.selectedOutboundTCP = outbound
|
g.selectedOutboundTCP = outbound
|
||||||
updated = true
|
|
||||||
}
|
}
|
||||||
if outbound, exists := g.Select(N.NetworkUDP); outbound != nil && (g.selectedOutboundUDP == nil || (exists && outbound != g.selectedOutboundUDP)) {
|
if outbound, exists := g.Select(N.NetworkUDP); outbound != nil && (g.selectedOutboundUDP == nil || (exists && outbound != g.selectedOutboundUDP)) {
|
||||||
|
if g.selectedOutboundUDP != nil {
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
g.selectedOutboundUDP = outbound
|
g.selectedOutboundUDP = outbound
|
||||||
updated = true
|
|
||||||
}
|
}
|
||||||
if updated {
|
if updated {
|
||||||
g.interruptGroup.Interrupt(g.interruptExternalConnections)
|
g.interruptGroup.Interrupt(g.interruptExternalConnections)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/inbound"
|
"github.com/sagernet/sing-box/adapter/inbound"
|
||||||
"github.com/sagernet/sing-box/common/humanize"
|
|
||||||
"github.com/sagernet/sing-box/common/listener"
|
"github.com/sagernet/sing-box/common/listener"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@@ -16,7 +15,6 @@ import (
|
|||||||
"github.com/sagernet/sing-quic/hysteria"
|
"github.com/sagernet/sing-quic/hysteria"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
@@ -56,19 +54,13 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
var sendBps, receiveBps uint64
|
var sendBps, receiveBps uint64
|
||||||
if len(options.Up) > 0 {
|
if options.Up.Value() > 0 {
|
||||||
sendBps, err = humanize.ParseBytes(options.Up)
|
sendBps = options.Up.Value()
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid up speed format: ", options.Up)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
if len(options.Down) > 0 {
|
if options.Down.Value() > 0 {
|
||||||
receiveBps, err = humanize.ParseBytes(options.Down)
|
receiveBps = options.Down.Value()
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid down speed format: ", options.Down)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/outbound"
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/common/humanize"
|
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
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"
|
||||||
@@ -59,19 +58,13 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
password = string(options.Auth)
|
password = string(options.Auth)
|
||||||
}
|
}
|
||||||
var sendBps, receiveBps uint64
|
var sendBps, receiveBps uint64
|
||||||
if len(options.Up) > 0 {
|
if options.Up.Value() > 0 {
|
||||||
sendBps, err = humanize.ParseBytes(options.Up)
|
sendBps = options.Up.Value()
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid up speed format: ", options.Up)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
if len(options.Down) > 0 {
|
if options.Down.Value() > 0 {
|
||||||
receiveBps, err = humanize.ParseBytes(options.Down)
|
receiveBps = options.Down.Value()
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid down speed format: ", options.Down)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/outbound"
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
@@ -191,9 +192,29 @@ func (s *Outbound) DialContext(ctx context.Context, network string, destination
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return client.Dial(network, destination.String())
|
conn, err := client.Dial(network, destination.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &chanConnWrapper{Conn: conn}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (s *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type chanConnWrapper struct {
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chanConnWrapper) SetDeadline(t time.Time) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chanConnWrapper) SetReadDeadline(t time.Time) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chanConnWrapper) SetWriteDeadline(t time.Time) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize auto-redirect")
|
return nil, E.Cause(err, "initialize auto-redirect")
|
||||||
}
|
}
|
||||||
if !C.IsAndroid && (len(inbound.routeRuleSet) > 0 || len(inbound.routeExcludeRuleSet) > 0) {
|
if !C.IsAndroid {
|
||||||
inbound.tunOptions.AutoRedirectMarkMode = true
|
inbound.tunOptions.AutoRedirectMarkMode = true
|
||||||
err = networkManager.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark)
|
err = networkManager.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -26,11 +26,6 @@ func RegisterEndpoint(registry *endpoint.Registry) {
|
|||||||
endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)
|
endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
_ adapter.Endpoint = (*Endpoint)(nil)
|
|
||||||
_ adapter.InterfaceUpdateListener = (*Endpoint)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
endpoint.Adapter
|
endpoint.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -61,6 +56,20 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
} else {
|
} else {
|
||||||
udpTimeout = C.UDPTimeout
|
udpTimeout = C.UDPTimeout
|
||||||
}
|
}
|
||||||
|
var amnezia *wireguard.AmneziaOptions
|
||||||
|
if options.Amnezia != nil {
|
||||||
|
amnezia = &wireguard.AmneziaOptions{
|
||||||
|
JC: options.Amnezia.JC,
|
||||||
|
JMin: options.Amnezia.JMin,
|
||||||
|
JMax: options.Amnezia.JMax,
|
||||||
|
S1: options.Amnezia.S1,
|
||||||
|
S2: options.Amnezia.S2,
|
||||||
|
H1: options.Amnezia.H1,
|
||||||
|
H2: options.Amnezia.H2,
|
||||||
|
H3: options.Amnezia.H3,
|
||||||
|
H4: options.Amnezia.H4,
|
||||||
|
}
|
||||||
|
}
|
||||||
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
@@ -96,6 +105,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Workers: options.Workers,
|
Workers: options.Workers,
|
||||||
|
Amnezia: amnezia,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -118,10 +128,6 @@ func (w *Endpoint) Close() error {
|
|||||||
return w.endpoint.Close()
|
return w.endpoint.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Endpoint) InterfaceUpdated() {
|
|
||||||
w.endpoint.BindUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
||||||
return w.router.PreMatch(adapter.InboundContext{
|
return w.router.PreMatch(adapter.InboundContext{
|
||||||
Inbound: w.Tag(),
|
Inbound: w.Tag(),
|
||||||
|
|||||||
@@ -25,11 +25,6 @@ func RegisterOutbound(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.LegacyWireGuardOutboundOptions](registry, C.TypeWireGuard, NewOutbound)
|
outbound.Register[option.LegacyWireGuardOutboundOptions](registry, C.TypeWireGuard, NewOutbound)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
_ adapter.Endpoint = (*Endpoint)(nil)
|
|
||||||
_ adapter.InterfaceUpdateListener = (*Endpoint)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -79,6 +74,20 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
Reserved: options.Reserved,
|
Reserved: options.Reserved,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
var amnezia *wireguard.AmneziaOptions
|
||||||
|
if options.Amnezia != nil {
|
||||||
|
amnezia = &wireguard.AmneziaOptions{
|
||||||
|
JC: options.Amnezia.JC,
|
||||||
|
JMin: options.Amnezia.JMin,
|
||||||
|
JMax: options.Amnezia.JMax,
|
||||||
|
S1: options.Amnezia.S1,
|
||||||
|
S2: options.Amnezia.S2,
|
||||||
|
H1: options.Amnezia.H1,
|
||||||
|
H2: options.Amnezia.H2,
|
||||||
|
H3: options.Amnezia.H3,
|
||||||
|
H4: options.Amnezia.H4,
|
||||||
|
}
|
||||||
|
}
|
||||||
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
@@ -102,6 +111,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
},
|
},
|
||||||
Peers: peers,
|
Peers: peers,
|
||||||
Workers: options.Workers,
|
Workers: options.Workers,
|
||||||
|
Amnezia: amnezia,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -124,10 +134,6 @@ func (o *Outbound) Close() error {
|
|||||||
return o.endpoint.Close()
|
return o.endpoint.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Outbound) InterfaceUpdated() {
|
|
||||||
o.endpoint.BindUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
switch network {
|
switch network {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
|
|||||||
11
release/config/openwrt.init
Normal file → Executable file
11
release/config/openwrt.init
Normal file → Executable file
@@ -1,26 +1,27 @@
|
|||||||
#!/bin/sh /etc/rc.common
|
#!/bin/sh /etc/rc.common
|
||||||
|
|
||||||
|
USE_PROCD=1
|
||||||
|
START=99
|
||||||
PROG="/usr/bin/sing-box"
|
PROG="/usr/bin/sing-box"
|
||||||
|
|
||||||
start_service() {
|
start_service() {
|
||||||
config_load "sing-box"
|
config_load "sing-box"
|
||||||
|
|
||||||
local enabled config_file working_directory
|
local enabled config_file working_directory
|
||||||
local log_stdout log_stderr
|
local log_stderr
|
||||||
config_get_bool enabled "main" "enabled" "0"
|
config_get_bool enabled "main" "enabled" "0"
|
||||||
[ "$enabled" -eq "1" ] || return 0
|
[ "$enabled" -eq "1" ] || return 0
|
||||||
|
|
||||||
config_get config_file "main" "conffile" "/etc/sing-box/config.json"
|
config_get config_file "main" "conffile" "/etc/sing-box/config.json"
|
||||||
config_get working_directory "main" "workdir" "/usr/share/sing-box"
|
config_get working_directory "main" "workdir" "/usr/share/sing-box"
|
||||||
config_get_bool log_stdout "main" "log_stdout" "1"
|
|
||||||
config_get_bool log_stderr "main" "log_stderr" "1"
|
config_get_bool log_stderr "main" "log_stderr" "1"
|
||||||
|
|
||||||
procd_open_instance
|
procd_open_instance
|
||||||
procd_swet_param command "$PROG" run -c "$conffile" -D "$workdir"
|
procd_set_param command "$PROG" run -c "$config_file" -D "$working_directory"
|
||||||
procd_set_param file "$conffile"
|
procd_set_param file "$config_file"
|
||||||
procd_set_param stderr "$log_stderr"
|
procd_set_param stderr "$log_stderr"
|
||||||
procd_set_param limits core="unlimited"
|
procd_set_param limits core="unlimited"
|
||||||
sprocd_set_param limits nofile="1000000 1000000"
|
procd_set_param limits nofile="1000000 1000000"
|
||||||
procd_set_param respawn
|
procd_set_param respawn
|
||||||
|
|
||||||
procd_close_instance
|
procd_close_instance
|
||||||
|
|||||||
1
release/config/openwrt.keep
Normal file
1
release/config/openwrt.keep
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/etc/sing-box/
|
||||||
4
release/config/openwrt.prerm
Executable file
4
release/config/openwrt.prerm
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
|
||||||
|
. ${IPKG_INSTROOT}/lib/functions.sh
|
||||||
|
default_prerm $0 $@
|
||||||
@@ -418,6 +418,7 @@ match:
|
|||||||
Port: metadata.Destination.Port,
|
Port: metadata.Destination.Port,
|
||||||
Fqdn: routeOptions.OverrideAddress.Fqdn,
|
Fqdn: routeOptions.OverrideAddress.Fqdn,
|
||||||
}
|
}
|
||||||
|
metadata.DestinationAddresses = nil
|
||||||
}
|
}
|
||||||
if routeOptions.OverridePort > 0 {
|
if routeOptions.OverridePort > 0 {
|
||||||
metadata.Destination = M.Socksaddr{
|
metadata.Destination = M.Socksaddr{
|
||||||
|
|||||||
@@ -161,7 +161,14 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
|||||||
case *R.RuleActionReject:
|
case *R.RuleActionReject:
|
||||||
switch action.Method {
|
switch action.Method {
|
||||||
case C.RuleActionRejectMethodDefault:
|
case C.RuleActionRejectMethodDefault:
|
||||||
return dns.FixedResponse(message.Id, message.Question[0], nil, 0), nil
|
return &mDNS.Msg{
|
||||||
|
MsgHdr: mDNS.MsgHdr{
|
||||||
|
Id: message.Id,
|
||||||
|
Rcode: mDNS.RcodeRefused,
|
||||||
|
Response: true,
|
||||||
|
},
|
||||||
|
Question: []mDNS.Question{message.Question[0]},
|
||||||
|
}, nil
|
||||||
case C.RuleActionRejectMethodDrop:
|
case C.RuleActionRejectMethodDrop:
|
||||||
return nil, tun.ErrDrop
|
return nil, tun.ErrDrop
|
||||||
}
|
}
|
||||||
@@ -223,6 +230,9 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
printResult := func() {
|
printResult := func() {
|
||||||
|
if err == nil && len(responseAddrs) == 0 {
|
||||||
|
err = E.New("empty result")
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, dns.ErrResponseRejectedCached) {
|
if errors.Is(err, dns.ErrResponseRejectedCached) {
|
||||||
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)")
|
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)")
|
||||||
@@ -231,9 +241,6 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
|||||||
} else {
|
} else {
|
||||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
||||||
}
|
}
|
||||||
} else if len(responseAddrs) == 0 {
|
|
||||||
r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
|
|
||||||
err = dns.RCodeNameError
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responseAddrs, cached = r.dnsClient.LookupCache(ctx, domain, strategy)
|
responseAddrs, cached = r.dnsClient.LookupCache(ctx, domain, strategy)
|
||||||
|
|||||||
@@ -102,7 +102,10 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
|
|||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
||||||
item := NewDomainItem(options.Domain, options.DomainSuffix)
|
item, err := NewDomainItem(options.Domain, options.DomainSuffix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
|
|||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
||||||
item := NewDomainItem(options.Domain, options.DomainSuffix)
|
item, err := NewDomainItem(options.Domain, options.DomainSuffix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
|
|||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
||||||
item := NewDomainItem(options.Domain, options.DomainSuffix)
|
item, err := NewDomainItem(options.Domain, options.DomainSuffix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
} else if options.DomainMatcher != nil {
|
} else if options.DomainMatcher != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing/common/domain"
|
"github.com/sagernet/sing/common/domain"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ RuleItem = (*DomainItem)(nil)
|
var _ RuleItem = (*DomainItem)(nil)
|
||||||
@@ -14,7 +15,17 @@ type DomainItem struct {
|
|||||||
description string
|
description string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
|
func NewDomainItem(domains []string, domainSuffixes []string) (*DomainItem, error) {
|
||||||
|
for _, domainItem := range domains {
|
||||||
|
if domainItem == "" {
|
||||||
|
return nil, E.New("domain: empty item is not allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, domainSuffixItem := range domainSuffixes {
|
||||||
|
if domainSuffixItem == "" {
|
||||||
|
return nil, E.New("domain_suffix: empty item is not allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
var description string
|
var description string
|
||||||
if dLen := len(domains); dLen > 0 {
|
if dLen := len(domains); dLen > 0 {
|
||||||
if dLen == 1 {
|
if dLen == 1 {
|
||||||
@@ -40,7 +51,7 @@ func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
|
|||||||
return &DomainItem{
|
return &DomainItem{
|
||||||
domain.NewMatcher(domains, domainSuffixes, false),
|
domain.NewMatcher(domains, domainSuffixes, false),
|
||||||
description,
|
description,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRawDomainItem(matcher *domain.Matcher) *DomainItem {
|
func NewRawDomainItem(matcher *domain.Matcher) *DomainItem {
|
||||||
|
|||||||
@@ -91,10 +91,7 @@ func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers h
|
|||||||
} else {
|
} else {
|
||||||
deadlineConn = conn
|
deadlineConn = conn
|
||||||
}
|
}
|
||||||
err = deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout))
|
deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout))
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "set read deadline")
|
|
||||||
}
|
|
||||||
var protocols []string
|
var protocols []string
|
||||||
if protocolHeader := headers.Get("Sec-WebSocket-Protocol"); protocolHeader != "" {
|
if protocolHeader := headers.Get("Sec-WebSocket-Protocol"); protocolHeader != "" {
|
||||||
protocols = []string{protocolHeader}
|
protocols = []string{protocolHeader}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
@@ -141,7 +142,7 @@ func (e *Endpoint) Start(resolve bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var bind conn.Bind
|
var bind conn.Bind
|
||||||
wgListener, isWgListener := e.options.Dialer.(conn.Listener)
|
wgListener, isWgListener := common.Cast[conn.Listener](e.options.Dialer)
|
||||||
if isWgListener {
|
if isWgListener {
|
||||||
bind = conn.NewStdNetBind(wgListener)
|
bind = conn.NewStdNetBind(wgListener)
|
||||||
} else {
|
} else {
|
||||||
@@ -150,7 +151,7 @@ func (e *Endpoint) Start(resolve bool) error {
|
|||||||
connectAddr netip.AddrPort
|
connectAddr netip.AddrPort
|
||||||
reserved [3]uint8
|
reserved [3]uint8
|
||||||
)
|
)
|
||||||
if len(e.peers) == 1 {
|
if len(e.peers) == 1 && e.peers[0].endpoint.IsValid() {
|
||||||
isConnect = true
|
isConnect = true
|
||||||
connectAddr = e.peers[0].endpoint
|
connectAddr = e.peers[0].endpoint
|
||||||
reserved = e.peers[0].reserved
|
reserved = e.peers[0].reserved
|
||||||
@@ -179,6 +180,17 @@ func (e *Endpoint) Start(resolve bool) error {
|
|||||||
wgDevice := device.NewDevice(e.options.Context, e.tunDevice, bind, logger, e.options.Workers)
|
wgDevice := device.NewDevice(e.options.Context, e.tunDevice, bind, logger, e.options.Workers)
|
||||||
e.tunDevice.SetDevice(wgDevice)
|
e.tunDevice.SetDevice(wgDevice)
|
||||||
ipcConf := e.ipcConf
|
ipcConf := e.ipcConf
|
||||||
|
if e.options.Amnezia != nil {
|
||||||
|
ipcConf += "\njc=" + strconv.Itoa(e.options.Amnezia.JC) + "\n"
|
||||||
|
ipcConf += "jmin=" + strconv.Itoa(e.options.Amnezia.JMin) + "\n"
|
||||||
|
ipcConf += "jmax=" + strconv.Itoa(e.options.Amnezia.JMax) + "\n"
|
||||||
|
ipcConf += "s1=" + strconv.Itoa(e.options.Amnezia.S1) + "\n"
|
||||||
|
ipcConf += "s2=" + strconv.Itoa(e.options.Amnezia.S2) + "\n"
|
||||||
|
ipcConf += "h1=" + strconv.FormatUint(uint64(e.options.Amnezia.H1), 10) + "\n"
|
||||||
|
ipcConf += "h2=" + strconv.FormatUint(uint64(e.options.Amnezia.H2), 10) + "\n"
|
||||||
|
ipcConf += "h3=" + strconv.FormatUint(uint64(e.options.Amnezia.H3), 10) + "\n"
|
||||||
|
ipcConf += "h4=" + strconv.FormatUint(uint64(e.options.Amnezia.H4), 10)
|
||||||
|
}
|
||||||
for _, peer := range e.peers {
|
for _, peer := range e.peers {
|
||||||
ipcConf += peer.GenerateIpcLines()
|
ipcConf += peer.GenerateIpcLines()
|
||||||
}
|
}
|
||||||
@@ -208,10 +220,6 @@ func (e *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
return e.tunDevice.ListenPacket(ctx, destination)
|
return e.tunDevice.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) BindUpdate() error {
|
|
||||||
return e.device.BindUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Endpoint) Close() error {
|
func (e *Endpoint) Close() error {
|
||||||
if e.device != nil {
|
if e.device != nil {
|
||||||
e.device.Close()
|
e.device.Close()
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ type EndpointOptions struct {
|
|||||||
ResolvePeer func(domain string) (netip.Addr, error)
|
ResolvePeer func(domain string) (netip.Addr, error)
|
||||||
Peers []PeerOptions
|
Peers []PeerOptions
|
||||||
Workers int
|
Workers int
|
||||||
|
Amnezia *AmneziaOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerOptions struct {
|
type PeerOptions struct {
|
||||||
@@ -37,3 +38,15 @@ type PeerOptions struct {
|
|||||||
PersistentKeepaliveInterval uint16
|
PersistentKeepaliveInterval uint16
|
||||||
Reserved []uint8
|
Reserved []uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AmneziaOptions struct {
|
||||||
|
JC int
|
||||||
|
JMin int
|
||||||
|
JMax int
|
||||||
|
S1 int
|
||||||
|
S2 int
|
||||||
|
H1 uint32
|
||||||
|
H2 uint32
|
||||||
|
H3 uint32
|
||||||
|
H4 uint32
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user