Compare commits

..

121 Commits

Author SHA1 Message Date
世界
1974c767c9 documentation: Bump version 2025-07-07 18:56:29 +08:00
世界
2c286b7adc Add firewalld compatibility for auto redirect 2025-07-07 18:56:29 +08:00
世界
e8b70162de Fix DNS reject check 2025-07-07 14:12:43 +08:00
世界
30cfd620c2 Improve copy 2025-07-07 14:08:49 +08:00
世界
e8bd70341a Increase default mtu under network extension to 4064 2025-07-07 14:08:48 +08:00
世界
7acf52d650 release: Fix publish testflight 2025-07-07 14:08:48 +08:00
世界
1ba0827995 Improve darwin tun performance 2025-07-07 14:08:48 +08:00
世界
eb3baf56e7 Improve nftables rules for openwrt 2025-07-07 14:08:48 +08:00
世界
97a44e654a Fixed DoH server recover from conn freezes 2025-07-07 14:08:47 +08:00
世界
5dec974d09 Update libresolv usage 2025-07-07 14:08:47 +08:00
yu
60dd283043 documentation: Update client configuration manual 2025-07-07 14:08:47 +08:00
yanwo
fcefb52ce4 documentation: Fix typo
Signed-off-by: yanwo <ogilvy@gmail.com>
2025-07-07 14:08:47 +08:00
anytinz
c2aaa9903b documentation: Fix wrong SideStore loopback ip 2025-07-07 14:08:47 +08:00
世界
df2e80d126 Revert "release: Add IPA build"
After testing, it seems that since extensions are not handled correctly, it cannot be installed by SideStore.
2025-07-07 14:08:47 +08:00
世界
7552ea4fef release: Add IPA build 2025-07-07 14:08:46 +08:00
世界
28683d277f Add API to dump AdGuard rules 2025-07-07 14:08:46 +08:00
Sukka
2476f43123 Improve AdGuard rule-set parser 2025-07-07 14:08:46 +08:00
Restia-Ashbell
c20c41eccc Add ECH support for uTLS 2025-07-07 14:08:45 +08:00
世界
046ee4a5ba Improve TLS fragments 2025-07-07 14:08:45 +08:00
世界
52a13e6927 Add cache support for ssm-api 2025-07-07 14:08:45 +08:00
世界
aacbab0874 Fix service will not be closed 2025-07-07 14:08:44 +08:00
世界
388d489246 Add loopback address support for tun 2025-07-07 14:08:44 +08:00
世界
4d66403bfa Fix tproxy listener 2025-07-07 14:08:44 +08:00
世界
0c3b4ef9ca Fix systemd package 2025-07-07 14:08:44 +08:00
世界
d0b6ca7099 Fix missing home for derp service 2025-07-07 14:08:43 +08:00
Zero Clover
2be22948f0 documentation: Fix services 2025-07-07 14:08:43 +08:00
世界
fb33008243 Fix dns.client_subnet ignored 2025-07-07 14:08:43 +08:00
世界
8455326d9a documentation: Minor fixes 2025-07-07 14:08:43 +08:00
世界
f51d8df6b0 Fix tailscale forward 2025-07-07 14:08:43 +08:00
世界
e0d14675ad Minor fixes 2025-07-07 14:08:42 +08:00
世界
a668aaaa02 Add SSM API service 2025-07-07 14:08:42 +08:00
世界
e785431242 Add resolved service and DNS server 2025-07-07 14:08:41 +08:00
世界
6b5de379c6 Add DERP service 2025-07-07 14:08:41 +08:00
世界
c448b2d1ad Add service component type 2025-07-07 14:08:41 +08:00
世界
e9fc4c3779 Fix tproxy tcp control 2025-07-07 14:08:41 +08:00
愚者
1ba70c8f07 release: Fix build tags for android
Signed-off-by: 愚者 <11926619+FansChou@users.noreply.github.com>
2025-07-07 14:08:40 +08:00
世界
9f4bb555f7 prevent creation of bind and mark controls on unsupported platforms 2025-07-07 14:08:40 +08:00
PuerNya
46afe058e6 documentation: Fix description of reject DNS action behavior 2025-07-07 14:08:40 +08:00
Restia-Ashbell
9c3a985b79 Fix TLS record fragment 2025-07-07 14:08:40 +08:00
世界
4ed65e0fa9 Add missing accept_routes option for Tailscale 2025-07-07 14:08:40 +08:00
世界
6cbc771bfb Add TLS record fragment support 2025-07-07 14:08:39 +08:00
世界
ddee7ecb6f Fix set edns0 client subnet 2025-07-07 14:08:39 +08:00
世界
219d8658be Update minor dependencies 2025-07-07 14:08:39 +08:00
世界
0f51a47ffe Update certmagic and providers 2025-07-07 14:08:39 +08:00
世界
a0699e09f8 Update protobuf and grpc 2025-07-07 14:08:38 +08:00
世界
1a55024c95 Add control options for listeners 2025-07-07 14:08:38 +08:00
世界
a57ed46111 Update quic-go to v0.52.0 2025-07-07 14:08:37 +08:00
世界
e3381dea33 Update utls to v1.7.2 2025-07-07 14:08:37 +08:00
世界
ad6fc71e42 Handle EDNS version downgrade 2025-07-07 14:08:37 +08:00
世界
577fc63722 documentation: Fix anytls padding scheme description 2025-07-07 14:08:37 +08:00
安容
f62b579430 Report invalid DNS address early 2025-07-07 14:08:36 +08:00
世界
99496599c2 Fix wireguard listen_port 2025-07-07 14:08:36 +08:00
世界
b1a02fd528 clash-api: Add more meta api 2025-07-07 14:08:36 +08:00
世界
bfbdd05e9d Fix DNS lookup 2025-07-07 14:08:36 +08:00
世界
59cf61cac4 Fix fetch ECH configs 2025-07-07 14:08:35 +08:00
reletor
97b447d399 documentation: Minor fixes 2025-07-07 14:08:35 +08:00
caelansar
ba2009d8a4 Fix callback deletion in UDP transport 2025-07-07 14:08:35 +08:00
世界
a56b9fa0d0 documentation: Try to make the play review happy 2025-07-07 14:08:34 +08:00
世界
cdfec40038 Fix missing handling of legacy domain_strategy options 2025-07-07 14:08:34 +08:00
世界
1df6852ad8 Improve local DNS server 2025-07-07 14:08:34 +08:00
anytls
f4a318cac0 Update anytls
Co-authored-by: anytls <anytls>
2025-07-07 14:08:34 +08:00
世界
8dc620fb97 Fix DNS dialer 2025-07-07 14:08:33 +08:00
世界
acdfb8affa release: Skip override version for iOS 2025-07-07 14:08:33 +08:00
iikira
f982d3dcdb Fix UDP DNS server crash
Signed-off-by: iikira <i2@mail.iikira.com>
2025-07-07 14:08:33 +08:00
ReleTor
5f1c5ecc21 Fix fetch ECH configs 2025-07-07 14:08:32 +08:00
世界
e881d5cb2c Allow direct outbounds without domain_resolver 2025-07-07 14:08:32 +08:00
世界
f340a4445c Fix Tailscale dialer 2025-07-07 14:08:32 +08:00
dyhkwong
4452ae83e8 Fix DNS over QUIC stream close 2025-07-07 14:08:31 +08:00
anytls
7bc92d219a Update anytls
Co-authored-by: anytls <anytls>
2025-07-07 14:08:31 +08:00
Rambling2076
5b5babb7d3 Fix missing with_tailscale in Dockerfile
Signed-off-by: Rambling2076 <Rambling2076@proton.me>
2025-07-07 14:08:31 +08:00
世界
6c6a215038 Fail when default DNS server not found 2025-07-07 14:08:31 +08:00
世界
34f03a4151 Update gVisor to 20250319.0 2025-07-07 14:08:30 +08:00
世界
338d67bbd5 Explicitly reject detour to empty direct outbounds 2025-07-07 14:08:30 +08:00
世界
d3b3320f51 Add netns support 2025-07-07 14:08:29 +08:00
世界
dda405d580 Add wildcard name support for predefined records 2025-07-07 14:08:29 +08:00
世界
28946f65f7 Remove map usage in options 2025-07-07 14:08:29 +08:00
世界
1119c06dc2 Fix unhandled DNS loop 2025-07-07 14:08:29 +08:00
世界
a577734efb Add wildcard-sni support for shadow-tls inbound 2025-07-07 14:08:28 +08:00
k9982874
706f4adca4 Add ntp protocol sniffing 2025-07-07 14:08:28 +08:00
世界
ab13ffe20c option: Fix marshal legacy DNS options 2025-07-07 14:08:28 +08:00
世界
a550e8a563 Make domain_resolver optional when only one DNS server is configured 2025-07-07 14:08:28 +08:00
世界
ff00aeb580 Fix DNS lookup context pollution 2025-07-07 14:08:27 +08:00
世界
a810e3213e Fix http3 DNS server connecting to wrong address 2025-07-07 14:08:27 +08:00
Restia-Ashbell
eecd3d7376 documentation: Fix typo 2025-07-07 14:08:27 +08:00
anytls
d4ed82ebaa Update sing-anytls
Co-authored-by: anytls <anytls>
2025-07-07 14:08:26 +08:00
k9982874
8184d9ee9f Fix hosts DNS server 2025-07-07 14:08:26 +08:00
世界
f1edfe581e Fix UDP DNS server crash 2025-07-07 14:08:26 +08:00
世界
7571ff6450 documentation: Fix missing ip_accept_any DNS rule option 2025-07-07 14:08:26 +08:00
世界
60f21507ad Fix anytls dialer usage 2025-07-07 14:08:26 +08:00
世界
b97ca94600 Move predefined DNS server to rule action 2025-07-07 14:08:25 +08:00
世界
7e05192370 Fix domain resolver on direct outbound 2025-07-07 14:08:25 +08:00
Zephyruso
e8c7e278f0 Fix missing AnyTLS display name 2025-07-07 14:08:24 +08:00
anytls
b5d199d8a7 Update sing-anytls
Co-authored-by: anytls <anytls>
2025-07-07 14:08:24 +08:00
Estel
c408916b47 documentation: Fix typo
Signed-off-by: Estel <callmebedrockdigger@gmail.com>
2025-07-07 14:08:24 +08:00
TargetLocked
fa1dfa3970 Fix parsing legacy DNS options 2025-07-07 14:08:24 +08:00
世界
a8da3bf4b7 Fix DNS fallback 2025-07-07 14:08:23 +08:00
世界
a1a00d36b8 documentation: Fix missing hosts DNS server 2025-07-07 14:08:23 +08:00
anytls
b6a8a84a99 Add MinIdleSession option to AnyTLS outbound
Co-authored-by: anytls <anytls>
2025-07-07 14:08:23 +08:00
ReleTor
99b9e7c14c documentation: Minor fixes 2025-07-07 14:08:23 +08:00
libtry486
fc3d43fc7f documentation: Fix typo
fix typo

Signed-off-by: libtry486 <89328481+libtry486@users.noreply.github.com>
2025-07-07 14:08:22 +08:00
Alireza Ahmadi
69c0f424d9 Fix Outbound deadlock 2025-07-07 14:08:22 +08:00
世界
6e87e49690 documentation: Fix AnyTLS doc 2025-07-07 14:08:22 +08:00
anytls
638e5ad2be Add AnyTLS protocol 2025-07-07 14:08:21 +08:00
世界
366406a558 Migrate to stdlib ECH support 2025-07-07 14:08:21 +08:00
世界
4cf1e58eed Add fallback local DNS server for iOS 2025-07-07 14:08:21 +08:00
世界
5927d7affe Get darwin local DNS server from libresolv 2025-07-07 14:08:21 +08:00
世界
6f5dd46b32 Improve resolve action 2025-07-07 14:08:20 +08:00
世界
7d048a014f Add back port hopping to hysteria 1 2025-07-07 14:08:20 +08:00
xchacha20-poly1305
d51593e8a5 Remove single quotes of raw Moziila certs 2025-07-07 14:08:19 +08:00
世界
fc88d34edc Add Tailscale endpoint 2025-07-07 14:08:19 +08:00
世界
12a3fd3b60 Build legacy binaries with latest Go 2025-07-07 14:08:19 +08:00
世界
b69a381aa8 documentation: Remove outdated icons 2025-07-07 14:08:18 +08:00
世界
7f0419d34c documentation: Certificate store 2025-07-07 14:08:18 +08:00
世界
e758cbcd8c documentation: TLS fragment 2025-07-07 14:08:18 +08:00
世界
8b494f1c4b documentation: Outbound domain resolver 2025-07-07 14:08:17 +08:00
世界
0eaeef81fe documentation: Refactor DNS 2025-07-07 14:08:17 +08:00
世界
25ebabeb63 Add certificate store 2025-07-07 14:08:17 +08:00
世界
d2cb0ee4ae Add TLS fragment support 2025-07-07 14:08:17 +08:00
世界
c81a0ec4d7 refactor: Outbound domain resolver 2025-07-07 14:08:16 +08:00
世界
59fbf08651 refactor: DNS 2025-07-07 14:08:16 +08:00
世界
f792420a78 Bump version 2025-07-07 14:03:10 +08:00
88 changed files with 627 additions and 1190 deletions

View File

@@ -16,7 +16,7 @@ release/config/sing-box.service=/usr/lib/systemd/system/sing-box.service
release/config/sing-box@.service=/usr/lib/systemd/system/sing-box@.service release/config/sing-box@.service=/usr/lib/systemd/system/sing-box@.service
release/config/sing-box.sysusers=/usr/lib/sysusers.d/sing-box.conf release/config/sing-box.sysusers=/usr/lib/sysusers.d/sing-box.conf
release/config/sing-box.rules=usr/share/polkit-1/rules.d/sing-box.rules release/config/sing-box.rules=usr/share/polkit-1/rules.d/sing-box.rules
release/config/sing-box-split-dns.xml=/usr/share/dbus-1/system.d/sing-box-split-dns.conf release/config/sing-box-dbus.xml=/usr/share/dbus-1/system.d/sing-box-dbus.conf
release/completions/sing-box.bash=/usr/share/bash-completion/completions/sing-box.bash 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.fish=/usr/share/fish/vendor_completions.d/sing-box.fish

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
VERSION="1.23.12" VERSION="1.23.6"
mkdir -p $HOME/go mkdir -p $HOME/go
cd $HOME/go cd $HOME/go

View File

