Compare commits

..

108 Commits

Author SHA1 Message Date
世界
275d102587 documentation: Bump version 2026-01-09 22:52:17 +08:00
世界
d553a761a9 Add dial option bind_address_no_port 2026-01-09 22:52:12 +08:00
世界
642719b3b6 platform: Display k based bytes 2026-01-09 22:52:05 +08:00
世界
c325c4ab51 Fix tailscale endpoint 2026-01-09 22:52:05 +08:00
世界
5842fde994 documentation: Bump version 2026-01-08 23:29:31 +08:00
世界
f45291fa79 tailscale: Add system interface support 2026-01-08 23:29:31 +08:00
世界
7f56a3ae8f platform: Fix gomobile build 2026-01-08 23:29:31 +08:00
世界
d5bb1a30dd platform: Update apple build comamnds 2026-01-08 23:29:31 +08:00
世界
4fbf0c4bd6 platform: Improve interface 2026-01-08 23:29:31 +08:00
世界
762b567693 documentation: Bump version 2026-01-07 15:16:51 +08:00
世界
1a511ae711 Downgrade quic-go to v0.57.1 2026-01-07 15:16:51 +08:00
世界
dcf5fde029 Fix linux musl builds 2026-01-07 15:16:51 +08:00
世界
04949914f1 Fix openwrt builds 2026-01-07 15:16:51 +08:00
世界
fbc0fe43ad Add kmod-nft-queue dependency for openwrt package 2026-01-07 15:16:51 +08:00
世界
857319ef3d Fix nfqueue fallback 2026-01-07 15:16:51 +08:00
世界
b48ddee8b1 Disable multipath TCP by default via GODEBUG 2026-01-07 15:16:51 +08:00
世界
8e3d70d197 Fix missing relay support for Tailscale 2026-01-07 15:16:51 +08:00
世界
21468cda93 cronet: Fix windows DNS hijack 2026-01-07 15:16:51 +08:00
世界
2ff67c9ece Fix DNS transports 2026-01-07 15:16:51 +08:00
世界
6ff279ba49 platform: Expose process info 2026-01-07 15:16:51 +08:00
世界
5d15a241e3 Update bypass action behavior for auto redirect 2026-01-07 15:16:51 +08:00
世界
e01a5c96a5 platform: Add GetStartedAt for StartedService 2026-01-07 15:16:51 +08:00
世界
9d40074cfa documentation: Format changes header 2026-01-07 15:16:51 +08:00
世界
4796bce0c2 Add format_docs command for documentation trailing space formatting 2026-01-07 15:16:51 +08:00
世界
fe632ef3ff Fix panic when closing Box before Start with file log output 2026-01-07 15:16:51 +08:00
世界
b15fecf992 Add pre-match support for auto redirect 2026-01-07 15:16:51 +08:00
世界
774269a2bf Fix cronet on iOS 2026-01-07 15:16:51 +08:00
世界
c995273244 Ignore darwin IP_DONTFRAG error when not supported 2026-01-07 15:15:29 +08:00
世界
a80208e7b1 Update tailscale to v1.92.4 2026-01-07 15:15:29 +08:00
世界
60580df446 Update cronet-go to v143.0.7499.109-1 2026-01-07 15:15:29 +08:00
世界
d28305be5f platform: Split library for Android SDK 21 and 23 2026-01-07 15:15:29 +08:00
世界
886a45a204 Fix missing RootPoolFromContext and TimeFuncFromContext in HTTP clients 2026-01-07 15:15:29 +08:00
世界
401d195944 documentation: Minor fixes 2026-01-07 15:15:29 +08:00
世界
d7489c0653 documentation: Add Wi-Fi state shared page 2026-01-07 15:15:29 +08:00
世界
1d6d829dee Fix missing build constraints for linux wifi state monitor 2026-01-07 15:15:28 +08:00
世界
69555fc6ea Update dependencies 2026-01-07 15:15:28 +08:00
世界
655e534ea8 Update quic-go to v0.58.0 2026-01-07 15:15:28 +08:00
世界
5d86f63906 Add Chrome Root Store certificate option
Adds `chrome` as a new certificate store option alongside `mozilla`.
Both stores filter out China-based CA certificates.
2026-01-07 15:15:28 +08:00
世界
2162dab4a1 Fix cronet-go crash 2026-01-07 15:15:28 +08:00
世界
51fd4b7e1a Add trace logging for lifecycle calls
Log start/close operations with timing information for debugging.
2026-01-07 15:15:28 +08:00
世界
ac51a74dbe documentation: Minor fixes 2026-01-07 15:15:28 +08:00
世界
d8a15ecddf Remove certificate_public_key_sha256 for naive 2026-01-07 15:15:28 +08:00
世界
dda91d50bd platform: Use new crash log api 2026-01-07 15:15:28 +08:00
世界
b3aec2997b Fix naive network 2026-01-07 15:15:27 +08:00
世界
f3ae84442a Add QUIC support for naiveproxy 2026-01-07 15:15:27 +08:00
世界
8d373fb004 Add ECH support for NaiveProxy outbound and tls.ech.query_server_name option
- Enable ECH for NaiveProxy outbound with DNS resolver integration
- Add query_server_name option to override domain for ECH HTTPS record queries
- Update cronet-go dependency and remove windows_386 support
2026-01-07 15:15:27 +08:00
世界
db3c3a6621 Fix naiveproxy build 2026-01-07 15:15:27 +08:00
世界
eaaaddb61e Add OpenAI Codex Multiplexer service 2026-01-07 15:15:27 +08:00
世界
e97784d05b Update pricing for CCM service 2026-01-07 15:15:27 +08:00
世界
7a0d211224 release: Upload only other apks 2026-01-07 15:15:26 +08:00
世界
5264802927 Fix bugs and add UoT option for naiveproxy outbound 2026-01-07 15:15:26 +08:00
世界
69896f8b80 Add naiveproxy outbound 2026-01-07 15:15:26 +08:00
世界
0e3552da6f Apply ping destination filter for Windows 2026-01-07 15:15:26 +08:00
世界
0ed29288a0 platform: Add UsePlatformWIFIMonitor to gRPC interface
Align dev-next-grpc with wip2 by adding UsePlatformWIFIMonitor()
to the new PlatformInterface, allowing platform clients to indicate
they handle WIFI monitoring themselves.
2026-01-07 15:15:26 +08:00
世界
774fed206b daemon: Add clear logs 2026-01-07 15:15:26 +08:00
世界
71e1eb1e7f Revert "Stop using DHCP on iOS and tvOS" 2026-01-07 15:15:25 +08:00
世界
5112488d39 platform: Refactoring libbox to use gRPC-based protocol 2026-01-07 15:15:25 +08:00
世界
f228bd5b49 Add Windows WI-FI state support 2026-01-07 15:15:25 +08:00
世界
6424476ace Add Linux WI-FI state support
Support monitoring WIFI state on Linux through:
- NetworkManager (D-Bus)
- IWD (D-Bus)
- wpa_supplicant (control socket)
- ConnMan (D-Bus)
2026-01-07 15:15:24 +08:00
世界
51dc7c4f88 Add more tcp keep alive options
Also update default TCP keep-alive initial period from 10 minutes to 5 minutes.
2026-01-07 15:15:24 +08:00
世界
92c072d849 Update quic-go to v0.57.1 2026-01-07 15:15:24 +08:00
世界
ac7f0c9a3d Fix read credentials for ccm service 2026-01-07 15:15:24 +08:00
世界
31dcdc4f14 Add claude code multiplexer service 2026-01-07 15:15:23 +08:00
世界
3a267e1557 Fix compatibility with MPTCP 2026-01-07 15:15:23 +08:00
世界
73c5575866 Use a more conservative strategy for resolving with systemd-resolved for local DNS server 2026-01-07 15:15:23 +08:00
世界
f00854dd72 Fix missing mTLS support in client options 2026-01-07 15:15:23 +08:00
世界
c04d1414dd Add curve preferences, pinned public key SHA256 and mTLS for TLS options 2026-01-07 15:15:23 +08:00
世界
ec4e411fa7 Fix WireGuard input packet 2026-01-07 15:15:23 +08:00
世界
cc3a4c7dec Update tfo-go to latest 2026-01-07 15:15:22 +08:00
世界
20b0e4c3a0 Remove compatibility codes 2026-01-07 15:15:22 +08:00
世界
5aabc42823 Do not use linkname by default to simplify debugging 2026-01-07 15:15:22 +08:00
世界
f0771fb623 documentation: Update chinese translations 2026-01-07 15:15:22 +08:00
世界
67c79ebac8 Update quic-go to v0.55.0 2026-01-07 15:15:22 +08:00
世界
ef185fed09 Update WireGuard and Tailscale 2026-01-07 15:15:22 +08:00
世界
f8cdc40d62 Fix preConnectionCopy 2026-01-07 15:15:22 +08:00
世界
3bcf072750 Fix ping domain 2026-01-07 15:15:22 +08:00
世界
3ef15fa8aa release: Fix linux build 2026-01-07 15:15:21 +08:00
世界
159418324a Improve ktls rx error handling 2026-01-07 15:15:21 +08:00
世界
0c30128c44 Improve compatibility for kTLS 2026-01-07 15:15:21 +08:00
世界
354330cd80 ktls: Add warning for inappropriate scenarios 2026-01-07 15:15:21 +08:00
世界
b991ae1c91 Add support for kTLS
Reference: https://gitlab.com/go-extension/tls
2026-01-07 15:15:21 +08:00
世界
53fef79ab3 Add proxy support for ICMP echo request 2026-01-07 15:15:21 +08:00
世界
dfe2e083d5 Fix resolve using resolved 2026-01-07 15:15:21 +08:00
世界
ce408b2989 documentation: Update behavior of local DNS server on darwin 2026-01-07 15:15:21 +08:00
世界
395a1aadfa Remove use of ldflags -checklinkname=0 on darwin 2026-01-07 15:15:20 +08:00
世界
905573e386 Fix legacy DNS config 2026-01-07 15:15:20 +08:00
世界
5814753603 Fix rule-set format 2026-01-07 15:15:20 +08:00
世界
8cef967847 documentation: Remove outdated icons 2026-01-07 15:15:20 +08:00
世界
af532b8d05 documentation: Improve local DNS server 2026-01-07 15:15:20 +08:00
世界
5585855367 Stop using DHCP on iOS and tvOS
We do not have the `com.apple.developer.networking.multicast` entitlement and are unable to obtain it for non-technical reasons.
2026-01-07 15:15:20 +08:00
世界
2344b35b0f Improve local DNS server on darwin
We mistakenly believed that `libresolv`'s `search` function worked correctly in NetworkExtension, but it seems only `getaddrinfo` does.

This commit changes the behavior of the `local` DNS server in NetworkExtension to prefer DHCP, falling back to `getaddrinfo` if DHCP servers are unavailable.

It's worth noting that `prefer_go` does not disable DHCP since it respects Dial Fields, but `getaddrinfo` does the opposite. The new behavior only applies to NetworkExtension, not to all scenarios (primarily command-line binaries) as it did previously.

In addition, this commit also improves the DHCP DNS server to use the same robust query logic as `local`.
2026-01-07 15:15:20 +08:00
世界
3a9ae0f91f Use resolved in local DNS server if available 2026-01-07 15:15:20 +08:00
xchacha20-poly1305
4fdbf786e1 Fix rule set version 2026-01-07 15:15:19 +08:00
世界
1e3ea29602 documentation: Add preferred_by route rule item 2026-01-07 15:15:19 +08:00
世界
ac5cb50368 Add preferred_by route rule item 2026-01-07 15:15:19 +08:00
世界
722f9ee971 documentation: Add interface address rule items 2026-01-07 15:15:19 +08:00
世界
3df72d04e9 Add interface address rule items 2026-01-07 15:15:19 +08:00
世界
4f93a515d6 Fix ECH retry support 2026-01-07 15:15:19 +08:00
neletor
a3c9ceca60 Add support for ech retry configs 2026-01-07 15:15:19 +08:00
Zephyruso
a57bdc82e8 Add /dns/flush-clash meta api 2026-01-07 15:15:18 +08:00
世界
0a812f2a46 Bump version 2026-01-07 15:13:35 +08:00
Gavin Luo
fffe9fc566 Fix reset buffer in dhcp response loop
Previously, the buffer was not reset within the response loop. If a packet
handle failed or completed, the buffer retained its state. Specifically,
if `ReadPacketFrom` returned `io.ErrShortBuffer`, the error was ignored
via `continue`, but the buffer remained full. This caused the next
read attempt to immediately fail with the same error, creating a tight
busy-wait loop that consumed 100% CPU.

Validates `buffer.Reset()` is called at the start of each iteration to
ensure a clean state for 'ReadPacketFrom'.
2026-01-05 17:46:59 +08:00
世界
6fdf27a701 Fix Tailscale endpoint using wrong source IP with advertise_routes 2026-01-04 22:14:54 +08:00
Bruce Wayne
7fa7d4f0a9 ducumentation: update Shadowsocks inbound documentation for SSM API 2026-01-02 19:18:52 +08:00
世界
f511ebc1d4 Fix lint errors 2026-01-02 19:17:53 +08:00
世界
84bbdc2eba Revert "Pin gofumpt and golangci-lint versions"
This reverts commit d9d7f7880d.
2026-01-02 19:14:13 +08:00
世界
568612fc70 Fix duplicate tag detection for empty tags
Closes https://github.com/SagerNet/sing-box/issues/3665
2026-01-02 19:14:13 +08:00
世界
d78828fd81 Fix quic sniffer 2026-01-02 19:14:13 +08:00
45 changed files with 1165 additions and 1135 deletions

View File

@@ -14,6 +14,7 @@
--depends kmod-inet-diag
--depends kmod-tun
--depends firewall4
--depends kmod-nft-queue
--before-remove release/config/openwrt.prerm

View File

@@ -1 +1 @@
1cc61ad20399081362ccbc18d650432d1a6d42ec
92d4602aba0ab6084673af0fe4887dccbc1049a5

View File

