Compare commits

..
Author SHA1 Message Date
世界 9240f6e885 documentation: Bump version 2025-04-08 16:32:16 +08:00
世界 36ba3e5782 release: Skip override version for iOS 2025-04-08 16:15:25 +08:00
iikiraand世界 24e8a19b62 Fix UDP DNS server crash
Signed-off-by: iikira <i2@mail.iikira.com>
2025-04-08 16:03:14 +08:00
ReleTorand世界 2e2455edf0 Fix fetch ECH configs 2025-04-08 16:03:13 +08:00
世界 7442172833 release: Update Go to 1.24.2 2025-04-08 16:03:13 +08:00
世界 803f7d04c9 Allow direct outbounds without domain_resolver 2025-04-08 16:03:13 +08:00
世界 5827ba536b Fix Tailscale dialer 2025-04-08 16:03:12 +08:00
dyhkwongand世界 588e723124 Fix DNS over QUIC stream close 2025-04-08 16:03:12 +08:00
anytls世界anytls <anytls>
161668ffdc Update anytls
Co-authored-by: anytls <anytls>
2025-04-08 16:03:12 +08:00
Rambling2076and世界 32a14f5fb2 Fix missing with_tailscale in Dockerfile
Signed-off-by: Rambling2076 <Rambling2076@proton.me>
2025-04-08 16:03:11 +08:00
世界 e61bdf27a1 Fail when default DNS server not found 2025-04-08 16:03:11 +08:00
世界 34e0ab27fb Update gVisor to 20250319.0 2025-04-08 16:03:10 +08:00
世界 09120a251c release: Do not build tailscale on iOS and tvOS 2025-04-08 16:03:10 +08:00
世界 104079ad63 Explicitly reject detour to empty direct outbounds 2025-04-08 16:03:09 +08:00
世界 55818312b4 Add netns support 2025-04-08 16:03:09 +08:00
世界 18e4ba8681 Add wildcard name support for predefined records 2025-04-08 16:03:09 +08:00
世界 513c1f1139 Remove map usage in options 2025-04-08 16:03:09 +08:00
世界 d09b424d04 Fix unhandled DNS loop 2025-04-08 16:03:08 +08:00
世界 b9558f04c9 Add wildcard-sni support for shadow-tls inbound 2025-04-08 16:03:08 +08:00
世界 40232d5505 Fix Tailscale DNS 2025-04-08 16:02:55 +08:00
k9982874and世界 c272a15085 Add ntp protocol sniffing 2025-04-08 16:02:55 +08:00
世界 e896367a7e option: Fix marshal legacy DNS options 2025-04-08 16:02:54 +08:00
世界 f3e17520e9 Make domain_resolver optional when only one DNS server is configured 2025-04-08 16:02:54 +08:00
世界 c26fd66bbd Fix DNS lookup context pollution 2025-04-08 16:02:54 +08:00
世界 29210d2573 Fix http3 DNS server connecting to wrong address 2025-04-08 16:02:54 +08:00
Restia-Ashbelland世界 6ea7bc3811 documentation: Fix typo 2025-04-08 16:02:53 +08:00
anytls世界anytls <anytls>
225d5316e6 Update sing-anytls
Co-authored-by: anytls <anytls>
2025-04-08 16:02:53 +08:00
k9982874and世界 8418d7b4f1 Fix hosts DNS server 2025-04-08 16:02:53 +08:00
世界 51442076f2 Fix UDP DNS server crash 2025-04-08 16:02:53 +08:00
世界 6743864d1c documentation: Fix missing ip_accept_any DNS rule option 2025-04-08 16:02:52 +08:00
世界 65a784816e Fix anytls dialer usage 2025-04-08 16:02:52 +08:00
世界 03e7dd7f91 Move predefined DNS server to rule action 2025-04-08 16:02:52 +08:00
世界 bb6fc6e74f Fix domain resolver on direct outbound 2025-04-08 16:02:51 +08:00
Zephyrusoand世界 5c22180675 Fix missing AnyTLS display name 2025-04-08 16:02:51 +08:00
anytls世界anytls <anytls>
04c7899255 Update sing-anytls
Co-authored-by: anytls <anytls>
2025-04-08 16:02:51 +08:00
Esteland世界 ad1e5035ee documentation: Fix typo
Signed-off-by: Estel <callmebedrockdigger@gmail.com>
2025-04-08 16:02:50 +08:00
TargetLockedand世界 cc1c616029 Fix parsing legacy DNS options 2025-04-08 16:02:50 +08:00
世界 8d287cccae Fix DNS fallback 2025-04-08 16:02:50 +08:00
世界 8da74a709a documentation: Fix missing hosts DNS server 2025-04-08 16:02:49 +08:00
anytls世界anytls <anytls>
6672b999e6 Add MinIdleSession option to AnyTLS outbound
Co-authored-by: anytls <anytls>
2025-04-08 16:02:49 +08:00
ReleTorand世界 7a3c3f2f6d documentation: Minor fixes 2025-04-08 16:02:49 +08:00
libtry486and世界 4193575a98 documentation: Fix typo
fix typo