@@ -40,13 +40,13 @@ jobs:
version: ${{ steps.outputs.outputs.version }} version: ${{ steps.outputs.outputs.version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.25.0 go-version: ^1.24
- name: Check input version - name: Check input version
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
run: |- run: |-
@@ -88,14 +88,13 @@ jobs:
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" } - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
- { os: windows, arch: amd64 } - { os: windows, arch: amd64 }
- { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" } - { os: windows, arch: amd64, legacy_go: true }
- { os: windows, arch: "386" } - { os: windows, arch: "386" }
- { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" } - { os: windows, arch: "386", legacy_go: true }
- { os: windows, arch: arm64 } - { os: windows, arch: arm64 }
- { os: darwin, arch: amd64 } - { os: darwin, arch: amd64 }
- { os: darwin, arch: arm64 } - { os: darwin, arch: arm64 }
- { os: darwin, arch: amd64, legacy_go124: true, legacy_name: "macos-11" }
- { os: android, arch: arm64, ndk: "aarch64-linux-android21" } - { os: android, arch: arm64, ndk: "aarch64-linux-android21" }
- { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" } - { os: android, arch: arm, ndk: "armv7a-linux-androideabi21" }
@@ -103,33 +102,28 @@ jobs:
- { os: android, arch: "386", ndk: "i686-linux-android21" } - { os: android, arch: "386", ndk: "i686-linux-android21" }
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }} if: ${{ ! matrix.legacy_go }}
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.25.0 go-version: ^1.24
- name: Setup Go 1.24 - name: Cache Legacy Go
if: matrix.legacy_go124 if: matrix.require_legacy_go
uses: actions/setup-go@v5
with:
go-version: ~1.24.6
- name: Cache Go 1.23
if: matrix.legacy_go123
id: cache-legacy-go id: cache-legacy-go
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/go/go_legacy ~/go/go_legacy
key: go_legacy_12312 key: go_legacy_1236
- name: Setup Go 1.23 - name: Setup Legacy Go
if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true' if: matrix.legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true'
run: |- run: |-
.github/setup_legacy_go.sh .github/setup_legacy_go.sh
- name: Setup Go 1.23 - name: Setup Legacy Go 2
if: matrix.legacy_go123 if: matrix.legacy_go
run: |- run: |-
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
@@ -190,8 +184,8 @@ jobs:
DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}" DIR_NAME="${DIR_NAME}-${{ matrix.go386 }}"
elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then elif [[ -n "${{ matrix.gomips }}" && "${{ matrix.gomips }}" != 'hardfloat' ]]; then
DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}" DIR_NAME="${DIR_NAME}-${{ matrix.gomips }}"
elif [[ -n "${{ matrix.legacy_name }}" ]]; then elif [[ "${{ matrix.legacy_go }}" == 'true' ]]; then
DIR_NAME="${DIR_NAME}-legacy-${{ matrix.legacy_name }}" DIR_NAME="${DIR_NAME}-legacy"
fi fi
echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}" echo "DIR_NAME=${DIR_NAME}" >> "${GITHUB_ENV}"
PKG_VERSION="${{ needs.calculate_version.outputs.version }}" PKG_VERSION="${{ needs.calculate_version.outputs.version }}"
@@ -283,7 +277,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.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_name && format('-legacy-{0}', matrix.legacy_name) }} name: binary-${{ matrix.os }}_${{ matrix.arch }}${{ matrix.goarm && format('v{0}', matrix.goarm) }}${{ matrix.go386 && format('_{0}', matrix.go386) }}${{ matrix.gomips && format('_{0}', matrix.gomips) }}${{ matrix.legacy_go && '-legacy' || '' }}
path: "dist" path: "dist"
build_android: build_android:
name: Build Android name: Build Android
@@ -293,14 +287,14 @@ jobs:
- calculate_version - calculate_version
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: 'recursive' submodules: 'recursive'
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.25.0 go-version: ^1.24
- name: Setup Android NDK - name: Setup Android NDK
id: setup-ndk id: setup-ndk
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
@@ -373,14 +367,14 @@ jobs:
- calculate_version - calculate_version
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: 'recursive' submodules: 'recursive'
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.25.0 go-version: ^1.24
- name: Setup Android NDK - name: Setup Android NDK
id: setup-ndk id: setup-ndk
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
@@ -470,7 +464,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
if: matrix.if if: matrix.if
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: 'recursive' submodules: 'recursive'
@@ -478,7 +472,7 @@ jobs:
if: matrix.if if: matrix.if
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.25.0 go-version: ^1.24
- name: Setup Xcode stable - name: Setup Xcode stable
if: matrix.if && github.ref == 'refs/heads/main-next' if: matrix.if && github.ref == 'refs/heads/main-next'
run: |- run: |-
@@ -630,7 +624,7 @@ jobs:
- build_apple - build_apple
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Cache ghr - name: Cache ghr
@@ -653,7 +647,7 @@ jobs:
git tag v${{ needs.calculate_version.outputs.version }} -f git tag v${{ needs.calculate_version.outputs.version }} -f
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
- name: Download builds - name: Download builds
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
path: dist path: dist
merge-multiple: true merge-multiple: true

View File

@@ -39,7 +39,7 @@ jobs:
echo "ref=$ref" echo "ref=$ref"
echo "ref=$ref" >> $GITHUB_OUTPUT echo "ref=$ref" >> $GITHUB_OUTPUT
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
ref: ${{ steps.ref.outputs.ref }} ref: ${{ steps.ref.outputs.ref }}
fetch-depth: 0 fetch-depth: 0
@@ -107,7 +107,7 @@ jobs:
echo "latest=$latest" echo "latest=$latest"
echo "latest=$latest" >> $GITHUB_OUTPUT echo "latest=$latest" >> $GITHUB_OUTPUT
- name: Download digests - name: Download digests
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
path: /tmp/digests path: /tmp/digests
pattern: digests-* pattern: digests-*

View File

@@ -22,15 +22,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ~1.24.6 go-version: ^1.24
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v8 uses: golangci/golangci-lint-action@v6
with: with:
version: latest version: latest
args: --timeout=30m args: --timeout=30m

View File

@@ -7,11 +7,6 @@ on:
description: "Version name" description: "Version name"
required: true required: true
type: string type: string
forceBeta:
description: "Force beta"
required: false
type: boolean
default: false
release: release:
types: types:
- published - published
@@ -24,13 +19,13 @@ jobs:
version: ${{ steps.outputs.outputs.version }} version: ${{ steps.outputs.outputs.version }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.25.0 go-version: ^1.24
- name: Check input version - name: Check input version
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
run: |- run: |-
@@ -65,13 +60,13 @@ jobs:
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 } - { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64 }
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: ^1.25.0 go-version: ^1.24
- name: Setup Android NDK - name: Setup Android NDK
if: matrix.os == 'android' if: matrix.os == 'android'
uses: nttld/setup-ndk@v1 uses: nttld/setup-ndk@v1
@@ -104,11 +99,11 @@ jobs:
run: |- run: |-
TZ=UTC touch -t '197001010000' dist/sing-box TZ=UTC touch -t '197001010000' dist/sing-box
- name: Set name - name: Set name
if: (! contains(needs.calculate_version.outputs.version, '-')) && !inputs.forceBeta if: ${{ ! contains(needs.calculate_version.outputs.version, '-') }}
run: |- run: |-
echo "NAME=sing-box" >> "$GITHUB_ENV" echo "NAME=sing-box" >> "$GITHUB_ENV"
- name: Set beta name - name: Set beta name
if: contains(needs.calculate_version.outputs.version, '-') || inputs.forceBeta if: contains(needs.calculate_version.outputs.version, '-')
run: |- run: |-
echo "NAME=sing-box-beta" >> "$GITHUB_ENV" echo "NAME=sing-box-beta" >> "$GITHUB_ENV"
- name: Set version - name: Set version
@@ -171,7 +166,7 @@ jobs:
- build - build
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set tag - name: Set tag
@@ -180,7 +175,7 @@ jobs:
git tag v${{ needs.calculate_version.outputs.version }} -f git tag v${{ needs.calculate_version.outputs.version }} -f
echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV" echo "VERSION=${{ needs.calculate_version.outputs.version }}" >> "$GITHUB_ENV"
- name: Download builds - name: Download builds
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
path: dist path: dist
merge-multiple: true merge-multiple: true

View File

@@ -1,6 +1,27 @@
version: "2" linters:
disable-all: true
enable:
- gofumpt
- govet
- gci
- staticcheck
- paralleltest
- ineffassign
linters-settings:
gci:
custom-order: true
sections:
- standard
- prefix(github.com/sagernet/)
- default
staticcheck:
checks:
- all
- -SA1003
run: run:
go: "1.24" go: "1.23"
build-tags: build-tags:
- with_gvisor - with_gvisor
- with_quic - with_quic
@@ -9,51 +30,7 @@ run:
- with_utls - with_utls
- with_acme - with_acme
- with_clash_api - with_clash_api
linters:
default: none issues:
enable: exclude-dirs:
- govet - transport/simple-obfs
- ineffassign
- paralleltest
- staticcheck
settings:
staticcheck:
checks:
- all
- -S1000
- -S1008
- -S1017
- -ST1003
- -QF1001
- -QF1003
- -QF1008
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- transport/simple-obfs
- third_party$
- builtin$
- examples$
formatters:
enable:
- gci
- gofumpt
settings:
gci:
sections:
- standard
- prefix(github.com/sagernet/)
- default
custom-order: true
exclusions:
generated: lax
paths:
- transport/simple-obfs
- third_party$
- builtin$
- examples$

View File

@@ -59,8 +59,8 @@ nfpms:
dst: /usr/lib/sysusers.d/sing-box.conf dst: /usr/lib/sysusers.d/sing-box.conf
- src: release/config/sing-box.rules - src: release/config/sing-box.rules
dst: /usr/share/polkit-1/rules.d/sing-box.rules dst: /usr/share/polkit-1/rules.d/sing-box.rules
- src: release/config/sing-box-split-dns.xml - src: release/config/sing-box-dbus.xml
dst: /usr/share/dbus-1/system.d/sing-box-split-dns.conf dst: /usr/share/dbus-1/system.d/sing-box-dbus.conf
- src: release/completions/sing-box.bash - src: release/completions/sing-box.bash
dst: /usr/share/bash-completion/completions/sing-box.bash dst: /usr/share/bash-completion/completions/sing-box.bash

View File

@@ -140,8 +140,8 @@ nfpms:
dst: /usr/lib/sysusers.d/sing-box.conf dst: /usr/lib/sysusers.d/sing-box.conf
- src: release/config/sing-box.rules - src: release/config/sing-box.rules
dst: /usr/share/polkit-1/rules.d/sing-box.rules dst: /usr/share/polkit-1/rules.d/sing-box.rules
- src: release/config/sing-box-split-dns.xml - src: release/config/sing-box-dbus.xml
dst: /usr/share/dbus-1/system.d/sing-box-split-dns.conf dst: /usr/share/dbus-1/system.d/sing-box-dbus.conf
- src: release/completions/sing-box.bash - src: release/completions/sing-box.bash
dst: /usr/share/bash-completion/completions/sing-box.bash dst: /usr/share/bash-completion/completions/sing-box.bash

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
LABEL maintainer="nekohasekai <contact-git@sekai.icu>" LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
COPY . /go/src/github.com/sagernet/sing-box COPY . /go/src/github.com/sagernet/sing-box
WORKDIR /go/src/github.com/sagernet/sing-box WORKDIR /go/src/github.com/sagernet/sing-box

View File

@@ -45,7 +45,7 @@ lint:
GOOS=freebsd golangci-lint run ./... GOOS=freebsd golangci-lint run ./...
lint_install: lint_install:
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest
proto: proto:
@go run ./cmd/internal/protogen @go run ./cmd/internal/protogen
@@ -245,8 +245,8 @@ lib:
go run ./cmd/internal/build_libbox -target ios go run ./cmd/internal/build_libbox -target ios
lib_install: lib_install:
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.8 go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.7
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.8 go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.7
docs: docs:
venv/bin/mkdocs serve venv/bin/mkdocs serve

View File