@@ -69,19 +69,19 @@ jobs:
strategy:
matrix:
include:
- { os: linux, arch: amd64, variant: purego, naive: true, openwrt: "x86_64" }
- { os: linux, arch: amd64, variant: purego, naive: true }
- { os: linux, arch: amd64, variant: glibc, naive: true }
- { os: linux, arch: amd64, variant: musl, naive: true, debian: amd64, rpm: x86_64, pacman: x86_64, openwrt: "x86_64" }
- { os: linux, arch: arm64, variant: purego, naive: true, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
- { os: linux, arch: arm64, variant: purego, naive: true }
- { os: linux, arch: arm64, variant: glibc, naive: true }
- { os: linux, arch: arm64, variant: musl, naive: true, debian: arm64, rpm: aarch64, pacman: aarch64, openwrt: "aarch64_cortex-a53 aarch64_cortex-a72 aarch64_cortex-a76 aarch64_generic" }
- { os: linux, arch: "386", go386: sse2, openwrt: "i386_pentium4" }
- { os: linux, arch: "386", go386: sse2 }
- { os: linux, arch: "386", variant: glibc, naive: true, go386: sse2 }
- { os: linux, arch: "386", variant: musl, naive: true, go386: sse2, debian: i386, rpm: i386, openwrt: "i386_pentium4" }
- { os: linux, arch: arm, goarm: "7", openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
- { os: linux, arch: arm, goarm: "7" }
- { os: linux, arch: arm, variant: glibc, naive: true, goarm: "7" }
- { os: linux, arch: arm, variant: musl, naive: true, goarm: "7", debian: armhf, rpm: armv7hl, pacman: armv7hl, openwrt: "arm_cortex-a5_vfpv4 arm_cortex-a7_neon-vfpv4 arm_cortex-a7_vfpv4 arm_cortex-a8_vfpv3 arm_cortex-a9_neon arm_cortex-a9_vfpv3-d16 arm_cortex-a15_neon-vfpv4" }
@@ -206,7 +206,7 @@ jobs:
set -xeuo pipefail
mkdir -p dist
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "0"
@@ -228,7 +228,7 @@ jobs:
set -xeuo pipefail
mkdir -p dist
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "1"
@@ -243,7 +243,7 @@ jobs:
set -xeuo pipefail
mkdir -p dist
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "1"
@@ -258,7 +258,7 @@ jobs:
set -xeuo pipefail
mkdir -p dist
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "0"
@@ -278,7 +278,7 @@ jobs:
export CXX="${CC}++"
mkdir -p dist
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "1"
@@ -369,12 +369,8 @@ jobs:
-p "dist/openwrt.deb" \
--architecture all \
dist/sing-box=/usr/bin/sing-box
SUFFIX=""
if [[ "${{ matrix.variant }}" == "musl" ]]; then
SUFFIX="_musl"
fi
for architecture in ${{ matrix.openwrt }}; do
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}${SUFFIX}.ipk"
.github/deb2ipk.sh "$architecture" "dist/openwrt.deb" "dist/sing-box_${{ needs.calculate_version.outputs.version }}_openwrt_${architecture}.ipk"
done
rm "dist/openwrt.deb"
- name: Archive
@@ -445,7 +441,7 @@ jobs:
set -xeuo pipefail
mkdir -p dist
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "1"
@@ -505,7 +501,7 @@ jobs:
run: |
mkdir -p dist
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_naive_outbound,with_purego,badlinkname,tfogo_checklinkname0" `
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0" `
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0" `
./cmd/sing-box
env:
CGO_ENABLED: "0"
@@ -517,7 +513,7 @@ jobs:
run: |
mkdir -p dist
go build -v -trimpath -o dist/sing-box.exe -tags "with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" `
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0" `
-ldflags "-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0" `
./cmd/sing-box
env:
CGO_ENABLED: "0"

View File

@@ -103,7 +103,7 @@ jobs:
run: |
set -xeuo pipefail
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -s -w -buildid= -checklinkname=0" \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
./cmd/sing-box
env:
CGO_ENABLED: "1"
@@ -115,7 +115,7 @@ jobs:
run: |
set -xeuo pipefail
go build -v -trimpath -o sing-box -tags "${BUILD_TAGS}" \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -s -w -buildid= -checklinkname=0" \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=${VERSION}\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
./cmd/sing-box
env:
CGO_ENABLED: "0"

View File

@@ -32,7 +32,7 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
with:
version: v2.4.0
version: latest
args: --timeout=30m
install-mode: binary
verify: false

View File

@@ -127,7 +127,7 @@ jobs:
set -xeuo pipefail
mkdir -p dist
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "1"
@@ -141,7 +141,7 @@ jobs:
set -xeuo pipefail
mkdir -p dist
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0' \
./cmd/sing-box
env:
CGO_ENABLED: "0"

View File

@@ -15,7 +15,7 @@ RUN set -ex \
&& go build -v -trimpath -tags \
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,badlinkname,tfogo_checklinkname0" \
-o /go/bin/sing-box \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0" \
./cmd/sing-box
FROM --platform=$TARGETPLATFORM alpine AS dist
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"

View File

@@ -6,7 +6,7 @@ GOHOSTOS = $(shell go env GOHOSTOS)
GOHOSTARCH = $(shell go env GOHOSTARCH)
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest)
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid= -checklinkname=0"
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0"
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS)"
MAIN = ./cmd/sing-box
PREFIX ?= $(shell go env GOPATH)
@@ -41,7 +41,7 @@ fmt_docs:
go run ./cmd/internal/format_docs
fmt_install:
go install -v mvdan.cc/gofumpt@v0.8.0
go install -v mvdan.cc/gofumpt@latest
go install -v github.com/daixiang0/gci@latest
lint:
@@ -52,7 +52,7 @@ lint:
GOOS=freebsd golangci-lint run ./...
lint_install:
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
proto:
@go run ./cmd/internal/protogen
@@ -109,7 +109,7 @@ build_ios:
cd ../sing-box-for-apple && \
rm -rf build/SFI.xcarchive && \
xcodebuild clean -scheme SFI && \
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates
xcodebuild archive -scheme SFI -configuration Release -destination 'generic/platform=iOS' -archivePath build/SFI.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
upload_ios_app_store:
cd ../sing-box-for-apple && \
@@ -130,7 +130,7 @@ release_ios: build_ios upload_ios_app_store
build_macos:
cd ../sing-box-for-apple && \
rm -rf build/SFM.xcarchive && \
xcodebuild archive -scheme SFM -configuration Release -archivePath build/SFM.xcarchive -allowProvisioningUpdates
xcodebuild archive -scheme SFM -configuration Release -archivePath build/SFM.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
upload_macos_app_store:
cd ../sing-box-for-apple && \
@@ -139,54 +139,50 @@ upload_macos_app_store:
release_macos: build_macos upload_macos_app_store
build_macos_standalone:
cd ../sing-box-for-apple && \
rm -rf build/SFM.System.xcarchive && \
xcodebuild archive -scheme SFM.System -configuration Release -archivePath build/SFM.System.xcarchive -allowProvisioningUpdates
$(MAKE) -C ../sing-box-for-apple archive_macos_standalone
build_macos_dmg:
rm -rf dist/SFM
mkdir -p dist/SFM
cd ../sing-box-for-apple && \
rm -rf build/SFM.System && \
rm -rf build/SFM.dmg && \
xcodebuild -exportArchive \
-archivePath "build/SFM.System.xcarchive" \
-exportOptionsPlist SFM.System/Export.plist -allowProvisioningUpdates \
-exportPath "build/SFM.System" && \
create-dmg \
--volname "sing-box" \
--volicon "build/SFM.System/SFM.app/Contents/Resources/AppIcon.icns" \
--icon "SFM.app" 0 0 \
--hide-extension "SFM.app" \
--app-drop-link 0 0 \
--skip-jenkins \
"../sing-box/dist/SFM/SFM.dmg" "build/SFM.System/SFM.app"
$(MAKE) -C ../sing-box-for-apple build_macos_dmg
build_macos_pkg:
$(MAKE) -C ../sing-box-for-apple build_macos_pkg
notarize_macos_dmg:
xcrun notarytool submit "dist/SFM/SFM.dmg" --wait \
--keychain-profile "notarytool-password" \
--no-s3-acceleration
$(MAKE) -C ../sing-box-for-apple notarize_macos_dmg
notarize_macos_pkg:
$(MAKE) -C ../sing-box-for-apple notarize_macos_pkg
upload_macos_dmg:
cd dist/SFM && \
cp SFM.dmg "SFM-${VERSION}-universal.dmg" && \
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dmg"
mkdir -p dist/SFM
cp ../sing-box-for-apple/build/SFM-Apple.dmg "dist/SFM/SFM-${VERSION}-Apple.dmg"
cp ../sing-box-for-apple/build/SFM-Intel.dmg "dist/SFM/SFM-${VERSION}-Intel.dmg"
cp ../sing-box-for-apple/build/SFM-Universal.dmg "dist/SFM/SFM-${VERSION}-Universal.dmg"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Apple.dmg"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.dmg"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.dmg"
upload_macos_pkg:
mkdir -p dist/SFM
cp ../sing-box-for-apple/build/SFM-Apple.pkg "dist/SFM/SFM-${VERSION}-Apple.pkg"
cp ../sing-box-for-apple/build/SFM-Intel.pkg "dist/SFM/SFM-${VERSION}-Intel.pkg"
cp ../sing-box-for-apple/build/SFM-Universal.pkg "dist/SFM/SFM-${VERSION}-Universal.pkg"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Apple.pkg"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Intel.pkg"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}-Universal.pkg"
upload_macos_dsyms:
pushd ../sing-box-for-apple/build/SFM.System.xcarchive && \
zip -r SFM.dSYMs.zip dSYMs && \
mv SFM.dSYMs.zip ../../../sing-box/dist/SFM && \
popd && \
cd dist/SFM && \
cp SFM.dSYMs.zip "SFM-${VERSION}-universal.dSYMs.zip" && \
ghr --replace --draft --prerelease "v${VERSION}" "SFM-${VERSION}-universal.dSYMs.zip"
mkdir -p dist/SFM
cd ../sing-box-for-apple/build/SFM.System-universal.xcarchive && zip -r SFM.dSYMs.zip dSYMs
cp ../sing-box-for-apple/build/SFM.System-universal.xcarchive/SFM.dSYMs.zip "dist/SFM/SFM-${VERSION}.dSYMs.zip"
ghr --replace --draft --prerelease "v${VERSION}" "dist/SFM/SFM-${VERSION}.dSYMs.zip"
release_macos_standalone: build_macos_standalone build_macos_dmg notarize_macos_dmg upload_macos_dmg upload_macos_dsyms
release_macos_standalone: build_macos_pkg notarize_macos_pkg upload_macos_pkg upload_macos_dsyms
build_tvos:
cd ../sing-box-for-apple && \
rm -rf build/SFT.xcarchive && \
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive -allowProvisioningUpdates
xcodebuild archive -scheme SFT -configuration Release -archivePath build/SFT.xcarchive -allowProvisioningUpdates | xcbeautify | grep -A 10 -e "Archive Succeeded" -e "ARCHIVE FAILED" -e "❌"
upload_tvos_app_store:
cd ../sing-box-for-apple && \
@@ -252,8 +248,8 @@ lib:
go run ./cmd/internal/build_libbox -target ios
lib_install:
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.10
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.10
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.11
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.11
docs:
venv/bin/mkdocs serve

View File

@@ -25,6 +25,7 @@ type Router interface {
ConnectionRouterEx
RuleSet(tag string) (RuleSet, bool)
Rules() []Rule
NeedFindProcess() bool
AppendTracker(tracker ConnectionTracker)
ResetNetwork()
}

View File

@@ -60,8 +60,8 @@ func init() {
if err != nil {
currentTag = "unknown"
}
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0")
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -X internal/godebug.defaultGODEBUG=multipathtcp=0 -checklinkname=0")
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
darwinTags = append(darwinTags, "with_dhcp")
@@ -194,7 +194,7 @@ func buildApple() {
} else if debugEnabled {
bindTarget = "ios"
} else {
bindTarget = "ios,tvos,macos"
bindTarget = "ios,iossimulator,tvos,tvossimulator,macos"
}
args := []string{

View File

@@ -137,6 +137,12 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath))
listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath))
}
if options.BindAddressNoPort {
if !C.IsLinux {
return nil, E.New("`bind_address_no_port` is only supported on Linux")
}
dialer.Control = control.Append(dialer.Control, control.BindAddressNoPort())
}
if options.ConnectTimeout != 0 {
dialer.Timeout = time.Duration(options.ConnectTimeout)
} else {

View File

@@ -303,8 +303,6 @@ find:
metadata.Protocol = C.ProtocolQUIC
fingerprint, err := ja3.Compute(buffer.Bytes())
if err != nil {
metadata.Protocol = C.ProtocolQUIC
metadata.Client = C.ClientChromium
metadata.SniffContext = fragments
return E.Cause1(ErrNeedMoreData, err)
}
@@ -334,7 +332,7 @@ find:
}
if count(frameTypeList, frameTypeCrypto) > 1 || count(frameTypeList, frameTypePing) > 0 {
if maybeUQUIC(fingerprint) {
if isQUICGo(fingerprint) {
metadata.Client = C.ClientQUICGo
} else {
metadata.Client = C.ClientChromium

View File

@@ -1,21 +1,29 @@
package sniff
import (
"crypto/tls"
"github.com/sagernet/sing-box/common/ja3"
)
// Chromium sends separate client hello packets, but UQUIC has not yet implemented this behavior
// The cronet without this behavior does not have version 115
var uQUICChrome115 = &ja3.ClientHello{
Version: tls.VersionTLS12,
CipherSuites: []uint16{4865, 4866, 4867},
Extensions: []uint16{0, 10, 13, 16, 27, 43, 45, 51, 57, 17513},
EllipticCurves: []uint16{29, 23, 24},
SignatureAlgorithms: []uint16{1027, 2052, 1025, 1283, 2053, 1281, 2054, 1537, 513},
}
const (
// X25519Kyber768Draft00 - post-quantum curve used by Go crypto/tls
x25519Kyber768Draft00 uint16 = 0x11EC // 4588
// renegotiation_info extension used by Go crypto/tls
extensionRenegotiationInfo uint16 = 0xFF01 // 65281
)
func maybeUQUIC(fingerprint *ja3.ClientHello) bool {
return !uQUICChrome115.Equals(fingerprint, true)
// isQUICGo detects native quic-go by checking for Go crypto/tls specific features.
// Note: uQUIC with Chromium mimicry cannot be reliably distinguished from real Chromium
// since it uses the same TLS fingerprint, so it will be identified as Chromium.
func isQUICGo(fingerprint *ja3.ClientHello) bool {
for _, curve := range fingerprint.EllipticCurves {
if curve == x25519Kyber768Draft00 {
return true
}
}
for _, ext := range fingerprint.Extensions {
if ext == extensionRenegotiationInfo {
return true
}
}
return false
}

View File

@@ -0,0 +1,188 @@
package sniff_test
import (
"context"
"crypto/tls"
"encoding/hex"
"errors"
"net"
"testing"
"time"
"github.com/sagernet/quic-go"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/sniff"
"github.com/stretchr/testify/require"
)
func TestSniffQUICQuicGoFingerprint(t *testing.T) {
t.Parallel()
const testSNI = "test.example.com"
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
require.NoError(t, err)
defer udpConn.Close()
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
packetsChan := make(chan [][]byte, 1)
go func() {
var packets [][]byte
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
for i := 0; i < 10; i++ {
buf := make([]byte, 2048)
n, _, err := udpConn.ReadFromUDP(buf)
if err != nil {
break
}
packets = append(packets, buf[:n])
}
packetsChan <- packets
}()
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
require.NoError(t, err)
defer clientConn.Close()
tlsConfig := &tls.Config{
ServerName: testSNI,
InsecureSkipVerify: true,
NextProtos: []string{"h3"},
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
select {
case packets := <-packetsChan:
t.Logf("Captured %d packets", len(packets))
var metadata adapter.InboundContext
for i, pkt := range packets {
err := sniff.QUICClientHello(context.Background(), &metadata, pkt)
t.Logf("Packet %d: err=%v, domain=%s, client=%s", i, err, metadata.Domain, metadata.Client)
if metadata.Domain != "" {
break
}
}
t.Logf("\n=== quic-go TLS Fingerprint Analysis ===")
t.Logf("Domain: %s", metadata.Domain)
t.Logf("Client: %s", metadata.Client)
t.Logf("Protocol: %s", metadata.Protocol)
// The client should be identified as quic-go, not chromium
// Current issue: it's being identified as chromium
if metadata.Client == "chromium" {
t.Log("WARNING: quic-go is being misidentified as chromium!")
}
case <-time.After(5 * time.Second):
t.Fatal("Timeout")
}
}
func TestSniffQUICInitialFromQuicGo(t *testing.T) {
t.Parallel()
const testSNI = "test.example.com"
// Create UDP listener to capture ALL initial packets
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
require.NoError(t, err)
defer udpConn.Close()
serverAddr := udpConn.LocalAddr().(*net.UDPAddr)
// Channel to receive captured packets
packetsChan := make(chan [][]byte, 1)
// Start goroutine to capture packets
go func() {
var packets [][]byte
udpConn.SetReadDeadline(time.Now().Add(3 * time.Second))
for i := 0; i < 5; i++ { // Capture up to 5 packets
buf := make([]byte, 2048)
n, _, err := udpConn.ReadFromUDP(buf)
if err != nil {
break
}
packets = append(packets, buf[:n])
}
packetsChan <- packets
}()
// Create QUIC client connection (will fail but we capture the initial packet)
clientConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
require.NoError(t, err)
defer clientConn.Close()
tlsConfig := &tls.Config{
ServerName: testSNI,
InsecureSkipVerify: true,
NextProtos: []string{"h3"},
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// This will fail (no server) but sends initial packet
_, _ = quic.Dial(ctx, clientConn, serverAddr, tlsConfig, &quic.Config{})
// Wait for captured packets
select {
case packets := <-packetsChan:
t.Logf("Captured %d QUIC packets", len(packets))
for i, packet := range packets {
t.Logf("Packet %d: length=%d, first 30 bytes: %x", i, len(packet), packet[:min(30, len(packet))])
}
// Test sniffer with first packet
if len(packets) > 0 {
var metadata adapter.InboundContext
err := sniff.QUICClientHello(context.Background(), &metadata, packets[0])
t.Logf("First packet sniff error: %v", err)
t.Logf("Protocol: %s", metadata.Protocol)
t.Logf("Domain: %s", metadata.Domain)
t.Logf("Client: %s", metadata.Client)
// If first packet needs more data, try with subsequent packets
// IMPORTANT: reuse metadata to accumulate CRYPTO fragments via SniffContext
if errors.Is(err, sniff.ErrNeedMoreData) && len(packets) > 1 {
t.Log("First packet needs more data, trying subsequent packets with shared context...")
for i := 1; i < len(packets); i++ {
// Reuse same metadata to accumulate fragments
err = sniff.QUICClientHello(context.Background(), &metadata, packets[i])
t.Logf("Packet %d sniff result: err=%v, domain=%s, sniffCtx=%v", i, err, metadata.Domain, metadata.SniffContext != nil)
if metadata.Domain != "" || (err != nil && !errors.Is(err, sniff.ErrNeedMoreData)) {
break
}
}
}
// Print hex dump for debugging
t.Logf("First packet hex:\n%s", hex.Dump(packets[0][:min(256, len(packets[0]))]))
// Log final results
t.Logf("Final: Protocol=%s, Domain=%s, Client=%s", metadata.Protocol, metadata.Domain, metadata.Client)
// Verify SNI extraction
if metadata.Domain == "" {
t.Errorf("Failed to extract SNI, expected: %s", testSNI)
} else {
require.Equal(t, testSNI, metadata.Domain, "SNI should match")
}
// Check client identification - quic-go should be identified as quic-go, not chromium
t.Logf("Client identified as: %s (expected: quic-go)", metadata.Client)
}
case <-time.After(5 * time.Second):
t.Fatal("Timeout waiting for QUIC packets")
}
}

View File

@@ -19,7 +19,7 @@ func TestSniffQUICChromeNew(t *testing.T) {
var metadata adapter.InboundContext
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
require.Equal(t, metadata.Client, C.ClientChromium)
require.Empty(t, metadata.Client)
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
require.NoError(t, err)
@@ -39,7 +39,7 @@ func TestSniffQUICChromium(t *testing.T) {
var metadata adapter.InboundContext
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
require.Equal(t, metadata.Client, C.ClientChromium)
require.Empty(t, metadata.Client)
require.ErrorIs(t, err, sniff.ErrNeedMoreData)
pkt, err = hex.DecodeString("c90000000108f40d654cc09b27f5000044d073eb38807026d4088455e650e7ccf750d01a72f15f9bfc8ff40d223499db1a485cff14dbd45b9be118172834dc35dca3cf62f61a1266f40b92faf3d28d67a466cfdca678ddced15cd606d31959cf441828467857b226d1a241847c82c57312cefe68ba5042d929919bcd4403b39e5699fe87dda05df1b3801e048edee792458e9b1a9b1d4039df05847bcee3be567494b5876e3bd4c3220fe9dfdb2c07d77410f907f744251ef15536cc03b267d3668d5b75bc1ad2fe735cd3bb73519dd9f1625a49e17ad27bdeccf706c83b5ea339a0a05dd0072f4a8f162bd29926b4997f05613c6e4b0270b0c02805ca0543f27c1ff8505a5750bdd33529ee73c491050a10c6903f53c1121dbe0380e84c007c8df74a1b02443ed80ba7766aef5549e618d4fd249844ee28565142005369869299e8c3035ecef3d799f6cada8549e75b4ce4cbf4c85ef071fd7ff067b1ca9b5968dc41d13d011f6d7843823bac97acb1eb8ee45883f0f254b5f9bd4c763b67e2d8c70a7618a0ef0de304cf597a485126e09f8b2fd795b394c0b4bc4cd2634c2057970da2c798c5e8af7aed4f76f5e25d04e3f8c9c5a5b150d17e0d4c74229898c69b8dc7b8bcc9d359eb441de75c68fbdebec62fb669dcccfb1aad03e3fa073adb2ccf7bb14cbaf99e307d2c903ee71a8f028102eb510caee7e7397512086a78d1f95635c7d06845b5a708652dc4e5cd61245aae5b3c05b84815d84d367bce9b9e3f6d6b90701ac3679233c14d5ce2a1eff26469c966266dc6284bdb95c9c6158934c413a872ce22101e4163e3293d236b301592ca4ccacc1fd4c37066e79c2d9857c8a2560dcf0b33b19163c4240c471b19907476e7e25c65f7eb37276594a0f6b4c33c340cc3284178f17ac5e34dbe7509db890e4ddfd0540fbf9deb32a0101d24fe58b26c5f81c627db9d6ae59d7a111a3d5d1f6109f4eec0d0234e6d73c73a44f50999462724b51ce0fd8283535d70d9e83872c79c59897407a0736741011ae5c64862eb0712f9e7b07aa1d5418ca3fde8626257c6fe418f3c5479055bb2b0ab4c25f649923fc2a41c79aaa7d0f3af6d8b8cf06f61f0230d09bbb60bb49b9e49cc5973748a6cf7ffdee7804d424f9423c63e7ff22f4bd24e4867636ef9fe8dd37f59941a8a47c27765caa8e875a30b62834f17c569227e5e6ed15d58e05d36e76332befad065a2cd4079e66d5af189b0337624c89b1560c3b1b0befd5c1f20e6de8e3d664b3ac06b3d154b488983e14aa93266f5f8b621d2a9bb7ccce509eb26e025c9c45f7cccc09ce85b3103af0c93ce9822f82ecb168ca3177829afb2ea0da2c380e7b1728add55a5d42632e2290363d4cbe432b67e13691648e1acfab22cf0d551eee857709b428bb78e27a45aff6eca301c02e4d13cf36cc2494fdd1aef8dede6e18febd79dca4c6964d09b91c25a08f0947c76ab5104de9404459c2edf5f4adb9dfd771be83656f77fbbafb1ad3281717066010be8778952495383c9f2cf0a38527228c662a35171c5981731f1af09bab842fe6c3162ad4152a4221f560eb6f9bea66b294ffbd3643da2fe34096da13c246505452540177a2a0a1a69106e5cfc279a4890fc3be2952f26be245f930e6c2d9e7e26ee960481e72b99594a1185b46b94b6436d00ba6c70ffe135d43907c92c6f1c09fb9453f103730714f5700fa4347f9715c774cb04a7218dacc66d9c2fade18b14e684aa7fc9ebda0a28")
require.NoError(t, err)

View File

@@ -1,702 +0,0 @@
package daemon
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type SubscribeHelperRequestRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
AcceptGetWIFIStateRequests bool `protobuf:"varint,1,opt,name=acceptGetWIFIStateRequests,proto3" json:"acceptGetWIFIStateRequests,omitempty"`
AcceptFindConnectionOwnerRequests bool `protobuf:"varint,2,opt,name=acceptFindConnectionOwnerRequests,proto3" json:"acceptFindConnectionOwnerRequests,omitempty"`
AcceptSendNotificationRequests bool `protobuf:"varint,3,opt,name=acceptSendNotificationRequests,proto3" json:"acceptSendNotificationRequests,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SubscribeHelperRequestRequest) Reset() {
*x = SubscribeHelperRequestRequest{}
mi := &file_daemon_helper_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SubscribeHelperRequestRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SubscribeHelperRequestRequest) ProtoMessage() {}
func (x *SubscribeHelperRequestRequest) ProtoReflect() protoreflect.Message {
mi := &file_daemon_helper_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubscribeHelperRequestRequest.ProtoReflect.Descriptor instead.
func (*SubscribeHelperRequestRequest) Descriptor() ([]byte, []int) {
return file_daemon_helper_proto_rawDescGZIP(), []int{0}
}
func (x *SubscribeHelperRequestRequest) GetAcceptGetWIFIStateRequests() bool {
if x != nil {
return x.AcceptGetWIFIStateRequests
}
return false
}
func (x *SubscribeHelperRequestRequest) GetAcceptFindConnectionOwnerRequests() bool {
if x != nil {
return x.AcceptFindConnectionOwnerRequests
}
return false
}
func (x *SubscribeHelperRequestRequest) GetAcceptSendNotificationRequests() bool {
if x != nil {
return x.AcceptSendNotificationRequests
}
return false
}
type HelperRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
// Types that are valid to be assigned to Request:
//
// *HelperRequest_GetWIFIState
// *HelperRequest_FindConnectionOwner
// *HelperRequest_SendNotification
Request isHelperRequest_Request `protobuf_oneof:"request"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HelperRequest) Reset() {
*x = HelperRequest{}
mi := &file_daemon_helper_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HelperRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelperRequest) ProtoMessage() {}
func (x *HelperRequest) ProtoReflect() protoreflect.Message {
mi := &file_daemon_helper_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HelperRequest.ProtoReflect.Descriptor instead.
func (*HelperRequest) Descriptor() ([]byte, []int) {
return file_daemon_helper_proto_rawDescGZIP(), []int{1}
}
func (x *HelperRequest) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *HelperRequest) GetRequest() isHelperRequest_Request {
if x != nil {
return x.Request
}
return nil
}
func (x *HelperRequest) GetGetWIFIState() *emptypb.Empty {
if x != nil {
if x, ok := x.Request.(*HelperRequest_GetWIFIState); ok {
return x.GetWIFIState
}
}
return nil
}
func (x *HelperRequest) GetFindConnectionOwner() *FindConnectionOwnerRequest {
if x != nil {
if x, ok := x.Request.(*HelperRequest_FindConnectionOwner); ok {
return x.FindConnectionOwner
}
}
return nil
}
func (x *HelperRequest) GetSendNotification() *Notification {
if x != nil {
if x, ok := x.Request.(*HelperRequest_SendNotification); ok {
return x.SendNotification
}
}
return nil
}
type isHelperRequest_Request interface {
isHelperRequest_Request()
}
type HelperRequest_GetWIFIState struct {
GetWIFIState *emptypb.Empty `protobuf:"bytes,2,opt,name=getWIFIState,proto3,oneof"`
}
type HelperRequest_FindConnectionOwner struct {
FindConnectionOwner *FindConnectionOwnerRequest `protobuf:"bytes,3,opt,name=findConnectionOwner,proto3,oneof"`
}
type HelperRequest_SendNotification struct {
SendNotification *Notification `protobuf:"bytes,4,opt,name=sendNotification,proto3,oneof"`
}
func (*HelperRequest_GetWIFIState) isHelperRequest_Request() {}
func (*HelperRequest_FindConnectionOwner) isHelperRequest_Request() {}
func (*HelperRequest_SendNotification) isHelperRequest_Request() {}
type FindConnectionOwnerRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
IpProtocol int32 `protobuf:"varint,1,opt,name=ipProtocol,proto3" json:"ipProtocol,omitempty"`
SourceAddress string `protobuf:"bytes,2,opt,name=sourceAddress,proto3" json:"sourceAddress,omitempty"`
SourcePort int32 `protobuf:"varint,3,opt,name=sourcePort,proto3" json:"sourcePort,omitempty"`
DestinationAddress string `protobuf:"bytes,4,opt,name=destinationAddress,proto3" json:"destinationAddress,omitempty"`
DestinationPort int32 `protobuf:"varint,5,opt,name=destinationPort,proto3" json:"destinationPort,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *FindConnectionOwnerRequest) Reset() {
*x = FindConnectionOwnerRequest{}
mi := &file_daemon_helper_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *FindConnectionOwnerRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*FindConnectionOwnerRequest) ProtoMessage() {}
func (x *FindConnectionOwnerRequest) ProtoReflect() protoreflect.Message {
mi := &file_daemon_helper_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use FindConnectionOwnerRequest.ProtoReflect.Descriptor instead.
func (*FindConnectionOwnerRequest) Descriptor() ([]byte, []int) {
return file_daemon_helper_proto_rawDescGZIP(), []int{2}
}
func (x *FindConnectionOwnerRequest) GetIpProtocol() int32 {
if x != nil {
return x.IpProtocol
}
return 0
}
func (x *FindConnectionOwnerRequest) GetSourceAddress() string {
if x != nil {
return x.SourceAddress
}
return ""
}
func (x *FindConnectionOwnerRequest) GetSourcePort() int32 {
if x != nil {
return x.SourcePort
}
return 0
}
func (x *FindConnectionOwnerRequest) GetDestinationAddress() string {
if x != nil {
return x.DestinationAddress
}
return ""
}
func (x *FindConnectionOwnerRequest) GetDestinationPort() int32 {
if x != nil {
return x.DestinationPort
}
return 0
}
type Notification struct {
state protoimpl.MessageState `protogen:"open.v1"`
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
TypeName string `protobuf:"bytes,2,opt,name=typeName,proto3" json:"typeName,omitempty"`
TypeId int32 `protobuf:"varint,3,opt,name=typeId,proto3" json:"typeId,omitempty"`
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
Subtitle string `protobuf:"bytes,5,opt,name=subtitle,proto3" json:"subtitle,omitempty"`
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
OpenURL string `protobuf:"bytes,7,opt,name=openURL,proto3" json:"openURL,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Notification) Reset() {
*x = Notification{}
mi := &file_daemon_helper_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Notification) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Notification) ProtoMessage() {}
func (x *Notification) ProtoReflect() protoreflect.Message {
mi := &file_daemon_helper_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
func (*Notification) Descriptor() ([]byte, []int) {
return file_daemon_helper_proto_rawDescGZIP(), []int{3}
}
func (x *Notification) GetIdentifier() string {
if x != nil {
return x.Identifier
}
return ""
}
func (x *Notification) GetTypeName() string {
if x != nil {
return x.TypeName
}
return ""
}
func (x *Notification) GetTypeId() int32 {
if x != nil {
return x.TypeId
}
return 0
}
func (x *Notification) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *Notification) GetSubtitle() string {
if x != nil {
return x.Subtitle
}
return ""
}
func (x *Notification) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
func (x *Notification) GetOpenURL() string {
if x != nil {
return x.OpenURL
}
return ""
}
type HelperResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
// Types that are valid to be assigned to Response:
//
// *HelperResponse_WifiState
// *HelperResponse_Error
// *HelperResponse_ConnectionOwner
Response isHelperResponse_Response `protobuf_oneof:"response"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HelperResponse) Reset() {
*x = HelperResponse{}
mi := &file_daemon_helper_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HelperResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelperResponse) ProtoMessage() {}
func (x *HelperResponse) ProtoReflect() protoreflect.Message {
mi := &file_daemon_helper_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HelperResponse.ProtoReflect.Descriptor instead.
func (*HelperResponse) Descriptor() ([]byte, []int) {
return file_daemon_helper_proto_rawDescGZIP(), []int{4}
}
func (x *HelperResponse) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *HelperResponse) GetResponse() isHelperResponse_Response {
if x != nil {
return x.Response
}
return nil
}
func (x *HelperResponse) GetWifiState() *WIFIState {
if x != nil {
if x, ok := x.Response.(*HelperResponse_WifiState); ok {
return x.WifiState
}
}
return nil
}
func (x *HelperResponse) GetError() string {
if x != nil {
if x, ok := x.Response.(*HelperResponse_Error); ok {
return x.Error
}
}
return ""
}
func (x *HelperResponse) GetConnectionOwner() *ConnectionOwner {
if x != nil {
if x, ok := x.Response.(*HelperResponse_ConnectionOwner); ok {
return x.ConnectionOwner
}
}
return nil
}
type isHelperResponse_Response interface {
isHelperResponse_Response()
}
type HelperResponse_WifiState struct {
WifiState *WIFIState `protobuf:"bytes,2,opt,name=wifiState,proto3,oneof"`
}
type HelperResponse_Error struct {
Error string `protobuf:"bytes,3,opt,name=error,proto3,oneof"`
}
type HelperResponse_ConnectionOwner struct {
ConnectionOwner *ConnectionOwner `protobuf:"bytes,4,opt,name=connectionOwner,proto3,oneof"`
}
func (*HelperResponse_WifiState) isHelperResponse_Response() {}
func (*HelperResponse_Error) isHelperResponse_Response() {}
func (*HelperResponse_ConnectionOwner) isHelperResponse_Response() {}
type ConnectionOwner struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"`
UserName string `protobuf:"bytes,2,opt,name=userName,proto3" json:"userName,omitempty"`
ProcessPath string `protobuf:"bytes,3,opt,name=processPath,proto3" json:"processPath,omitempty"`
AndroidPackageName string `protobuf:"bytes,4,opt,name=androidPackageName,proto3" json:"androidPackageName,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ConnectionOwner) Reset() {
*x = ConnectionOwner{}
mi := &file_daemon_helper_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ConnectionOwner) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ConnectionOwner) ProtoMessage() {}
func (x *ConnectionOwner) ProtoReflect() protoreflect.Message {
mi := &file_daemon_helper_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ConnectionOwner.ProtoReflect.Descriptor instead.
func (*ConnectionOwner) Descriptor() ([]byte, []int) {
return file_daemon_helper_proto_rawDescGZIP(), []int{5}
}
func (x *ConnectionOwner) GetUserId() int32 {
if x != nil {
return x.UserId
}
return 0
}
func (x *ConnectionOwner) GetUserName() string {
if x != nil {
return x.UserName
}
return ""
}
func (x *ConnectionOwner) GetProcessPath() string {
if x != nil {
return x.ProcessPath
}
return ""
}
func (x *ConnectionOwner) GetAndroidPackageName() string {
if x != nil {
return x.AndroidPackageName
}
return ""
}
type WIFIState struct {
state protoimpl.MessageState `protogen:"open.v1"`
Ssid string `protobuf:"bytes,1,opt,name=ssid,proto3" json:"ssid,omitempty"`
Bssid string `protobuf:"bytes,2,opt,name=bssid,proto3" json:"bssid,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WIFIState) Reset() {
*x = WIFIState{}
mi := &file_daemon_helper_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WIFIState) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WIFIState) ProtoMessage() {}
func (x *WIFIState) ProtoReflect() protoreflect.Message {
mi := &file_daemon_helper_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WIFIState.ProtoReflect.Descriptor instead.
func (*WIFIState) Descriptor() ([]byte, []int) {
return file_daemon_helper_proto_rawDescGZIP(), []int{6}
}
func (x *WIFIState) GetSsid() string {
if x != nil {
return x.Ssid
}
return ""
}
func (x *WIFIState) GetBssid() string {
if x != nil {
return x.Bssid
}
return ""
}
var File_daemon_helper_proto protoreflect.FileDescriptor
const file_daemon_helper_proto_rawDesc = "" +
"\n" +
"\x13daemon/helper.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xf5\x01\n" +
"\x1dSubscribeHelperRequestRequest\x12>\n" +
"\x1aacceptGetWIFIStateRequests\x18\x01 \x01(\bR\x1aacceptGetWIFIStateRequests\x12L\n" +
"!acceptFindConnectionOwnerRequests\x18\x02 \x01(\bR!acceptFindConnectionOwnerRequests\x12F\n" +
"\x1eacceptSendNotificationRequests\x18\x03 \x01(\bR\x1eacceptSendNotificationRequests\"\x84\x02\n" +
"\rHelperRequest\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12<\n" +
"\fgetWIFIState\x18\x02 \x01(\v2\x16.google.protobuf.EmptyH\x00R\fgetWIFIState\x12V\n" +
"\x13findConnectionOwner\x18\x03 \x01(\v2\".daemon.FindConnectionOwnerRequestH\x00R\x13findConnectionOwner\x12B\n" +
"\x10sendNotification\x18\x04 \x01(\v2\x14.daemon.NotificationH\x00R\x10sendNotificationB\t\n" +
"\arequest\"\xdc\x01\n" +
"\x1aFindConnectionOwnerRequest\x12\x1e\n" +
"\n" +
"ipProtocol\x18\x01 \x01(\x05R\n" +
"ipProtocol\x12$\n" +
"\rsourceAddress\x18\x02 \x01(\tR\rsourceAddress\x12\x1e\n" +
"\n" +
"sourcePort\x18\x03 \x01(\x05R\n" +
"sourcePort\x12.\n" +
"\x12destinationAddress\x18\x04 \x01(\tR\x12destinationAddress\x12(\n" +
"\x0fdestinationPort\x18\x05 \x01(\x05R\x0fdestinationPort\"\xc2\x01\n" +
"\fNotification\x12\x1e\n" +
"\n" +
"identifier\x18\x01 \x01(\tR\n" +
"identifier\x12\x1a\n" +
"\btypeName\x18\x02 \x01(\tR\btypeName\x12\x16\n" +
"\x06typeId\x18\x03 \x01(\x05R\x06typeId\x12\x14\n" +
"\x05title\x18\x04 \x01(\tR\x05title\x12\x1a\n" +
"\bsubtitle\x18\x05 \x01(\tR\bsubtitle\x12\x12\n" +
"\x04body\x18\x06 \x01(\tR\x04body\x12\x18\n" +
"\aopenURL\x18\a \x01(\tR\aopenURL\"\xbc\x01\n" +
"\x0eHelperResponse\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x121\n" +
"\twifiState\x18\x02 \x01(\v2\x11.daemon.WIFIStateH\x00R\twifiState\x12\x16\n" +
"\x05error\x18\x03 \x01(\tH\x00R\x05error\x12C\n" +
"\x0fconnectionOwner\x18\x04 \x01(\v2\x17.daemon.ConnectionOwnerH\x00R\x0fconnectionOwnerB\n" +
"\n" +
"\bresponse\"\x97\x01\n" +
"\x0fConnectionOwner\x12\x16\n" +
"\x06userId\x18\x01 \x01(\x05R\x06userId\x12\x1a\n" +
"\buserName\x18\x02 \x01(\tR\buserName\x12 \n" +
"\vprocessPath\x18\x03 \x01(\tR\vprocessPath\x12.\n" +
"\x12androidPackageName\x18\x04 \x01(\tR\x12androidPackageName\"5\n" +
"\tWIFIState\x12\x12\n" +
"\x04ssid\x18\x01 \x01(\tR\x04ssid\x12\x14\n" +
"\x05bssid\x18\x02 \x01(\tR\x05bssidB%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
var (
file_daemon_helper_proto_rawDescOnce sync.Once
file_daemon_helper_proto_rawDescData []byte
)
func file_daemon_helper_proto_rawDescGZIP() []byte {
file_daemon_helper_proto_rawDescOnce.Do(func() {
file_daemon_helper_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)))
})
return file_daemon_helper_proto_rawDescData
}
var (
file_daemon_helper_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
file_daemon_helper_proto_goTypes = []any{
(*SubscribeHelperRequestRequest)(nil), // 0: daemon.SubscribeHelperRequestRequest
(*HelperRequest)(nil), // 1: daemon.HelperRequest
(*FindConnectionOwnerRequest)(nil), // 2: daemon.FindConnectionOwnerRequest
(*Notification)(nil), // 3: daemon.Notification
(*HelperResponse)(nil), // 4: daemon.HelperResponse
(*ConnectionOwner)(nil), // 5: daemon.ConnectionOwner
(*WIFIState)(nil), // 6: daemon.WIFIState
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
}
)
var file_daemon_helper_proto_depIdxs = []int32{
7, // 0: daemon.HelperRequest.getWIFIState:type_name -> google.protobuf.Empty
2, // 1: daemon.HelperRequest.findConnectionOwner:type_name -> daemon.FindConnectionOwnerRequest
3, // 2: daemon.HelperRequest.sendNotification:type_name -> daemon.Notification
6, // 3: daemon.HelperResponse.wifiState:type_name -> daemon.WIFIState
5, // 4: daemon.HelperResponse.connectionOwner:type_name -> daemon.ConnectionOwner
5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_daemon_helper_proto_init() }
func file_daemon_helper_proto_init() {
if File_daemon_helper_proto != nil {
return
}
file_daemon_helper_proto_msgTypes[1].OneofWrappers = []any{
(*HelperRequest_GetWIFIState)(nil),
(*HelperRequest_FindConnectionOwner)(nil),
(*HelperRequest_SendNotification)(nil),
}
file_daemon_helper_proto_msgTypes[4].OneofWrappers = []any{
(*HelperResponse_WifiState)(nil),
(*HelperResponse_Error)(nil),
(*HelperResponse_ConnectionOwner)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_daemon_helper_proto_goTypes,
DependencyIndexes: file_daemon_helper_proto_depIdxs,
MessageInfos: file_daemon_helper_proto_msgTypes,
}.Build()
File_daemon_helper_proto = out.File
file_daemon_helper_proto_goTypes = nil
file_daemon_helper_proto_depIdxs = nil
}