Signed-off-by: libtry486 <89328481+libtry486@users.noreply.github.com>
2025-04-08 16:02:49 +08:00
Alireza Ahmadiand世界 74d66ed907 Fix Outbound deadlock 2025-04-08 16:02:48 +08:00
世界 0648abc603 documentation: Fix AnyTLS doc 2025-04-08 16:02:48 +08:00
anytlsand世界 0dbd10aaef Add AnyTLS protocol 2025-04-08 16:02:47 +08:00
世界 ed141b15f6 Migrate to stdlib ECH support 2025-04-08 16:02:47 +08:00
世界 9207dfa0a3 Add fallback local DNS server for iOS 2025-04-08 16:02:46 +08:00
世界 a11564dbe8 Get darwin local DNS server from libresolv 2025-04-08 16:02:46 +08:00
世界 20ca0395b4 Improve resolve action 2025-04-08 16:02:46 +08:00
世界 9eecdc02c8 Fix toolchain version 2025-04-08 16:02:45 +08:00
世界 ea127216cd Add back port hopping to hysteria 1 2025-04-08 16:02:45 +08:00
世界 1955c683b0 Update dependencies 2025-04-08 16:02:44 +08:00
xchacha20-poly1305and世界 930acaa6a8 Remove single quotes of raw Moziila certs 2025-04-08 16:02:44 +08:00
世界 8572e20794 Add Tailscale endpoint 2025-04-08 16:02:44 +08:00
世界 fee4f16e14 Build legacy binaries with latest Go 2025-04-08 16:02:43 +08:00
世界 2d440f0874 documentation: Remove outdated icons 2025-04-08 16:02:43 +08:00
世界 dfb34ee116 documentation: Certificate store 2025-04-08 16:02:42 +08:00
世界 e9c81c9df3 documentation: TLS fragment 2025-04-08 16:02:42 +08:00
世界 b1ba455a57 documentation: Outbound domain resolver 2025-04-08 16:02:42 +08:00
世界 0c1bb6366a documentation: Refactor DNS 2025-04-08 16:02:41 +08:00
世界 2a6eff6c55 Add certificate store 2025-04-08 16:02:41 +08:00
世界 09769de838 Add TLS fragment support 2025-04-08 16:02:41 +08:00
世界 5336f09ddc refactor: Outbound domain resolver 2025-04-08 16:02:40 +08:00
世界 84e579823e refactor: DNS 2025-04-08 16:02:40 +08:00
世界 78608d0c62 test: Fix auto redirect UDP rules 2025-04-08 16:02:15 +08:00
世界 680f374a25 test: Improve auto redirect 2025-04-08 15:00:44 +08:00
73 changed files with 387 additions and 1088 deletions
+1 -2
View File
@@ -1,12 +1,11 @@
-s dir
--name sing-box
--category net
--license GPL-3.0-or-later
--license GPLv3-or-later
--description "The universal proxy platform."
--url "https://sing-box.sagernet.org/"
--maintainer "nekohasekai <contact-git@sekai.icu>"
--deb-field "Bug: https://github.com/SagerNet/sing-box/issues"
--no-deb-generate-changes
--config-files /etc/sing-box/config.json
release/config/config.json=/etc/sing-box/config.json
-30
View File
@@ -1,30 +0,0 @@
-s dir
--name sing-box
--category net
--license GPL-3.0-or-later
--description "The universal proxy platform."
--url "https://sing-box.sagernet.org/"
--maintainer "nekohasekai <contact-git@sekai.icu>"
--no-deb-generate-changes
--config-files /etc/config/sing-box
--config-files /etc/sing-box/config.json
--depends ca-bundle
--depends kmod-inet-diag
--depends kmod-tun
--depends firewall4
--before-remove release/config/openwrt.prerm
release/config/config.json=/etc/sing-box/config.json
release/config/openwrt.conf=/etc/config/sing-box
release/config/openwrt.init=/etc/init.d/sing-box
release/config/openwrt.keep=/lib/upgrade/keep.d/sing-box
release/completions/sing-box.bash=/usr/share/bash-completion/completions/sing-box.bash
release/completions/sing-box.fish=/usr/share/fish/vendor_completions.d/sing-box.fish
release/completions/sing-box.zsh=/usr/share/zsh/site-functions/_sing-box
LICENSE=/usr/share/licenses/sing-box/LICENSE
-28
View File
@@ -1,28 +0,0 @@
#!/usr/bin/env bash
# mod from https://gist.github.com/pldubouilh/c5703052986bfdd404005951dee54683
set -e -o pipefail
PROJECT=$(dirname "$0")/../..
TMP_PATH=`mktemp -d`
cp $2 $TMP_PATH
pushd $TMP_PATH
DEB_NAME=`ls *.deb`
ar x $DEB_NAME
mkdir control
pushd control
tar xf ../control.tar.gz
rm md5sums
sed "s/Architecture:\\ \w*/Architecture:\\ $1/g" ./control -i
cat control
tar czf ../control.tar.gz ./*
popd
DEB_NAME=${DEB_NAME%.deb}
tar czf $DEB_NAME.ipk control.tar.gz data.tar.gz debian-binary
popd
cp $TMP_PATH/$DEB_NAME.ipk $3
rm -r $TMP_PATH
+26 -61
View File
@@ -68,38 +68,31 @@ jobs:
- calculate_version
strategy:
matrix:
os: [ linux, windows, darwin, android ]
arch: [ "386", amd64, arm64 ]
legacy_go: [ false ]
include:
- { os: linux, arch: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
- { os: linux, arch: "386", go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
- { os: linux, arch: "386", go386: softfloat, openwrt: "i386_pentium-mmx" }
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
- { os: linux, arch: arm, goarm: "5", openwrt: "arm_arm926ej-s arm_cortex-a7 arm_cortex-a9 arm_fa526 arm_xscale" }
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl, openwrt: "arm_arm1176jzf-s_vfp" }
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
- { os: linux, arch: mips, gomips: softfloat, openwrt: "mips_24kc mips_4kec mips_mips32" }
- { os: linux, arch: mipsle, gomips: hardfloat, debian: mipsel, rpm: mipsel, openwrt: "mipsel_24kc_24kf" }
- { os: linux, arch: mipsle, gomips: softfloat, openwrt: "mipsel_24kc mipsel_74kc mipsel_mips32" }
- { 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: amd64, debian: amd64, rpm: x86_64, pacman: x86_64 }
- { os: linux, arch: "386", debian: i386, rpm: i386 }
- { os: linux, arch: arm, goarm: "6", debian: armel, rpm: armv6hl }
- { os: linux, arch: arm, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl }
- { os: linux, arch: arm64, debian: arm64, rpm: aarch64, pacman: aarch64 }
- { os: linux, arch: mips64le, debian: mips64el, rpm: mips64el }
- { os: linux, arch: mipsle, debian: mipsel, rpm: mipsel }
- { os: linux, arch: s390x, debian: s390x, rpm: s390x }
- { os: linux, arch: ppc64le, debian: ppc64el, rpm: ppc64le }
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64, openwrt: "riscv64_generic" }
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
- { os: linux, arch: riscv64, debian: riscv64, rpm: riscv64 }
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
- { os: windows, arch: amd64 }
- { os: windows, 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: arm64 }
- { os: windows, arch: amd64, legacy_go: true }
- { 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: arm, ndk: "armv7a-linux-androideabi21" }
- { os: android, arch: amd64, ndk: "x86_64-linux-android21" }
- { os: android, arch: "386", ndk: "i686-linux-android21" }
exclude:
- { os: darwin, arch: "386" }
steps:
- name: Checkout
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
@@ -154,10 +147,7 @@ jobs:
CGO_ENABLED: "0"
GOOS: ${{ matrix.os }}
GOARCH: ${{ matrix.arch }}
GO386: ${{ matrix.go386 }}
GOARM: ${{ matrix.goarm }}
GOMIPS: ${{ matrix.gomips }}
GOMIPS64: ${{ matrix.gomips }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build Android
if: matrix.os == 'android'
@@ -177,17 +167,12 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set name
run: |-
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}"
if [[ -n "${{ matrix.goarm }}" ]]; then
DIR_NAME="${DIR_NAME}v${{ matrix.goarm }}"
elif [[ -n "${{ matrix.go386 }}" && "${{ matrix.go386 }}" != 'sse2' ]]; then
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then
DIR_NAME="${DIR_NAME}-legacy"
fi
ARM_VERSION=$([ -n '${{ matrix.goarm}}' ] && echo 'v${{ matrix.goarm}}' || true)
LEGACY=$([ '${{ matrix.legacy_go }}' = 'true' ] && echo "-legacy" || true)
DIR_NAME="sing-box-${{ needs.calculate_version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }}${ARM_VERSION}${LEGACY}"
PKG_NAME="sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.arch }}${ARM_VERSION}"
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
echo "PKG_NAME=${PKG_NAME}" >> "${GITHUB_ENV}"
PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
PKG_VERSION="${PKG_VERSION//-/\~}"
echo "PKG_VERSION=${PKG_VERSION}" >> "${GITHUB_ENV}"
@@ -196,12 +181,10 @@ jobs:
run: |
set -xeuo pipefail
sudo gem install fpm
sudo apt-get update
sudo apt-get install -y debsigs
cp .fpm_systemd .fpm
fpm -t deb \
-v "$PKG_VERSION" \
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.debian }}.deb" \
-p "dist/${PKG_NAME}.deb" \
--architecture ${{ matrix.debian }} \
dist/sing-box=/usr/bin/sing-box
curl -Lo '/tmp/debsigs.diff' 'https://gitlab.com/debsigs/debsigs/-/commit/160138f5de1ec110376d3c807b60a37388bc7c90.diff'
@@ -216,10 +199,9 @@ jobs:
run: |-
set -xeuo pipefail
sudo gem install fpm
cp .fpm_systemd .fpm
fpm -t rpm \
-v "$PKG_VERSION" \
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.rpm }}.rpm" \
-p "dist/${PKG_NAME}.rpm" \
--architecture ${{ matrix.rpm }} \
dist/sing-box=/usr/bin/sing-box
cat > $HOME/.rpmmacros <<EOF
@@ -235,29 +217,12 @@ jobs:
run: |-
set -xeuo pipefail
sudo gem install fpm
sudo apt-get update
sudo apt-get install -y libarchive-tools
cp .fpm_systemd .fpm
fpm -t pacman \
-v "$PKG_VERSION" \
-p "dist/sing-box_${{ needs.calculate_version.outputs.version }}_${{ matrix.os }}_${{ matrix.pacman }}.pkg.tar.zst" \
-p "dist/${PKG_NAME}.pkg.tar.zst" \
--architecture ${{ matrix.pacman }} \
dist/sing-box=/usr/bin/sing-box
- name: Package OpenWrt
if: matrix.openwrt != ''
run: |-
set -xeuo pipefail
sudo gem install fpm
cp .fpm_openwrt .fpm
fpm -t deb \
-v "$PKG_VERSION" \
-p "dist/openwrt.deb" \
--architecture all \
dist/sing-box=/usr/bin/sing-box
for architecture in ${{ matrix.openwrt }}; do
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
done
rm "dist/openwrt.deb"
- name: Archive
run: |
set -xeuo pipefail
@@ -277,7 +242,7 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_go && '-legacy' || '' }}
name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.legacy_go && '-legacy' || '' }}
path: "dist"
build_android:
name: Build Android
-2
View File
@@ -120,7 +120,6 @@ jobs:
set -xeuo pipefail
sudo gem install fpm
sudo apt-get install -y debsigs
cp .fpm_systemd .fpm
fpm -t deb \
--name "${NAME}" \
-v "$PKG_VERSION" \
@@ -139,7 +138,6 @@ jobs:
run: |-
set -xeuo pipefail
sudo gem install fpm
cp .fpm_systemd .fpm
fpm -t rpm \
--name "${NAME}" \
-v "$PKG_VERSION" \
+3 -3
View File
@@ -8,7 +8,7 @@ GOHOSTARCH = $(shell go env GOHOSTARCH)
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="
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS)"
MAIN_PARAMS = $(PARAMS) -tags $(TAGS)
MAIN = ./cmd/sing-box
PREFIX ?= $(shell go env GOPATH)
@@ -24,7 +24,7 @@ ci_build:
go build $(MAIN_PARAMS) $(MAIN)
generate_completions:
go run -v --tags "$(TAGS),generate,generate_completions" $(MAIN)
go run -v --tags $(TAGS),generate,generate_completions $(MAIN)
install:
go build -o $(PREFIX)/bin/$(NAME) $(MAIN_PARAMS) $(MAIN)
@@ -246,4 +246,4 @@ clean:
update:
git fetch
git reset FETCH_HEAD --hard
git clean -fdx
git clean -fdx
+15 -31
View File
@@ -71,8 +71,18 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
listener.Control = control.Append(listener.Control, bindFunc)
}
if options.RoutingMark > 0 {
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, uint32(options.RoutingMark), false))
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, uint32(options.RoutingMark), false))
dialer.Control = control.Append(dialer.Control, control.RoutingMark(uint32(options.RoutingMark)))
listener.Control = control.Append(listener.Control, control.RoutingMark(uint32(options.RoutingMark)))
}
if networkManager != nil {
autoRedirectOutputMark := networkManager.AutoRedirectOutputMark()
if autoRedirectOutputMark > 0 {
if options.RoutingMark > 0 {
return nil, E.New("`routing_mark` is conflict with `tun.auto_redirect` with `tun.route_[_exclude]_address_set")
}
dialer.Control = control.Append(dialer.Control, control.RoutingMark(autoRedirectOutputMark))
listener.Control = control.Append(listener.Control, control.RoutingMark(autoRedirectOutputMark))
}
}
disableDefaultBind := options.BindInterface != "" || options.Inet4BindAddress != nil || options.Inet6BindAddress != nil
if disableDefaultBind || options.TCPFastOpen {
@@ -117,8 +127,8 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
}
}
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
dialer.Control = control.Append(dialer.Control, control.RoutingMark(defaultOptions.RoutingMark))
listener.Control = control.Append(listener.Control, control.RoutingMark(defaultOptions.RoutingMark))
}
}
if options.ReuseAddr {
@@ -200,22 +210,6 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
}, nil
}
func setMarkWrapper(networkManager adapter.NetworkManager, mark uint32, isDefault bool) control.Func {
if networkManager == nil {
return control.RoutingMark(mark)
}
return func(network, address string, conn syscall.RawConn) error {
if networkManager.AutoRedirectOutputMark() != 0 {
if isDefault {
return E.New("`route.default_mark` is conflict with `tun.auto_redirect`")
} else {
return E.New("`routing_mark` is conflict with `tun.auto_redirect`")
}
}
return control.RoutingMark(mark)(network, address, conn)
}
}
func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) {
if !address.IsValid() {
return nil, E.New("invalid address")
@@ -341,17 +335,7 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
}
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
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)
return d.udpListener.ListenPacket(context.Background(), network, address)
}
func trackConn(conn net.Conn, err error) (net.Conn, error) {
+15 -25
View File
@@ -83,7 +83,6 @@ func NewWithOptions(options Options) (N.Dialer, error) {
dialOptions.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) {
//nolint:staticcheck
strategy = C.DomainStrategy(dialOptions.DomainStrategy)
deprecated.Report(options.Context, deprecated.OptionLegacyDomainStrategyOptions)
}
server = dialOptions.DomainResolver.Server
dnsQueryOptions = adapter.DNSQueryOptions{
@@ -96,31 +95,22 @@ func NewWithOptions(options Options) (N.Dialer, error) {
resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay)
} else if options.DirectResolver {
return nil, E.New("missing domain resolver for domain server address")
} else {
if defaultOptions.DomainResolver != "" {
dnsQueryOptions = defaultOptions.DomainResolveOptions
transport, loaded := dnsTransport.Transport(defaultOptions.DomainResolver)
if !loaded {
return nil, E.New("default domain resolver not found: " + defaultOptions.DomainResolver)
}
dnsQueryOptions.Transport = transport
resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay)
} else {
transports := dnsTransport.Transports()
if len(transports) < 2 {
dnsQueryOptions.Transport = dnsTransport.Default()
} else if options.NewDialer {
return nil, E.New("missing domain resolver for domain server address")
} else if !options.DirectOutbound {
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
}
} else if defaultOptions.DomainResolver != "" {
dnsQueryOptions = defaultOptions.DomainResolveOptions
transport, loaded := dnsTransport.Transport(defaultOptions.DomainResolver)
if !loaded {
return nil, E.New("default domain resolver not found: " + defaultOptions.DomainResolver)
}
if
//nolint:staticcheck
dialOptions.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) {
//nolint:staticcheck
dnsQueryOptions.Strategy = C.DomainStrategy(dialOptions.DomainStrategy)
deprecated.Report(options.Context, deprecated.OptionLegacyDomainStrategyOptions)
dnsQueryOptions.Transport = transport
resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay)
} else {
transports := dnsTransport.Transports()
if len(transports) < 2 {
dnsQueryOptions.Transport = dnsTransport.Default()
} else if options.NewDialer {
return nil, E.New("missing domain resolver for domain server address")
} else if !options.DirectOutbound {
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
}
}
dialer = NewResolveDialer(
+3 -8
View File
@@ -31,18 +31,13 @@ func BitTorrent(_ context.Context, metadata *adapter.InboundContext, reader io.R
return os.ErrInvalid
}
const header = "BitTorrent protocol"
var protocol [19]byte
var n int
n, err = reader.Read(protocol[:])
if string(protocol[:n]) != header[:n] {
return os.ErrInvalid
}
_, err = reader.Read(protocol[:])
if err != nil {
return E.Cause1(ErrNeedMoreData, err)
}
if n < 19 {
return ErrNeedMoreData
if string(protocol[:]) != "BitTorrent protocol" {
return os.ErrInvalid
}
metadata.Protocol = C.ProtocolBitTorrent
-21
View File
@@ -32,27 +32,6 @@ func TestSniffBittorrent(t *testing.T) {
}
}
func TestSniffIncompleteBittorrent(t *testing.T) {
t.Parallel()
pkt, err := hex.DecodeString("13426974546f7272656e74")
require.NoError(t, err)
var metadata adapter.InboundContext
err = sniff.BitTorrent(context.TODO(), &metadata, bytes.NewReader(pkt))
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
}
func TestSniffNotBittorrent(t *testing.T) {
t.Parallel()
pkt, err := hex.DecodeString("13426974546f7272656e75")
require.NoError(t, err)
var metadata adapter.InboundContext
err = sniff.BitTorrent(context.TODO(), &metadata, bytes.NewReader(pkt))
require.NotEmpty(t, err)
require.NotErrorIs(t, err, sniff.ErrNeedMoreData)
}
func TestSniffUTP(t *testing.T) {
t.Parallel()
+4 -18
View File
@@ -20,36 +20,22 @@ func StreamDomainNameQuery(readCtx context.Context, metadata *adapter.InboundCon
if err != nil {
return E.Cause1(ErrNeedMoreData, err)
}
if length < 12 {
if length == 0 {
return os.ErrInvalid
}
buffer := buf.NewSize(int(length))
defer buffer.Release()
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
}
}
_, err = buffer.ReadFullFrom(reader, buffer.FreeLen())
if err != nil {
return E.Cause1(ErrNeedMoreData, err)
}
return DomainNameQuery(readCtx, metadata, packet)
return DomainNameQuery(readCtx, metadata, buffer.Bytes())
}
func DomainNameQuery(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error {
var msg mDNS.Msg
err := msg.Unpack(packet)
if err != nil || msg.Response || len(msg.Question) == 0 || len(msg.Answer) > 0 || len(msg.Ns) > 0 {
if err != nil {
return err
}
metadata.Protocol = C.ProtocolDNS
-30
View File
@@ -1,7 +1,6 @@
package sniff_test
import (
"bytes"
"context"
"encoding/hex"
"testing"
@@ -22,32 +21,3 @@ func TestSniffDNS(t *testing.T) {
require.NoError(t, err)
require.Equal(t, C.ProtocolDNS, metadata.Protocol)
}
func TestSniffStreamDNS(t *testing.T) {
t.Parallel()
query, err := hex.DecodeString("001e740701000001000000000000012a06676f6f676c6503636f6d0000010001")
require.NoError(t, err)
var metadata adapter.InboundContext
err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query))
require.NoError(t, err)
require.Equal(t, C.ProtocolDNS, metadata.Protocol)
}
func TestSniffIncompleteStreamDNS(t *testing.T) {
t.Parallel()
query, err := hex.DecodeString("001e740701000001000000000000")
require.NoError(t, err)
var metadata adapter.InboundContext
err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query))
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
}
func TestSniffNotStreamDNS(t *testing.T) {
t.Parallel()
query, err := hex.DecodeString("001e740701000000000000000000")
require.NoError(t, err)
var metadata adapter.InboundContext
err = sniff.StreamDomainNameQuery(context.TODO(), &metadata, bytes.NewReader(query))
require.NotEmpty(t, err)
require.NotErrorIs(t, err, sniff.ErrNeedMoreData)
}
+1 -1
View File
@@ -68,7 +68,7 @@ func PeekStream(ctx context.Context, metadata *adapter.InboundContext, conn net.
}
sniffError = E.Errors(sniffError, err)
}
if !errors.Is(sniffError, ErrNeedMoreData) {
if !errors.Is(err, ErrNeedMoreData) {
break
}
}
+2 -3
View File
@@ -15,11 +15,10 @@ func SSH(_ context.Context, metadata *adapter.InboundContext, reader io.Reader)
const sshPrefix = "SSH-2.0-"
bReader := bufio.NewReader(reader)
prefix, err := bReader.Peek(len(sshPrefix))
if string(prefix[:]) != sshPrefix[:len(prefix)] {
return os.ErrInvalid
}
if err != nil {
return E.Cause1(ErrNeedMoreData, err)
} else if string(prefix) != sshPrefix {
return os.ErrInvalid
}
fistLine, _, err := bReader.ReadLine()
if err != nil {
-21
View File
@@ -24,24 +24,3 @@ func TestSniffSSH(t *testing.T) {
require.Equal(t, C.ProtocolSSH, metadata.Protocol)
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)
}
+13 -29
View File
@@ -10,8 +10,6 @@ import (
"net"
"os"
"strings"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/dns"
@@ -48,10 +46,7 @@ func parseECHClientConfig(ctx context.Context, options option.OutboundTLSOptions
tlsConfig.EncryptedClientHelloConfigList = block.Bytes
return &STDClientConfig{tlsConfig}, nil
} else {
return &STDECHClientConfig{
STDClientConfig: STDClientConfig{tlsConfig},
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
}, nil
return &STDECHClientConfig{STDClientConfig{tlsConfig}, service.FromContext[adapter.DNSRouter](ctx)}, nil
}
}
@@ -104,28 +99,11 @@ func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error {
type STDECHClientConfig struct {
STDClientConfig
access sync.Mutex
dnsRouter adapter.DNSRouter
lastTTL time.Duration
lastUpdate time.Time
dnsRouter adapter.DNSRouter
}
func (s *STDECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
tlsConn, err := s.fetchAndHandshake(ctx, conn)
if err != nil {
return nil, err
}
err = tlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err
}
return tlsConn, nil
}
func (s *STDECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
s.access.Lock()
defer s.access.Unlock()
if len(s.config.EncryptedClientHelloConfigList) == 0 || s.lastTTL == 0 || time.Now().Sub(s.lastUpdate) > s.lastTTL {
if len(s.config.EncryptedClientHelloConfigList) == 0 {
message := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{
RecursionDesired: true,
@@ -155,8 +133,6 @@ func (s *STDECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Con
if err != nil {
return nil, E.Cause(err, "decode ECH config")
}
s.lastTTL = time.Duration(rr.Header().Ttl) * time.Second
s.lastUpdate = time.Now()
s.config.EncryptedClientHelloConfigList = echConfigList
break match
}
@@ -167,11 +143,19 @@ func (s *STDECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Con
return nil, E.New("no ECH config found in DNS records")
}
}
return s.Client(conn)
tlsConn, err := s.Client(conn)
if err != nil {
return nil, err
}
err = tlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err
}
return tlsConn, nil
}
func (s *STDECHClientConfig) Clone() Config {
return &STDECHClientConfig{STDClientConfig: STDClientConfig{s.config.Clone()}, dnsRouter: s.dnsRouter, lastUpdate: s.lastUpdate}
return &STDECHClientConfig{STDClientConfig{s.config.Clone()}, s.dnsRouter}
}
func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
+9 -13
View File
@@ -89,20 +89,16 @@ func NewRealityServer(ctx context.Context, logger log.Logger, options option.Inb
tlsConfig.MaxTimeDiff = time.Duration(options.Reality.MaxTimeDifference)
tlsConfig.ShortIds = make(map[[8]byte]bool)
if len(options.Reality.ShortID) == 0 {
tlsConfig.ShortIds[[8]byte{0}] = true
} else {
for i, shortIDString := range options.Reality.ShortID {
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
for i, shortIDString := range options.Reality.ShortID {
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
}
handshakeDialer, err := dialer.New(ctx, options.Reality.Handshake.DialerOptions, options.Reality.Handshake.ServerIsDomain())
+1 -6
View File
@@ -6,7 +6,6 @@ import (
"net"
"os"
"strings"
"time"
"github.com/sagernet/fswatch"
"github.com/sagernet/sing-box/adapter"
@@ -234,12 +233,8 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
key = content
}
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) {
return GenerateKeyPair(nil, nil, timeFunc, info.ServerName)
return GenerateKeyPair(nil, nil, ntp.TimeFuncFromContext(ctx), info.ServerName)
}
} else {
if certificate == nil {
+1 -1
View File
@@ -483,7 +483,7 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
}
func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) {
if response.Rcode != dns.RcodeSuccess {
if response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError {
return nil, RcodeError(response.Rcode)
}
addresses := make([]netip.Addr, 0, len(response.Answer))
+4 -7
View File
@@ -323,9 +323,6 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
err error
)
printResult := func() {
if err == nil && len(responseAddrs) == 0 {
err = E.New("empty result")
}
if err != nil {
if errors.Is(err, ErrResponseRejectedCached) {
r.logger.DebugContext(ctx, "response rejected for ", domain, " (cached)")
@@ -334,15 +331,15 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
} else {
r.logger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
}
}
if err != nil {
err = E.Cause(err, "lookup ", domain)
} else if len(responseAddrs) == 0 {
r.logger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
err = RcodeNameError
}
}
responseAddrs, cached = r.client.LookupCache(domain, options.Strategy)
if cached {
if len(responseAddrs) == 0 {
return nil, E.New("lookup ", domain, ": empty result (cached)")
return nil, RcodeNameError
}
return responseAddrs, nil
}
+1 -3
View File
@@ -93,9 +93,7 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
return nil, err
}
serverAddr := options.DNSServerAddressOptions.Build()
if !serverAddr.Addr.IsValid() {
return nil, E.New("invalid server address: ", serverAddr)
} else if serverAddr.Port == 0 {
if serverAddr.Port == 0 {
serverAddr.Port = 443
}
return NewHTTPSRaw(
+3 -6
View File
@@ -3,7 +3,6 @@ package local
import (
"context"
"math/rand"
"net/netip"
"time"
"github.com/sagernet/sing-box/adapter"
@@ -36,7 +35,6 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
}
return &Transport{
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
ctx: ctx,
hosts: hosts.NewFile(hosts.DefaultPath),
dialer: transportDialer,
}, nil
@@ -59,7 +57,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
}
}
systemConfig := getSystemDNSConfig(t.ctx)
systemConfig := getSystemDNSConfig()
if systemConfig.singleRequest || !(message.Question[0].Qtype == mDNS.TypeA || message.Question[0].Qtype == mDNS.TypeAAAA) {
return t.exchangeSingleRequest(ctx, systemConfig, message, domain)
} else {
@@ -91,9 +89,8 @@ func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfi
startRacer := func(ctx context.Context, fqdn string) {
response, err := t.tryOneName(ctx, systemConfig, fqdn, message)
if err == nil {
var addresses []netip.Addr
addresses, err = dns.MessageToAddresses(response)
if err == nil && len(addresses) == 0 {
addresses, _ := dns.MessageToAddresses(response)
if len(addresses) == 0 {
err = E.New(fqdn, ": empty result")
}
}
+7 -10
View File
@@ -1,7 +1,6 @@
package local
import (
"context"
"os"
"runtime"
"strings"
@@ -24,21 +23,19 @@ type resolverConfig struct {
var resolvConf resolverConfig
func getSystemDNSConfig(ctx context.Context) *dnsConfig {
resolvConf.tryUpdate(ctx, "/etc/resolv.conf")
func getSystemDNSConfig() *dnsConfig {
resolvConf.tryUpdate("/etc/resolv.conf")
return resolvConf.dnsConfig.Load()
}
func (conf *resolverConfig) init(ctx context.Context) {
conf.dnsConfig.Store(dnsReadConfig(ctx, "/etc/resolv.conf"))
func (conf *resolverConfig) init() {
conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
conf.lastChecked = time.Now()
conf.ch = make(chan struct{}, 1)
}
func (conf *resolverConfig) tryUpdate(ctx context.Context, name string) {
conf.initOnce.Do(func() {
conf.init(ctx)
})
func (conf *resolverConfig) tryUpdate(name string) {
conf.initOnce.Do(conf.init)
if conf.dnsConfig.Load().noReload {
return
@@ -62,7 +59,7 @@ func (conf *resolverConfig) tryUpdate(ctx context.Context, name string) {
return
}
}
dnsConf := dnsReadConfig(ctx, name)
dnsConf := dnsReadConfig(name)
conf.dnsConfig.Store(dnsConf)
}
+1 -2
View File
@@ -11,7 +11,6 @@ package local
import "C"
import (
"context"
"time"
E "github.com/sagernet/sing/common/exceptions"
@@ -19,7 +18,7 @@ import (
"github.com/miekg/dns"
)
func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
func dnsReadConfig(_ string) *dnsConfig {
if C.res_init() != 0 {
return &dnsConfig{
servers: defaultNS,
+1 -2
View File
@@ -4,7 +4,6 @@ package local
import (
"bufio"
"context"
"net"
"net/netip"
"os"
@@ -14,7 +13,7 @@ import (
"github.com/miekg/dns"
)
func dnsReadConfig(_ context.Context, name string) *dnsConfig {
func dnsReadConfig(name string) *dnsConfig {
conf := &dnsConfig{
ndots: 1,
timeout: 5 * time.Second,
+20 -37
View File
@@ -1,7 +1,6 @@
package local
import (
"context"
"net"
"net/netip"
"os"
@@ -9,13 +8,10 @@ import (
"time"
"unsafe"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/service"
"golang.org/x/sys/windows"
)
func dnsReadConfig(ctx context.Context, _ string) *dnsConfig {
func dnsReadConfig(_ string) *dnsConfig {
conf := &dnsConfig{
ndots: 1,
timeout: 5 * time.Second,
@@ -26,35 +22,35 @@ func dnsReadConfig(ctx context.Context, _ string) *dnsConfig {
conf.servers = defaultNS
}
}()
addresses, err := adapterAddresses()
aas, err := adapterAddresses()
if err != nil {
return nil
}
var dnsAddresses []struct {
ifName string
netip.Addr
}
for _, address := range addresses {
if address.OperStatus != windows.IfOperStatusUp {
for _, aa := range aas {
// Only take interfaces whose OperStatus is IfOperStatusUp(0x01) into DNS configs.
if aa.OperStatus != windows.IfOperStatusUp {
continue
}
if address.IfType == windows.IF_TYPE_TUNNEL {
// Only take interfaces which have at least one gateway
if aa.FirstGatewayAddress == nil {
continue
}
if address.FirstGatewayAddress == nil {
continue
}
for dnsServerAddress := address.FirstDnsServerAddress; dnsServerAddress != nil; dnsServerAddress = dnsServerAddress.Next {
rawSockaddr, err := dnsServerAddress.Address.Sockaddr.Sockaddr()
for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next {
sa, err := dns.Address.Sockaddr.Sockaddr()
if err != nil {
continue
}
var dnsServerAddr netip.Addr
switch sockaddr := rawSockaddr.(type) {
var ip netip.Addr
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
dnsServerAddr = netip.AddrFrom4(sockaddr.Addr)
ip = netip.AddrFrom4([4]byte{sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]})
case *syscall.SockaddrInet6:
if sockaddr.Addr[0] == 0xfe && sockaddr.Addr[1] == 0xc0 {
var addr16 [16]byte
copy(addr16[:], sa.Addr[:])
if addr16[0] == 0xfe && addr16[1] == 0xc0 {
// fec0/10 IPv6 addresses are site local anycast DNS
// addresses Microsoft sets by default if no other
// IPv6 DNS address is set. Site local anycast is
@@ -62,27 +58,14 @@ func dnsReadConfig(ctx context.Context, _ string) *dnsConfig {
// https://datatracker.ietf.org/doc/html/rfc3879
continue
}
dnsServerAddr = netip.AddrFrom16(sockaddr.Addr)
ip = netip.AddrFrom16(addr16)
default:
// Unexpected type.
continue
}
dnsAddresses = append(dnsAddresses, struct {
ifName string
netip.Addr
}{ifName: windows.UTF16PtrToString(address.FriendlyName), Addr: dnsServerAddr})
conf.servers = append(conf.servers, net.JoinHostPort(ip.String(), "53"))
}
}
var myInterface string
if networkManager := service.FromContext[adapter.NetworkManager](ctx); networkManager != nil {
myInterface = networkManager.InterfaceMonitor().MyInterface()
}
for _, address := range dnsAddresses {
if address.ifName == myInterface {
continue
}
conf.servers = append(conf.servers, net.JoinHostPort(address.String(), "53"))
}
return conf
}
+1 -3
View File
@@ -89,9 +89,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
return nil, err
}
serverAddr := options.DNSServerAddressOptions.Build()
if !serverAddr.Addr.IsValid() {
return nil, E.New("invalid server address: ", serverAddr)
} else if serverAddr.Port == 0 {
if serverAddr.Port == 0 {
serverAddr.Port = 443
}
return &HTTP3Transport{
+1 -4
View File
@@ -16,7 +16,6 @@ import (
sQUIC "github.com/sagernet/sing-quic"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -56,9 +55,7 @@ func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options
tlsConfig.SetNextProtos([]string{"doq"})
}
serverAddr := options.DNSServerAddressOptions.Build()
if !serverAddr.Addr.IsValid() {
return nil, E.New("invalid server address: ", serverAddr)
} else if serverAddr.Port == 0 {
if serverAddr.Port == 0 {
serverAddr.Port = 853
}
return &Transport{
+1 -4
View File
@@ -13,7 +13,6 @@ import (
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -38,9 +37,7 @@ func NewTCP(ctx context.Context, logger log.ContextLogger, tag string, options o
return nil, err
}
serverAddr := options.DNSServerAddressOptions.Build()
if !serverAddr.Addr.IsValid() {
return nil, E.New("invalid server address: ", serverAddr)
} else if serverAddr.Port == 0 {
if serverAddr.Port == 0 {
serverAddr.Port = 53
}
return &TCPTransport{
+1 -3
View File
@@ -54,9 +54,7 @@ func NewTLS(ctx context.Context, logger log.ContextLogger, tag string, options o
return nil, err
}
serverAddr := options.DNSServerAddressOptions.Build()
if !serverAddr.Addr.IsValid() {
return nil, E.New("invalid server address: ", serverAddr)
} else if serverAddr.Port == 0 {
if serverAddr.Port == 0 {
serverAddr.Port = 853
}
return &TLSTransport{
+2 -5
View File
@@ -13,7 +13,6 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -45,9 +44,7 @@ func NewUDP(ctx context.Context, logger log.ContextLogger, tag string, options o
return nil, err
}
serverAddr := options.DNSServerAddressOptions.Build()
if !serverAddr.Addr.IsValid() {
return nil, E.New("invalid server address: ", serverAddr)
} else if serverAddr.Port == 0 {
if serverAddr.Port == 0 {
serverAddr.Port = 53
}
return NewUDPRaw(logger, dns.NewTransportAdapterWithRemoteOptions(C.DNSTypeUDP, tag, options), transportDialer, serverAddr), nil
@@ -120,7 +117,7 @@ func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
conn.access.Unlock()
defer func() {
conn.access.Lock()
delete(conn.callbacks, exMessage.Id)
delete(conn.callbacks, messageId)
conn.access.Unlock()
}()
rawMessage, err := exMessage.PackBuffer(buffer.FreeBytes())
+1 -27
View File
@@ -2,33 +2,7 @@
icon: material/alert-decagram
---
#### 1.12.0-beta.6
* Fixes and improvements
### 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.12.0-beta.5
* Fixes and improvements
### 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.12.0-beta.3
#### 1.12.0-beta.2
* Fixes and improvements
-4
View File
@@ -9,10 +9,6 @@ and the data generated by the software is always on your device.
## Android
The broad package (App) visibility (QUERY_ALL_PACKAGES) permission
is used to provide per-application proxy features for VPN,
sing-box will not collect your app list.
If your configuration contains `wifi_ssid` or `wifi_bssid` routing rules,
sing-box uses the location permission in the background
to get information about the connected Wi-Fi network to make them work.
+13 -14
View File
@@ -27,20 +27,19 @@ icon: material/alert-decagram
The type of the DNS server.
| Type | Format |
|-----------------|---------------------------|
| empty (default) | [Legacy](./legacy/) |
| `local` | [Local](./local/) |
| `hosts` | [Hosts](./hosts/) |
| `tcp` | [TCP](./tcp/) |
| `udp` | [UDP](./udp/) |
| `tls` | [TLS](./tls/) |
| `quic` | [QUIC](./quic/) |
| `https` | [HTTPS](./https/) |
| `h3` | [HTTP/3](./http3/) |
| `dhcp` | [DHCP](./dhcp/) |
| `fakeip` | [Fake IP](./fakeip/) |
| `tailscale` | [Tailscale](./tailscale/) |
| Type | Format |
|-----------------|-----------------------------|
| empty (default) | [Legacy](./legacy/) |
| `tcp` | [TCP](./tcp/) |
| `udp` | [UDP](./udp/) |
| `tls` | [TLS](./tls/) |
| `https` | [HTTPS](./https/) |
| `quic` | [QUIC](./quic/) |
| `h3` | [HTTP/3](./http3/) |
| `predefined` | [Predefined](./predefined/) |
| `dhcp` | [DHCP](./dhcp/) |
| `fakeip` | [Fake IP](./fakeip/) |
| `tailscale` | [Tailscale](./tailscale/) |
#### tag
+13 -14
View File
@@ -27,20 +27,19 @@ icon: material/alert-decagram
DNS 服务器的类型。
| 类型 | 格式 |
|-----------------|---------------------------|
| empty (default) | [Legacy](./legacy/) |
| `local` | [Local](./local/) |
| `hosts` | [Hosts](./hosts/) |
| `tcp` | [TCP](./tcp/) |
| `udp` | [UDP](./udp/) |
| `tls` | [TLS](./tls/) |
| `quic` | [QUIC](./quic/) |
| `https` | [HTTPS](./https/) |
| `h3` | [HTTP/3](./http3/) |
| `dhcp` | [DHCP](./dhcp/) |
| `fakeip` | [Fake IP](./fakeip/) |
| `tailscale` | [Tailscale](./tailscale/) |
| 类型 | 格式 |
|-----------------|-----------------------------|
| empty (default) | [Legacy](./legacy/) |
| `tcp` | [TCP](./tcp/) |
| `udp` | [UDP](./udp/) |
| `tls` | [TLS](./tls/) |
| `https` | [HTTPS](./https/) |
| `quic` | [QUIC](./quic/) |
| `h3` | [HTTP/3](./http3/) |
| `predefined` | [Predefined](./predefined/) |
| `dhcp` | [DHCP](./dhcp/) |
| `fakeip` | [Fake IP](./fakeip/) |
| `tailscale` | [Tailscale](./tailscale/) |
#### tag
+16 -15
View File
@@ -213,7 +213,7 @@ Set the default route to the Tun.
!!! 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.
`auto_redirect` is always recommended on Linux, it provides better routing, higher performance (better than tproxy), and avoids conflicts with Docker bridge networks.
#### iproute2_table_index
@@ -239,21 +239,20 @@ Linux iproute2 rule start index generated by `auto_route`.
Only supported on Linux with `auto_route` enabled.
Improve TUN routing and performance using nftables.
Automatically configure iptables/nftables to redirect connections.
`auto_redirect` is always recommended on Linux, it provides better routing,
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.
and avoids conflicts with Docker bridge networks.
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,
*In Android*:
Only local IPv4 connections are forwarded. To share your VPN connection over hotspot or repeater,
use [VPNHotspot](https://github.com/Mygod/VPNHotspot).
`auto_redirect` also automatically inserts compatibility rules
into the OpenWrt fw4 table, i.e.
it will work on routers without any extra configuration.
*In Linux*:
`auto_route` with `auto_redirect` works as expected on routers **without intervention**.
Conflict with `route.default_mark` and `[dialOptions].routing_mark`.
@@ -280,15 +279,17 @@ Enforce strict routing rules when `auto_route` is enabled:
*In Linux*:
* Let unsupported network unreachable
* For legacy reasons, when neither `strict_route` nor `auto_redirect` are enabled, all ICMP traffic will not go through TUN.
* Make ICMP traffic route to tun instead of upstream interfaces
* Route all connections to tun
It prevents IP address leaks and makes DNS hijacking work on Android.
*In Windows*:
* Let unsupported network unreachable
* prevent DNS leak caused by
* Add firewall rules to 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)
It may prevent some Windows applications (such as VirtualBox) from working properly in certain situations.
It may prevent some applications (such as VirtualBox) from working properly in certain situations.
#### route_address
+24 -17
View File
@@ -217,7 +217,7 @@ tun 接口的 IPv6 前缀。
!!! note "也启用 `auto_redirect`"
在 Linux 上始终推荐使用 `auto_redirect`,它提供更好的路由, 更高的性能(优于 tproxy), 并避免 TUN 与 Docker 桥接网络冲突。
在 Linux 上始终推荐使用 `auto_redirect`,它提供更好的路由, 更高的性能(优于 tproxy), 并避免与 Docker 桥接网络冲突。
#### iproute2_table_index
@@ -241,16 +241,19 @@ tun 接口的 IPv6 前缀。
!!! quote ""
仅支持 Linux,且需要 `auto_route` 已启用。
仅支持 Linux,且需要 `auto_route` 已启用。
通过使用 nftables 改善 TUN 路由和性能。
自动配置 iptables/nftables 以重定向连接。
在 Linux 上始终推荐使用 `auto_redirect`,它提供更好的路由、更高的性能(优于 tproxy),并避免了 TUN 和 Docker 桥接网络之间的冲突。
在 Linux 上始终推荐使用 auto redirect,它提供更好的路由, 更高的性能(优于 tproxy), 并避免与 Docker 桥接网络冲突。
请注意,`auto_redirect` 也适用于 Android,但由于缺少 `nftables` 和 `ip6tables`,仅执行简单的 IPv4 TCP 转发。
若要在 Android 上通过热点或中继器共享 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
*在 Android 中*:
`auto_redirect` 还会自动将兼容性规则插入 OpenWrt 的 fw4 表中,即无需额外配置即可在路由器上工作。
仅转发本地 IPv4 连接。 要通过热点或中继共享您的 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
*在 Linux 中*:
带有 `auto_redirect` 的 `auto_route` 在路由器上**无需干预**即可按预期工作。
与 `route.default_mark` 和 `[dialOptions].routing_mark` 冲突。
@@ -258,7 +261,7 @@ tun 接口的 IPv6 前缀。
!!! question "自 sing-box 1.10.0 起"
`auto_redirect` 使用的连接输入标记。
`auto_redriect` 使用的连接输入标记。
默认使用 `0x2023`。
@@ -266,25 +269,29 @@ tun 接口的 IPv6 前缀。
!!! question "自 sing-box 1.10.0 起"
`auto_redirect` 使用的连接输出标记。
`auto_redriect` 使用的连接输出标记。
默认使用 `0x2024`。
#### strict_route
当启用 `auto_route` 时,强制执行严格的路由规则:
启用 `auto_route` 时执行严格的路由规则。
*在 Linux 中*:
*在 Linux 中*:
* 使不支持的网络不可达。
* 出于历史遗留原因,当未启用 `strict_route` 或 `auto_redirect` 时,所有 ICMP 流量将不会通过 TUN。
* 让不支持的网络无法到达
* 使 ICMP 流量路由到 tun 而不是上游接口
* 将所有连接路由到 tun
*在 Windows 中*:
它可以防止 IP 地址泄漏,并使 DNS 劫持在 Android 上工作。
* 使不支持的网络不可达。
* 阻止 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)在某些情况下无法正常工作。
* 添加防火墙规则以阻止 Windows
的 [普通多宿主 DNS 解析行为](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd197552%28v%3Dws.10%29)
造成的 DNS 泄露
它可能会使某些应用程序(如 VirtualBox)在某些情况下无法正常工作。
#### route_address
+2 -2
View File
@@ -26,7 +26,7 @@
| QUIC 客户端 | 类型 |
|:------------------------:|:----------:|
| Chromium/Cronet | `chromium` |
| Chromium/Cronet | `chrimium` |
| Safari/Apple Network API | `safari` |
| Firefox / uquic firefox | `firefox` |
| quic-go / uquic chrome | `quic-go` |
| quic-go / uquic chrome | `quic-go` |
+1 -1
View File
@@ -206,7 +206,7 @@ Only take effect when `domain_strategy` or `network_strategy` is set.
!!! failure "Deprecated in sing-box 1.12.0"
`domain_strategy` is deprecated and will be removed in sing-box 1.14.0, check [Migration](/migration/#migrate-outbound-domain-strategy-option-to-domain-resolver).
`domain_strategy` is merged to [domain_resolver](#domain_resolver) in sing-box 1.12.0.
Available values: `prefer_ipv4`, `prefer_ipv6`, `ipv4_only`, `ipv6_only`.
-4
View File
@@ -194,10 +194,6 @@ icon: material/new-box
#### domain_strategy
!!! failure "已在 sing-box 1.12.0 废弃"
`domain_strategy` 已废弃且将在 sing-box 1.14.0 中被移除,参阅 [迁移指南](/migration/#migrate-outbound-domain-strategy-option-to-domain-resolver)。
可选值:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。
如果设置,域名将在请求发出之前解析为 IP。
+23 -36
View File
@@ -9,56 +9,43 @@ icon: material/package
=== ":material-debian: Debian / APT"
```bash
sudo mkdir -p /etc/apt/keyrings &&
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc &&
sudo chmod a+r /etc/apt/keyrings/sagernet.asc &&
echo '
Types: deb
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
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
sudo apt-get update
sudo apt-get install sing-box # or sing-box-beta
```
=== ":material-redhat: Redhat / DNF 5"
=== ":material-redhat: Redhat / DNF"
```bash
sudo dnf config-manager addrepo --from-repofile=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 -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
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
The script download and install the latest package from GitHub releases
for deb or rpm based Linux distributions, ArchLinux and OpenWrt.
=== ":material-debian: Debian / DEB"
```shell
curl -fsSL https://sing-box.app/install.sh | sh
```
```bash
bash <(curl -fsSL https://sing-box.app/deb-install.sh)
```
or latest beta:
=== ":material-redhat: Redhat / RPM"
```shell
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta
```
```bash
bash <(curl -fsSL https://sing-box.app/rpm-install.sh)
```
(This applies to any distribution that uses `rpm` and `systemd`. Because of how `rpm` defines dependencies, if it installs, it probably works.)
or specific version:
=== ":simple-archlinux: Archlinux / PKG"
```shell
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version>
```
```bash
bash <(curl -fsSL https://sing-box.app/arch-install.sh)
```
## :material-book-lock-open: Managed Installation
+23 -35
View File
@@ -9,55 +9,43 @@ icon: material/package
=== ":material-debian: Debian / APT"
```bash
sudo mkdir -p /etc/apt/keyrings &&
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc &&
sudo chmod a+r /etc/apt/keyrings/sagernet.asc &&
echo '
Types: deb
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
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
sudo apt-get update
sudo apt-get install sing-box # or sing-box-beta
```
=== ":material-redhat: Redhat / DNF 5"
=== ":material-redhat: Redhat / DNF"
```bash
sudo dnf config-manager addrepo --from-repofile=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 -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
sudo dnf install sing-box # or sing-box-beta
```
(这适用于任何使用 `dnf` 作为包管理器的发行版:Fedora、CentOS,甚至安装了 DNF 的 OpenSUSE。)
## :material-download-box: 手动安装
该脚本从 GitHub 发布中下载并安装最新的软件包,适用于基于 deb 或 rpm 的 Linux 发行版、ArchLinux 和 OpenWrt。
=== ":material-debian: Debian / DEB"
```shell
curl -fsSL https://sing-box.app/install.sh | sh
```
```bash
bash <(curl -fsSL https://sing-box.app/deb-install.sh)
```
或最新测试版:
=== ":material-redhat: Redhat / RPM"
```shell
curl -fsSL https://sing-box.app/install.sh | sh -s -- --beta
```
```bash
bash <(curl -fsSL https://sing-box.app/rpm-install.sh)
```
(这适用于任何使用 `rpm` 和 `systemd` 的发行版。由于 `rpm` 定义依赖关系的方式,如果安装成功,就多半能用。)
或指定版本:
=== ":simple-archlinux: Archlinux / PKG"
```shell
curl -fsSL https://sing-box.app/install.sh | sh -s -- --version <version>
```
```bash
bash <(curl -fsSL https://sing-box.app/arch-install.sh)
```
## :material-book-lock-open: 托管安装
-116
View File
@@ -1,116 +0,0 @@
#!/bin/sh
download_beta=false
download_version=""
while [ $# -gt 0 ]; do
case "$1" in
--beta)
download_beta=true
shift
;;
--version)
shift
if [ $# -eq 0 ]; then
echo "Missing argument for --version"
echo "Usage: $0 [--beta] [--version <version>]"
exit 1
fi
download_version="$1"
shift
;;
*)
echo "Unknown argument: $1"
echo "Usage: $0 [--beta] [--version <version>]"
exit 1
;;
esac
done
if command -v pacman >/dev/null 2>&1; then
os="linux"
arch=$(uname -m)
package_suffix=".pkg.tar.zst"
package_install="pacman -U --noconfirm"
elif command -v dpkg >/dev/null 2>&1; then
os="linux"
arch=$(dpkg --print-architecture)
package_suffix=".deb"
package_install="dpkg -i"
elif command -v dnf >/dev/null 2>&1; then
os="linux"
arch=$(uname -m)
package_suffix=".rpm"
package_install="dnf install -y"
elif command -v rpm >/dev/null 2>&1; then
os="linux"
arch=$(uname -m)
package_suffix=".rpm"
package_install="rpm -i"
elif command -v opkg >/dev/null 2>&1; then
os="openwrt"
. /etc/os-release
arch="$OPENWRT_ARCH"
package_suffix=".ipk"
package_install="opkg update && opkg install"
else
echo "Missing supported package manager."
exit 1
fi
if [ -z "$download_version" ]; then
if [ "$download_beta" != "true" ]; then
if [ -n "$GITHUB_TOKEN" ]; then
latest_release=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases/latest)
else
latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases/latest)
fi
curl_exit_status=$?
if [ $curl_exit_status -ne 0 ]; then
exit $curl_exit_status
fi
if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then
echo "$latest_release"
exit 1
fi
download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g')
else
if [ -n "$GITHUB_TOKEN" ]; then
latest_release=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/SagerNet/sing-box/releases)
else
latest_release=$(curl -s https://api.github.com/repos/SagerNet/sing-box/releases)
fi
curl_exit_status=$?
if [ $curl_exit_status -ne 0 ]; then
exit $curl_exit_status
fi
if [ "$(echo "$latest_release" | grep tag_name | wc -l)" -eq 0 ]; then
echo "$latest_release"
exit 1
fi
download_version=$(echo "$latest_release" | grep tag_name | head -n 1 | awk -F: '{print $2}' | sed 's/[", v]//g')
fi
fi
package_name="sing-box_${download_version}_${os}_${arch}${package_suffix}"
package_url="https://github.com/SagerNet/sing-box/releases/download/v${download_version}/${package_name}"
echo "Downloading $package_url"
if [ -n "$GITHUB_TOKEN" ]; then
curl --fail -Lo "$package_name" -H "Authorization: token ${GITHUB_TOKEN}" "$package_url"
else
curl --fail -Lo "$package_name" "$package_url"
fi
curl_exit_status=$?
if [ $curl_exit_status -ne 0 ]; then
exit $curl_exit_status
fi
if command -v sudo >/dev/null 2>&1; then
package_install="sudo $package_install"
fi
echo "$package_install $package_name"
sh -c "$package_install \"$package_name\""
rm -f "$package_name"
+4 -57
View File
@@ -292,7 +292,7 @@ DNS servers are refactored for better performance and scalability.
}
],
"fakeip": {
"enabled": true,
"enable": true,
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18"
}
@@ -516,13 +516,13 @@ DNS servers are refactored for better performance and scalability.
The legacy outbound DNS rules are deprecated and can be replaced by new domain resolver options.
!!! info "References"
[DNS rule](/configuration/dns/rule/#outbound) /
[Dial Fields](/configuration/shared/dial/#domain_resolver) /
[Route](/configuration/route/#domain_resolver)
=== ":material-card-remove: Deprecated"
```json
{
"dns": {
@@ -556,8 +556,7 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r
"dns": {
"servers": [
{
"type": "local",
"tag": "local"
"type": "local"
}
]
},
@@ -587,58 +586,6 @@ The legacy outbound DNS rules are deprecated and can be replaced by new domain r
}
```
### Migrate outbound domain strategy option to domain resolver
!!! info "References"
[Dial Fields](/configuration/shared/dial/#domain_strategy)
The `domain_strategy` option in Dial Fields has been deprecated and can be replaced with the new domain resolver option.
Note that due to the use of Dial Fields by some of the new DNS servers introduced in sing-box 1.12,
some people mistakenly believe that `domain_strategy` is the same feature as in the legacy DNS servers.
=== ":material-card-remove: Deprecated"
```json
{
"outbounds": [
{
"type": "socks",
"server": "example.org",
"server_port": 2080,
"domain_strategy": "prefer_ipv4",
}
]
}
```
=== ":material-card-multiple: New"
```json
{
"dns": {
"servers": [
{
"type": "local",
"tag": "local"
}
]
},
"outbounds": [
{
"type": "socks",
"server": "example.org",
"server_port": 2080,
"domain_resolver": {
"server": "local",
"strategy": "prefer_ipv4"
}
}
]
}
```
## 1.11.0
### Migrate legacy special outbounds to rule actions
+50 -102
View File
@@ -16,7 +16,7 @@ DNS 服务器已经重构。
=== "Local"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -28,9 +28,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -46,7 +46,7 @@ DNS 服务器已经重构。
=== "TCP"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -58,9 +58,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -77,7 +77,7 @@ DNS 服务器已经重构。
=== "UDP"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -89,9 +89,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -108,7 +108,7 @@ DNS 服务器已经重构。
=== "TLS"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -120,9 +120,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -139,7 +139,7 @@ DNS 服务器已经重构。
=== "HTTPS"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -151,9 +151,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -170,7 +170,7 @@ DNS 服务器已经重构。
=== "QUIC"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -182,9 +182,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -201,7 +201,7 @@ DNS 服务器已经重构。
=== "HTTP3"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -213,9 +213,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -232,7 +232,7 @@ DNS 服务器已经重构。
=== "DHCP"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -247,9 +247,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -269,7 +269,7 @@ DNS 服务器已经重构。
=== "FakeIP"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -292,16 +292,16 @@ DNS 服务器已经重构。
}
],
"fakeip": {
"enabled": true,
"enable": true,
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18"
}
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -333,7 +333,7 @@ DNS 服务器已经重构。
=== "RCode"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -345,9 +345,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -368,7 +368,7 @@ DNS 服务器已经重构。
=== "带有域名地址的服务器"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -385,9 +385,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -410,7 +410,7 @@ DNS 服务器已经重构。
=== "带有域策略的服务器"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -434,9 +434,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -466,7 +466,7 @@ DNS 服务器已经重构。
=== "带有客户端子网的服务器"
=== ":material-card-remove: 弃用的"
```json
{
"dns": {
@@ -483,9 +483,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
@@ -556,8 +556,7 @@ DNS 服务器已经重构。
"dns": {
"servers": [
{
"type": "local",
"tag": "local"
"type": "local"
}
]
},
@@ -587,57 +586,6 @@ DNS 服务器已经重构。
}
```
### 迁移出站域名策略选项到域名解析器
拨号字段中的 `domain_strategy` 选项已被弃用,可以用新的域名解析器选项替代。
请注意,由于 sing-box 1.12 中引入的一些新 DNS 服务器使用了拨号字段,一些人错误地认为 `domain_strategy` 与旧 DNS 服务器中的功能相同。
!!! info "参考"
[拨号字段](/configuration/shared/dial/#domain_strategy)
=== ":material-card-remove: 弃用的"
```json
{
"outbounds": [
{
"type": "socks",
"server": "example.org",
"server_port": 2080,
"domain_strategy": "prefer_ipv4",
}
]
}
```
=== ":material-card-multiple: 新的"
```json
{
"dns": {
"servers": [
{
"type": "local",
"tag": "local"
}
]
},
"outbounds": [
{
"type": "socks",
"server": "example.org",
"server_port": 2080,
"domain_resolver": {
"server": "local",
"strategy": "prefer_ipv4"
}
}
]
}
```
## 1.11.0
### 迁移旧的特殊出站到规则动作
@@ -653,7 +601,7 @@ DNS 服务器已经重构。
=== "Block"
=== ":material-card-remove: 弃用的"
```json
{
"outbounds": [
@@ -666,7 +614,7 @@ DNS 服务器已经重构。
"rules": [
{
...,
"outbound": "block"
}
]
@@ -675,14 +623,14 @@ DNS 服务器已经重构。
```
=== ":material-card-multiple: 新的"
```json
{
"route": {
"rules": [
{
...,
"action": "reject"
}
]
@@ -693,13 +641,13 @@ DNS 服务器已经重构。
=== "DNS"
=== ":material-card-remove: 弃用的"
```json
{
"inbound": [
{
...,
"sniff": true
}
],
@@ -719,9 +667,9 @@ DNS 服务器已经重构。
}
}
```
=== ":material-card-multiple: 新的"
```json
{
"route": {
@@ -1185,4 +1133,4 @@ sing-box 1.9.0 使 QueryFullProcessImageNameW 输出 Win32 路径(如 `C:\fold
}
}
}
```
```
-10
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"net"
"net/http"
"runtime/debug"
"time"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
@@ -13,23 +12,14 @@ import (
"github.com/sagernet/ws/wsutil"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/render"
)
// API created by Clash.Meta
func (s *Server) setupMetaAPI(r chi.Router) {
if s.logDebug {
r := chi.NewRouter()
r.Put("/gc", func(w http.ResponseWriter, r *http.Request) {
debug.FreeOSMemory()
})
r.Mount("/", middleware.Profiler())
}
r.Get("/memory", memory(s.trafficManager))
r.Mount("/group", groupRouter(s))
r.Mount("/upgrade", upgradeRouter(s))
}
type Memory struct {
-36
View File
@@ -1,36 +0,0 @@
package clashapi
import (
"net/http"
E "github.com/sagernet/sing/common/exceptions"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)
func upgradeRouter(server *Server) http.Handler {
r := chi.NewRouter()
r.Post("/ui", updateExternalUI(server))
return r
}
func updateExternalUI(server *Server) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if server.externalUI == "" {
render.Status(r, http.StatusNotFound)
render.JSON(w, r, newError("external UI not enabled"))
return
}
server.logger.Info("upgrading external UI")
err := server.downloadExternalUI()
if err != nil {
server.logger.Error(E.Cause(err, "upgrade external ui"))
render.Status(r, http.StatusInternalServerError)
render.JSON(w, r, newError(err.Error()))
return
}
server.logger.Info("updated external UI")
render.JSON(w, r, render.M{"status": "ok"})
}
}
-3
View File
@@ -49,8 +49,6 @@ type Server struct {
httpServer *http.Server
trafficManager *trafficontrol.Manager
urlTestHistory adapter.URLTestHistoryStorage
logDebug bool
mode string
modeList []string
modeUpdateHook chan<- struct{}
@@ -76,7 +74,6 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
Handler: chiRouter,
},
trafficManager: trafficManager,
logDebug: logFactory.Level() >= log.LevelDebug,
modeList: options.ModeList,
externalController: options.ExternalController != "",
externalUIDownloadURL: options.ExternalUIDownloadURL,
-14
View File
@@ -161,7 +161,6 @@ var OptionLegacyDNSFakeIPOptions = Note{
Description: "legacy DNS fakeip options",
DeprecatedVersion: "1.12.0",
ScheduledVersion: "1.14.0",
EnvName: "LEGACY_DNS_FAKEIP_OPTIONS",
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-to-new-dns-server-formats",
}
@@ -170,7 +169,6 @@ var OptionOutboundDNSRuleItem = Note{
Description: "outbound DNS rule item",
DeprecatedVersion: "1.12.0",
ScheduledVersion: "1.14.0",
EnvName: "OUTBOUND_DNS_RULE_ITEM",
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-outbound-dns-rule-items-to-domain-resolver",
}
@@ -179,7 +177,6 @@ var OptionMissingDomainResolver = Note{
Description: "missing `route.default_domain_resolver` or `domain_resolver` in dial fields",
DeprecatedVersion: "1.12.0",
ScheduledVersion: "1.14.0",
EnvName: "MISSING_DOMAIN_RESOLVER",
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-outbound-dns-rule-items-to-domain-resolver",
}
@@ -188,19 +185,9 @@ var OptionLegacyECHOptions = Note{
Description: "legacy ECH options",
DeprecatedVersion: "1.12.0",
ScheduledVersion: "1.13.0",
EnvName: "LEGACY_ECH_OPTIONS",
MigrationLink: "https://sing-box.sagernet.org/deprecated/#legacy-ech-fields",
}
var OptionLegacyDomainStrategyOptions = Note{
Name: "legacy-domain-strategy-options",
Description: "legacy domain strategy options",
DeprecatedVersion: "1.12.0",
ScheduledVersion: "1.14.0",
EnvName: "LEGACY_DOMAIN_STRATEGY_OPTIONS",
MigrationLink: "https://sing-box.sagernet.org/migration/#migrate-domain-strategy-options",
}
var Options = []Note{
OptionBadMatchSource,
OptionGEOIP,
@@ -217,5 +204,4 @@ var Options = []Note{
OptionOutboundDNSRuleItem,
OptionMissingDomainResolver,
OptionLegacyECHOptions,
OptionLegacyDomainStrategyOptions,
}
+2 -9
View File
@@ -116,10 +116,6 @@ func (s *platformInterfaceStub) FindProcessInfo(ctx context.Context, network str
return nil, os.ErrInvalid
}
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
return nil
}
type interfaceMonitorStub struct{}
func (s *interfaceMonitorStub) Start() error {
@@ -149,11 +145,8 @@ func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultInterfaceUpd
func (s *interfaceMonitorStub) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
}
func (s *interfaceMonitorStub) RegisterMyInterface(interfaceName string) {
}
func (s *interfaceMonitorStub) MyInterface() string {
return ""
func (s *platformInterfaceStub) SendNotification(notification *platform.Notification) error {
return nil
}
func FormatConfig(configContent string) (*StringBox, error) {
+3 -16
View File
@@ -15,10 +15,9 @@ var (
type platformDefaultInterfaceMonitor struct {
*platformInterfaceWrapper
logger logger.Logger
element *list.Element[tun.NetworkUpdateCallback]
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
myInterface string
element *list.Element[tun.NetworkUpdateCallback]
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
logger logger.Logger
}
func (m *platformDefaultInterfaceMonitor) Start() error {
@@ -103,15 +102,3 @@ func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName s
callback(newInterface, 0)
}
}
func (m *platformDefaultInterfaceMonitor) RegisterMyInterface(interfaceName string) {
m.defaultInterfaceAccess.Lock()
defer m.defaultInterfaceAccess.Unlock()
m.myInterface = interfaceName
}
func (m *platformDefaultInterfaceMonitor) MyInterface() string {
m.defaultInterfaceAccess.Lock()
defer m.defaultInterfaceAccess.Unlock()
return m.myInterface
}
-1
View File
@@ -164,7 +164,6 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
if err != nil {
return nil, E.Cause(err, "query tun name")
}
options.InterfaceMonitor.RegisterMyInterface(options.Name)
dupFd, err := dup(int(tunFd))
if err != nil {
return nil, E.Cause(err, "dup tun file descriptor")
+7 -7
View File
@@ -3,7 +3,7 @@ module github.com/sagernet/sing-box
go 1.23.1
require (
github.com/anytls/sing-anytls v0.0.8
github.com/anytls/sing-anytls v0.0.7
github.com/caddyserver/certmagic v0.21.7
github.com/cloudflare/circl v1.6.0
github.com/cretz/bine v0.2.0
@@ -26,18 +26,18 @@ require (
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
github.com/sagernet/quic-go v0.49.0-beta.1
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a
github.com/sagernet/sing v0.6.6-0.20250406122223-d47540857e58
github.com/sagernet/sing-mux v0.3.1
github.com/sagernet/sing-quic v0.4.1-0.20250423030647-0eb05f373a76
github.com/sagernet/sing-quic v0.4.1
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056
github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a
github.com/sagernet/sing-vmess v0.2.1
github.com/sagernet/sing-tun v0.6.3-0.20250408075728-dc32f539f4cd
github.com/sagernet/sing-vmess v0.2.0
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/tailscale v1.80.3-mod.4
github.com/sagernet/tailscale v1.80.3-mod.2
github.com/sagernet/utls v1.6.7
github.com/sagernet/wireguard-go v0.0.1-beta.7
github.com/sagernet/wireguard-go v0.0.1-beta.5
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0
+14 -14
View File
@@ -8,8 +8,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/anytls/sing-anytls v0.0.8 h1:1u/fnH1HoeeMV5mX7/eUOjLBvPdkd1UJRmXiRi6Vymc=
github.com/anytls/sing-anytls v0.0.8/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
github.com/anytls/sing-anytls v0.0.7 h1:0Q5dHNB2sqkFAWZCyK2vjQ/ckI5Iz3V/Frf3k7mBrGc=
github.com/anytls/sing-anytls v0.0.7/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg=
@@ -178,30 +178,30 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W
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/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a h1:oE67hmp5rzLlE6clE7FpK4Hg6yLXsa1Zu3A01vcazb0=
github.com/sagernet/sing v0.6.8-0.20250425035333-84184da91a3a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.6.6-0.20250406122223-d47540857e58 h1:hq0W1/K6/UOrv+tCm9YrZ/yDFZJlPVtLmBvayuwUxDM=
github.com/sagernet/sing v0.6.6-0.20250406122223-d47540857e58/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
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-0.20250423030647-0eb05f373a76/go.mod h1:tqPa0/Wqa19MkkSlKVZZX5sHxtiDR9BROcn4ufcbVdY=
github.com/sagernet/sing-quic v0.4.1 h1:pxlMa4efZu/M07RgGagNNDDyl6ZUwpmNUjRTpgHOWK4=
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/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
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-shadowtls v0.2.1-0.20250316154757-6f9e732e5056 h1:GFNJQAHhSXqAfxAw1wDG/QWbdpGH5Na3k8qUynqWnEA=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250316154757-6f9e732e5056/go.mod h1:HyacBPIFiKihJQR8LQp56FM4hBtd/7MZXnRxxQIOPsc=
github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a h1:2aLxZFD2HPCLrnFGpH+KBuPqMOk0cuaDE2dgEvANuMk=
github.com/sagernet/sing-tun v0.6.5-0.20250412112220-15069fc1c20a/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-vmess v0.2.1 h1:6izHC2+B68aQCxTagki6eZZc+g5eh4dYwxOV5a2Lhug=
github.com/sagernet/sing-vmess v0.2.1/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
github.com/sagernet/sing-tun v0.6.3-0.20250408075728-dc32f539f4cd h1:nfzjTTkClrcj4h6J1nvzE65ZTeLvuJ8oS5+mv8i32ls=
github.com/sagernet/sing-tun v0.6.3-0.20250408075728-dc32f539f4cd/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/tailscale v1.80.3-mod.4 h1:9UgYq8m9mwX5dbTbueVxbRh+bq7AayxemJGM2PkJQnE=
github.com/sagernet/tailscale v1.80.3-mod.4/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
github.com/sagernet/tailscale v1.80.3-mod.2 h1:hT0CI74q727EuCcgQ+T4pvon8V0aoi4vTAxah7GsNMQ=
github.com/sagernet/tailscale v1.80.3-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
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/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI=
github.com/sagernet/wireguard-go v0.0.1-beta.7/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
github.com/sagernet/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/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+1
View File
@@ -83,6 +83,7 @@ type DialerOptions struct {
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"`
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
IsWireGuardListener bool `json:"-"`
// Deprecated: migrated to domain resolver
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
+1 -1
View File
@@ -94,7 +94,7 @@ func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
case 2:
destination.Addr = i.overrideDestination.Addr
case 3:
destination.Port = i.overrideDestination.Port
destination.Port = metadata.Destination.Port
}
metadata.Destination = destination
if i.overrideOption != 0 {
+2 -6
View File
@@ -396,16 +396,12 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
func (g *URLTestGroup) performUpdateCheck() {
var updated bool
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
updated = true
}
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
updated = true
}
if updated {
g.interruptGroup.Interrupt(g.interruptExternalConnections)
+1 -22
View File
@@ -10,7 +10,6 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/outbound"
@@ -192,29 +191,9 @@ func (s *Outbound) DialContext(ctx context.Context, network string, destination
if err != nil {
return nil, err
}
conn, err := client.Dial(network, destination.String())
if err != nil {
return nil, err
}
return &chanConnWrapper{Conn: conn}, nil
return client.Dial(network, destination.String())
}
func (s *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
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
}
+11 -2
View File
@@ -26,6 +26,11 @@ func RegisterEndpoint(registry *endpoint.Registry) {
endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)
}
var (
_ adapter.Endpoint = (*Endpoint)(nil)
_ adapter.InterfaceUpdateListener = (*Endpoint)(nil)
)
type Endpoint struct {
endpoint.Adapter
ctx context.Context
@@ -45,8 +50,8 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
logger: logger,
localAddresses: options.Address,
}
if options.Detour != "" && options.ListenPort != 0 {
return nil, E.New("`listen_port` is conflict with `detour`")
if options.Detour == "" {
options.IsWireGuardListener = true
}
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
Context: ctx,
@@ -122,6 +127,10 @@ func (w *Endpoint) Close() error {
return w.endpoint.Close()
}
func (w *Endpoint) InterfaceUpdated() {
w.endpoint.BindUpdate()
}
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
return w.router.PreMatch(adapter.InboundContext{
Inbound: w.Tag(),
+12 -1
View File
@@ -25,6 +25,11 @@ func RegisterOutbound(registry *outbound.Registry) {
outbound.Register[option.LegacyWireGuardOutboundOptions](registry, C.TypeWireGuard, NewOutbound)
}
var (
_ adapter.Endpoint = (*Endpoint)(nil)
_ adapter.InterfaceUpdateListener = (*Endpoint)(nil)
)
type Outbound struct {
outbound.Adapter
ctx context.Context
@@ -46,7 +51,9 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
logger: logger,
localAddresses: options.LocalAddress,
}
if options.Detour != "" && options.GSO {
if options.Detour == "" {
options.IsWireGuardListener = true
} else if options.GSO {
return nil, E.New("gso is conflict with detour")
}
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
@@ -124,6 +131,10 @@ func (o *Outbound) Close() error {
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) {
switch network {
case N.NetworkTCP:
Executable → Regular
+6 -7
View File
@@ -1,27 +1,26 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=99
PROG="/usr/bin/sing-box"
start_service() {
config_load "sing-box"
local enabled config_file working_directory
local log_stderr
local log_stdout log_stderr
config_get_bool enabled "main" "enabled" "0"
[ "$enabled" -eq "1" ] || return 0
config_get config_file "main" "conffile" "/etc/sing-box/config.json"
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"
procd_open_instance
procd_set_param command "$PROG" run -c "$config_file" -D "$working_directory"
procd_set_param file "$config_file"
procd_swet_param command "$PROG" run -c "$conffile" -D "$workdir"
procd_set_param file "$conffile"
procd_set_param stderr "$log_stderr"
procd_set_param limits core="unlimited"
procd_set_param limits nofile="1000000 1000000"
sprocd_set_param limits nofile="1000000 1000000"
procd_set_param respawn
procd_close_instance
@@ -29,4 +28,4 @@ start_service() {
service_triggers() {
procd_add_reload_trigger "sing-box"
}
}
-1
View File
@@ -1 +0,0 @@
/etc/sing-box/
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
default_prerm $0 $@
+3 -30
View File
@@ -7,7 +7,6 @@ import (
"net"
"net/netip"
"os"
"strings"
"sync"
"sync/atomic"
"time"
@@ -67,17 +66,7 @@ func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, co
remoteConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
}
if err != nil {
var remoteString string
if len(metadata.DestinationAddresses) > 0 {
remoteString = "[" + strings.Join(common.Map(metadata.DestinationAddresses, netip.Addr.String), ",") + "]"
} else {
remoteString = metadata.Destination.String()
}
var dialerString string
if outbound, isOutbound := this.(adapter.Outbound); isOutbound {
dialerString = " using outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
}
err = E.Cause(err, "open connection to ", remoteString, dialerString)
err = E.Cause(err, "open outbound connection")
N.CloseOnHandshakeFailure(conn, onClose, err)
m.logger.ErrorContext(ctx, err)
return
@@ -144,19 +133,8 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
remoteConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
}
if err != nil {
var remoteString string
if len(metadata.DestinationAddresses) > 0 {
remoteString = "[" + strings.Join(common.Map(metadata.DestinationAddresses, netip.Addr.String), ",") + "]"
} else {
remoteString = metadata.Destination.String()
}
var dialerString string
if outbound, isOutbound := this.(adapter.Outbound); isOutbound {
dialerString = " using outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
}
err = E.Cause(err, "open packet connection to ", remoteString, dialerString)
N.CloseOnHandshakeFailure(conn, onClose, err)
m.logger.ErrorContext(ctx, err)
m.logger.ErrorContext(ctx, "open outbound packet connection: ", err)
return
}
remotePacketConn = bufio.NewUnbindPacketConn(remoteConn)
@@ -171,13 +149,8 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
remotePacketConn, err = this.ListenPacket(ctx, metadata.Destination)
}
if err != nil {
var dialerString string
if outbound, isOutbound := this.(adapter.Outbound); isOutbound {
dialerString = " using outbound/" + outbound.Type() + "[" + outbound.Tag() + "]"
}
err = E.Cause(err, "listen packet connection using ", dialerString)
N.CloseOnHandshakeFailure(conn, onClose, err)
m.logger.ErrorContext(ctx, err)
m.logger.ErrorContext(ctx, "listen outbound packet connection: ", err)
return
}
}
+1 -11
View File
@@ -303,7 +303,7 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
if r.interfaceMonitor == nil {
return nil
}
bindFunc := control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
remoteAddr := M.ParseSocksaddr(address).Addr
if remoteAddr.IsValid() {
iif, err := r.interfaceFinder.ByAddr(remoteAddr)
@@ -317,16 +317,6 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
}
return defaultInterface.Name, defaultInterface.Index, nil
})
return func(network, address string, conn syscall.RawConn) error {
err := bindFunc(network, address, conn)
if err != nil {
return err
}
if r.autoRedirectOutputMark > 0 {
return control.RoutingMark(r.autoRedirectOutputMark)(network, address, conn)
}
return nil
}
}
}
-1
View File
@@ -418,7 +418,6 @@ match:
Port: metadata.Destination.Port,
Fqdn: routeOptions.OverrideAddress.Fqdn,
}
metadata.DestinationAddresses = nil
}
if routeOptions.OverridePort > 0 {
metadata.Destination = M.Socksaddr{
+1 -4
View File
@@ -102,10 +102,7 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
rule.allItems = append(rule.allItems, item)
}
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
item, err := NewDomainItem(options.Domain, options.DomainSuffix)
if err != nil {
return nil, err
}
item := NewDomainItem(options.Domain, options.DomainSuffix)
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item)
}
+1 -4
View File
@@ -93,10 +93,7 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
rule.allItems = append(rule.allItems, item)
}
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
item, err := NewDomainItem(options.Domain, options.DomainSuffix)
if err != nil {
return nil, err
}
item := NewDomainItem(options.Domain, options.DomainSuffix)
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item)
}
+1 -4
View File
@@ -47,10 +47,7 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
rule.allItems = append(rule.allItems, item)
}
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
item, err := NewDomainItem(options.Domain, options.DomainSuffix)
if err != nil {
return nil, err
}
item := NewDomainItem(options.Domain, options.DomainSuffix)
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
rule.allItems = append(rule.allItems, item)
} else if options.DomainMatcher != nil {
+2 -13
View File
@@ -5,7 +5,6 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common/domain"
E "github.com/sagernet/sing/common/exceptions"
)
var _ RuleItem = (*DomainItem)(nil)
@@ -15,17 +14,7 @@ type DomainItem struct {
description string
}
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")
}
}
func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
var description string
if dLen := len(domains); dLen > 0 {
if dLen == 1 {
@@ -51,7 +40,7 @@ func NewDomainItem(domains []string, domainSuffixes []string) (*DomainItem, erro
return &DomainItem{
domain.NewMatcher(domains, domainSuffixes, false),
description,
}, nil
}
}
func NewRawDomainItem(matcher *domain.Matcher) *DomainItem {
+4 -1
View File
@@ -91,7 +91,10 @@ func (c *Client) dialContext(ctx context.Context, requestURL *url.URL, headers h
} else {
deadlineConn = conn
}
deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout))
err = deadlineConn.SetDeadline(time.Now().Add(C.TCPTimeout))
if err != nil {
return nil, E.Cause(err, "set read deadline")
}
var protocols []string
if protocolHeader := headers.Get("Sec-WebSocket-Protocol"); protocolHeader != "" {
protocols = []string{protocolHeader}
+6 -2
View File
@@ -141,7 +141,7 @@ func (e *Endpoint) Start(resolve bool) error {
return nil
}
var bind conn.Bind
wgListener, isWgListener := common.Cast[conn.Listener](e.options.Dialer)
wgListener, isWgListener := e.options.Dialer.(conn.Listener)
if isWgListener {
bind = conn.NewStdNetBind(wgListener)
} else {
@@ -150,7 +150,7 @@ func (e *Endpoint) Start(resolve bool) error {
connectAddr netip.AddrPort
reserved [3]uint8
)
if len(e.peers) == 1 && e.peers[0].endpoint.IsValid() {
if len(e.peers) == 1 {
isConnect = true
connectAddr = e.peers[0].endpoint
reserved = e.peers[0].reserved
@@ -208,6 +208,10 @@ func (e *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return e.tunDevice.ListenPacket(ctx, destination)
}
func (e *Endpoint) BindUpdate() error {
return e.device.BindUpdate()
}
func (e *Endpoint) Close() error {
if e.device != nil {
e.device.Close()