@@ -135,7 +135,8 @@ func ExtendContext(ctx context.Context) (context.Context, *InboundContext) {
func OverrideContext(ctx context.Context) context.Context { func OverrideContext(ctx context.Context) context.Context {
if metadata := ContextFrom(ctx); metadata != nil { if metadata := ContextFrom(ctx); metadata != nil {
newMetadata := *metadata var newMetadata InboundContext
newMetadata = *metadata
return WithContext(ctx, &newMetadata) return WithContext(ctx, &newMetadata)
} }
return ctx return ctx

View File

@@ -20,7 +20,6 @@ type NetworkManager interface {
DefaultOptions() NetworkOptions DefaultOptions() NetworkOptions
RegisterAutoRedirectOutputMark(mark uint32) error RegisterAutoRedirectOutputMark(mark uint32) error
AutoRedirectOutputMark() uint32 AutoRedirectOutputMark() uint32
AutoRedirectOutputMarkFunc() control.Func
NetworkMonitor() tun.NetworkUpdateMonitor NetworkMonitor() tun.NetworkUpdateMonitor
InterfaceMonitor() tun.DefaultInterfaceMonitor InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager PackageManager() tun.PackageManager

View File

@@ -30,7 +30,7 @@ type Manager struct {
outboundByTag map[string]adapter.Outbound outboundByTag map[string]adapter.Outbound
dependByTag map[string][]string dependByTag map[string][]string
defaultOutbound adapter.Outbound defaultOutbound adapter.Outbound
defaultOutboundFallback func() (adapter.Outbound, error) defaultOutboundFallback adapter.Outbound
} }
func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry, endpoint adapter.EndpointManager, defaultTag string) *Manager { func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry, endpoint adapter.EndpointManager, defaultTag string) *Manager {
@@ -44,7 +44,7 @@ func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry,
} }
} }
func (m *Manager) Initialize(defaultOutboundFallback func() (adapter.Outbound, error)) { func (m *Manager) Initialize(defaultOutboundFallback adapter.Outbound) {
m.defaultOutboundFallback = defaultOutboundFallback m.defaultOutboundFallback = defaultOutboundFallback
} }
@@ -55,31 +55,18 @@ func (m *Manager) Start(stage adapter.StartStage) error {
} }
m.started = true m.started = true
m.stage = stage m.stage = stage
outbounds := m.outbounds
m.access.Unlock()
if stage == adapter.StartStateStart { if stage == adapter.StartStateStart {
if m.defaultTag != "" && m.defaultOutbound == nil { if m.defaultTag != "" && m.defaultOutbound == nil {
defaultEndpoint, loaded := m.endpoint.Get(m.defaultTag) defaultEndpoint, loaded := m.endpoint.Get(m.defaultTag)
if !loaded { if !loaded {
m.access.Unlock()
return E.New("default outbound not found: ", m.defaultTag) return E.New("default outbound not found: ", m.defaultTag)
} }
m.defaultOutbound = defaultEndpoint m.defaultOutbound = defaultEndpoint
} }
if m.defaultOutbound == nil {
directOutbound, err := m.defaultOutboundFallback()
if err != nil {
m.access.Unlock()
return E.Cause(err, "create direct outbound for fallback")
}
m.outbounds = append(m.outbounds, directOutbound)
m.outboundByTag[directOutbound.Tag()] = directOutbound
m.defaultOutbound = directOutbound
}
outbounds := m.outbounds
m.access.Unlock()
return m.startOutbounds(append(outbounds, common.Map(m.endpoint.Endpoints(), func(it adapter.Endpoint) adapter.Outbound { return it })...)) return m.startOutbounds(append(outbounds, common.Map(m.endpoint.Endpoints(), func(it adapter.Endpoint) adapter.Outbound { return it })...))
} else { } else {
outbounds := m.outbounds
m.access.Unlock()
for _, outbound := range outbounds { for _, outbound := range outbounds {
err := adapter.LegacyStart(outbound, stage) err := adapter.LegacyStart(outbound, stage)
if err != nil { if err != nil {
@@ -200,7 +187,11 @@ func (m *Manager) Outbound(tag string) (adapter.Outbound, bool) {
func (m *Manager) Default() adapter.Outbound { func (m *Manager) Default() adapter.Outbound {
m.access.RLock() m.access.RLock()
defer m.access.RUnlock() defer m.access.RUnlock()
return m.defaultOutbound if m.defaultOutbound != nil {
return m.defaultOutbound
} else {
return m.defaultOutboundFallback
}
} }
func (m *Manager) Remove(tag string) error { func (m *Manager) Remove(tag string) error {

8
box.go
View File

@@ -314,15 +314,15 @@ func New(options Options) (*Box, error) {
return nil, E.Cause(err, "initialize service[", i, "]") return nil, E.Cause(err, "initialize service[", i, "]")
} }
} }
outboundManager.Initialize(func() (adapter.Outbound, error) { outboundManager.Initialize(common.Must1(
return direct.NewOutbound( direct.NewOutbound(
ctx, ctx,
router, router,
logFactory.NewLogger("outbound/direct"), logFactory.NewLogger("outbound/direct"),
"direct", "direct",
option.DirectOutboundOptions{}, option.DirectOutboundOptions{},
) ),
}) ))
dnsTransportManager.Initialize(common.Must1( dnsTransportManager.Initialize(common.Must1(
local.NewTransport( local.NewTransport(
ctx, ctx,

View File

@@ -107,10 +107,8 @@ func buildAndroid() {
} }
if !debugEnabled { if !debugEnabled {
sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
args = append(args, sharedFlags...) args = append(args, sharedFlags...)
} else { } else {
debugFlags[1] = debugFlags[1] + " -checklinkname=0"
args = append(args, debugFlags...) args = append(args, debugFlags...)
} }

View File

@@ -1,284 +0,0 @@
package main
import (
"context"
"fmt"
"io"
"net/netip"
"os"
"os/exec"
"strings"
"syscall"
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/include"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/shell"
)
var iperf3Path string
func main() {
err := main0()
if err != nil {
log.Fatal(err)
}
}
func main0() error {
err := shell.Exec("sudo", "ls").Run()
if err != nil {
return err
}
results, err := runTests()
if err != nil {
return err
}
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
return encoder.Encode(results)
}
func runTests() ([]TestResult, error) {
boxPaths := []string{
os.ExpandEnv("$HOME/Downloads/sing-box-1.11.15-darwin-arm64/sing-box"),
//"/Users/sekai/Downloads/sing-box-1.11.15-linux-arm64/sing-box",
"./sing-box",
}
stacks := []string{
"gvisor",
"system",
}
mtus := []int{
1500,
4064,
// 16384,
// 32768,
// 49152,
65535,
}
flagList := [][]string{
{},
}
var results []TestResult
for _, boxPath := range boxPaths {
for _, stack := range stacks {
for _, mtu := range mtus {
if strings.HasPrefix(boxPath, ".") {
for _, flags := range flagList {
result, err := testOnce(boxPath, stack, mtu, false, flags)
if err != nil {
return nil, err
}
results = append(results, *result)
}
} else {
result, err := testOnce(boxPath, stack, mtu, false, nil)
if err != nil {
return nil, err
}
results = append(results, *result)
}
}
}
}
return results, nil
}
type TestResult struct {
BoxPath string `json:"box_path"`
Stack string `json:"stack"`
MTU int `json:"mtu"`
Flags []string `json:"flags"`
MultiThread bool `json:"multi_thread"`
UploadSpeed string `json:"upload_speed"`
DownloadSpeed string `json:"download_speed"`
}
func testOnce(boxPath string, stackName string, mtu int, multiThread bool, flags []string) (result *TestResult, err error) {
testAddress := netip.MustParseAddr("1.1.1.1")
testConfig := option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeTun,
Options: &option.TunInboundOptions{
Address: []netip.Prefix{netip.MustParsePrefix("172.18.0.1/30")},
AutoRoute: true,
MTU: uint32(mtu),
Stack: stackName,
RouteAddress: []netip.Prefix{netip.PrefixFrom(testAddress, testAddress.BitLen())},
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultRule{
RawDefaultRule: option.RawDefaultRule{
IPCIDR: []string{testAddress.String()},
},
RuleAction: option.RuleAction{
Action: C.RuleActionTypeRouteOptions,
RouteOptionsOptions: option.RouteOptionsActionOptions{
OverrideAddress: "127.0.0.1",
},
},
},
},
},
AutoDetectInterface: true,
},
}
ctx := include.Context(context.Background())
tempConfig, err := os.CreateTemp("", "tun-bench-*.json")
if err != nil {
return
}
defer os.Remove(tempConfig.Name())
encoder := json.NewEncoderContext(ctx, tempConfig)
encoder.SetIndent("", " ")
err = encoder.Encode(testConfig)
if err != nil {
return nil, E.Cause(err, "encode test config")
}
tempConfig.Close()
var sudoArgs []string
if len(flags) > 0 {
sudoArgs = append(sudoArgs, "env")
sudoArgs = append(sudoArgs, flags...)
}
sudoArgs = append(sudoArgs, boxPath, "run", "-c", tempConfig.Name())
boxProcess := shell.Exec("sudo", sudoArgs...)
boxProcess.Stdout = &stderrWriter{}
boxProcess.Stderr = io.Discard
err = boxProcess.Start()
if err != nil {
return
}
if C.IsDarwin {
iperf3Path, err = exec.LookPath("iperf3-darwin")
} else {
iperf3Path, err = exec.LookPath("iperf3")
}
if err != nil {
return
}
serverProcess := shell.Exec(iperf3Path, "-s")
serverProcess.Stdout = io.Discard
serverProcess.Stderr = io.Discard
err = serverProcess.Start()
if err != nil {
return nil, E.Cause(err, "start iperf3 server")
}
time.Sleep(time.Second)
args := []string{"-c", testAddress.String()}
if multiThread {
args = append(args, "-P", "10")
}
uploadProcess := shell.Exec(iperf3Path, args...)
output, err := uploadProcess.Read()
if err != nil {
boxProcess.Process.Signal(syscall.SIGKILL)
serverProcess.Process.Signal(syscall.SIGKILL)
println(output)
return
}
uploadResult := common.SubstringBeforeLast(output, "iperf Done.")
uploadResult = common.SubstringBeforeLast(uploadResult, "sender")
uploadResult = common.SubstringBeforeLast(uploadResult, "bits/sec")
uploadResult = common.SubstringAfterLast(uploadResult, "Bytes")
uploadResult = strings.ReplaceAll(uploadResult, " ", "")
result = &TestResult{
BoxPath: boxPath,
Stack: stackName,
MTU: mtu,
Flags: flags,
MultiThread: multiThread,
UploadSpeed: uploadResult,
}
downloadProcess := shell.Exec(iperf3Path, append(args, "-R")...)
output, err = downloadProcess.Read()
if err != nil {
boxProcess.Process.Signal(syscall.SIGKILL)
serverProcess.Process.Signal(syscall.SIGKILL)
println(output)
return
}
downloadResult := common.SubstringBeforeLast(output, "iperf Done.")
downloadResult = common.SubstringBeforeLast(downloadResult, "receiver")
downloadResult = common.SubstringBeforeLast(downloadResult, "bits/sec")
downloadResult = common.SubstringAfterLast(downloadResult, "Bytes")
downloadResult = strings.ReplaceAll(downloadResult, " ", "")
result.DownloadSpeed = downloadResult
printArgs := []any{boxPath, stackName, mtu, "upload", uploadResult, "download", downloadResult}
if len(flags) > 0 {
printArgs = append(printArgs, "flags", strings.Join(flags, " "))
}
if multiThread {
printArgs = append(printArgs, "(-P 10)")
}
fmt.Println(printArgs...)
err = boxProcess.Process.Signal(syscall.SIGTERM)
if err != nil {
return
}
err = serverProcess.Process.Signal(syscall.SIGTERM)
if err != nil {
return
}
boxDone := make(chan struct{})
go func() {
boxProcess.Cmd.Wait()
close(boxDone)
}()
serverDone := make(chan struct{})
go func() {
serverProcess.Process.Wait()
close(serverDone)
}()
select {
case <-boxDone:
case <-time.After(2 * time.Second):
boxProcess.Process.Kill()
case <-time.After(4 * time.Second):
println("box process did not close!")
os.Exit(1)
}
select {
case <-serverDone:
case <-time.After(2 * time.Second):
serverProcess.Process.Kill()
case <-time.After(4 * time.Second):
println("server process did not close!")
os.Exit(1)
}
return
}
type stderrWriter struct{}
func (w *stderrWriter) Write(p []byte) (n int, err error) {
return os.Stderr.Write(p)
}

View File

@@ -128,10 +128,6 @@ func (c *ReadWaitConn) Upstream() any {
return c.Conn return c.Conn
} }
func (c *ReadWaitConn) ReaderReplaceable() bool {
return true
}
var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error)
func init() { func init() {

View File

@@ -6,26 +6,22 @@ import (
"net" "net"
_ "unsafe" _ "unsafe"
"github.com/sagernet/sing/common"
"github.com/metacubex/utls" "github.com/metacubex/utls"
) )
func init() { func init() {
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) { tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
switch tlsConn := conn.(type) { tlsConn, loaded := common.Cast[*tls.UConn](conn)
case *tls.UConn: if !loaded {
return true, func() error { return
return utlsReadRecord(tlsConn.Conn)
}, func() error {
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
}
case *tls.Conn:
return true, func() error {
return utlsReadRecord(tlsConn)
}, func() error {
return utlsHandlePostHandshakeMessage(tlsConn)
}
} }
return return true, func() error {
return utlsReadRecord(tlsConn.Conn)
}, func() error {
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
}
}) })
} }

View File

@@ -454,5 +454,5 @@ func parseADGuardIPCIDRLine(ruleLine string) (netip.Prefix, error) {
for len(ruleParts) < 4 { for len(ruleParts) < 4 {
ruleParts = append(ruleParts, 0) ruleParts = append(ruleParts, 0)
} }
return netip.PrefixFrom(netip.AddrFrom4([4]byte(ruleParts)), bitLen), nil return netip.PrefixFrom(netip.AddrFrom4(*(*[4]byte)(ruleParts)), bitLen), nil
} }

View File

@@ -15,6 +15,7 @@ import (
"github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -42,7 +43,7 @@ type DefaultDialer struct {
networkType []C.InterfaceType networkType []C.InterfaceType
fallbackNetworkType []C.InterfaceType fallbackNetworkType []C.InterfaceType
networkFallbackDelay time.Duration networkFallbackDelay time.Duration
networkLastFallback common.TypedValue[time.Time] networkLastFallback atomic.TypedValue[time.Time]
} }
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) { func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
@@ -120,16 +121,11 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} }
} }
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))
}
} }
} if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
if networkManager != nil { dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
markFunc := networkManager.AutoRedirectOutputMarkFunc() listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
dialer.Control = control.Append(dialer.Control, markFunc) }
listener.Control = control.Append(listener.Control, markFunc)
} }
if options.ReuseAddr { if options.ReuseAddr {
listener.Control = control.Append(listener.Control, control.ReuseAddr()) listener.Control = control.Append(listener.Control, control.ReuseAddr())
@@ -275,7 +271,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
} else { } else {
dialer = d.udpDialer4 dialer = d.udpDialer4
} }
fastFallback := time.Since(d.networkLastFallback.Load()) < C.TCPTimeout fastFallback := time.Now().Sub(d.networkLastFallback.Load()) < C.TCPTimeout
var ( var (
conn net.Conn conn net.Conn
isPrimary bool isPrimary bool

View File

@@ -111,7 +111,7 @@ func NewWithOptions(options Options) (N.Dialer, error) {
dnsQueryOptions.Transport = dnsTransport.Default() dnsQueryOptions.Transport = dnsTransport.Default()
} else if options.NewDialer { } else if options.NewDialer {
return nil, E.New("missing domain resolver for domain server address") return nil, E.New("missing domain resolver for domain server address")
} else { } else if !options.DirectOutbound {
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver) deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
} }
} }

View File

@@ -8,10 +8,8 @@ import (
"net" "net"
"os" "os"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
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"
@@ -24,7 +22,7 @@ type slowOpenConn struct {
ctx context.Context ctx context.Context
network string network string
destination M.Socksaddr destination M.Socksaddr
conn atomic.Pointer[net.TCPConn] conn net.Conn
create chan struct{} create chan struct{}
done chan struct{} done chan struct{}
access sync.Mutex access sync.Mutex
@@ -52,25 +50,22 @@ func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, des
} }
func (c *slowOpenConn) Read(b []byte) (n int, err error) { func (c *slowOpenConn) Read(b []byte) (n int, err error) {
conn := c.conn.Load() if c.conn == nil {
if conn != nil { select {
return conn.Read(b) case <-c.create:
} if c.err != nil {
select { return 0, c.err
case <-c.create: }
if c.err != nil { case <-c.done:
return 0, c.err return 0, os.ErrClosed
} }
return c.conn.Load().Read(b)
case <-c.done:
return 0, os.ErrClosed
} }
return c.conn.Read(b)
} }
func (c *slowOpenConn) Write(b []byte) (n int, err error) { func (c *slowOpenConn) Write(b []byte) (n int, err error) {
tcpConn := c.conn.Load() if c.conn != nil {
if tcpConn != nil { return c.conn.Write(b)
return tcpConn.Write(b)
} }
c.access.Lock() c.access.Lock()
defer c.access.Unlock() defer c.access.Unlock()
@@ -79,7 +74,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
if c.err != nil { if c.err != nil {
return 0, c.err return 0, c.err
} }
return c.conn.Load().Write(b) return c.conn.Write(b)
case <-c.done: case <-c.done:
return 0, os.ErrClosed return 0, os.ErrClosed
default: default:
@@ -88,7 +83,7 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
if err != nil { if err != nil {
c.err = err c.err = err
} else { } else {
c.conn.Store(conn.(*net.TCPConn)) c.conn = conn
} }
n = len(b) n = len(b)
close(c.create) close(c.create)
@@ -98,77 +93,70 @@ func (c *slowOpenConn) Write(b []byte) (n int, err error) {
func (c *slowOpenConn) Close() error { func (c *slowOpenConn) Close() error {
c.closeOnce.Do(func() { c.closeOnce.Do(func() {
close(c.done) close(c.done)
conn := c.conn.Load() if c.conn != nil {
if conn != nil { c.conn.Close()
conn.Close()
} }
}) })
return nil return nil
} }
func (c *slowOpenConn) LocalAddr() net.Addr { func (c *slowOpenConn) LocalAddr() net.Addr {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return M.Socksaddr{} return M.Socksaddr{}
} }
return conn.LocalAddr() return c.conn.LocalAddr()
} }
func (c *slowOpenConn) RemoteAddr() net.Addr { func (c *slowOpenConn) RemoteAddr() net.Addr {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return M.Socksaddr{} return M.Socksaddr{}
} }
return conn.RemoteAddr() return c.conn.RemoteAddr()
} }
func (c *slowOpenConn) SetDeadline(t time.Time) error { func (c *slowOpenConn) SetDeadline(t time.Time) error {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return os.ErrInvalid return os.ErrInvalid
} }
return conn.SetDeadline(t) return c.conn.SetDeadline(t)
} }
func (c *slowOpenConn) SetReadDeadline(t time.Time) error { func (c *slowOpenConn) SetReadDeadline(t time.Time) error {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return os.ErrInvalid return os.ErrInvalid
} }
return conn.SetReadDeadline(t) return c.conn.SetReadDeadline(t)
} }
func (c *slowOpenConn) SetWriteDeadline(t time.Time) error { func (c *slowOpenConn) SetWriteDeadline(t time.Time) error {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return os.ErrInvalid return os.ErrInvalid
} }
return conn.SetWriteDeadline(t) return c.conn.SetWriteDeadline(t)
} }
func (c *slowOpenConn) Upstream() any { func (c *slowOpenConn) Upstream() any {
return common.PtrOrNil(c.conn.Load()) return c.conn
} }
func (c *slowOpenConn) ReaderReplaceable() bool { func (c *slowOpenConn) ReaderReplaceable() bool {
return c.conn.Load() != nil return c.conn != nil
} }
func (c *slowOpenConn) WriterReplaceable() bool { func (c *slowOpenConn) WriterReplaceable() bool {
return c.conn.Load() != nil return c.conn != nil
} }
func (c *slowOpenConn) LazyHeadroom() bool { func (c *slowOpenConn) LazyHeadroom() bool {
return c.conn.Load() == nil return c.conn == nil
} }
func (c *slowOpenConn) NeedHandshake() bool { func (c *slowOpenConn) NeedHandshake() bool {
return c.conn.Load() == nil return c.conn == nil
} }
func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) { func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
select { select {
case <-c.create: case <-c.create:
if c.err != nil { if c.err != nil {
@@ -178,5 +166,5 @@ func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
return 0, c.err return 0, c.err
} }
} }
return bufio.Copy(w, c.conn.Load()) return bufio.Copy(w, c.conn)
} }

View File

@@ -164,8 +164,9 @@ func (l *Listener) loopUDPOut() {
if l.shutdown.Load() && E.IsClosed(err) { if l.shutdown.Load() && E.IsClosed(err) {
return return
} }
l.udpConn.Close()
l.logger.Error("udp listener write back: ", destination, ": ", err) l.logger.Error("udp listener write back: ", destination, ": ", err)
continue return
} }
continue continue
case <-l.packetOutboundClosed: case <-l.packetOutboundClosed:

View File

@@ -96,11 +96,11 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
switch { switch {
case flag&0x1 > 0 && isIPv4: case flag&0x1 > 0 && isIPv4:
// ipv4 // ipv4
srcIP = netip.AddrFrom4([4]byte(buf[inp+76 : inp+80])) srcIP = netip.AddrFrom4(*(*[4]byte)(buf[inp+76 : inp+80]))
srcIsIPv4 = true srcIsIPv4 = true
case flag&0x2 > 0 && !isIPv4: case flag&0x2 > 0 && !isIPv4:
// ipv6 // ipv6
srcIP = netip.AddrFrom16([16]byte(buf[inp+64 : inp+80])) srcIP = netip.AddrFrom16(*(*[16]byte)(buf[inp+64 : inp+80]))
default: default:
continue continue
} }

View File

@@ -17,5 +17,8 @@ var uQUICChrome115 = &ja3.ClientHello{
} }
func maybeUQUIC(fingerprint *ja3.ClientHello) bool { func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
return !uQUICChrome115.Equals(fingerprint, true) if uQUICChrome115.Equals(fingerprint, true) {
return true
}
return false
} }

View File