View File

@@ -1,61 +0,0 @@
syntax = "proto3";
package daemon;
option go_package = "github.com/sagernet/sing-box/daemon";
import "google/protobuf/empty.proto";
message SubscribeHelperRequestRequest {
bool acceptGetWIFIStateRequests = 1;
bool acceptFindConnectionOwnerRequests = 2;
bool acceptSendNotificationRequests = 3;
}
message HelperRequest {
int64 id = 1;
oneof request {
google.protobuf.Empty getWIFIState = 2;
FindConnectionOwnerRequest findConnectionOwner = 3;
Notification sendNotification = 4;
}
}
message FindConnectionOwnerRequest {
int32 ipProtocol = 1;
string sourceAddress = 2;
int32 sourcePort = 3;
string destinationAddress = 4;
int32 destinationPort = 5;
}
message Notification {
string identifier = 1;
string typeName = 2;
int32 typeId = 3;
string title = 4;
string subtitle = 5;
string body = 6;
string openURL = 7;
}
message HelperResponse {
int64 id = 1;
oneof response {
WIFIState wifiState = 2;
string error = 3;
ConnectionOwner connectionOwner = 4;
}
}
message ConnectionOwner {
int32 userId = 1;
string userName = 2;
string processPath = 3;
string androidPackageName = 4;
}
message WIFIState {
string ssid = 1;
string bssid = 2;
}