@@ -125,7 +125,7 @@ func (s *ECHClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (a
func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) { func (s *ECHClientConfig) fetchAndHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
s.access.Lock() s.access.Lock()
defer s.access.Unlock() defer s.access.Unlock()
if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Since(s.lastUpdate) > s.lastTTL { if len(s.ECHConfigList()) == 0 || s.lastTTL == 0 || time.Now().Sub(s.lastUpdate) > s.lastTTL {
message := &mDNS.Msg{ message := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{ MsgHdr: mDNS.MsgHdr{
RecursionDesired: true, RecursionDesired: true,

View File

@@ -1,11 +1,14 @@
package tls package tls
import ( import (
"crypto/ecdh" "bytes"
"crypto/rand" "encoding/binary"
"encoding/pem" "encoding/pem"
"golang.org/x/crypto/cryptobyte" E "github.com/sagernet/sing/common/exceptions"
"github.com/cloudflare/circl/hpke"
"github.com/cloudflare/circl/kem"
) )
type ECHCapableConfig interface { type ECHCapableConfig interface {
@@ -14,68 +17,145 @@ type ECHCapableConfig interface {
SetECHConfigList([]byte) SetECHConfigList([]byte)
} }
func ECHKeygenDefault(publicName string) (configPem string, keyPem string, err error) { func ECHKeygenDefault(serverName string) (configPem string, keyPem string, err error) {
echKey, err := ecdh.X25519().GenerateKey(rand.Reader) cipherSuites := []echCipherSuite{
{
kdf: hpke.KDF_HKDF_SHA256,
aead: hpke.AEAD_AES128GCM,
}, {
kdf: hpke.KDF_HKDF_SHA256,
aead: hpke.AEAD_ChaCha20Poly1305,
},
}
keyConfig := []myECHKeyConfig{
{id: 0, kem: hpke.KEM_X25519_HKDF_SHA256},
}
keyPairs, err := echKeygen(0xfe0d, serverName, keyConfig, cipherSuites)
if err != nil { if err != nil {
return return
} }
echConfig, err := marshalECHConfig(0, echKey.PublicKey().Bytes(), publicName, 0)
if err != nil { var configBuffer bytes.Buffer
return var totalLen uint16
for _, keyPair := range keyPairs {
totalLen += uint16(len(keyPair.rawConf))
} }
configBuilder := cryptobyte.NewBuilder(nil) binary.Write(&configBuffer, binary.BigEndian, totalLen)
configBuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { for _, keyPair := range keyPairs {
builder.AddBytes(echConfig) configBuffer.Write(keyPair.rawConf)
})
configBytes, err := configBuilder.Bytes()
if err != nil {
return
} }
keyBuilder := cryptobyte.NewBuilder(nil)
keyBuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { var keyBuffer bytes.Buffer
builder.AddBytes(echKey.Bytes()) for _, keyPair := range keyPairs {
}) keyBuffer.Write(keyPair.rawKey)
keyBuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
builder.AddBytes(echConfig)
})
keyBytes, err := keyBuilder.Bytes()
if err != nil {
return
} }
configPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBytes}))
keyPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBytes})) configPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer.Bytes()}))
keyPem = string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer.Bytes()}))
return return
} }
func marshalECHConfig(id uint8, pubKey []byte, publicName string, maxNameLen uint8) ([]byte, error) { type echKeyConfigPair struct {
const extensionEncryptedClientHello = 0xfe0d id uint8
const DHKEM_X25519_HKDF_SHA256 = 0x0020 rawKey []byte
const KDF_HKDF_SHA256 = 0x0001 conf myECHKeyConfig
builder := cryptobyte.NewBuilder(nil) rawConf []byte
builder.AddUint16(extensionEncryptedClientHello) }
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
builder.AddUint8(id) type echCipherSuite struct {
kdf hpke.KDF
builder.AddUint16(DHKEM_X25519_HKDF_SHA256) // The only DHKEM we support aead hpke.AEAD
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { }
builder.AddBytes(pubKey)
}) type myECHKeyConfig struct {
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { id uint8
const ( kem hpke.KEM
AEAD_AES_128_GCM = 0x0001 seed []byte
AEAD_AES_256_GCM = 0x0002 }
AEAD_ChaCha20Poly1305 = 0x0003
) func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite []echCipherSuite) ([]echKeyConfigPair, error) {
for _, aeadID := range []uint16{AEAD_AES_128_GCM, AEAD_AES_256_GCM, AEAD_ChaCha20Poly1305} { be := binary.BigEndian
builder.AddUint16(KDF_HKDF_SHA256) // The only KDF we support // prepare for future update
builder.AddUint16(aeadID) if version != 0xfe0d {
} return nil, E.New("unsupported ECH version", version)
}) }
builder.AddUint8(maxNameLen)
builder.AddUint8LengthPrefixed(func(builder *cryptobyte.Builder) { suiteBuf := make([]byte, 0, len(suite)*4+2)
builder.AddBytes([]byte(publicName)) suiteBuf = be.AppendUint16(suiteBuf, uint16(len(suite))*4)
}) for _, s := range suite {
builder.AddUint16(0) // extensions if !s.kdf.IsValid() || !s.aead.IsValid() {
}) return nil, E.New("invalid HPKE cipher suite")
return builder.Bytes() }
suiteBuf = be.AppendUint16(suiteBuf, uint16(s.kdf))
suiteBuf = be.AppendUint16(suiteBuf, uint16(s.aead))
}
pairs := []echKeyConfigPair{}
for _, c := range conf {
pair := echKeyConfigPair{}
pair.id = c.id
pair.conf = c
if !c.kem.IsValid() {
return nil, E.New("invalid HPKE KEM")
}
kpGenerator := c.kem.Scheme().GenerateKeyPair
if len(c.seed) > 0 {
kpGenerator = func() (kem.PublicKey, kem.PrivateKey, error) {
pub, sec := c.kem.Scheme().DeriveKeyPair(c.seed)
return pub, sec, nil
}
if len(c.seed) < c.kem.Scheme().PrivateKeySize() {
return nil, E.New("HPKE KEM seed too short")
}
}
pub, sec, err := kpGenerator()
if err != nil {
return nil, E.Cause(err, "generate ECH config key pair")
}
b := []byte{}
b = be.AppendUint16(b, version)
b = be.AppendUint16(b, 0) // length field
// contents
// key config
b = append(b, c.id)
b = be.AppendUint16(b, uint16(c.kem))
pubBuf, err := pub.MarshalBinary()
if err != nil {
return nil, E.Cause(err, "serialize ECH public key")
}
b = be.AppendUint16(b, uint16(len(pubBuf)))
b = append(b, pubBuf...)
b = append(b, suiteBuf...)
// end key config
// max name len, not supported
b = append(b, 0)
// server name
b = append(b, byte(len(serverName)))
b = append(b, []byte(serverName)...)
// extensions, not supported
b = be.AppendUint16(b, 0)
be.PutUint16(b[2:], uint16(len(b)-4))
pair.rawConf = b
secBuf, err := sec.MarshalBinary()
if err != nil {
return nil, E.Cause(err, "serialize ECH private key")
}
sk := []byte{}
sk = be.AppendUint16(sk, uint16(len(secBuf)))
sk = append(sk, secBuf...)
sk = be.AppendUint16(sk, uint16(len(b)))
sk = append(sk, b...)
pair.rawKey = sk
pairs = append(pairs, pair)
}
return pairs, nil
} }

View File

@@ -307,11 +307,3 @@ func (c *realityClientConnWrapper) Upstream() any {
func (c *realityClientConnWrapper) CloseWrite() error { func (c *realityClientConnWrapper) CloseWrite() error {
return c.Close() return c.Close()
} }
func (c *realityClientConnWrapper) ReaderReplaceable() bool {
return true
}
func (c *realityClientConnWrapper) WriterReplaceable() bool {
return true
}

View File

@@ -206,11 +206,3 @@ func (c *realityConnWrapper) Upstream() any {
func (c *realityConnWrapper) CloseWrite() error { func (c *realityConnWrapper) CloseWrite() error {
return c.Close() return c.Close()
} }
func (c *realityConnWrapper) ReaderReplaceable() bool {
return true
}
func (c *realityConnWrapper) WriterReplaceable() bool {
return true
}

View File

@@ -86,16 +86,12 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb
tlsConfig.InsecureSkipVerify = true tlsConfig.InsecureSkipVerify = true
tlsConfig.VerifyConnection = func(state tls.ConnectionState) error { tlsConfig.VerifyConnection = func(state tls.ConnectionState) error {
verifyOptions := x509.VerifyOptions{ verifyOptions := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
DNSName: serverName, DNSName: serverName,
Intermediates: x509.NewCertPool(), Intermediates: x509.NewCertPool(),
} }
for _, cert := range state.PeerCertificates[1:] { for _, cert := range state.PeerCertificates[1:] {
verifyOptions.Intermediates.AddCert(cert) verifyOptions.Intermediates.AddCert(cert)
} }
if tlsConfig.Time != nil {
verifyOptions.CurrentTime = tlsConfig.Time()
}
_, err := state.PeerCertificates[0].Verify(verifyOptions) _, err := state.PeerCertificates[0].Verify(verifyOptions)
return err return err
} }

View File

@@ -11,13 +11,10 @@ type TimeServiceWrapper struct {
} }
func (w *TimeServiceWrapper) TimeFunc() func() time.Time { func (w *TimeServiceWrapper) TimeFunc() func() time.Time {
return func() time.Time { if w.TimeService == nil {
if w.TimeService != nil { return nil
return w.TimeService.TimeFunc()()
} else {
return time.Now()
}
} }
return w.TimeService.TimeFunc()
} }
func (w *TimeServiceWrapper) Upstream() any { func (w *TimeServiceWrapper) Upstream() any {

View File

@@ -106,14 +106,6 @@ func (c *utlsConnWrapper) Upstream() any {
return c.UConn return c.UConn
} }
func (c *utlsConnWrapper) ReaderReplaceable() bool {
return true
}
func (c *utlsConnWrapper) WriterReplaceable() bool {
return true
}
type utlsALPNWrapper struct { type utlsALPNWrapper struct {
utlsConnWrapper utlsConnWrapper
nextProtocols []string nextProtocols []string
@@ -153,16 +145,11 @@ func NewUTLSClient(ctx context.Context, serverAddress string, options option.Out
var tlsConfig utls.Config var tlsConfig utls.Config
tlsConfig.Time = ntp.TimeFuncFromContext(ctx) tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx) tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx)
if !options.DisableSNI { tlsConfig.ServerName = serverName
tlsConfig.ServerName = serverName
}
if options.Insecure { if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI { } else if options.DisableSNI {
if options.Reality != nil && options.Reality.Enabled { return nil, E.New("disable_sni is unsupported in uTLS")
return nil, E.New("disable_sni is unsupported in reality")
}
tlsConfig.InsecureServerNameToVerify = serverName
} }
if len(options.ALPN) > 0 { if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN tlsConfig.NextProtos = options.ALPN

View File

@@ -45,7 +45,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
defer func() { defer func() {
c.firstPacketWritten = true c.firstPacketWritten = true
}() }()
serverName := IndexTLSServerName(b) serverName := indexTLSServerName(b)
if serverName != nil { if serverName != nil {
if c.splitPacket { if c.splitPacket {
if c.tcpConn != nil { if c.tcpConn != nil {
@@ -109,9 +109,6 @@ func (c *Conn) Write(b []byte) (n int, err error) {
if err != nil { if err != nil {
return return
} }
if i != len(splitIndexes) {
time.Sleep(c.fallbackDelay)
}
} }
} }
} }

View File

@@ -22,13 +22,13 @@ const (
tls13 uint16 = 0x0304 tls13 uint16 = 0x0304
) )
type MyServerName struct { type myServerName struct {
Index int Index int
Length int Length int
ServerName string ServerName string
} }
func IndexTLSServerName(payload []byte) *MyServerName { func indexTLSServerName(payload []byte) *myServerName {
if len(payload) < recordLayerHeaderLen || payload[0] != contentType { if len(payload) < recordLayerHeaderLen || payload[0] != contentType {
return nil return nil
} }
@@ -36,48 +36,47 @@ func IndexTLSServerName(payload []byte) *MyServerName {
if len(payload) < recordLayerHeaderLen+int(segmentLen) { if len(payload) < recordLayerHeaderLen+int(segmentLen) {
return nil return nil
} }
serverName := indexTLSServerNameFromHandshake(payload[recordLayerHeaderLen:]) serverName := indexTLSServerNameFromHandshake(payload[recordLayerHeaderLen : recordLayerHeaderLen+int(segmentLen)])
if serverName == nil { if serverName == nil {
return nil return nil
} }
serverName.Index += recordLayerHeaderLen serverName.Length += recordLayerHeaderLen
return serverName return serverName
} }
func indexTLSServerNameFromHandshake(handshake []byte) *MyServerName { func indexTLSServerNameFromHandshake(hs []byte) *myServerName {
if len(handshake) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen { if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen {
return nil return nil
} }
if handshake[0] != handshakeType { if hs[0] != handshakeType {
return nil return nil
} }
handshakeLen := uint32(handshake[1])<<16 | uint32(handshake[2])<<8 | uint32(handshake[3]) handshakeLen := uint32(hs[1])<<16 | uint32(hs[2])<<8 | uint32(hs[3])
if len(handshake[4:]) != int(handshakeLen) { if len(hs[4:]) != int(handshakeLen) {
return nil return nil
} }
tlsVersion := uint16(handshake[4])<<8 | uint16(handshake[5]) tlsVersion := uint16(hs[4])<<8 | uint16(hs[5])
if tlsVersion&tlsVersionBitmask != 0x0300 && tlsVersion != tls13 { if tlsVersion&tlsVersionBitmask != 0x0300 && tlsVersion != tls13 {
return nil return nil
} }
sessionIDLen := handshake[38] sessionIDLen := hs[38]
currentIndex := handshakeHeaderLen + randomDataLen + sessionIDHeaderLen + int(sessionIDLen) if len(hs) < handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen) {
if len(handshake) < currentIndex {
return nil return nil
} }
cipherSuites := handshake[currentIndex:] cs := hs[handshakeHeaderLen+randomDataLen+sessionIDHeaderLen+int(sessionIDLen):]
if len(cipherSuites) < cipherSuiteHeaderLen { if len(cs) < cipherSuiteHeaderLen {
return nil return nil
} }
csLen := uint16(cipherSuites[0])<<8 | uint16(cipherSuites[1]) csLen := uint16(cs[0])<<8 | uint16(cs[1])
if len(cipherSuites) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen { if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen {
return nil return nil
} }
compressMethodLen := uint16(cipherSuites[cipherSuiteHeaderLen+int(csLen)]) compressMethodLen := uint16(cs[cipherSuiteHeaderLen+int(csLen)])
currentIndex += cipherSuiteHeaderLen + int(csLen) + compressMethodHeaderLen + int(compressMethodLen) if len(cs) < cipherSuiteHeaderLen+int(csLen)+compressMethodHeaderLen+int(compressMethodLen) {
if len(handshake) < currentIndex {
return nil return nil
} }
serverName := indexTLSServerNameFromExtensions(handshake[currentIndex:]) currentIndex := cipherSuiteHeaderLen + int(csLen) + compressMethodHeaderLen + int(compressMethodLen)
serverName := indexTLSServerNameFromExtensions(cs[currentIndex:])
if serverName == nil { if serverName == nil {
return nil return nil
} }
@@ -85,7 +84,7 @@ func indexTLSServerNameFromHandshake(handshake []byte) *MyServerName {
return serverName return serverName
} }
func indexTLSServerNameFromExtensions(exs []byte) *MyServerName { func indexTLSServerNameFromExtensions(exs []byte) *myServerName {
if len(exs) == 0 { if len(exs) == 0 {
return nil return nil
} }
@@ -119,8 +118,7 @@ func indexTLSServerNameFromExtensions(exs []byte) *MyServerName {
} }
sniLen := uint16(sex[3])<<8 | uint16(sex[4]) sniLen := uint16(sex[3])<<8 | uint16(sex[4])
sex = sex[sniExtensionHeaderLen:] sex = sex[sniExtensionHeaderLen:]
return &myServerName{
return &MyServerName{
Index: currentIndex + extensionHeaderLen + sniExtensionHeaderLen, Index: currentIndex + extensionHeaderLen + sniExtensionHeaderLen,
Length: int(sniLen), Length: int(sniLen),
ServerName: string(sex), ServerName: string(sex),

View File

@@ -1,20 +0,0 @@
package tf_test
import (
"encoding/hex"
"testing"
"github.com/sagernet/sing-box/common/tlsfragment"
"github.com/stretchr/testify/require"
)
func TestIndexTLSServerName(t *testing.T) {
t.Parallel()
payload, err := hex.DecodeString("16030105f8010005f403036e35de7389a679c54029cf452611f2211c70d9ac3897271de589ab6155f8e4ab20637d225f1ef969ad87ed78bfb9d171300bcb1703b6f314ccefb964f79b7d0961002a0a0a130213031301c02cc02bcca9c030c02fcca8c00ac009c014c013009d009c0035002fc008c012000a01000581baba00000000000f000d00000a6769746875622e636f6d00170000ff01000100000a000e000c3a3a11ec001d001700180019000b000201000010000e000c02683208687474702f312e31000500050100000000000d00160014040308040401050308050805050108060601020100120000003304ef04ed3a3a00010011ec04c0aeb2250c092a3463161cccb29d9183331a424964248579507ed23a180b0ceab2a5f5d9ce41547e497a89055471ea572867ba3a1fc3c9e45025274a20f60c6b60e62476b6afed0403af59ab83660ef4112ae20386a602010d0a5d454c0ed34c84ed4423e750213e6a2baab1bf9c4367a6007ab40a33d95220c2dcaa44f257024a5626b545db0510f4311b1a60714154909c6a61fdfca011fb2626d657aeb6070bf078508babe3b584555013e34acc56198ed4663742b3155a664a9901794c4586820a7dc162c01827291f3792e1237f801a8d1ef096013c181c4a58d2f6859ba75022d18cc4418bd4f351d5c18f83a58857d05af860c4b9ac018a5b63f17184e591532c6bc2cf2215d4a282c8a8a4f6f7aee110422c8bc9ebd3b1d609c568523aaae555db320e6c269473d87af38c256cbb9febc20aea6380c32a8916f7a373c8b1e37554e3260bf6621f6b804ee80b3c516b1d01985bf4c603b6daa9a5991de6a7a29f3a7122b8afb843a7660110fce62b43c615f5bcc2db688ba012649c0952b0a2c031e732d2b454c6b2968683cb8d244be2c9a7fa163222979eaf92722b92b862d81a3d94450c2b60c318421ebb4307c42d1f0473592a5c30e42039cc68cda9721e61aa63f49def17c15221680ed444896340133bbee67556f56b9f9d78a4df715f926a12add0cc9c862e46ea8b7316ae468282c18601b2771c9c9322f982228cf93effaacd3f80cbd12bce5fc36f56e2a3caf91e578a5fae00c9b23a8ed1a66764f4433c3628a70b8f0a6196adc60a4cb4226f07ba4c6b363fe9065563bfc1347452946386bab488686e837ab979c64f9047417fca635fe1bb4f074f256cc8af837c7b455e280426547755af90a61640169ef180aea3a77e662bb6dac1b6c3696027129b1a5edf495314e9c7f4b6110e16378ec893fa24642330a40aba1a85326101acb97c620fd8d71389e69eaed7bdb01bbe1fd428d66191150c7b2cd1ad4257391676a82ba8ce07fb2667c3b289f159003a7c7bc31d361b7b7f49a802961739d950dfcc0fa1c7abce5abdd2245101da391151490862028110465950b9e9c03d08a90998ab83267838d2e74a0593bc81f74cdf734519a05b351c0e5488c68dd810e6e9142ccc1e2f4a7f464297eb340e27acc6b9d64e12e38cce8492b3d939140b5a9e149a75597f10a23874c84323a07cdd657274378f887c85c4259b9c04cd33ba58ed630ef2a744f8e19dd34843dff331d2a6be7e2332c599289cd248a611c73d7481cd4a9bd43449a3836f14b2af18a1739e17999e4c67e85cc5bcecabb14185e5bcaff3c96098f03dc5aba819f29587758f49f940585354a2a780830528d68ccd166920dadcaa25cab5fc1907272a826aba3f08bc6b88757776812ecb6c7cec69a223ec0a13a7b62a2349a0f63ed7a27a3b15ba21d71fe6864ec6e089ae17cadd433fa3138f7ee24353c11365818f8fc34f43a05542d18efaac24bfccc1f748a0cc1a67ad379468b76fd34973dba785f5c91d618333cd810fe0700d1bbc8422029782628070a624c52c5309a4a64d625b11f8033ab28df34a1add297517fcc06b92b6817b3c5144438cf260867c57bde68c8c4b82e6a135ef676a52fbae5708002a404e6189a60e2836de565ad1b29e3819e5ed49f6810bcb28e1bd6de57306f94b79d9dae1cc4624d2a068499beef81cd5fe4b76dcbfff2a2008001d002001976128c6d5a934533f28b9914d2480aab2a8c1ab03d212529ce8b27640a716002d00020101002b000706caca03040303001b00030200015a5a000100")
require.NoError(t, err)
serverName := tf.IndexTLSServerName(payload)
require.NotNil(t, serverName)
require.Equal(t, serverName.ServerName, string(payload[serverName.Index:serverName.Index+serverName.Length]))
require.Equal(t, "github.com", serverName.ServerName)
}

View File

@@ -9,10 +9,6 @@ import (
) )
func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error { func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error {
_, err := conn.Write(payload)
if err != nil {
return err
}
time.Sleep(fallbackDelay) time.Sleep(fallbackDelay)
return nil return nil
} }

View File

@@ -16,9 +16,6 @@ func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fal
err := winiphlpapi.WriteAndWaitAck(ctx, conn, payload) err := winiphlpapi.WriteAndWaitAck(ctx, conn, payload)
if err != nil { if err != nil {
if errors.Is(err, windows.ERROR_ACCESS_DENIED) { if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
if _, err := conn.Write(payload); err != nil {
return err
}
time.Sleep(fallbackDelay) time.Sleep(fallbackDelay)
return nil return nil
} }

View File

@@ -293,7 +293,12 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
} else if errors.Is(err, ErrResponseRejected) { } else if errors.Is(err, ErrResponseRejected) {
rejected = true rejected = true
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String()))) r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
/*} else if responseCheck!= nil && errors.Is(err, RcodeError(mDNS.RcodeNameError)) {
rejected = true
r.logger.DebugContext(ctx, E.Cause(err, "response rejected for ", FormatQuestion(message.Question[0].String())))
*/
} else if len(message.Question) > 0 { } else if len(message.Question) > 0 {
rejected = true
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", FormatQuestion(message.Question[0].String()))) r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", FormatQuestion(message.Question[0].String())))
} else { } else {
r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for <empty query>")) r.logger.ErrorContext(ctx, E.Cause(err, "exchange failed for <empty query>"))

View File

@@ -104,7 +104,7 @@ func (c *dnsConfig) serverOffset() uint32 {
return 0 return 0
} }
func (c *dnsConfig) nameList(name string) []string { func (conf *dnsConfig) nameList(name string) []string {
l := len(name) l := len(name)
rooted := l > 0 && name[l-1] == '.' rooted := l > 0 && name[l-1] == '.'
if l > 254 || l == 254 && !rooted { if l > 254 || l == 254 && !rooted {
@@ -118,15 +118,15 @@ func (c *dnsConfig) nameList(name string) []string {
return []string{name} return []string{name}
} }
hasNdots := strings.Count(name, ".") >= c.ndots hasNdots := strings.Count(name, ".") >= conf.ndots
name += "." name += "."
// l++ // l++
names := make([]string, 0, 1+len(c.search)) names := make([]string, 0, 1+len(conf.search))
if hasNdots && !avoidDNS(name) { if hasNdots && !avoidDNS(name) {
names = append(names, name) names = append(names, name)
} }
for _, suffix := range c.search { for _, suffix := range conf.search {
fqdn := name + suffix fqdn := name + suffix
if !avoidDNS(fqdn) && len(fqdn) <= 254 { if !avoidDNS(fqdn) && len(fqdn) <= 254 {
names = append(names, fqdn) names = append(names, fqdn)

View File

@@ -20,8 +20,8 @@ import (
) )
func dnsReadConfig(_ context.Context, _ string) *dnsConfig { func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
var state C.struct___res_state var state C.res_state
if C.res_ninit(&state) != 0 { if C.res_ninit(state) != 0 {
return &dnsConfig{ return &dnsConfig{
servers: defaultNS, servers: defaultNS,
search: dnsDefaultSearch(), search: dnsDefaultSearch(),

View File

@@ -1,13 +0,0 @@
package local
import (
"context"
"testing"
"github.com/stretchr/testify/require"
)
func TestDNSReadConfig(t *testing.T) {
t.Parallel()
require.NoError(t, dnsReadConfig(context.Background(), "/etc/resolv.conf").err)
}

View File

@@ -60,7 +60,7 @@ func NewUDPRaw(logger logger.ContextLogger, adapter dns.TransportAdapter, dialer
logger: logger, logger: logger,
dialer: dialer, dialer: dialer,
serverAddr: serverAddr, serverAddr: serverAddr,
udpSize: 2048, udpSize: 512,
tcpTransport: &TCPTransport{ tcpTransport: &TCPTransport{
dialer: dialer, dialer: dialer,
serverAddr: serverAddr, serverAddr: serverAddr,
@@ -97,19 +97,15 @@ func (t *UDPTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
} }
func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
t.access.Lock()
if edns0Opt := message.IsEdns0(); edns0Opt != nil {
if udpSize := int(edns0Opt.UDPSize()); udpSize > t.udpSize {
t.udpSize = udpSize
close(t.done)
t.done = make(chan struct{})
}
}
t.access.Unlock()
conn, err := t.open(ctx) conn, err := t.open(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if edns0Opt := message.IsEdns0(); edns0Opt != nil {
if udpSize := int(edns0Opt.UDPSize()); udpSize > t.udpSize {
t.udpSize = udpSize
}
}
buffer := buf.NewSize(1 + message.Len()) buffer := buf.NewSize(1 + message.Len())
defer buffer.Release() defer buffer.Release()
exMessage := *message exMessage := *message

View File

@@ -2,173 +2,11 @@
icon: material/alert-decagram icon: material/alert-decagram
--- ---
#### 1.12.4 #### 1.12.0-beta.33
* Add firewalld compatibility for auto redirect
* Fixes and improvements * Fixes and improvements
#### 1.12.3
* Fixes and improvements
#### 1.12.2
* Fixes and improvements
#### 1.12.1
* Fixes and improvements
#### 1.12.0
* Refactor DNS servers **1**
* Add domain resolver options**2**
* Add TLS fragment/record fragment support to route options and outbound TLS options **3**
* Add certificate options **4**
* Add Tailscale endpoint and DNS server **5**
* Drop support for go1.22 **6**
* Add AnyTLS protocol **7**
* Migrate to stdlib ECH implementation **8**
* Add NTP sniffer **9**
* Add wildcard SNI support for ShadowTLS inbound **10**
* Improve `auto_redirect` **11**
* Add control options for listeners **12**
* Add DERP service **13**
* Add Resolved service and DNS server **14**
* Add SSM API service **15**
* Add loopback address support for tun **16**
* Improve tun performance on Apple platforms **17**
* Update quic-go to v0.52.0
* Update gVisor to 20250319.0
* Update the status of graphical clients in stores **18**
**1**:
DNS servers are refactored for better performance and scalability.
See [DNS server](/configuration/dns/server/).
For migration, see [Migrate to new DNS server formats](/migration/#migrate-to-new-dns-servers).
Compatibility for old formats will be removed in sing-box 1.14.0.
**2**:
Legacy `outbound` DNS rules are deprecated
and can be replaced by the new `domain_resolver` option.
See [Dial Fields](/configuration/shared/dial/#domain_resolver) and
[Route](/configuration/route/#default_domain_resolver).
For migration,
see [Migrate outbound DNS rule items to domain resolver](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver).
**3**:
See [Route Action](/configuration/route/rule_action/#tls_fragment) and [TLS](/configuration/shared/tls/).
**4**:
New certificate options allow you to manage the default list of trusted X509 CA certificates.
For the system certificate list, fixed Go not reading Android trusted certificates correctly.
You can also use the Mozilla Included List instead, or add trusted certificates yourself.
See [Certificate](/configuration/certificate/).
**5**:
See [Tailscale](/configuration/endpoint/tailscale/).
**6**:
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
**7**:
The new AnyTLS protocol claims to mitigate TLS proxy traffic characteristics and comes with a new multiplexing scheme.
See [AnyTLS Inbound](/configuration/inbound/anytls/) and [AnyTLS Outbound](/configuration/outbound/anytls/).
**8**:
See [TLS](/configuration/shared/tls).
The build tag `with_ech` is no longer needed and has been removed.
**9**:
See [Protocol Sniff](/configuration/route/sniff/).
**10**:
See [ShadowTLS](/configuration/inbound/shadowtls/#wildcard_sni).
**11**:
Now `auto_redirect` fixes compatibility issues between tun and Docker bridge networks,
see [Tun](/configuration/inbound/tun/#auto_redirect).
**12**:
You can now set `bind_interface`, `routing_mark` and `reuse_addr` in Listen Fields.
See [Listen Fields](/configuration/shared/listen/).
**13**:
DERP service is a Tailscale DERP server, similar to [derper](https://pkg.go.dev/tailscale.com/cmd/derper).
See [DERP Service](/configuration/service/derp/).
**14**:
Resolved service is a fake systemd-resolved DBUS service to receive DNS settings from other programs
(e.g. NetworkManager) and provide DNS resolution.
See [Resolved Service](/configuration/service/resolved/) and [Resolved DNS Server](/configuration/dns/server/resolved/).
**15**:
SSM API service is a RESTful API server for managing Shadowsocks servers.
See [SSM API Service](/configuration/service/ssm-api/).
**16**:
TUN now implements SideStore's StosVPN.
See [Tun](/configuration/inbound/tun/#loopback_address).
**17**:
We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack.
The following data was tested using [tun_bench](https://github.com/SagerNet/sing-box/blob/dev-next/cmd/internal/tun_bench/main.go) on M4 MacBook pro.
| Version | Stack | MTU | Upload | Download |
|-------------|--------|-------|--------|----------|
| 1.11.15 | gvisor | 1500 | 852M | 2.57G |
| 1.12.0-rc.4 | gvisor | 1500 | 2.90G | 4.68G |
| 1.11.15 | gvisor | 4064 | 2.31G | 6.34G |
| 1.12.0-rc.4 | gvisor | 4064 | 7.54G | 12.2G |
| 1.11.15 | gvisor | 65535 | 27.6G | 18.1G |
| 1.12.0-rc.4 | gvisor | 65535 | 39.8G | 34.7G |
| 1.11.15 | system | 1500 | 664M | 706M |
| 1.12.0-rc.4 | system | 1500 | 2.44G | 2.51G |
| 1.11.15 | system | 4064 | 1.88G | 1.94G |
| 1.12.0-rc.4 | system | 4064 | 6.45G | 6.27G |
| 1.11.15 | system | 65535 | 26.2G | 17.4G |
| 1.12.0-rc.4 | system | 65535 | 17.6G | 21.0G |
**18**:
We continue to experience issues updating our sing-box apps on the App Store and Play Store.
Until we rewrite and resubmit the apps, they are considered irrecoverable.
Therefore, after this release, we will not be repeating this notice unless there is new information.
### 1.11.15 ### 1.11.15
* Fixes and improvements * Fixes and improvements

View File

@@ -61,7 +61,7 @@ WireGuard MTU。
==必填== ==必填==
接口的 IPv4/IPv6 地址或地址段的列表。 接口的 IPv4/IPv6 地址或地址段的列表
要分配给接口的 IPv4 或 v6地址段列表。 要分配给接口的 IPv4 或 v6地址段列表。

View File

@@ -88,7 +88,7 @@ icon: material/delete-clock
==必填== ==必填==
接口的 IPv4/IPv6 地址或地址段的列表。 接口的 IPv4/IPv6 地址或地址段的列表
要分配给接口的 IPv4 或 v6地址段列表。 要分配给接口的 IPv4 或 v6地址段列表。

View File

@@ -108,7 +108,7 @@ flowchart TB
"inbounds": [ "inbounds": [
{ {
"type": "tun", "type": "tun",
"address": ["172.19.0.1/30"], "inet4_address": "172.19.0.1/30",
"auto_route": true, "auto_route": true,
// "auto_redirect": true, // On linux // "auto_redirect": true, // On linux
"strict_route": true "strict_route": true
@@ -162,7 +162,8 @@ flowchart TB
"inbounds": [ "inbounds": [
{ {
"type": "tun", "type": "tun",
"address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"], "inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"auto_route": true, "auto_route": true,
// "auto_redirect": true, // On linux // "auto_redirect": true, // On linux
"strict_route": true "strict_route": true
@@ -232,7 +233,8 @@ flowchart TB
"inbounds": [ "inbounds": [
{ {
"type": "tun", "type": "tun",
"address": ["172.19.0.1/30","fdfe:dcba:9876::1/126"], "inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"auto_route": true, "auto_route": true,
// "auto_redirect": true, // On linux // "auto_redirect": true, // On linux
"strict_route": true "strict_route": true

View File

@@ -351,15 +351,14 @@ DNS servers are refactored for better performance and scalability.
```json ```json
{ {
"dns": { "dns": {
"rules": [ "servers": [
{ {
"domain": [ "type": "predefined",
"example.com" "responses": [
], {
// other rules "rcode": "REFUSED"
}
"action": "predefined", ]
"rcode": "REFUSED"
} }
] ]
} }
@@ -1188,4 +1187,4 @@ which will disrupt the existing `process_path` use cases in Windows.
} }
} }
} }
``` ```

View File

@@ -8,7 +8,7 @@ icon: material/arrange-bring-forward
DNS 服务器已经重构。 DNS 服务器已经重构。
!!! info "用" !!! info "用"
[DNS 服务器](/configuration/dns/server/) / [DNS 服务器](/configuration/dns/server/) /
[旧 DNS 服务器](/configuration/dns/server/legacy/) [旧 DNS 服务器](/configuration/dns/server/legacy/)
@@ -351,15 +351,14 @@ DNS 服务器已经重构。
```json ```json
{ {
"dns": { "dns": {
"rules": [ "servers": [
{ {
"domain": [ "type": "predefined",
"example.com" "responses": [
], {
// 其它规则 "rcode": "REFUSED"
}
"action": "predefined", ]
"rcode": "REFUSED"
} }
] ]
} }

View File

@@ -3,12 +3,12 @@ package trafficontrol
import ( import (
"runtime" "runtime"
"sync" "sync"
"sync/atomic"
"time" "time"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/clashapi/compatible" "github.com/sagernet/sing-box/experimental/clashapi/compatible"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/common/x/list"

View File

@@ -2,11 +2,11 @@ package trafficontrol
import ( import (
"net" "net"
"sync/atomic"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"

View File

@@ -62,7 +62,10 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
return false return false
} }
_, isGroup := it.(adapter.OutboundGroup) _, isGroup := it.(adapter.OutboundGroup)
return !isGroup if isGroup {
return false
}
return true
}) })
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10)) b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
for _, detour := range outbounds { for _, detour := range outbounds {

View File

@@ -22,7 +22,6 @@ import (
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/common/x/list"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/filemanager"
) )
func BaseContext(platformInterface PlatformInterface) context.Context { func BaseContext(platformInterface PlatformInterface) context.Context {
@@ -34,9 +33,7 @@ func BaseContext(platformInterface PlatformInterface) context.Context {
}) })
} }
} }
ctx := context.Background() return box.Context(context.Background(), include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
return box.Context(ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
} }
func parseConfig(ctx context.Context, configContent string) (option.Options, error) { func parseConfig(ctx context.Context, configContent string) (option.Options, error) {

View File

@@ -18,7 +18,6 @@ func newIterator[T any](values []T) *iterator[T] {
return &iterator[T]{values} return &iterator[T]{values}
} }
//go:noinline
func newPtrIterator[T any](values []T) *iterator[*T] { func newPtrIterator[T any](values []T) *iterator[*T] {
return &iterator[*T]{common.Map(values, func(value T) *T { return &value })} return &iterator[*T]{common.Map(values, func(value T) *T { return &value })}
} }

View File

@@ -1,19 +0,0 @@
package libbox
import (
"os"
_ "unsafe"
)
// https://github.com/SagerNet/sing-box/issues/3233
// https://github.com/golang/go/issues/70508
// https://github.com/tailscale/tailscale/issues/13452
//go:linkname checkPidfdOnce os.checkPidfdOnce
var checkPidfdOnce func() error
func init() {
checkPidfdOnce = func() error {
return os.ErrInvalid
}
}

View File

@@ -27,6 +27,7 @@ import (
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/filemanager"
"github.com/sagernet/sing/service/pause" "github.com/sagernet/sing/service/pause"
) )
@@ -43,6 +44,7 @@ type BoxService struct {
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) { func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
ctx := BaseContext(platformInterface) ctx := BaseContext(platformInterface)
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager)) service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
options, err := parseConfig(ctx, configContent) options, err := parseConfig(ctx, configContent)
if err != nil { if err != nil {

View File

@@ -7,11 +7,11 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -115,7 +115,7 @@ func (s *StatsService) RoutedPacketConnection(ctx context.Context, conn N.Packet
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink")) writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
} }
s.access.Unlock() s.access.Unlock()
return bufio.NewInt64CounterPacketConn(conn, readCounter, nil, writeCounter, nil) return bufio.NewInt64CounterPacketConn(conn, readCounter, writeCounter)
} }
func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
@@ -192,7 +192,7 @@ func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest
var rtm runtime.MemStats var rtm runtime.MemStats
runtime.ReadMemStats(&rtm) runtime.ReadMemStats(&rtm)
response := &SysStatsResponse{ response := &SysStatsResponse{
Uptime: uint32(time.Since(s.createdAt).Seconds()), Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
NumGoroutine: uint32(runtime.NumGoroutine()), NumGoroutine: uint32(runtime.NumGoroutine()),
Alloc: rtm.Alloc, Alloc: rtm.Alloc,
TotalAlloc: rtm.TotalAlloc, TotalAlloc: rtm.TotalAlloc,

55
go.mod
View File

@@ -5,38 +5,39 @@ go 1.23.1
require ( require (
github.com/anytls/sing-anytls v0.0.8 github.com/anytls/sing-anytls v0.0.8
github.com/caddyserver/certmagic v0.23.0 github.com/caddyserver/certmagic v0.23.0
github.com/coder/websocket v1.8.13 github.com/cloudflare/circl v1.6.1
github.com/coder/websocket v1.8.12
github.com/cretz/bine v0.2.0 github.com/cretz/bine v0.2.0
github.com/go-chi/chi/v5 v5.2.2 github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/render v1.0.3 github.com/go-chi/render v1.0.3
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466
github.com/gofrs/uuid/v5 v5.3.2 github.com/gofrs/uuid/v5 v5.3.2
github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f github.com/insomniacslk/dhcp v0.0.0-20250417080101-5f8cf70e8c5f
github.com/libdns/alidns v1.0.5-libdns.v1.beta1 github.com/libdns/alidns v1.0.4-libdns.v1.beta1
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6
github.com/logrusorgru/aurora v2.0.3+incompatible github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422
github.com/metacubex/utls v1.8.0 github.com/metacubex/utls v1.7.0-alpha.3
github.com/mholt/acmez/v3 v3.1.2 github.com/mholt/acmez/v3 v3.1.2
github.com/miekg/dns v1.1.67 github.com/miekg/dns v1.1.66
github.com/oschwald/maxminddb-golang v1.13.1 github.com/oschwald/maxminddb-golang v1.13.1
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/cors v1.2.1 github.com/sagernet/cors v1.2.1
github.com/sagernet/fswatch v0.1.1 github.com/sagernet/fswatch v0.1.1
github.com/sagernet/gomobile v0.1.8 github.com/sagernet/gomobile v0.1.7
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
github.com/sagernet/quic-go v0.52.0-beta.1 github.com/sagernet/quic-go v0.52.0-beta.1
github.com/sagernet/sing v0.7.6-0.20250825114712-2aeec120ce28 github.com/sagernet/sing v0.6.12-0.20250704043954-da981379f151
github.com/sagernet/sing-mux v0.3.3 github.com/sagernet/sing-mux v0.3.2
github.com/sagernet/sing-quic v0.5.0 github.com/sagernet/sing-quic v0.5.0-beta.2
github.com/sagernet/sing-shadowsocks v0.2.8 github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1 github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
github.com/sagernet/sing-tun v0.7.0-beta.1 github.com/sagernet/sing-tun v0.6.10-0.20250707094843-b2e2674d73e5
github.com/sagernet/sing-vmess v0.2.7 github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88
github.com/sagernet/smux v1.5.34-mod.2 github.com/sagernet/smux v1.5.34-mod.2
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1 github.com/sagernet/tailscale v1.80.3-mod.5
github.com/sagernet/wireguard-go v0.0.1-beta.7 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.9.1 github.com/spf13/cobra v1.9.1
@@ -44,13 +45,13 @@ require (
github.com/vishvananda/netns v0.0.5 github.com/vishvananda/netns v0.0.5
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.41.0 golang.org/x/crypto v0.39.0
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
golang.org/x/mod v0.27.0 golang.org/x/mod v0.25.0
golang.org/x/net v0.43.0 golang.org/x/net v0.41.0
golang.org/x/sys v0.35.0 golang.org/x/sys v0.33.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
google.golang.org/grpc v1.73.0 google.golang.org/grpc v1.72.0
google.golang.org/protobuf v1.36.6 google.golang.org/protobuf v1.36.6
howett.net/plist v1.0.1 howett.net/plist v1.0.1
) )
@@ -80,7 +81,7 @@ require (
github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/pool v0.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.3 // indirect github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
@@ -94,7 +95,7 @@ require (
github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
github.com/libdns/libdns v1.1.0 // indirect github.com/libdns/libdns v1.0.0-beta.1 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/sdnotify v1.0.0 // indirect github.com/mdlayher/sdnotify v1.0.0 // indirect
@@ -106,7 +107,7 @@ require (
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/safchain/ethtool v0.3.0 // indirect github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/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-mod.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
@@ -122,14 +123,14 @@ require (
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
golang.org/x/sync v0.16.0 // indirect golang.org/x/sync v0.15.0 // indirect
golang.org/x/term v0.34.0 // indirect golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.28.0 // indirect golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.9.0 // indirect golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.36.0 // indirect golang.org/x/tools v0.33.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
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
) )

129
go.sum
View File

@@ -20,8 +20,10 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -45,8 +47,8 @@ github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.2/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-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84= github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 h1:KbX3Z3CgiYlbaavUq3Cj9/MjpO+88S7/AGXzynVDv84=
@@ -72,8 +74,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
@@ -105,13 +107,12 @@ github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
github.com/libdns/alidns v1.0.5-libdns.v1.beta1 h1:txHK7UxDed3WFBDjrTZPuMn8X+WmhjBTTAMW5xdy5pQ= github.com/libdns/alidns v1.0.4-libdns.v1.beta1 h1:ods22gD4PcT0g4qRX77ucykjz7Rppnkz3vQoxDbbKTM=
github.com/libdns/alidns v1.0.5-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g= github.com/libdns/alidns v1.0.4-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g=
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 h1:3MGrVWs2COjMkQR17oUw1zMIPbm2YAzxDC3oGVZvQs8= github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6 h1:0dlpPjNr8TaYZbkpwCiee4udBNrYrWG8EZPYEbjHEn8=
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60= github.com/libdns/cloudflare v0.2.2-0.20250430151523-b46a2b0885f6/go.mod h1:Aq4IXdjalB6mD0ELvKqJiIGim8zSC6mlIshRPMOAb5w=
github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/libdns v1.1.0 h1:9ze/tWvt7Df6sbhOJRB8jT33GHEHpEQXdtkE3hPthbU=
github.com/libdns/libdns v1.1.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
@@ -122,14 +123,14 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/utls v1.8.0 h1:mSYi6FMnmc5riARl5UZDmWVy710z+P5b7xuGW0lV9ac= github.com/metacubex/utls v1.7.0-alpha.3 h1:cp1cEMUnoifiWrGHRzo+nCwPRveN9yPD8QaRFmfcYxA=
github.com/metacubex/utls v1.8.0/go.mod h1:FdjYzVfCtgtna19hX0ER1Xsa5uJInwdQ4IcaaI98lEQ= github.com/metacubex/utls v1.7.0-alpha.3/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0= github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
@@ -156,37 +157,37 @@ github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
github.com/sagernet/gomobile v0.1.8 h1:vXgoN0pjsMONAaYCTdsKBX2T1kxuS7sbT/mZ7PElGoo= github.com/sagernet/gomobile v0.1.7 h1:I9jCJZTH0weP5MsuydvYHX5QfN/r6Fe8ptAIj1+SJVg=
github.com/sagernet/gomobile v0.1.8/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY= github.com/sagernet/gomobile v0.1.7/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38= github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38=
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4= github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-mod.1 h1:OMe+qoEAx8EipYAQbD2FI5erVvKmTS9+cYhdpg+vezY=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/nftables v0.3.0-mod.1/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs= github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.7.6-0.20250825114712-2aeec120ce28 h1:C8Lnqd0Q+C15kwaMiDsfq5S45rhhaQMBG91TT+6oFVo= github.com/sagernet/sing v0.6.12-0.20250704043954-da981379f151 h1:UCiQ1d/t5Y9uKAL9ir3i06+ClqS93OGGG8oqB82RMCE=
github.com/sagernet/sing v0.7.6-0.20250825114712-2aeec120ce28/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing v0.6.12-0.20250704043954-da981379f151/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw= github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA= github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.5.0 h1:jNLIyVk24lFPvu8A4x+ZNEnZdI+Tg1rp7eCJ6v0Csak= github.com/sagernet/sing-quic v0.5.0-beta.2 h1:j7KAbBuGmsKwSxVAQL5soJ+wDqxim4/llK2kxB0hSKk=
github.com/sagernet/sing-quic v0.5.0/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0= github.com/sagernet/sing-quic v0.5.0-beta.2/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE= github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI= github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo= github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-tun v0.7.0-beta.1 h1:mBIFXYAnGO5ey/HcCYanqnBx61E7yF8zTFGRZonGYmY= github.com/sagernet/sing-tun v0.6.10-0.20250707094843-b2e2674d73e5 h1:JHa9vyTie1FbWGofPt4TEpysl7tBeEoiQDtwVK0Scqg=
github.com/sagernet/sing-tun v0.7.0-beta.1/go.mod h1:AHJuRrLbNRJuivuFZ2VhXwDj4ViYp14szG5EkkKAqRQ= github.com/sagernet/sing-tun v0.6.10-0.20250707094843-b2e2674d73e5/go.mod h1:c/7Blmaw8GRL4JPvoajBfwUfdzoa2KCMtAnq5Q9AjA0=
github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk= github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88 h1:0pVm8sPOel+BoiCddW3pV3cKDKEaSioVTYDdTSKjyFI=
github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs= github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4= github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc= github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1 h1:gMC0q+0VvZBotZMZ9G0R8ZMEIT/Q6KnXbw0/OgMjmdk= github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= github.com/sagernet/tailscale v1.80.3-mod.5/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI=
github.com/sagernet/wireguard-go v0.0.1-beta.7 h1:ltgBwYHfr+9Wz1eG59NiWnHrYEkDKHG7otNZvu85DXI= 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.7/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=
@@ -239,16 +240,16 @@ 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.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
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/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=
@@ -262,21 +263,21 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wus
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -285,20 +286,20 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -308,10 +309,10 @@ golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdI
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

View File

@@ -100,6 +100,7 @@ func rewriteRcodeAction(rcodeMap map[string]int, ruleAction *DNSRuleAction) {
} }
ruleAction.Action = C.RuleActionTypePredefined ruleAction.Action = C.RuleActionTypePredefined
ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode)) ruleAction.PredefinedOptions.Rcode = common.Ptr(DNSRCode(rcode))
return
} }
type DNSClientOptions struct { type DNSClientOptions struct {
@@ -179,7 +180,7 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
options := o.Options.(*LegacyDNSServerOptions) options := o.Options.(*LegacyDNSServerOptions)
serverURL, _ := url.Parse(options.Address) serverURL, _ := url.Parse(options.Address)
var serverType string var serverType string
if serverURL != nil && serverURL.Scheme != "" { if serverURL.Scheme != "" {
serverType = serverURL.Scheme serverType = serverURL.Scheme
} else { } else {
switch options.Address { switch options.Address {
@@ -216,7 +217,7 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
o.Type = C.DNSTypeUDP o.Type = C.DNSTypeUDP
o.Options = &remoteOptions o.Options = &remoteOptions
var serverAddr M.Socksaddr var serverAddr M.Socksaddr
if serverURL == nil || serverURL.Scheme == "" { if serverURL.Scheme == "" {
serverAddr = M.ParseSocksaddr(options.Address) serverAddr = M.ParseSocksaddr(options.Address)
} else { } else {
serverAddr = M.ParseSocksaddr(serverURL.Host) serverAddr = M.ParseSocksaddr(serverURL.Host)
@@ -231,9 +232,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
case C.DNSTypeTCP: case C.DNSTypeTCP:
o.Type = C.DNSTypeTCP o.Type = C.DNSTypeTCP
o.Options = &remoteOptions o.Options = &remoteOptions
if serverURL == nil {
return E.New("invalid server address")
}
serverAddr := M.ParseSocksaddr(serverURL.Host) serverAddr := M.ParseSocksaddr(serverURL.Host)
if !serverAddr.IsValid() { if !serverAddr.IsValid() {
return E.New("invalid server address") return E.New("invalid server address")
@@ -244,9 +242,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
} }
case C.DNSTypeTLS, C.DNSTypeQUIC: case C.DNSTypeTLS, C.DNSTypeQUIC:
o.Type = serverType o.Type = serverType
if serverURL == nil {
return E.New("invalid server address")
}
serverAddr := M.ParseSocksaddr(serverURL.Host) serverAddr := M.ParseSocksaddr(serverURL.Host)
if !serverAddr.IsValid() { if !serverAddr.IsValid() {
return E.New("invalid server address") return E.New("invalid server address")
@@ -266,9 +261,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
}, },
} }
o.Options = &httpsOptions o.Options = &httpsOptions
if serverURL == nil {
return E.New("invalid server address")
}
serverAddr := M.ParseSocksaddr(serverURL.Host) serverAddr := M.ParseSocksaddr(serverURL.Host)
if !serverAddr.IsValid() { if !serverAddr.IsValid() {
return E.New("invalid server address") return E.New("invalid server address")
@@ -282,9 +274,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
} }
case "rcode": case "rcode":
var rcode int var rcode int
if serverURL == nil {
return E.New("invalid server address")
}
switch serverURL.Host { switch serverURL.Host {
case "success": case "success":
rcode = dns.RcodeSuccess rcode = dns.RcodeSuccess
@@ -306,9 +295,6 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
case C.DNSTypeDHCP: case C.DNSTypeDHCP:
o.Type = C.DNSTypeDHCP o.Type = C.DNSTypeDHCP
dhcpOptions := DHCPDNSServerOptions{} dhcpOptions := DHCPDNSServerOptions{}
if serverURL == nil {
return E.New("invalid server address")
}
if serverURL.Host != "" && serverURL.Host != "auto" { if serverURL.Host != "" && serverURL.Host != "auto" {
dhcpOptions.Interface = serverURL.Host dhcpOptions.Interface = serverURL.Host
} }

View File

@@ -10,7 +10,7 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common/atomic"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -37,7 +37,7 @@ type Selector struct {
tags []string tags []string
defaultTag string defaultTag string
outbounds map[string]adapter.Outbound outbounds map[string]adapter.Outbound
selected common.TypedValue[adapter.Outbound] selected atomic.TypedValue[adapter.Outbound]
interruptGroup *interrupt.Group interruptGroup *interrupt.Group
interruptExternalConnections bool interruptExternalConnections bool
} }

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"net" "net"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -15,6 +14,7 @@ import (
"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" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/batch" "github.com/sagernet/sing/common/batch"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -192,7 +192,7 @@ type URLTestGroup struct {
ticker *time.Ticker ticker *time.Ticker
close chan struct{} close chan struct{}
started bool started bool
lastActive common.TypedValue[time.Time] lastActive atomic.TypedValue[time.Time]
} }
func NewURLTestGroup(ctx context.Context, outboundManager adapter.OutboundManager, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16, idleTimeout time.Duration, interruptExternalConnections bool) (*URLTestGroup, error) { func NewURLTestGroup(ctx context.Context, outboundManager adapter.OutboundManager, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16, idleTimeout time.Duration, interruptExternalConnections bool) (*URLTestGroup, error) {
@@ -313,7 +313,7 @@ func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
} }
func (g *URLTestGroup) loopCheck() { func (g *URLTestGroup) loopCheck() {
if time.Since(g.lastActive.Load()) > g.interval { if time.Now().Sub(g.lastActive.Load()) > g.interval {
g.lastActive.Store(time.Now()) g.lastActive.Store(time.Now())
g.CheckOutbounds(false) g.CheckOutbounds(false)
} }
@@ -323,7 +323,7 @@ func (g *URLTestGroup) loopCheck() {
return return
case <-g.ticker.C: case <-g.ticker.C:
} }
if time.Since(g.lastActive.Load()) > g.idleTimeout { if time.Now().Sub(g.lastActive.Load()) > g.idleTimeout {
g.access.Lock() g.access.Lock()
g.ticker.Stop() g.ticker.Stop()
g.ticker = nil g.ticker = nil
@@ -360,7 +360,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
continue continue
} }
history := g.history.LoadURLTestHistory(realTag) history := g.history.LoadURLTestHistory(realTag)
if !force && history != nil && time.Since(history.Time) < g.interval { if !force && history != nil && time.Now().Sub(history.Time) < g.interval {
continue continue
} }
checked[realTag] = true checked[realTag] = true

View File

@@ -128,6 +128,7 @@ func (h *Outbound) InterfaceUpdated() {
if h.multiplexDialer != nil { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {

View File

@@ -180,6 +180,7 @@ func (s *Outbound) connect() (*ssh.Client, error) {
func (s *Outbound) InterfaceUpdated() { func (s *Outbound) InterfaceUpdated() {
common.Close(s.clientConn) common.Close(s.clientConn)
return
} }
func (s *Outbound) Close() error { func (s *Outbound) Close() error {

View File

@@ -105,6 +105,7 @@ func (h *Outbound) InterfaceUpdated() {
if h.multiplexDialer != nil { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {

View File

@@ -132,16 +132,12 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
platformInterface := service.FromContext[platform.Interface](ctx) platformInterface := service.FromContext[platform.Interface](ctx)
tunMTU := options.MTU tunMTU := options.MTU
enableGSO := C.IsLinux && options.Stack == "gvisor" && platformInterface == nil && tunMTU > 0 && tunMTU < 49152
if tunMTU == 0 { if tunMTU == 0 {
if platformInterface != nil && platformInterface.UnderNetworkExtension() { if platformInterface != nil && platformInterface.UnderNetworkExtension() {
// In Network Extension, when MTU exceeds 4064 (4096-UTUN_IF_HEADROOM_SIZE), the performance of tun will drop significantly, which may be a system bug. // In Network Extension, when MTU exceeds 4064 (4096-UTUN_IF_HEADROOM_SIZE), the performance of tun will drop significantly, which may be a system bug.
tunMTU = 4064 tunMTU = 4064
} else if C.IsAndroid {
// Some Android devices report ENOBUFS when using MTU 65535
tunMTU = 9000
} else { } else {
tunMTU = 65535 tunMTU = 9000
} }
} }
var udpTimeout time.Duration var udpTimeout time.Duration
@@ -183,7 +179,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
outputMark = tun.DefaultAutoRedirectOutputMark outputMark = tun.DefaultAutoRedirectOutputMark
} }
networkManager := service.FromContext[adapter.NetworkManager](ctx) networkManager := service.FromContext[adapter.NetworkManager](ctx)
multiPendingPackets := C.IsDarwin && ((options.Stack == "gvisor" && tunMTU < 32768) || (options.Stack != "gvisor" && options.MTU <= 9000))
inbound := &Inbound{ inbound := &Inbound{
tag: tag, tag: tag,
ctx: ctx, ctx: ctx,
@@ -194,7 +189,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
tunOptions: tun.Options{ tunOptions: tun.Options{
Name: options.InterfaceName, Name: options.InterfaceName,
MTU: tunMTU, MTU: tunMTU,
GSO: enableGSO,
Inet4Address: inet4Address, Inet4Address: inet4Address,
Inet6Address: inet6Address, Inet6Address: inet6Address,
AutoRoute: options.AutoRoute, AutoRoute: options.AutoRoute,
@@ -217,7 +211,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
IncludePackage: options.IncludePackage, IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage, ExcludePackage: options.ExcludePackage,
InterfaceMonitor: networkManager.InterfaceMonitor(), InterfaceMonitor: networkManager.InterfaceMonitor(),
EXP_MultiPendingPackets: multiPendingPackets,
}, },
udpTimeout: udpTimeout, udpTimeout: udpTimeout,
stack: options.Stack, stack: options.Stack,

View File

@@ -15,11 +15,11 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-box/transport/v2ray"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing-vmess/vless" "github.com/sagernet/sing-vmess/vless"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
@@ -189,7 +189,7 @@ func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn,
} }
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress { if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
metadata.Destination = M.Socksaddr{} metadata.Destination = M.Socksaddr{}
conn = packetaddr.NewConn(bufio.NewNetPacketConn(conn), metadata.Destination) conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection") h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
} else { } else {
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)

View File

@@ -124,6 +124,7 @@ func (h *Outbound) InterfaceUpdated() {
if h.multiplexDialer != nil { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {

View File

@@ -19,7 +19,6 @@ import (
"github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
@@ -204,7 +203,7 @@ func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn,
} }
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress { if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
metadata.Destination = M.Socksaddr{} metadata.Destination = M.Socksaddr{}
conn = packetaddr.NewConn(bufio.NewNetPacketConn(conn), metadata.Destination) conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection") h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
} else { } else {
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)

View File

@@ -108,6 +108,7 @@ func (h *Outbound) InterfaceUpdated() {
if h.multiplexDialer != nil { if h.multiplexDialer != nil {
h.multiplexDialer.Reset() h.multiplexDialer.Reset()
} }
return
} }
func (h *Outbound) Close() error { func (h *Outbound) Close() error {

View File

@@ -1,7 +1,9 @@
polkit.addRule(function(action, subject) { polkit.addRule(function(action, subject) {
if ((action.id == "org.freedesktop.resolve1.set-domains" || if ((action.id == "org.freedesktop.resolve1.set-domains" ||
action.id == "org.freedesktop.resolve1.set-default-route" || action.id == "org.freedesktop.resolve1.set-default-route" ||
action.id == "org.freedesktop.resolve1.set-dns-servers") && action.id == "org.freedesktop.resolve1.set-dns-servers" ||
action.id == "org.fedoraproject.FirewallD1.all" ||
action.id == "org.fedoraproject.FirewallD1.config") &&
subject.user == "sing-box") { subject.user == "sing-box") {
return polkit.Result.YES; return polkit.Result.YES;
} }

View File

@@ -277,7 +277,7 @@ func (m *ConnectionManager) connectionCopy(ctx context.Context, source net.Conn,
return return
} }
} }
_, err := bufio.CopyWithCounters(destinationWriter, sourceReader, source, readCounters, writeCounters, bufio.DefaultIncreaseBufferAfter, bufio.DefaultBatchSize) _, err := bufio.CopyWithCounters(destinationWriter, sourceReader, source, readCounters, writeCounters, bufio.DefaultIncreaseBufferAfter)
if err != nil { if err != nil {
common.Close(source, destination) common.Close(source, destination)
} else if duplexDst, isDuplex := destination.(N.WriteCloser); isDuplex { } else if duplexDst, isDuplex := destination.(N.WriteCloser); isDuplex {

View File

@@ -19,6 +19,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
@@ -36,7 +37,7 @@ var _ adapter.NetworkManager = (*NetworkManager)(nil)
type NetworkManager struct { type NetworkManager struct {
logger logger.ContextLogger logger logger.ContextLogger
interfaceFinder *control.DefaultInterfaceFinder interfaceFinder *control.DefaultInterfaceFinder
networkInterfaces common.TypedValue[[]adapter.NetworkInterface] networkInterfaces atomic.TypedValue[[]adapter.NetworkInterface]
autoDetectInterface bool autoDetectInterface bool
defaultOptions adapter.NetworkOptions defaultOptions adapter.NetworkOptions
@@ -311,7 +312,7 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
if r.interfaceMonitor == nil { if r.interfaceMonitor == nil {
return nil return nil
} }
return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) { bindFunc := control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
remoteAddr := M.ParseSocksaddr(address).Addr remoteAddr := M.ParseSocksaddr(address).Addr
if remoteAddr.IsValid() { if remoteAddr.IsValid() {
iif, err := r.interfaceFinder.ByAddr(remoteAddr) iif, err := r.interfaceFinder.ByAddr(remoteAddr)
@@ -325,6 +326,16 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
} }
return defaultInterface.Name, defaultInterface.Index, nil 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
}
} }
} }
@@ -355,15 +366,6 @@ func (r *NetworkManager) AutoRedirectOutputMark() uint32 {
return r.autoRedirectOutputMark return r.autoRedirectOutputMark
} }
func (r *NetworkManager) AutoRedirectOutputMarkFunc() control.Func {
return func(network, address string, conn syscall.RawConn) error {
if r.autoRedirectOutputMark == 0 {
return nil
}
return control.RoutingMark(r.autoRedirectOutputMark)(network, address, conn)
}
}
func (r *NetworkManager) NetworkMonitor() tun.NetworkUpdateMonitor { func (r *NetworkManager) NetworkMonitor() tun.NetworkUpdateMonitor {
return r.networkMonitor return r.networkMonitor
} }

View File

@@ -6,7 +6,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"github.com/sagernet/fswatch" "github.com/sagernet/fswatch"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -14,6 +13,7 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"

View File

@@ -10,7 +10,6 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -18,6 +17,7 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json" "github.com/sagernet/sing/common/json"

View File

@@ -182,9 +182,9 @@ func (t *resolve1Manager) logRequest(sender dbus.Sender, message ...any) context
} else if metadata.ProcessInfo.UserId != 0 { } else if metadata.ProcessInfo.UserId != 0 {
prefix = F.ToString("uid:", metadata.ProcessInfo.UserId) prefix = F.ToString("uid:", metadata.ProcessInfo.UserId)
} }
t.logger.InfoContext(ctx, "(", prefix, ") ", strings.Join(F.MapToString(message), " ")) t.logger.InfoContext(ctx, "(", prefix, ") ", F.ToString(message...))
} else { } else {
t.logger.InfoContext(ctx, strings.Join(F.MapToString(message), " ")) t.logger.InfoContext(ctx, F.ToString(message...))
} }
return adapter.WithContext(ctx, &metadata) return adapter.WithContext(ctx, &metadata)
} }
@@ -280,10 +280,7 @@ func (t *resolve1Manager) ResolveAddress(sender dbus.Sender, ifIndex int32, fami
}, },
} }
ctx := t.logRequest(sender, "ResolveAddress ", link.iif.Name, familyToString(family), addr, flags) ctx := t.logRequest(sender, "ResolveAddress ", link.iif.Name, familyToString(family), addr, flags)
var metadata adapter.InboundContext response, lookupErr := t.dnsRouter.Exchange(ctx, request, adapter.DNSQueryOptions{})
metadata.InboundType = t.Type()
metadata.Inbound = t.Tag()
response, lookupErr := t.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), request, adapter.DNSQueryOptions{})
if lookupErr != nil { if lookupErr != nil {
err = wrapError(err) err = wrapError(err)
return return
@@ -304,7 +301,7 @@ func (t *resolve1Manager) ResolveAddress(sender dbus.Sender, ifIndex int32, fami
return return
} }
func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, hostname string, qClass uint16, qType uint16, flags uint64) (records []ResourceRecord, outflags uint64, err *dbus.Error) { func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, family int32, hostname string, qClass uint16, qType uint16, flags uint64) (records []ResourceRecord, outflags uint64, err *dbus.Error) {
t.linkAccess.Lock() t.linkAccess.Lock()
link, err := t.getLink(ifIndex) link, err := t.getLink(ifIndex)
if err != nil { if err != nil {
@@ -323,11 +320,8 @@ func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, hostn
}, },
}, },
} }
ctx := t.logRequest(sender, "ResolveRecord", link.iif.Name, hostname, mDNS.Class(qClass), mDNS.Type(qType), flags) ctx := t.logRequest(sender, "ResolveRecord ", link.iif.Name, familyToString(family), hostname, mDNS.Class(qClass), mDNS.Type(qType), flags)
var metadata adapter.InboundContext response, exchangeErr := t.dnsRouter.Exchange(ctx, request, adapter.DNSQueryOptions{})
metadata.InboundType = t.Type()
metadata.Inbound = t.Tag()
response, exchangeErr := t.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), request, adapter.DNSQueryOptions{})
if exchangeErr != nil { if exchangeErr != nil {
err = wrapError(exchangeErr) err = wrapError(exchangeErr)
return return
@@ -347,7 +341,6 @@ func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, hostn
err = wrapError(unpackErr) err = wrapError(unpackErr)
} }
record.Data = data record.Data = data
records = append(records, record)
} }
return return
} }
@@ -387,10 +380,8 @@ func (t *resolve1Manager) ResolveService(sender dbus.Sender, ifIndex int32, host
}, },
}, },
} }
var metadata adapter.InboundContext
metadata.InboundType = t.Type() srvResponse, exchangeErr := t.dnsRouter.Exchange(ctx, srvRequest, adapter.DNSQueryOptions{})
metadata.Inbound = t.Tag()
srvResponse, exchangeErr := t.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), srvRequest, adapter.DNSQueryOptions{})
if exchangeErr != nil { if exchangeErr != nil {
err = wrapError(exchangeErr) err = wrapError(exchangeErr)
return return

View File

@@ -91,6 +91,11 @@ func (i *Service) Start(stage adapter.StartStage) error {
return E.New("multiple resolved service are not supported") return E.New("multiple resolved service are not supported")
} }
} }
case adapter.StartStateStart:
err := i.listener.Start()
if err != nil {
return err
}
systemBus, err := dbus.SystemBus() systemBus, err := dbus.SystemBus()
if err != nil { if err != nil {
return err return err
@@ -112,11 +117,6 @@ func (i *Service) Start(stage adapter.StartStage) error {
return E.New("unknown request name reply: ", reply) return E.New("unknown request name reply: ", reply)
} }
i.networkUpdateCallback = i.network.NetworkMonitor().RegisterCallback(i.onNetworkUpdate) i.networkUpdateCallback = i.network.NetworkMonitor().RegisterCallback(i.onNetworkUpdate)
case adapter.StartStateStart:
err := i.listener.Start()
if err != nil {
return err
}
} }
return nil return nil
} }
@@ -167,8 +167,6 @@ func (i *Service) exchangePacket0(ctx context.Context, buffer *buf.Buffer, oob [
} }
var metadata adapter.InboundContext var metadata adapter.InboundContext
metadata.Source = source metadata.Source = source
metadata.InboundType = i.Type()
metadata.Inbound = i.Tag()
response, err := i.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), &message, adapter.DNSQueryOptions{}) response, err := i.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), &message, adapter.DNSQueryOptions{})
if err != nil { if err != nil {
return err return err

View File

@@ -156,14 +156,18 @@ func (s *APIServer) deleteUser(writer http.ResponseWriter, request *http.Request
} }
func (s *APIServer) getStats(writer http.ResponseWriter, request *http.Request) { func (s *APIServer) getStats(writer http.ResponseWriter, request *http.Request) {
requireClear := request.URL.Query().Get("clear") == "true" requireClear := chi.URLParam(request, "clear") == "true"
users := s.user.List() users := s.user.List()
s.traffic.ReadUsers(users, requireClear) s.traffic.ReadUsers(users)
for i := range users { for i := range users {
users[i].Password = "" users[i].Password = ""
} }
uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal(requireClear) uplinkBytes, downlinkBytes, uplinkPackets, downlinkPackets, tcpSessions, udpSessions := s.traffic.ReadGlobal()
if requireClear {
s.traffic.Clear()
}
render.JSON(writer, request, render.M{ render.JSON(writer, request, render.M{
"uplinkBytes": uplinkBytes, "uplinkBytes": uplinkBytes,

View File

@@ -5,8 +5,8 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"sync/atomic"
"github.com/sagernet/sing/common/atomic"
"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"
"github.com/sagernet/sing/service/filemanager" "github.com/sagernet/sing/service/filemanager"

View File

@@ -3,9 +3,9 @@ package ssmapi
import ( import (
"net" "net"
"sync" "sync"
"sync/atomic"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
@@ -50,24 +50,12 @@ func (s *TrafficManager) UpdateUsers(users []string) {
newUserTCPSessions := make(map[string]*atomic.Int64) newUserTCPSessions := make(map[string]*atomic.Int64)
newUserUDPSessions := make(map[string]*atomic.Int64) newUserUDPSessions := make(map[string]*atomic.Int64)
for _, user := range users { for _, user := range users {
if counter, loaded := s.userUplink[user]; loaded { newUserUplink[user] = s.userUplinkPackets[user]
newUserUplink[user] = counter newUserDownlink[user] = s.userDownlinkPackets[user]
} newUserUplinkPackets[user] = s.userUplinkPackets[user]
if counter, loaded := s.userDownlink[user]; loaded { newUserDownlinkPackets[user] = s.userDownlinkPackets[user]
newUserDownlink[user] = counter newUserTCPSessions[user] = s.userTCPSessions[user]
} newUserUDPSessions[user] = s.userUDPSessions[user]
if counter, loaded := s.userUplinkPackets[user]; loaded {
newUserUplinkPackets[user] = counter
}
if counter, loaded := s.userDownlinkPackets[user]; loaded {
newUserDownlinkPackets[user] = counter
}
if counter, loaded := s.userTCPSessions[user]; loaded {
newUserTCPSessions[user] = counter
}
if counter, loaded := s.userUDPSessions[user]; loaded {
newUserUDPSessions[user] = counter
}
} }
s.userUplink = newUserUplink s.userUplink = newUserUplink
s.userDownlink = newUserDownlink s.userDownlink = newUserDownlink
@@ -142,82 +130,86 @@ func (s *TrafficManager) TrackPacketConnection(conn N.PacketConn, metadata adapt
readPacketCounter = append(readPacketCounter, upPacketsCounter) readPacketCounter = append(readPacketCounter, upPacketsCounter)
writePacketCounter = append(writePacketCounter, downPacketsCounter) writePacketCounter = append(writePacketCounter, downPacketsCounter)
udpSessionCounter.Add(1) udpSessionCounter.Add(1)
return bufio.NewInt64CounterPacketConn(conn, readCounter, readPacketCounter, writeCounter, writePacketCounter) return bufio.NewInt64CounterPacketConn(conn, append(readCounter, readPacketCounter...), append(writeCounter, writePacketCounter...))
} }
func (s *TrafficManager) ReadUser(user *UserObject) { func (s *TrafficManager) ReadUser(user *UserObject) {
s.userAccess.Lock() s.userAccess.Lock()
defer s.userAccess.Unlock() defer s.userAccess.Unlock()
s.readUser(user, false) s.readUser(user)
} }
func (s *TrafficManager) readUser(user *UserObject, swap bool) { func (s *TrafficManager) readUser(user *UserObject) {
if counter, loaded := s.userUplink[user.UserName]; loaded { if counter, loaded := s.userUplink[user.UserName]; loaded {
if swap { user.UplinkBytes = counter.Load()
user.UplinkBytes = counter.Swap(0)
} else {
user.UplinkBytes = counter.Load()
}
} }
if counter, loaded := s.userDownlink[user.UserName]; loaded { if counter, loaded := s.userDownlink[user.UserName]; loaded {
if swap { user.DownlinkBytes = counter.Load()
user.DownlinkBytes = counter.Swap(0)
} else {
user.DownlinkBytes = counter.Load()
}
} }
if counter, loaded := s.userUplinkPackets[user.UserName]; loaded { if counter, loaded := s.userUplinkPackets[user.UserName]; loaded {
if swap { user.UplinkPackets = counter.Load()
user.UplinkPackets = counter.Swap(0)
} else {
user.UplinkPackets = counter.Load()
}
} }
if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded { if counter, loaded := s.userDownlinkPackets[user.UserName]; loaded {
if swap { user.DownlinkPackets = counter.Load()
user.DownlinkPackets = counter.Swap(0)
} else {
user.DownlinkPackets = counter.Load()
}
} }
if counter, loaded := s.userTCPSessions[user.UserName]; loaded { if counter, loaded := s.userTCPSessions[user.UserName]; loaded {
if swap { user.TCPSessions = counter.Load()
user.TCPSessions = counter.Swap(0)
} else {
user.TCPSessions = counter.Load()
}
} }
if counter, loaded := s.userUDPSessions[user.UserName]; loaded { if counter, loaded := s.userUDPSessions[user.UserName]; loaded {
if swap { user.UDPSessions = counter.Load()
user.UDPSessions = counter.Swap(0)
} else {
user.UDPSessions = counter.Load()
}
} }
} }
func (s *TrafficManager) ReadUsers(users []*UserObject, swap bool) { func (s *TrafficManager) ReadUsers(users []*UserObject) {
s.userAccess.Lock() s.userAccess.Lock()
defer s.userAccess.Unlock() defer s.userAccess.Unlock()
for _, user := range users { for _, user := range users {
s.readUser(user, swap) s.readUser(user)
} }
return
} }
func (s *TrafficManager) ReadGlobal(swap bool) (uplinkBytes int64, downlinkBytes int64, uplinkPackets int64, downlinkPackets int64, tcpSessions int64, udpSessions int64) { func (s *TrafficManager) ReadGlobal() (
if swap { uplinkBytes int64,
return s.globalUplink.Swap(0), downlinkBytes int64,
s.globalDownlink.Swap(0), uplinkPackets int64,
s.globalUplinkPackets.Swap(0), downlinkPackets int64,
s.globalDownlinkPackets.Swap(0), tcpSessions int64,
s.globalTCPSessions.Swap(0), udpSessions int64,
s.globalUDPSessions.Swap(0) ) {
} else { return s.globalUplink.Load(),
return s.globalUplink.Load(), s.globalDownlink.Load(),
s.globalDownlink.Load(), s.globalUplinkPackets.Load(),
s.globalUplinkPackets.Load(), s.globalDownlinkPackets.Load(),
s.globalDownlinkPackets.Load(), s.globalTCPSessions.Load(),
s.globalTCPSessions.Load(), s.globalUDPSessions.Load()
s.globalUDPSessions.Load() }
func (s *TrafficManager) Clear() {
s.globalUplink.Store(0)
s.globalDownlink.Store(0)
s.globalUplinkPackets.Store(0)
s.globalDownlinkPackets.Store(0)
s.globalTCPSessions.Store(0)
s.globalUDPSessions.Store(0)
s.userAccess.Lock()
defer s.userAccess.Unlock()
for _, counter := range s.userUplink {
counter.Store(0)
}
for _, counter := range s.userDownlink {
counter.Store(0)
}
for _, counter := range s.userUplinkPackets {
counter.Store(0)
}
for _, counter := range s.userDownlinkPackets {
counter.Store(0)
}
for _, counter := range s.userTCPSessions {
counter.Store(0)
}
for _, counter := range s.userUDPSessions {
counter.Store(0)
} }
} }

View File

@@ -26,7 +26,7 @@ func defaultRouteIP() (netip.Addr, error) {
for _, addr := range addrs { for _, addr := range addrs {
ip := addr.(*net.IPNet).IP ip := addr.(*net.IPNet).IP
if ip.To4() != nil { if ip.To4() != nil {
return netip.AddrFrom4([4]byte(ip)), nil return netip.AddrFrom4(*(*[4]byte)(ip)), nil
} }
} }

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"net" "net"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -30,7 +29,7 @@ type Client struct {
serverAddr string serverAddr string
serviceName string serviceName string
dialOptions []grpc.DialOption dialOptions []grpc.DialOption
conn atomic.Pointer[grpc.ClientConn] conn *grpc.ClientConn
connAccess sync.Mutex connAccess sync.Mutex
} }
@@ -75,13 +74,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
} }
func (c *Client) connect() (*grpc.ClientConn, error) { func (c *Client) connect() (*grpc.ClientConn, error) {
conn := c.conn.Load() conn := c.conn
if conn != nil && conn.GetState() != connectivity.Shutdown { if conn != nil && conn.GetState() != connectivity.Shutdown {
return conn, nil return conn, nil
} }
c.connAccess.Lock() c.connAccess.Lock()
defer c.connAccess.Unlock() defer c.connAccess.Unlock()
conn = c.conn.Load() conn = c.conn
if conn != nil && conn.GetState() != connectivity.Shutdown { if conn != nil && conn.GetState() != connectivity.Shutdown {
return conn, nil return conn, nil
} }
@@ -90,7 +89,7 @@ func (c *Client) connect() (*grpc.ClientConn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.conn.Store(conn) c.conn = conn
return conn, nil return conn, nil
} }
@@ -110,9 +109,11 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
} }
func (c *Client) Close() error { func (c *Client) Close() error {
conn := c.conn.Swap(nil) c.connAccess.Lock()
if conn != nil { defer c.connAccess.Unlock()
conn.Close() if c.conn != nil {
c.conn.Close()
c.conn = nil
} }
return nil return nil
} }

View File

@@ -29,7 +29,7 @@ type Client struct {
tlsConfig tls.Config tlsConfig tls.Config
quicConfig *quic.Config quicConfig *quic.Config
connAccess sync.Mutex connAccess sync.Mutex
conn common.TypedValue[quic.Connection] conn quic.Connection
rawConn net.Conn rawConn net.Conn
} }
@@ -50,13 +50,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
} }
func (c *Client) offer() (quic.Connection, error) { func (c *Client) offer() (quic.Connection, error) {
conn := c.conn.Load() conn := c.conn
if conn != nil && !common.Done(conn.Context()) { if conn != nil && !common.Done(conn.Context()) {
return conn, nil return conn, nil
} }
c.connAccess.Lock() c.connAccess.Lock()
defer c.connAccess.Unlock() defer c.connAccess.Unlock()
conn = c.conn.Load() conn = c.conn
if conn != nil && !common.Done(conn.Context()) { if conn != nil && !common.Done(conn.Context()) {
return conn, nil return conn, nil
} }
@@ -72,13 +72,14 @@ func (c *Client) offerNew() (quic.Connection, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
packetConn := bufio.NewUnbindPacketConn(udpConn) var packetConn net.PacketConn
packetConn = bufio.NewUnbindPacketConn(udpConn)
quicConn, err := qtls.Dial(c.ctx, packetConn, udpConn.RemoteAddr(), c.tlsConfig, c.quicConfig) quicConn, err := qtls.Dial(c.ctx, packetConn, udpConn.RemoteAddr(), c.tlsConfig, c.quicConfig)
if err != nil { if err != nil {
packetConn.Close() packetConn.Close()
return nil, err return nil, err
} }
c.conn.Store(quicConn) c.conn = quicConn
c.rawConn = udpConn c.rawConn = udpConn
return quicConn, nil return quicConn, nil
} }
@@ -98,13 +99,13 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
func (c *Client) Close() error { func (c *Client) Close() error {
c.connAccess.Lock() c.connAccess.Lock()
defer c.connAccess.Unlock() defer c.connAccess.Unlock()
conn := c.conn.Swap(nil) if c.conn != nil {
if conn != nil { c.conn.CloseWithError(0, "")
conn.CloseWithError(0, "")
} }
if c.rawConn != nil { if c.rawConn != nil {
c.rawConn.Close() c.rawConn.Close()
} }
c.conn = nil
c.rawConn = nil c.rawConn = nil
return nil return nil
} }

View File

@@ -8,7 +8,6 @@ import (
"net" "net"
"os" "os"
"sync" "sync"
"sync/atomic"
"time" "time"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
@@ -136,22 +135,20 @@ func (c *WebsocketConn) Upstream() any {
type EarlyWebsocketConn struct { type EarlyWebsocketConn struct {
*Client *Client
ctx context.Context ctx context.Context
conn atomic.Pointer[WebsocketConn] conn *WebsocketConn
access sync.Mutex access sync.Mutex
create chan struct{} create chan struct{}
err error err error
} }
func (c *EarlyWebsocketConn) Read(b []byte) (n int, err error) { func (c *EarlyWebsocketConn) Read(b []byte) (n int, err error) {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
<-c.create <-c.create
if c.err != nil { if c.err != nil {
return 0, c.err return 0, c.err
} }
conn = c.conn.Load()
} }
return wrapWsError0(conn.Read(b)) return wrapWsError0(c.conn.Read(b))
} }
func (c *EarlyWebsocketConn) writeRequest(content []byte) error { func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
@@ -190,23 +187,21 @@ func (c *EarlyWebsocketConn) writeRequest(content []byte) error {
return err return err
} }
} }
c.conn.Store(conn) c.conn = conn
return nil return nil
} }
func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) { func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
conn := c.conn.Load() if c.conn != nil {
if conn != nil { return wrapWsError0(c.conn.Write(b))
return wrapWsError0(conn.Write(b))
} }
c.access.Lock() c.access.Lock()
defer c.access.Unlock() defer c.access.Unlock()
conn = c.conn.Load()
if c.err != nil { if c.err != nil {
return 0, c.err return 0, c.err
} }
if conn != nil { if c.conn != nil {
return wrapWsError0(conn.Write(b)) return wrapWsError0(c.conn.Write(b))
} }
err = c.writeRequest(b) err = c.writeRequest(b)
c.err = err c.err = err
@@ -218,19 +213,17 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) {
} }
func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error { func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
conn := c.conn.Load() if c.conn != nil {
if conn != nil { return wrapWsError(c.conn.WriteBuffer(buffer))
return wrapWsError(conn.WriteBuffer(buffer))
} }
c.access.Lock() c.access.Lock()
defer c.access.Unlock() defer c.access.Unlock()
if c.conn != nil {
return wrapWsError(c.conn.WriteBuffer(buffer))
}
if c.err != nil { if c.err != nil {
return c.err return c.err
} }
conn = c.conn.Load()
if conn != nil {
return wrapWsError(conn.WriteBuffer(buffer))
}
err := c.writeRequest(buffer.Bytes()) err := c.writeRequest(buffer.Bytes())
c.err = err c.err = err
close(c.create) close(c.create)
@@ -238,27 +231,24 @@ func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error {
} }
func (c *EarlyWebsocketConn) Close() error { func (c *EarlyWebsocketConn) Close() error {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return nil return nil
} }
return conn.Close() return c.conn.Close()
} }
func (c *EarlyWebsocketConn) LocalAddr() net.Addr { func (c *EarlyWebsocketConn) LocalAddr() net.Addr {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return M.Socksaddr{} return M.Socksaddr{}
} }
return conn.LocalAddr() return c.conn.LocalAddr()
} }
func (c *EarlyWebsocketConn) RemoteAddr() net.Addr { func (c *EarlyWebsocketConn) RemoteAddr() net.Addr {
conn := c.conn.Load() if c.conn == nil {
if conn == nil {
return M.Socksaddr{} return M.Socksaddr{}
} }
return conn.RemoteAddr() return c.conn.RemoteAddr()
} }
func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error { func (c *EarlyWebsocketConn) SetDeadline(t time.Time) error {
@@ -278,11 +268,11 @@ func (c *EarlyWebsocketConn) NeedAdditionalReadDeadline() bool {
} }
func (c *EarlyWebsocketConn) Upstream() any { func (c *EarlyWebsocketConn) Upstream() any {
return common.PtrOrNil(c.conn.Load()) return common.PtrOrNil(c.conn)
} }
func (c *EarlyWebsocketConn) LazyHeadroom() bool { func (c *EarlyWebsocketConn) LazyHeadroom() bool {
return c.conn.Load() == nil return c.conn == nil
} }
func wrapWsError(err error) error { func wrapWsError(err error) error {

View File

@@ -63,7 +63,7 @@ func (w *Writer) WriteBuffer(buffer *buf.Buffer) error {
if !w.isServer { if !w.isServer {
maskKey := rand.Uint32() maskKey := rand.Uint32()
binary.BigEndian.PutUint32(header[1+payloadBitLength:], maskKey) binary.BigEndian.PutUint32(header[1+payloadBitLength:], maskKey)
ws.Cipher(data, [4]byte(header[1+payloadBitLength:]), 0) ws.Cipher(data, *(*[4]byte)(header[1+payloadBitLength:]), 0)
} }
return wrapWsError(w.writer.WriteBuffer(buffer)) return wrapWsError(w.writer.WriteBuffer(buffer))