View File

@@ -823,14 +823,6 @@ func (s *StartedService) GetStartedAt(ctx context.Context, empty *emptypb.Empty)
return &StartedAt{StartedAt: s.startedAt.UnixMilli()}, nil
}
func (s *StartedService) SubscribeHelperEvents(empty *emptypb.Empty, server grpc.ServerStreamingServer[HelperRequest]) error {
return os.ErrInvalid
}
func (s *StartedService) SendHelperResponse(ctx context.Context, response *HelperResponse) (*emptypb.Empty, error) {
return nil, os.ErrInvalid
}
func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
}

View File

@@ -1751,7 +1751,7 @@ var File_daemon_started_service_proto protoreflect.FileDescriptor
const file_daemon_started_service_proto_rawDesc = "" +
"\n" +
"\x1cdaemon/started_service.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\x1a\x13daemon/helper.proto\"\xad\x01\n" +
"\x1cdaemon/started_service.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xad\x01\n" +
"\rServiceStatus\x122\n" +
"\x06status\x18\x01 \x01(\x0e2\x1a.daemon.ServiceStatus.TypeR\x06status\x12\"\n" +
"\ferrorMessage\x18\x02 \x01(\tR\ferrorMessage\"D\n" +
@@ -1883,7 +1883,7 @@ const file_daemon_started_service_proto_rawDesc = "" +
"\x10ConnectionSortBy\x12\b\n" +
"\x04DATE\x10\x00\x12\v\n" +
"\aTRAFFIC\x10\x01\x12\x11\n" +
"\rTOTAL_TRAFFIC\x10\x022\xf4\f\n" +
"\rTOTAL_TRAFFIC\x10\x022\xe0\v\n" +
"\x0eStartedService\x12=\n" +
"\vStopService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12?\n" +
"\rReloadService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12K\n" +
@@ -1905,9 +1905,7 @@ const file_daemon_started_service_proto_rawDesc = "" +
"\x0fCloseConnection\x12\x1e.daemon.CloseConnectionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12G\n" +
"\x13CloseAllConnections\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12M\n" +
"\x15GetDeprecatedWarnings\x12\x16.google.protobuf.Empty\x1a\x1a.daemon.DeprecatedWarnings\"\x00\x12;\n" +
"\fGetStartedAt\x12\x16.google.protobuf.Empty\x1a\x11.daemon.StartedAt\"\x00\x12J\n" +
"\x15SubscribeHelperEvents\x12\x16.google.protobuf.Empty\x1a\x15.daemon.HelperRequest\"\x000\x01\x12F\n" +
"\x12SendHelperResponse\x12\x16.daemon.HelperResponse\x1a\x16.google.protobuf.Empty\"\x00B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
"\fGetStartedAt\x12\x16.google.protobuf.Empty\x1a\x11.daemon.StartedAt\"\x00B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
var (
file_daemon_started_service_proto_rawDescOnce sync.Once
@@ -1955,8 +1953,6 @@ var (
(*StartedAt)(nil), // 27: daemon.StartedAt
(*Log_Message)(nil), // 28: daemon.Log.Message
(*emptypb.Empty)(nil), // 29: google.protobuf.Empty
(*HelperResponse)(nil), // 30: daemon.HelperResponse
(*HelperRequest)(nil), // 31: daemon.HelperRequest
}
)
@@ -1993,33 +1989,29 @@ var file_daemon_started_service_proto_depIdxs = []int32{
29, // 29: daemon.StartedService.CloseAllConnections:input_type -> google.protobuf.Empty
29, // 30: daemon.StartedService.GetDeprecatedWarnings:input_type -> google.protobuf.Empty
29, // 31: daemon.StartedService.GetStartedAt:input_type -> google.protobuf.Empty
29, // 32: daemon.StartedService.SubscribeHelperEvents:input_type -> google.protobuf.Empty
30, // 33: daemon.StartedService.SendHelperResponse:input_type -> daemon.HelperResponse
29, // 34: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
29, // 35: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
4, // 36: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
7, // 37: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
8, // 38: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
29, // 39: daemon.StartedService.ClearLogs:output_type -> google.protobuf.Empty
9, // 40: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
10, // 41: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
17, // 42: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
16, // 43: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
29, // 44: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
29, // 45: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
29, // 46: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
29, // 47: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
18, // 48: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
29, // 49: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
21, // 50: daemon.StartedService.SubscribeConnections:output_type -> daemon.Connections
29, // 51: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
29, // 52: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
25, // 53: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
27, // 54: daemon.StartedService.GetStartedAt:output_type -> daemon.StartedAt
31, // 55: daemon.StartedService.SubscribeHelperEvents:output_type -> daemon.HelperRequest
29, // 56: daemon.StartedService.SendHelperResponse:output_type -> google.protobuf.Empty
34, // [34:57] is the sub-list for method output_type
11, // [11:34] is the sub-list for method input_type
29, // 32: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
29, // 33: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
4, // 34: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
7, // 35: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
8, // 36: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
29, // 37: daemon.StartedService.ClearLogs:output_type -> google.protobuf.Empty
9, // 38: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
10, // 39: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
17, // 40: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
16, // 41: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
29, // 42: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
29, // 43: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
29, // 44: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
29, // 45: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
18, // 46: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
29, // 47: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
21, // 48: daemon.StartedService.SubscribeConnections:output_type -> daemon.Connections
29, // 49: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
29, // 50: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
25, // 51: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
27, // 52: daemon.StartedService.GetStartedAt:output_type -> daemon.StartedAt
32, // [32:53] is the sub-list for method output_type
11, // [11:32] is the sub-list for method input_type
11, // [11:11] is the sub-list for extension type_name
11, // [11:11] is the sub-list for extension extendee
0, // [0:11] is the sub-list for field type_name
@@ -2030,7 +2022,6 @@ func file_daemon_started_service_proto_init() {
if File_daemon_started_service_proto != nil {
return
}
file_daemon_helper_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{

View File

@@ -4,7 +4,6 @@ package daemon;
option go_package = "github.com/sagernet/sing-box/daemon";
import "google/protobuf/empty.proto";
import "daemon/helper.proto";
service StartedService {
rpc StopService(google.protobuf.Empty) returns (google.protobuf.Empty);
@@ -33,9 +32,6 @@ service StartedService {
rpc CloseAllConnections(google.protobuf.Empty) returns(google.protobuf.Empty) {}
rpc GetDeprecatedWarnings(google.protobuf.Empty) returns(DeprecatedWarnings) {}
rpc GetStartedAt(google.protobuf.Empty) returns(StartedAt) {}
rpc SubscribeHelperEvents(google.protobuf.Empty) returns(stream HelperRequest) {}
rpc SendHelperResponse(HelperResponse) returns(google.protobuf.Empty) {}
}
message ServiceStatus {

View File

@@ -36,8 +36,6 @@ const (
StartedService_CloseAllConnections_FullMethodName = "/daemon.StartedService/CloseAllConnections"
StartedService_GetDeprecatedWarnings_FullMethodName = "/daemon.StartedService/GetDeprecatedWarnings"
StartedService_GetStartedAt_FullMethodName = "/daemon.StartedService/GetStartedAt"
StartedService_SubscribeHelperEvents_FullMethodName = "/daemon.StartedService/SubscribeHelperEvents"
StartedService_SendHelperResponse_FullMethodName = "/daemon.StartedService/SendHelperResponse"
)
// StartedServiceClient is the client API for StartedService service.
@@ -65,8 +63,6 @@ type StartedServiceClient interface {
CloseAllConnections(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
GetDeprecatedWarnings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DeprecatedWarnings, error)
GetStartedAt(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*StartedAt, error)
SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error)
SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error)
}
type startedServiceClient struct {
@@ -341,35 +337,6 @@ func (c *startedServiceClient) GetStartedAt(ctx context.Context, in *emptypb.Emp
return out, nil
}
func (c *startedServiceClient) SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[6], StartedService_SubscribeHelperEvents_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[emptypb.Empty, HelperRequest]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type StartedService_SubscribeHelperEventsClient = grpc.ServerStreamingClient[HelperRequest]
func (c *startedServiceClient) SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, StartedService_SendHelperResponse_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// StartedServiceServer is the server API for StartedService service.
// All implementations must embed UnimplementedStartedServiceServer
// for forward compatibility.
@@ -395,8 +362,6 @@ type StartedServiceServer interface {
CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error)
GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error)
SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error
SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error)
mustEmbedUnimplementedStartedServiceServer()
}
@@ -490,14 +455,6 @@ func (UnimplementedStartedServiceServer) GetDeprecatedWarnings(context.Context,
func (UnimplementedStartedServiceServer) GetStartedAt(context.Context, *emptypb.Empty) (*StartedAt, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetStartedAt not implemented")
}
func (UnimplementedStartedServiceServer) SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error {
return status.Errorf(codes.Unimplemented, "method SubscribeHelperEvents not implemented")
}
func (UnimplementedStartedServiceServer) SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendHelperResponse not implemented")
}
func (UnimplementedStartedServiceServer) mustEmbedUnimplementedStartedServiceServer() {}
func (UnimplementedStartedServiceServer) testEmbeddedByValue() {}
@@ -855,35 +812,6 @@ func _StartedService_GetStartedAt_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
func _StartedService_SubscribeHelperEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(emptypb.Empty)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(StartedServiceServer).SubscribeHelperEvents(m, &grpc.GenericServerStream[emptypb.Empty, HelperRequest]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type StartedService_SubscribeHelperEventsServer = grpc.ServerStreamingServer[HelperRequest]
func _StartedService_SendHelperResponse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelperResponse)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StartedServiceServer).SendHelperResponse(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: StartedService_SendHelperResponse_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StartedServiceServer).SendHelperResponse(ctx, req.(*HelperResponse))
}
return interceptor(ctx, in, info, handler)
}
// StartedService_ServiceDesc is the grpc.ServiceDesc for StartedService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -951,10 +879,6 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetStartedAt",
Handler: _StartedService_GetStartedAt_Handler,
},
{
MethodName: "SendHelperResponse",
Handler: _StartedService_SendHelperResponse_Handler,
},
},
Streams: []grpc.StreamDesc{
{
@@ -987,11 +911,6 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
Handler: _StartedService_SubscribeConnections_Handler,
ServerStreams: true,
},
{
StreamName: "SubscribeHelperEvents",
Handler: _StartedService_SubscribeHelperEvents_Handler,
ServerStreams: true,
},
},
Metadata: "daemon/started_service.proto",
}

View File

@@ -263,6 +263,7 @@ func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn ne
defer buffer.Release()
for {
buffer.Reset()
_, _, err := buffer.ReadPacketFrom(packetConn)
if err != nil {
if errors.Is(err, io.ErrShortBuffer) {

View File

@@ -2,10 +2,42 @@
icon: material/alert-decagram
---
#### 1.13.0-beta.2
* Add `bind_address_no_port` option for dial fields **1**
* Fixes and improvements
**1**:
Adds the Linux socket option `IP_BIND_ADDRESS_NO_PORT` support when explicitly binding to a source address.
This allows reusing the same source port for multiple connections, improving scalability for high-concurrency proxy scenarios.
See [Dial Fields](/configuration/shared/dial/#bind_address_no_port).
#### 1.13.0-beta.1
* Add system interface support for Tailscale endpoint **1**
* Fixes and improvements
**1**:
Tailscale endpoint can now create a system TUN interface to handle traffic directly.
See [Tailscale endpoint](/configuration/endpoint/tailscale/#system_interface).
#### 1.12.15
* Fixes and improvements
#### 1.13.0-alpha.36
* Downgrade quic-go to v0.57.1
* Fixes and improvements
#### 1.13.0-alpha.35
* Add pre-match support for `auto_redirect` **1**
* Fix missing relay support for Tailscale **2**
* Fixes and improvements
**1**:
@@ -18,10 +50,6 @@ This feature requires Linux with `auto_redirect` enabled.
See [Pre-match](/configuration/shared/pre-match/) and [Rule Action](/configuration/route/rule_action/#bypass).
**2**:
See [Tailscale Endpoint](/configuration/endpoint/tailscale/#relay_server_port).
#### 1.13.0-alpha.34
* Add Chrome Root Store certificate option **1**

View File

@@ -5,7 +5,10 @@ icon: material/new-box
!!! quote "Changes in sing-box 1.13.0"
:material-plus: [relay_server_port](#relay_server_port)
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
:material-plus: [system_interface](#system_interface)
:material-plus: [system_interface_name](#system_interface_name)
:material-plus: [system_interface_mtu](#system_interface_mtu)
!!! question "Since sing-box 1.12.0"
@@ -27,8 +30,11 @@ icon: material/new-box
"advertise_exit_node": false,
"relay_server_port": 0,
"relay_server_static_endpoints": [],
"system_interface": false,
"system_interface_name": "",
"system_interface_mtu": 0,
"udp_timeout": "5m",
... // Dial Fields
}
```
@@ -98,12 +104,34 @@ Indicates whether the node should advertise itself as an exit node.
#### relay_server_port
!!! question "Since sing-box 1.13.0"
The port to listen on for incoming relay connections from other Tailscale nodes.
#### relay_server_static_endpoints
!!! question "Since sing-box 1.13.0"
Static endpoints to advertise for the relay server.
#### system_interface
!!! question "Since sing-box 1.13.0"
Create a system TUN interface for Tailscale.
#### system_interface_name
!!! question "Since sing-box 1.13.0"
Custom TUN interface name. By default, `tailscale` (or `utun` on macOS) will be used.
#### system_interface_mtu
!!! question "Since sing-box 1.13.0"
Override the TUN MTU. By default, Tailscale's own MTU is used.
#### udp_timeout
UDP NAT expiration time.

View File

@@ -5,7 +5,10 @@ icon: material/new-box
!!! quote "sing-box 1.13.0 中的更改"
:material-plus: [relay_server_port](#relay_server_port)
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
:material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints)
:material-plus: [system_interface](#system_interface)
:material-plus: [system_interface_name](#system_interface_name)
:material-plus: [system_interface_mtu](#system_interface_mtu)
!!! question "自 sing-box 1.12.0 起"
@@ -27,6 +30,9 @@ icon: material/new-box
"advertise_exit_node": false,
"relay_server_port": 0,
"relay_server_static_endpoints": [],
"system_interface": false,
"system_interface_name": "",
"system_interface_mtu": 0,
"udp_timeout": "5m",
... // 拨号字段
@@ -97,12 +103,34 @@ icon: material/new-box
#### relay_server_port
!!! question "自 sing-box 1.13.0 起"
监听来自其他 Tailscale 节点的中继连接的端口。
#### relay_server_static_endpoints
!!! question "自 sing-box 1.13.0 起"
为中继服务器通告的静态端点。
#### system_interface
!!! question "自 sing-box 1.13.0 起"
为 Tailscale 创建系统 TUN 接口。
#### system_interface_name
!!! question "自 sing-box 1.13.0 起"
自定义 TUN 接口名。默认使用 `tailscale`macOS 上为 `utun`)。
#### system_interface_mtu
!!! question "自 sing-box 1.13.0 起"
覆盖 TUN 的 MTU。默认使用 Tailscale 自己的 MTU。
#### udp_timeout
UDP NAT 过期时间。
@@ -115,4 +143,4 @@ UDP NAT 过期时间。
Tailscale 端点中的拨号字段仅控制它如何连接到控制平面,与实际连接无关。
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。

View File

@@ -9,6 +9,7 @@
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"managed": false,
"multiplex": {}
}
```
@@ -86,6 +87,10 @@ Both if empty.
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
| other methods | any string |
#### managed
Defaults to `false`. Enable this when the inbound is managed by the [SSM API](/configuration/service/ssm-api) for dynamic user.
#### multiplex
See [Multiplex](/configuration/shared/multiplex#inbound) for details.

View File

@@ -9,6 +9,7 @@
"method": "2022-blake3-aes-128-gcm",
"password": "8JCsPssfgS8tiRwiMlhARg==",
"managed": false,
"multiplex": {}
}
```
@@ -86,6 +87,10 @@
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
| other methods | 任意字符串 |
#### managed
默认为 `false`。当该入站需要由 [SSM API](/zh/configuration/service/ssm-api) 管理用户时必须启用此字段。
#### multiplex
参阅 [多路复用](/zh/configuration/shared/multiplex#inbound)。

View File

@@ -6,7 +6,8 @@ icon: material/new-box
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
:material-plus: [bind_address_no_port](#bind_address_no_port)
!!! quote "Changes in sing-box 1.12.0"
@@ -29,6 +30,7 @@ icon: material/new-box
"bind_interface": "",
"inet4_bind_address": "",
"inet6_bind_address": "",
"bind_address_no_port": false,
"routing_mark": 0,
"reuse_addr": false,
"netns": "",
@@ -76,6 +78,18 @@ The IPv4 address to bind to.
The IPv6 address to bind to.
#### bind_address_no_port
!!! question "Since sing-box 1.13.0"
!!! quote ""
Only supported on Linux.
Do not reserve a port when binding to a source address.
This allows reusing the same source port for multiple connections if the full 4-tuple (source IP, source port, destination IP, destination port) remains unique.
#### routing_mark
!!! quote ""

View File

@@ -6,7 +6,8 @@ icon: material/new-box
:material-plus: [disable_tcp_keep_alive](#disable_tcp_keep_alive)
:material-plus: [tcp_keep_alive](#tcp_keep_alive)
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
:material-plus: [tcp_keep_alive_interval](#tcp_keep_alive_interval)
:material-plus: [bind_address_no_port](#bind_address_no_port)
!!! quote "sing-box 1.12.0 中的更改"
@@ -29,6 +30,7 @@ icon: material/new-box
"bind_interface": "",
"inet4_bind_address": "",
"inet6_bind_address": "",
"bind_address_no_port": false,
"routing_mark": 0,
"reuse_addr": false,
"netns": "",
@@ -76,6 +78,18 @@ icon: material/new-box
要绑定的 IPv6 地址。
#### bind_address_no_port
!!! question "自 sing-box 1.13.0 起"
!!! quote ""
仅支持 Linux。
绑定到源地址时不保留端口。
这允许在完整的四元组(源 IP、源端口、目标 IP、目标端口保持唯一的情况下为多个连接复用同一源端口。
#### routing_mark
!!! quote ""

View File

@@ -62,6 +62,16 @@ type LogIterator interface {
Next() *LogEntry
}
type XPCDialer interface {
DialXPC() (int32, error)
}
var sXPCDialer XPCDialer
func SetXPCDialer(dialer XPCDialer) {
sXPCDialer = dialer
}
func NewStandaloneCommandClient() *CommandClient {
return new(CommandClient)
}
@@ -117,13 +127,113 @@ func (c *CommandClient) Connect() error {
c.clientMutex.Lock()
common.Close(common.PtrOrNil(c.grpcConn))
conn, err := c.grpcDial()
if sXPCDialer != nil {
fd, err := sXPCDialer.DialXPC()
if err != nil {
c.clientMutex.Unlock()
return err
}
file := os.NewFile(uintptr(fd), "xpc-command-socket")
if file == nil {
c.clientMutex.Unlock()
return E.New("invalid file descriptor")
}
netConn, err := net.FileConn(file)
if err != nil {
file.Close()
c.clientMutex.Unlock()
return E.Cause(err, "create connection from fd")
}
file.Close()
clientOptions := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return netConn, nil
}),
grpc.WithUnaryInterceptor(unaryClientAuthInterceptor),
grpc.WithStreamInterceptor(streamClientAuthInterceptor),
}
grpcConn, err := grpc.NewClient("passthrough:///xpc", clientOptions...)
if err != nil {
netConn.Close()
c.clientMutex.Unlock()
return err
}
c.grpcConn = grpcConn
c.grpcClient = daemon.NewStartedServiceClient(grpcConn)
c.ctx, c.cancel = context.WithCancel(context.Background())
c.clientMutex.Unlock()
} else {
conn, err := c.grpcDial()
if err != nil {
c.clientMutex.Unlock()
return err
}
c.grpcConn = conn
c.grpcClient = daemon.NewStartedServiceClient(conn)
c.ctx, c.cancel = context.WithCancel(context.Background())
c.clientMutex.Unlock()
}
c.handler.Connected()
for _, command := range c.options.commands {
switch command {
case CommandLog:
go c.handleLogStream()
case CommandStatus:
go c.handleStatusStream()
case CommandGroup:
go c.handleGroupStream()
case CommandClashMode:
go c.handleClashModeStream()
case CommandConnections:
go c.handleConnectionsStream()
default:
return E.New("unknown command: ", command)
}
}
return nil
}
func (c *CommandClient) ConnectWithFD(fd int32) error {
c.clientMutex.Lock()
common.Close(common.PtrOrNil(c.grpcConn))
file := os.NewFile(uintptr(fd), "xpc-command-socket")
if file == nil {
c.clientMutex.Unlock()
return E.New("invalid file descriptor")
}
netConn, err := net.FileConn(file)
if err != nil {
file.Close()
c.clientMutex.Unlock()
return E.Cause(err, "create connection from fd")
}
file.Close()
clientOptions := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return netConn, nil
}),
grpc.WithUnaryInterceptor(unaryClientAuthInterceptor),
grpc.WithStreamInterceptor(streamClientAuthInterceptor),
}
grpcConn, err := grpc.NewClient("passthrough:///xpc", clientOptions...)
if err != nil {
netConn.Close()
c.clientMutex.Unlock()
return err
}
c.grpcConn = conn
c.grpcClient = daemon.NewStartedServiceClient(conn)
c.grpcConn = grpcConn
c.grpcClient = daemon.NewStartedServiceClient(grpcConn)
c.ctx, c.cancel = context.WithCancel(context.Background())
c.clientMutex.Unlock()
@@ -171,6 +281,45 @@ func (c *CommandClient) getClientForCall() (daemon.StartedServiceClient, error)
return c.grpcClient, nil
}
if sXPCDialer != nil {
fd, err := sXPCDialer.DialXPC()
if err != nil {
return nil, err
}
file := os.NewFile(uintptr(fd), "xpc-command-socket")
if file == nil {
return nil, E.New("invalid file descriptor")
}
netConn, err := net.FileConn(file)
if err != nil {
file.Close()
return nil, E.Cause(err, "create connection from fd")
}
file.Close()
clientOptions := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return netConn, nil
}),
grpc.WithUnaryInterceptor(unaryClientAuthInterceptor),
grpc.WithStreamInterceptor(streamClientAuthInterceptor),
}
grpcConn, err := grpc.NewClient("passthrough:///xpc", clientOptions...)
if err != nil {
netConn.Close()
return nil, err
}
c.grpcConn = grpcConn
c.grpcClient = daemon.NewStartedServiceClient(grpcConn)
if c.ctx == nil {
c.ctx, c.cancel = context.WithCancel(context.Background())
}
return c.grpcClient, nil
}
conn, err := c.grpcDial()
if err != nil {
return nil, err

View File

@@ -195,6 +195,14 @@ func (s *CommandServer) NeedWIFIState() bool {
return instance.Box().Network().NeedWIFIState()
}
func (s *CommandServer) NeedFindProcess() bool {
instance := s.StartedService.Instance()
if instance == nil || instance.Box() == nil {
return false
}
return instance.Box().Router().NeedFindProcess()
}
func (s *CommandServer) Pause() {
instance := s.StartedService.Instance()
if instance == nil || instance.PauseManager() == nil {

View File

@@ -11,9 +11,7 @@ type PlatformInterface interface {
AutoDetectInterfaceControl(fd int32) error
OpenTun(options TunOptions) (int32, error)
UseProcFS() bool
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
PackageNameByUid(uid int32) (string, error)
UIDByPackageName(packageName string) (int32, error)
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (*ConnectionOwner, error)
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
GetInterfaces() (NetworkInterfaceIterator, error)
@@ -25,6 +23,13 @@ type PlatformInterface interface {
SendNotification(notification *Notification) error
}
type ConnectionOwner struct {
UserId int32
UserName string
ProcessPath string
AndroidPackageName string
}
type InterfaceUpdateListener interface {
UpdateDefaultInterface(interfaceName string, interfaceIndex int32, isExpensive bool, isConstrained bool)
}

View File

@@ -166,7 +166,6 @@ func (w *platformInterfaceWrapper) UsePlatformConnectionOwnerFinder() bool {
}
func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConnectionOwnerRequest) (*adapter.ConnectionOwner, error) {
var uid int32
if w.useProcFS {
var source netip.AddrPort
var destination netip.AddrPort
@@ -185,21 +184,24 @@ func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConn
return nil, E.New("unknown protocol: ", request.IpProtocol)
}
uid = procfs.ResolveSocketByProcSearch(network, source, destination)
uid := procfs.ResolveSocketByProcSearch(network, source, destination)
if uid == -1 {
return nil, E.New("procfs: not found")
}
} else {
var err error
uid, err = w.iif.FindConnectionOwner(request.IpProtocol, request.SourceAddress, request.SourcePort, request.DestinationAddress, request.DestinationPort)
if err != nil {
return nil, err
}
return &adapter.ConnectionOwner{
UserId: uid,
}, nil
}
result, err := w.iif.FindConnectionOwner(request.IpProtocol, request.SourceAddress, request.SourcePort, request.DestinationAddress, request.DestinationPort)
if err != nil {
return nil, err
}
packageName, _ := w.iif.PackageNameByUid(uid)
return &adapter.ConnectionOwner{
UserId: uid,
AndroidPackageName: packageName,
UserId: result.UserId,
UserName: result.UserName,
ProcessPath: result.ProcessPath,
AndroidPackageName: result.AndroidPackageName,
}, nil
}

View File

@@ -71,11 +71,11 @@ func Version() string {
}
func FormatBytes(length int64) string {
return byteformats.FormatBytes(uint64(length))
return byteformats.FormatKBytes(uint64(length))
}
func FormatMemoryBytes(length int64) string {
return byteformats.FormatMemoryBytes(uint64(length))
return byteformats.FormatMemoryKBytes(uint64(length))
}
func FormatDuration(duration int64) string {

61
go.mod
View File

@@ -26,22 +26,22 @@ require (
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/cors v1.2.1
github.com/sagernet/cronet-go v0.0.0-20251230094758-5aef2a9e7f0d
github.com/sagernet/cronet-go/all v0.0.0-20251230094758-5aef2a9e7f0d
github.com/sagernet/cronet-go v0.0.0-20251231120443-1d2d7341cbd8
github.com/sagernet/cronet-go/all v0.0.0-20251231120443-1d2d7341cbd8
github.com/sagernet/fswatch v0.1.1
github.com/sagernet/gomobile v0.1.10
github.com/sagernet/gomobile v0.1.11
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
github.com/sagernet/quic-go v0.58.0-sing-box-mod.1
github.com/sagernet/sing v0.8.0-beta.8
github.com/sagernet/sing v0.8.0-beta.9.0.20260109141034-50e1ed36c6f6
github.com/sagernet/sing-mux v0.3.3
github.com/sagernet/sing-quic v0.6.0-beta.7
github.com/sagernet/sing-quic v0.6.0-beta.8
github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20251226064455-a850c4f8a1c8
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20260107060547-525f783d005b
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
github.com/sagernet/smux v1.5.34-mod.2
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
github.com/spf13/cobra v1.10.2
@@ -68,6 +68,7 @@ require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
github.com/database64128/netx-go v0.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
@@ -85,6 +86,7 @@ require (
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
@@ -102,28 +104,28 @@ require (
github.com/prometheus-community/pro-bing v0.4.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251230094225-9a5fe902f561 // indirect
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251231115934-b87a9ae9bd80 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/spf13/pflag v1.0.9 // indirect
@@ -131,6 +133,7 @@ require (
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
github.com/tidwall/gjson v1.18.0 // indirect

125
go.sum
View File

@@ -22,6 +22,8 @@ github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
@@ -80,6 +82,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI=
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
@@ -145,58 +149,58 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/cronet-go v0.0.0-20251230094758-5aef2a9e7f0d h1:1oTc5+PjhCjdDro0VYZzwPee0CvTmMbDnyxZAThYvq4=
github.com/sagernet/cronet-go v0.0.0-20251230094758-5aef2a9e7f0d/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw=
github.com/sagernet/cronet-go/all v0.0.0-20251230094758-5aef2a9e7f0d h1:lRwTaavH9XOCf3QPEY9FNkOsy1jLWpo5Lg6Vas9XShs=
github.com/sagernet/cronet-go/all v0.0.0-20251230094758-5aef2a9e7f0d/go.mod h1:Wqam5NDbMkewyy+33wu6wsq4uNHGoskHaon1XsTxQMg=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251230094225-9a5fe902f561 h1:vjswtG+1CNj4kni3haVLzIn8C7RKa3RnFUXG8OofgSE=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:ciuXFp02usTUyj9MpzwlB0bEyEjF0VVINMl5QG5uIu4=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251230094225-9a5fe902f561 h1:qJVWgBiznBwkfQ9EB0TpjgVVeBVHb/COaItekq/EZ5w=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251230094225-9a5fe902f561/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:FEJJEbbmmOv37Lx+JgHlXIdTXzP0TNYHBvzGgbft4fA=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:w9Z3W30bdQrmme2qfJBfkZGUKirPe6N4caCDqp7+ArI=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:7lJXCswd6nIrw31MJYet0sNcIxD9MVxKW/UoSPynBxw=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:dCten+Kq71YCOGxezyIQqpe5zctf4Oxwhk8tgHUuy0g=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:Q6RE/v/XXaCLOh7w7iIuzCRhwWIjk+ikMcKSqO8vA6c=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:bzXuJwzMy/+RLVXYQrNL3cqPYP7pW9OaItF0zyNkBl4=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251230094225-9a5fe902f561 h1:bzYwMTJ1nenaNkf/TMTj3Cj1KCq2QAE9pvEy8ARdZsY=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251230094225-9a5fe902f561 h1:lfbECpdqczlAvfkpM6q8CjFWLMjRmkf6eHaFgNMOWZ8=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:RlA+oS6G9D5i1RNbUyGdGKTY9fAr9sjQs+zZiEghinU=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251230094225-9a5fe902f561 h1:MJLgEelOSdhX/3XU7n9dykExmJ5eoA2rZFXRzbiw+vE=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251230094225-9a5fe902f561 h1:0UIPfoF74+vwCIAYlCq8NGq+em+byaMIJLUIksakdCg=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251230094225-9a5fe902f561/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:h8zDiRnLpHY8oidZqIE0SOiDqswCBSskHWPRqHGD3F0=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251230094225-9a5fe902f561 h1:N/svqvPrxxjhPAmZ6OwROQiF3Lg/BoViwGZpnxma3U8=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251230094225-9a5fe902f561 h1:Hoqa5ub53wdkVKBy0rH0pacg5tDdfJq6ZJ4kORdgr+s=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251230094225-9a5fe902f561/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:w6Y6mcEDFXa8kuLIAlM87TuI4UXK9Nl/CGzYDnroTK8=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:4GmOCqjTzAqRZky/epW0zRgEYGbNXNcupxDf2WwWS4Y=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251230094225-9a5fe902f561 h1:dWnsBlKtGwd9qebvReFAL64CZ/DCMo5eZiqS7cwuP38=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251230094225-9a5fe902f561/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251230094225-9a5fe902f561 h1:ri/eIHXgK+PrK/Z05Gv+xwI7PsrsANWEB2EDr6NkBTY=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251230094225-9a5fe902f561 h1:JGN++AgXz57mkZ4UmxEuuvmaj32+yF2mZLNnsfvOm08=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251230094225-9a5fe902f561/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
github.com/sagernet/cronet-go v0.0.0-20251231120443-1d2d7341cbd8 h1:yQtAGNBF8c2cYdnsfkbOp8ShdWlpncua96KXqHd9svs=
github.com/sagernet/cronet-go v0.0.0-20251231120443-1d2d7341cbd8/go.mod h1:hwFHBEjjthyEquDULbr4c4ucMedp8Drb6Jvm2kt/0Bw=
github.com/sagernet/cronet-go/all v0.0.0-20251231120443-1d2d7341cbd8 h1:KMmO1p8bO2BTq7NdTNdmjurh7rg6S1vRfHrEvFPw8bY=
github.com/sagernet/cronet-go/all v0.0.0-20251231120443-1d2d7341cbd8/go.mod h1:A4cCrezhvuNnb2K6UVuM+IQDZXBjTi2iK5K/wBu7Olg=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251231115934-b87a9ae9bd80 h1:9dBJpiOdU121TZ+mOb3LW8T5tZ3ZBjSUB9zg2F9Cltg=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251231115934-b87a9ae9bd80 h1:vODDxjRh7CHHnfoapv1+pDFtEMPOFxWnBlVSskXCOSU=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251231115934-b87a9ae9bd80 h1:ivqSFl9JHV63ELQkSTzeUkltLb+owOZ9GoRBTDYC0Jk=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251231115934-b87a9ae9bd80 h1:UKvOG4VuR+8c4VFA11d1dXZfANXNjDecXeZJfmjHTY4=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251231115934-b87a9ae9bd80 h1:qtw9lKXyuDG8kBB3PyhD/xidVhffST7caUEQ2fS6Sbo=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251231115934-b87a9ae9bd80 h1:Vkx2x+f67sOoL5K4YRX6RfIfM8z14YeGqj6Xc/CnzWg=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251231115934-b87a9ae9bd80 h1:+jfTH4yHr+toXrUH2xGUnhql641IIvbBTCKpz9WvTxU=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251231115934-b87a9ae9bd80 h1:Ow8A3f3YAgBJ4HMYJVv8RK0PsvQhAt4GHTBsiuaXJYw=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251231115934-b87a9ae9bd80 h1:HvlLfPq+WxRQtilZD4L3WgMYwZnwGnFoQX29eZtdm1Y=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251231115934-b87a9ae9bd80 h1:nGPM9VpMWhfPDX4SOk7p+GvHYAAv2LOCE4hNIue+eAY=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251231115934-b87a9ae9bd80 h1:GvFk6EH/PmIqxJN2OQarXt6vNrS/tgQ78b/WPvXhAmg=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251231115934-b87a9ae9bd80 h1:SaIqJcgT0D3fK4zdXRA3E9kzq4mtfwkNDzAnv6bFQjE=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251231115934-b87a9ae9bd80 h1:pEnLOmZjgWk9jaezJt/RvMX91Mugq4KwyxdW2mYvrUU=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251231115934-b87a9ae9bd80 h1:/HPZI7eocQxu7Ho5gBoOWEQwH+IgmnD0pG7RVQzeZK8=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251231115934-b87a9ae9bd80 h1:ZxVgKaU1r748EPp2y6GNZhMxtxxbC9xHSmWnJP4qDdM=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251231115934-b87a9ae9bd80 h1:v8cjiAvXjWOkC4Li05MQrQIJ1NH2vK2OWMc/mS/c7FI=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251231115934-b87a9ae9bd80 h1:Hh3at5gCCc2YZNbErv/jMqO4FhYN3oms892bLwK0aHI=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251231115934-b87a9ae9bd80 h1:Mz9JbTXKCfIvnvDxzID7YtkC/5Y/6MG1BWNBmOoTkaQ=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251231115934-b87a9ae9bd80 h1:xM0Z8k3WheuWfJsZNKEBkRyAFK/NEB01UNxElMf8oy8=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251231115934-b87a9ae9bd80 h1:Nvvip5xwIw9GoTg/Tch0uYW11wdc+VhZHTp81LuVZGY=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251231115934-b87a9ae9bd80 h1:BkrIlIUtKHtSXSZe2xGjgEtbP0z1LYo0PXeM25obSSM=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251231115934-b87a9ae9bd80 h1:oJY5DO13q1x4XORhZYR7QVmYdFFhbua5w7Hp74h6Z10=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20251231115934-b87a9ae9bd80/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
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/gomobile v0.1.10 h1:ElqZ0OVDvyQlU91MU0C9cfU0FrILBbc65+NOKzZ1t0c=
github.com/sagernet/gomobile v0.1.10/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
github.com/sagernet/gomobile v0.1.11 h1:niMQAspvuThup5eRZQpsGcbM76zAvnsGr7RUIpnQMDQ=
github.com/sagernet/gomobile v0.1.11/go.mod h1:A8l3FlHi2D/+mfcd4HHvk5DGFPW/ShFb9jHP5VmSiDY=
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1 h1:AzCE2RhBjLJ4WIWc/GejpNh+z30d5H1hwaB0nD9eY3o=
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1/go.mod h1:NJKBtm9nVEK3iyOYWsUlrDQuoGh4zJ4KOPhSYVidvQ4=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
@@ -206,26 +210,26 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
github.com/sagernet/quic-go v0.58.0-sing-box-mod.1 h1:E9yZrU0ZxSiW5RrGUnFZeI02EIMdAAv0RxdoxXCqZyk=
github.com/sagernet/quic-go v0.58.0-sing-box-mod.1/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.8.0-beta.8 h1:hUo0wZ2HGTieV1flEIai96HFhF34mMHVnduRqJHQvxg=
github.com/sagernet/sing v0.8.0-beta.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.8.0-beta.9.0.20260109141034-50e1ed36c6f6 h1:TgmW2IKThK+3wWhoB/UKjwdW2NUyWqCyXZoCYTMizv8=
github.com/sagernet/sing v0.8.0-beta.9.0.20260109141034-50e1ed36c6f6/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.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.6.0-beta.7 h1:Sh6KltQ6nB69S9ZdDKs5oARqkyY99gOaHe1JPxNV7ag=
github.com/sagernet/sing-quic v0.6.0-beta.7/go.mod h1:0NodMFjlAvfLp87Enpx46fQPrGRvmbUsmy5hRLzomtM=
github.com/sagernet/sing-quic v0.6.0-beta.8 h1:Y0P8WTqWpfg80rLFsDfF22QumM+HEAjRQ2o+8Dv+vDs=
github.com/sagernet/sing-quic v0.6.0-beta.8/go.mod h1:Y3YVjPutLHLQvYjGtUFH+w8YmM4MTd19NDzxLZGNGIs=
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-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
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/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20251226064455-a850c4f8a1c8 h1:aIgk6YzS/7fNm92CycFWzithdwIc+NAwXGHAJce1dyM=
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20251226064455-a850c4f8a1c8/go.mod h1:+HAK/y9GZljdT0KYKMYDR8MjjqnqDDQZYp5ZZQoRzS8=
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20260107060547-525f783d005b h1:MqPEFejgxqechqBn1OkL+9JPW0W4AiGCI0Y1JhglIlQ=
github.com/sagernet/sing-tun v0.8.0-beta.11.0.20260107060547-525f783d005b/go.mod h1:+HAK/y9GZljdT0KYKMYDR8MjjqnqDDQZYp5ZZQoRzS8=
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
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/tailscale v1.92.4-sing-box-1.13-mod.4 h1:p+9JllOL5Q2pj6bmP9gu+LdjyRg/XxHLTpMfuhuQsY4=
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4/go.mod h1:HZxL3asFIkcIJtHdnqsdcXsY6d+1iMtq0SPUlX17TGM=
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6 h1:eYz/OpMqWCvO2++iw3dEuzrlfC2xv78GdlGvprIM6O8=
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6/go.mod h1:m87GAn4UcesHQF3leaPFEINZETO5za1LGn1GJdNDgNc=
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288 h1:E2tZFeg9mGYGQ7E7BbxMv1cU35HxwgRm6tPKI2Pp7DA=
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288/go.mod h1:WUxgxUDZoCF2sxVmW+STSxatP02Qn3FcafTiI2BLtE0=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
@@ -253,6 +257,8 @@ github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPx
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:UBPHPtv8+nEAy2PD8RyAhOYvau1ek0HDJqLS/Pysi14=
@@ -271,6 +277,7 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@@ -324,6 +331,8 @@ golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwE
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -5,6 +5,7 @@ import (
"context"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json"
)
@@ -60,37 +61,40 @@ func checkOptions(options *Options) error {
func checkInbounds(inbounds []Inbound) error {
seen := make(map[string]bool)
for _, inbound := range inbounds {
if inbound.Tag == "" {
continue
for i, inbound := range inbounds {
tag := inbound.Tag
if tag == "" {
tag = F.ToString(i)
}
if seen[inbound.Tag] {
return E.New("duplicate inbound tag: ", inbound.Tag)
if seen[tag] {
return E.New("duplicate inbound tag: ", tag)
}
seen[inbound.Tag] = true
seen[tag] = true
}
return nil
}
func checkOutbounds(outbounds []Outbound, endpoints []Endpoint) error {
seen := make(map[string]bool)
for _, outbound := range outbounds {
if outbound.Tag == "" {
continue
for i, outbound := range outbounds {
tag := outbound.Tag
if tag == "" {
tag = F.ToString(i)
}
if seen[outbound.Tag] {
return E.New("duplicate outbound/endpoint tag: ", outbound.Tag)
if seen[tag] {
return E.New("duplicate outbound/endpoint tag: ", tag)
}
seen[outbound.Tag] = true
seen[tag] = true
}
for _, endpoint := range endpoints {
if endpoint.Tag == "" {
continue
for i, endpoint := range endpoints {
tag := endpoint.Tag
if tag == "" {
tag = F.ToString(i)
}
if seen[endpoint.Tag] {
return E.New("duplicate outbound/endpoint tag: ", endpoint.Tag)
if seen[tag] {
return E.New("duplicate outbound/endpoint tag: ", tag)
}
seen[endpoint.Tag] = true
seen[tag] = true
}
return nil
}

View File

@@ -69,6 +69,7 @@ type DialerOptions struct {
BindInterface string `json:"bind_interface,omitempty"`
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
BindAddressNoPort bool `json:"bind_address_no_port,omitempty"`
ProtectPath string `json:"protect_path,omitempty"`
RoutingMark FwMark `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"`

View File

@@ -12,19 +12,22 @@ import (
type TailscaleEndpointOptions struct {
DialerOptions
StateDirectory string `json:"state_directory,omitempty"`
AuthKey string `json:"auth_key,omitempty"`
ControlURL string `json:"control_url,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
Hostname string `json:"hostname,omitempty"`
AcceptRoutes bool `json:"accept_routes,omitempty"`
ExitNode string `json:"exit_node,omitempty"`
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"`
RelayServerPort *uint16 `json:"relay_server_port,omitempty"`
RelayServerStaticEndpoints []netip.AddrPort `json:"relay_server_static_endpoints,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
StateDirectory string `json:"state_directory,omitempty"`
AuthKey string `json:"auth_key,omitempty"`
ControlURL string `json:"control_url,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"`
Hostname string `json:"hostname,omitempty"`
AcceptRoutes bool `json:"accept_routes,omitempty"`
ExitNode string `json:"exit_node,omitempty"`
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"`
RelayServerPort *uint16 `json:"relay_server_port,omitempty"`
RelayServerStaticEndpoints []netip.AddrPort `json:"relay_server_static_endpoints,omitempty"`
SystemInterface bool `json:"system_interface,omitempty"`
SystemInterfaceName string `json:"system_interface_name,omitempty"`
SystemInterfaceMTU uint32 `json:"system_interface_mtu,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
}
type TailscaleDNSServerOptions struct {

View File

@@ -22,8 +22,6 @@ import (
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/common/dialer"
@@ -49,8 +47,10 @@ import (
tsDNS "github.com/sagernet/tailscale/net/dns"
"github.com/sagernet/tailscale/net/netmon"
"github.com/sagernet/tailscale/net/tsaddr"
tsTUN "github.com/sagernet/tailscale/net/tstun"
"github.com/sagernet/tailscale/tsnet"
"github.com/sagernet/tailscale/types/ipproto"
"github.com/sagernet/tailscale/types/nettype"
"github.com/sagernet/tailscale/version"
"github.com/sagernet/tailscale/wgengine"
"github.com/sagernet/tailscale/wgengine/filter"
@@ -101,6 +101,51 @@ type Endpoint struct {
relayServerStaticEndpoints []netip.AddrPort
udpTimeout time.Duration
systemInterface bool
systemInterfaceName string
systemInterfaceMTU uint32
systemTun tun.Tun
fallbackTCPCloser func()
}
func (t *Endpoint) registerNetstackHandlers() {
netstack := t.server.ExportNetstack()
if netstack == nil {
return
}
previousTCP := netstack.GetTCPHandlerForFlow
netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
if previousTCP != nil {
handler, intercept = previousTCP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn net.Conn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
t.NewConnectionEx(ctx, conn, source, destination, nil)
}, true
}
previousUDP := netstack.GetUDPHandlerForFlow
netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) {
if previousUDP != nil {
handler, intercept = previousUDP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn nettype.ConnPacketConn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
packetConn := bufio.NewPacketConn(conn)
t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil)
}, true
}
}
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
@@ -202,6 +247,9 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
relayServerPort: options.RelayServerPort,
relayServerStaticEndpoints: options.RelayServerStaticEndpoints,
udpTimeout: udpTimeout,
systemInterface: options.SystemInterface,
systemInterfaceName: options.SystemInterfaceName,
systemInterfaceMTU: options.SystemInterfaceMTU,
}, nil
}
@@ -237,10 +285,59 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
setAndroidProtectFunc(t.platformInterface)
}
}
if t.systemInterface {
mtu := t.systemInterfaceMTU
if mtu == 0 {
mtu = uint32(tsTUN.DefaultTUNMTU())
}
tunName := t.systemInterfaceName
if tunName == "" {
tunName = tun.CalculateInterfaceName("tailscale")
}
tunOptions := tun.Options{
Name: tunName,
MTU: mtu,
GSO: true,
InterfaceScope: true,
InterfaceMonitor: t.network.InterfaceMonitor(),
InterfaceFinder: t.network.InterfaceFinder(),
Logger: t.logger,
EXP_ExternalConfiguration: true,
}
systemTun, err := tun.New(tunOptions)
if err != nil {
return err
}
err = systemTun.Start()
if err != nil {
_ = systemTun.Close()
return err
}
wgTunDevice, err := newTunDeviceAdapter(systemTun, int(mtu), t.logger)
if err != nil {
_ = systemTun.Close()
return err
}
t.systemTun = systemTun
t.server.TunDevice = wgTunDevice
}
err := t.server.Start()
if err != nil {
if t.systemTun != nil {
_ = t.systemTun.Close()
}
return err
}
if t.fallbackTCPCloser == nil {
t.fallbackTCPCloser = t.server.RegisterFallbackTCPHandler(func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
return func(conn net.Conn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
t.NewConnectionEx(ctx, conn, source, destination, nil)
}, true
})
}
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
ipStack := t.server.ExportNetstack().ExportIPStack()
@@ -252,13 +349,12 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
if gErr != nil {
return gonet.TranslateNetstackError(gErr)
}
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout).HandlePacket)
icmpForwarder := tun.NewICMPForwarder(t.ctx, ipStack, t, t.udpTimeout)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
t.stack = ipStack
t.icmpForwarder = icmpForwarder
t.registerNetstackHandlers()
localBackend := t.server.ExportLocalBackend()
perfs := &ipn.MaskedPrefs{
@@ -355,6 +451,10 @@ func (t *Endpoint) Close() error {
if runtime.GOOS == "android" {
setAndroidProtectFunc(nil)
}
if t.fallbackTCPCloser != nil {
t.fallbackTCPCloser()
t.fallbackTCPCloser = nil
}
return common.Close(common.PtrOrNil(t.server))
}
@@ -372,26 +472,42 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
}
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
}
addr := tcpip.FullAddress{
addr4, addr6 := t.server.TailscaleIPs()
remoteAddr := tcpip.FullAddress{
NIC: 1,
Port: destination.Port,
Addr: addressFromAddr(destination.Addr),
}
var localAddr tcpip.FullAddress
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
if !addr4.IsValid() {
return nil, E.New("missing Tailscale IPv4 address")
}
networkProtocol = header.IPv4ProtocolNumber
localAddr = tcpip.FullAddress{
NIC: 1,
Addr: addressFromAddr(addr4),
}
} else {
if !addr6.IsValid() {
return nil, E.New("missing Tailscale IPv6 address")
}
networkProtocol = header.IPv6ProtocolNumber
localAddr = tcpip.FullAddress{
NIC: 1,
Addr: addressFromAddr(addr6),
}
}
switch N.NetworkName(network) {
case N.NetworkTCP:
tcpConn, err := gonet.DialContextTCP(ctx, t.stack, addr, networkProtocol)
tcpConn, err := gonet.DialTCPWithBind(ctx, t.stack, localAddr, remoteAddr, networkProtocol)
if err != nil {
return nil, err
}
return tcpConn, nil
case N.NetworkUDP:
udpConn, err := gonet.DialUDP(t.stack, nil, &addr, networkProtocol)
udpConn, err := gonet.DialUDP(t.stack, &localAddr, &remoteAddr, networkProtocol)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,156 @@
//go:build !windows
package tailscale
import (
"encoding/hex"
"errors"
"io"
"os"
"sync"
"sync/atomic"
singTun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/logger"
wgTun "github.com/sagernet/wireguard-go/tun"
)
type tunDeviceAdapter struct {
tun singTun.Tun
linuxTUN singTun.LinuxTUN
events chan wgTun.Event
mtu int
logger logger.ContextLogger
debugTun bool
readCount atomic.Uint32
writeCount atomic.Uint32
closeOnce sync.Once
}
func newTunDeviceAdapter(tun singTun.Tun, mtu int, logger logger.ContextLogger) (wgTun.Device, error) {
if tun == nil {
return nil, os.ErrInvalid
}
if mtu == 0 {
mtu = 1500
}
adapter := &tunDeviceAdapter{
tun: tun,
events: make(chan wgTun.Event, 1),
mtu: mtu,
logger: logger,
debugTun: os.Getenv("SINGBOX_TS_TUN_DEBUG") != "",
}
if linuxTUN, ok := tun.(singTun.LinuxTUN); ok {
adapter.linuxTUN = linuxTUN
}
adapter.events <- wgTun.EventUp
return adapter, nil
}
func (a *tunDeviceAdapter) File() *os.File {
return nil
}
func (a *tunDeviceAdapter) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
if a.linuxTUN != nil {
n, err := a.linuxTUN.BatchRead(bufs, offset-singTun.PacketOffset, sizes)
if err == nil {
for i := 0; i < n; i++ {
a.debugPacket("read", bufs[i][offset:offset+sizes[i]])
}
}
return n, err
}
if offset < singTun.PacketOffset {
return 0, io.ErrShortBuffer
}
readBuf := bufs[0][offset-singTun.PacketOffset:]
n, err := a.tun.Read(readBuf)
if err == nil {
if n < singTun.PacketOffset {
return 0, io.ErrUnexpectedEOF
}
sizes[0] = n - singTun.PacketOffset
a.debugPacket("read", readBuf[singTun.PacketOffset:n])
return 1, nil
}
if errors.Is(err, singTun.ErrTooManySegments) {
err = wgTun.ErrTooManySegments
}
return 0, err
}
func (a *tunDeviceAdapter) Write(bufs [][]byte, offset int) (count int, err error) {
if a.linuxTUN != nil {
for i := range bufs {
a.debugPacket("write", bufs[i][offset:])
}
return a.linuxTUN.BatchWrite(bufs, offset)
}
for _, packet := range bufs {
a.debugPacket("write", packet[offset:])
if singTun.PacketOffset > 0 {
common.ClearArray(packet[offset-singTun.PacketOffset : offset])
singTun.PacketFillHeader(packet[offset-singTun.PacketOffset:], singTun.PacketIPVersion(packet[offset:]))
}
_, err = a.tun.Write(packet[offset-singTun.PacketOffset:])
if err != nil {
return 0, err
}
}
// WireGuard will not read count.
return 0, nil
}
func (a *tunDeviceAdapter) MTU() (int, error) {
return a.mtu, nil
}
func (a *tunDeviceAdapter) Name() (string, error) {
return a.tun.Name()
}
func (a *tunDeviceAdapter) Events() <-chan wgTun.Event {
return a.events
}
func (a *tunDeviceAdapter) Close() error {
var err error
a.closeOnce.Do(func() {
close(a.events)
err = a.tun.Close()
})
return err
}
func (a *tunDeviceAdapter) BatchSize() int {
if a.linuxTUN != nil {
return a.linuxTUN.BatchSize()
}
return 1
}
func (a *tunDeviceAdapter) debugPacket(direction string, packet []byte) {
if !a.debugTun || a.logger == nil {
return
}
var counter *atomic.Uint32
switch direction {
case "read":
counter = &a.readCount
case "write":
counter = &a.writeCount
default:
return
}
if counter.Add(1) > 8 {
return
}
sample := packet
if len(sample) > 64 {
sample = sample[:64]
}
a.logger.Trace("tailscale tun ", direction, " len=", len(packet), " head=", hex.EncodeToString(sample))
}

View File

@@ -0,0 +1,117 @@
//go:build windows
package tailscale
import (
"errors"
"os"
"sync"
"sync/atomic"
singTun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/logger"
wgTun "github.com/sagernet/wireguard-go/tun"
)
type tunDeviceAdapter struct {
tun singTun.WinTun
nativeTun *singTun.NativeTun
events chan wgTun.Event
mtu atomic.Int64
closeOnce sync.Once
}
func newTunDeviceAdapter(tun singTun.Tun, mtu int, _ logger.ContextLogger) (wgTun.Device, error) {
winTun, ok := tun.(singTun.WinTun)
if !ok {
return nil, errors.New("not a windows tun device")
}
nativeTun, ok := winTun.(*singTun.NativeTun)
if !ok {
return nil, errors.New("unsupported windows tun device")
}
if mtu == 0 {
mtu = 1500
}
adapter := &tunDeviceAdapter{
tun: winTun,
nativeTun: nativeTun,
events: make(chan wgTun.Event, 1),
}
adapter.mtu.Store(int64(mtu))
adapter.events <- wgTun.EventUp
return adapter, nil
}
func (a *tunDeviceAdapter) File() *os.File {
return nil
}
func (a *tunDeviceAdapter) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
packet, release, err := a.tun.ReadPacket()
if err != nil {
return 0, err
}
defer release()
sizes[0] = copy(bufs[0][offset-singTun.PacketOffset:], packet)
return 1, nil
}
func (a *tunDeviceAdapter) Write(bufs [][]byte, offset int) (count int, err error) {
for _, packet := range bufs {
if singTun.PacketOffset > 0 {
singTun.PacketFillHeader(packet[offset-singTun.PacketOffset:], singTun.PacketIPVersion(packet[offset:]))
}
_, err = a.tun.Write(packet[offset-singTun.PacketOffset:])
if err != nil {
return 0, err
}
}
return 0, nil
}
func (a *tunDeviceAdapter) MTU() (int, error) {
return int(a.mtu.Load()), nil
}
func (a *tunDeviceAdapter) ForceMTU(mtu int) {
if mtu <= 0 {
return
}
update := int(a.mtu.Load()) != mtu
a.mtu.Store(int64(mtu))
if update {
select {
case a.events <- wgTun.EventMTUUpdate:
default:
}
}
}
func (a *tunDeviceAdapter) LUID() uint64 {
if a.nativeTun == nil {
return 0
}
return a.nativeTun.LUID()
}
func (a *tunDeviceAdapter) Name() (string, error) {
return a.tun.Name()
}
func (a *tunDeviceAdapter) Events() <-chan wgTun.Event {
return a.events
}
func (a *tunDeviceAdapter) Close() error {
var err error
a.closeOnce.Do(func() {
close(a.events)
err = a.tun.Close()
})
return err
}
func (a *tunDeviceAdapter) BatchSize() int {
return 1
}

View File

@@ -44,7 +44,7 @@ get_version() {
get_ldflags() {
local version
version=$(get_version)
echo "-X 'github.com/sagernet/sing-box/constant.Version=${version}' -s -w -buildid= -checklinkname=0"
echo "-X 'github.com/sagernet/sing-box/constant.Version=${version}' -X 'internal/godebug.defaultGODEBUG=multipathtcp=0' -s -w -buildid= -checklinkname=0"
}
build_sing_box() {

View File

@@ -121,6 +121,7 @@ func (r *Router) Start(stage adapter.StartStage) error {
if C.IsAndroid && r.platformInterface != nil {
needFindProcess = true
}
r.needFindProcess = needFindProcess
if needFindProcess {
if r.platformInterface != nil && r.platformInterface.UsePlatformConnectionOwnerFinder() {
r.processSearcher = newPlatformSearcher(r.platformInterface)
@@ -201,6 +202,10 @@ func (r *Router) AppendTracker(tracker adapter.ConnectionTracker) {
r.trackers = append(r.trackers, tracker)
}
func (r *Router) NeedFindProcess() bool {
return r.needFindProcess
}
func (r *Router) ResetNetwork() {
r.network.ResetNetwork()
r.dns.ResetNetwork()