Compare commits

...

22 Commits

Author SHA1 Message Date
Shtorm
dec3f0713f Update sing-box core 2026-08-09 16:32:34 +03:00
Shtorm
21a23d7488 Fix OpenStream dropping connection errors 2026-08-09 15:03:14 +03:00
Shtorm
cb664d1a4b Fix DNS Fallback 2026-08-09 15:01:36 +03:00
世界
45ca32dcb9 Bump version 2026-08-09 14:57:01 +08:00
世界
93df3ef612 Fix slow open connection declaring lazy headroom 2026-08-09 13:13:24 +08:00
Shtorm
f682ceb8e2 Update packages 2026-08-08 22:56:39 +03:00
Shtorm
545424b86b Fix wireguard 2026-08-08 20:49:55 +03:00
Shtorm
1d10636a5f Fix mieru outbound with fqdn server address 2026-08-08 20:46:52 +03:00
Shtorm
9f34cdb2f8 Fix DefaultGateway 2026-08-08 20:43:13 +03:00
Shtorm
da4c532efb Merge branch 'extended' of https://github.com/shtorm-7/sing-box-extended into extended 2026-08-08 10:33:36 +03:00
Shtorm
97c981764d Add DefaultGateway 2026-08-08 10:33:31 +03:00
Shtorm
3310474137 Fix limiters 2026-08-08 10:11:10 +03:00
Shtorm
6c81ddd00f Fix Wireguard options 2026-08-08 10:11:01 +03:00
Shtorm
da50e2969d Fix XHTTP dialing 2026-08-08 10:10:05 +03:00
世界
7afc0aa2ba Update naiveproxy to v150.0.7871.63-1 2026-08-08 11:27:55 +08:00
Shtorm
1e35987658 Fix typo in Rmux description in README.md
Signed-off-by: Shtorm <108103062+shtorm-7@users.noreply.github.com>
2026-08-07 06:33:09 +03:00
Shtorm
44c9c36c65 Fix typo
Signed-off-by: Shtorm <108103062+shtorm-7@users.noreply.github.com>
2026-08-06 17:14:08 +03:00
Ofek Lev
4c02326989 Fix route_address_set duplicated IP sets causing route creation failure 2026-08-05 14:13:43 +08:00
世界
af40956e58 Update sing 2026-08-04 22:50:40 +08:00
世界
3b22615aec tailscale: Match bare hostnames in preferred_by when search domains exist
MagicDNS resolves single-label names via tailnet search domains, so
route preferred_by should treat them as tailscale domains, matching
official client behavior.
2026-08-04 22:29:14 +08:00
世界
cc1c891400 tailscale: Fix preferred_by matching all addresses with exit node
Exit node peers carry default routes (0.0.0.0/0, ::/0) in their allowed
IPs, so including them in routePrefixes made PreferredAddress report
every address as preferred.
2026-08-04 22:17:04 +08:00
世界
3d85cbd7f0 tailscale: Fix handle peer DNS query 2026-08-04 21:08:00 +08:00
38 changed files with 705 additions and 327 deletions

View File

@@ -1 +1 @@
617d38f41f935b46a68f550d9add2e38abb3f168 ec9a39c5ba3b4a8d625ede04deaf3c9020afb916

View File

@@ -10,4 +10,4 @@ git -C $PROJECTS/cronet-go fetch origin go_dev
go get -x github.com/sagernet/cronet-go/all@$(git -C $PROJECTS/cronet-go rev-parse origin/go_dev) go get -x github.com/sagernet/cronet-go/all@$(git -C $PROJECTS/cronet-go rev-parse origin/go_dev)
go get -x github.com/sagernet/cronet-go@$(git -C $PROJECTS/cronet-go rev-parse origin/go_dev) go get -x github.com/sagernet/cronet-go@$(git -C $PROJECTS/cronet-go rev-parse origin/go_dev)
go mod tidy go mod tidy
git -C $PROJECTS/cronet-go rev-parse origin/dev > "$SCRIPT_DIR/CRONET_GO_VERSION" git -C $PROJECTS/cronet-go rev-parse origin/go_dev > "$SCRIPT_DIR/CRONET_GO_VERSION"

View File

@@ -175,24 +175,25 @@ jobs:
with: with:
ndk-version: r28 ndk-version: r28
- name: Clone cronet-go - name: Clone cronet-go
if: matrix.naive if: matrix.naive && matrix.variant != 'purego'
run: | run: |
set -xeuo pipefail set -xeuo pipefail
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION) CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
git init ~/cronet-go git init ~/cronet-go
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION" git -C ~/cronet-go sparse-checkout set --no-cone '/*' '!/lib'
git -C ~/cronet-go fetch --depth=1 --filter=blob:none origin "$CRONET_GO_VERSION"
git -C ~/cronet-go checkout FETCH_HEAD git -C ~/cronet-go checkout FETCH_HEAD
git -C ~/cronet-go submodule update --init --recursive --depth=1 git -C ~/cronet-go submodule update --init --recursive --depth=1
- name: Regenerate Debian keyring - name: Regenerate Debian keyring
if: matrix.naive if: matrix.naive && matrix.variant != 'purego'
run: | run: |
set -xeuo pipefail set -xeuo pipefail
rm -f ~/cronet-go/naiveproxy/src/build/linux/sysroot_scripts/keyring.gpg rm -f ~/cronet-go/naiveproxy/src/build/linux/sysroot_scripts/keyring.gpg
cd ~/cronet-go cd ~/cronet-go
GPG_TTY=/dev/null ./naiveproxy/src/build/linux/sysroot_scripts/generate_keyring.sh GPG_TTY=/dev/null ./naiveproxy/src/build/linux/sysroot_scripts/generate_keyring.sh
- name: Download Chromium toolchain - name: Download Chromium toolchain
if: matrix.naive if: matrix.naive && matrix.variant != 'purego'
run: | run: |
set -xeuo pipefail set -xeuo pipefail
cd ~/cronet-go cd ~/cronet-go
@@ -202,7 +203,7 @@ jobs:
go run ./cmd/build-naive --target=linux/${{ matrix.arch }} download-toolchain go run ./cmd/build-naive --target=linux/${{ matrix.arch }} download-toolchain
fi fi
- name: Set Chromium toolchain environment - name: Set Chromium toolchain environment
if: matrix.naive if: matrix.naive && matrix.variant != 'purego'
run: | run: |
set -xeuo pipefail set -xeuo pipefail
cd ~/cronet-go cd ~/cronet-go
@@ -252,9 +253,15 @@ jobs:
- name: Extract libcronet.so - name: Extract libcronet.so
if: matrix.variant == 'purego' && matrix.naive if: matrix.variant == 'purego' && matrix.naive
run: | run: |
cd ~/cronet-go set -xeuo pipefail
CGO_ENABLED=0 go build -v -o "$RUNNER_TEMP/build-naive" ./cmd/build-naive CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
GOPROXY=direct GOSUMDB=off "$RUNNER_TEMP/build-naive" extract-lib --target ${{ matrix.os }}/${{ matrix.arch }} -o $GITHUB_WORKSPACE/dist LIBRARY_PATH="lib/${{ matrix.os }}_${{ matrix.arch }}/libcronet.so"
git init "$RUNNER_TEMP/cronet-go-lib"
git -C "$RUNNER_TEMP/cronet-go-lib" remote add origin https://github.com/sagernet/cronet-go.git
git -C "$RUNNER_TEMP/cronet-go-lib" sparse-checkout set --no-cone "/$LIBRARY_PATH"
git -C "$RUNNER_TEMP/cronet-go-lib" fetch --depth=1 --filter=blob:none origin "$CRONET_GO_VERSION"
git -C "$RUNNER_TEMP/cronet-go-lib" checkout FETCH_HEAD
cp "$RUNNER_TEMP/cronet-go-lib/$LIBRARY_PATH" dist/
- name: Build (glibc) - name: Build (glibc)
if: matrix.variant == 'glibc' if: matrix.variant == 'glibc'
run: | run: |
@@ -621,15 +628,13 @@ jobs:
if: matrix.naive if: matrix.naive
run: | run: |
$CRONET_GO_VERSION = Get-Content .github/CRONET_GO_VERSION $CRONET_GO_VERSION = Get-Content .github/CRONET_GO_VERSION
git init "$env:RUNNER_TEMP/cronet-go" $LIBRARY_PATH = "lib/windows_${{ matrix.arch }}/libcronet.dll"
git -C "$env:RUNNER_TEMP/cronet-go" remote add origin https://github.com/sagernet/cronet-go.git git init "$env:RUNNER_TEMP/cronet-go-lib"
git -C "$env:RUNNER_TEMP/cronet-go" fetch --depth=1 origin "$CRONET_GO_VERSION" git -C "$env:RUNNER_TEMP/cronet-go-lib" remote add origin https://github.com/sagernet/cronet-go.git
git -C "$env:RUNNER_TEMP/cronet-go" checkout FETCH_HEAD git -C "$env:RUNNER_TEMP/cronet-go-lib" sparse-checkout set --no-cone "/$LIBRARY_PATH"
$env:CGO_ENABLED = "0" git -C "$env:RUNNER_TEMP/cronet-go-lib" fetch --depth=1 --filter=blob:none origin "$CRONET_GO_VERSION"
go -C "$env:RUNNER_TEMP/cronet-go" build -v -o "$env:RUNNER_TEMP/build-naive.exe" ./cmd/build-naive git -C "$env:RUNNER_TEMP/cronet-go-lib" checkout FETCH_HEAD
$env:GOPROXY = "direct" Copy-Item "$env:RUNNER_TEMP/cronet-go-lib/$LIBRARY_PATH" dist
$env:GOSUMDB = "off"
& "$env:RUNNER_TEMP/build-naive.exe" extract-lib --target windows/${{ matrix.arch }} -o dist
- name: Archive - name: Archive
if: matrix.naive if: matrix.naive
run: | run: |

View File

@@ -63,7 +63,8 @@ jobs:
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION) CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
git init ~/cronet-go git init ~/cronet-go
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION" git -C ~/cronet-go sparse-checkout set --no-cone '/*' '!/lib'
git -C ~/cronet-go fetch --depth=1 --filter=blob:none origin "$CRONET_GO_VERSION"
git -C ~/cronet-go checkout FETCH_HEAD git -C ~/cronet-go checkout FETCH_HEAD
git -C ~/cronet-go submodule update --init --recursive --depth=1 git -C ~/cronet-go submodule update --init --recursive --depth=1
- name: Regenerate Debian keyring - name: Regenerate Debian keyring

View File

@@ -80,7 +80,8 @@ jobs:
CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION) CRONET_GO_VERSION=$(cat .github/CRONET_GO_VERSION)
git init ~/cronet-go git init ~/cronet-go
git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git git -C ~/cronet-go remote add origin https://github.com/sagernet/cronet-go.git
git -C ~/cronet-go fetch --depth=1 origin "$CRONET_GO_VERSION" git -C ~/cronet-go sparse-checkout set --no-cone '/*' '!/lib'
git -C ~/cronet-go fetch --depth=1 --filter=blob:none origin "$CRONET_GO_VERSION"
git -C ~/cronet-go checkout FETCH_HEAD git -C ~/cronet-go checkout FETCH_HEAD
git -C ~/cronet-go submodule update --init --recursive --depth=1 git -C ~/cronet-go submodule update --init --recursive --depth=1
- name: Regenerate Debian keyring - name: Regenerate Debian keyring

View File

@@ -42,7 +42,7 @@ Sing-box with extended features.
### Transports ### Transports
- **mKCP** — Reliable UDP-based transport - **mKCP** — Reliable UDP-based transport
- **XHTTP** — Modern XRAY transport - **XHTTP** — Modern XRAY transport
- **rmux** — Improved smux multiplex protocol - **Rmux** — Improved smux multiplexer
### Services ### Services
- **Admin Panel** — Web-based management interface - **Admin Panel** — Web-based management interface

View File

@@ -156,10 +156,6 @@ func (c *slowOpenConn) WriterReplaceable() bool {
return c.conn.Load() != nil return c.conn.Load() != nil
} }
func (c *slowOpenConn) LazyHeadroom() bool {
return c.conn.Load() == nil
}
func (c *slowOpenConn) NeedHandshake() bool { func (c *slowOpenConn) NeedHandshake() bool {
return c.conn.Load() == nil return c.conn.Load() == nil
} }

View File

@@ -43,7 +43,7 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
} }
servers[i] = server servers[i] = server
} }
strategy, err := CreateStrategy(options.Strategy, servers, logger) strategy, err := CreateStrategy(options.Strategy, servers, logger, options.Timeout.Build())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -2,18 +2,20 @@ package fallback
import ( import (
"context" "context"
"time"
mDNS "github.com/miekg/dns" mDNS "github.com/miekg/dns"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
) )
type ExchangeStrategy = func(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) type ExchangeStrategy = func(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error)
func parallelStrategy(servers []adapter.DNSTransport, logger logger.ContextLogger) ExchangeStrategy { func parallelStrategy(servers []adapter.DNSTransport, logger logger.ContextLogger, timeout time.Duration) ExchangeStrategy {
return func(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { return func(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
queryCtx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel() defer cancel()
type result struct { type result struct {
response *mDNS.Msg response *mDNS.Msg
@@ -22,10 +24,13 @@ func parallelStrategy(servers []adapter.DNSTransport, logger logger.ContextLogge
results := make(chan result) results := make(chan result)
for _, server := range servers { for _, server := range servers {
go func() { go func() {
response, err := server.Exchange(queryCtx, message) response, err := checkExchangeResponse(server.Exchange(ctx, message))
if err != nil {
logger.InfoContext(ctx, E.Cause(err, "resolve failed for server ", server.Tag()))
}
select { select {
case results <- result{response, err}: case results <- result{response, err}:
case <-queryCtx.Done(): case <-ctx.Done():
} }
}() }()
} }
@@ -46,12 +51,17 @@ func parallelStrategy(servers []adapter.DNSTransport, logger logger.ContextLogge
} }
} }
func sequentialStrategy(servers []adapter.DNSTransport, logger logger.ContextLogger) ExchangeStrategy { func sequentialStrategy(servers []adapter.DNSTransport, logger logger.ContextLogger, timeout time.Duration) ExchangeStrategy {
return func(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { return func(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
var lastErr error var lastErr error
for _, server := range servers { for index, server := range servers {
response, err := server.Exchange(ctx, message) exchangeCtx, exchangeCancel := context.WithTimeout(ctx, perAttemptTimeout(ctx, len(servers)-index))
response, err := checkExchangeResponse(server.Exchange(exchangeCtx, message))
exchangeCancel()
if err != nil { if err != nil {
logger.InfoContext(ctx, E.Cause(err, "resolve failed for server ", server.Tag()))
lastErr = err lastErr = err
continue continue
} }
@@ -61,12 +71,30 @@ func sequentialStrategy(servers []adapter.DNSTransport, logger logger.ContextLog
} }
} }
func CreateStrategy(strategy string, servers []adapter.DNSTransport, logger logger.ContextLogger) (ExchangeStrategy, error) { func checkExchangeResponse(response *mDNS.Msg, err error) (*mDNS.Msg, error) {
if err != nil {
return nil, err
}
if response.Rcode != mDNS.RcodeSuccess && response.Rcode != mDNS.RcodeNameError {
return nil, E.New("bad response rcode: ", mDNS.RcodeToString[response.Rcode])
}
return response, nil
}
func perAttemptTimeout(ctx context.Context, remaining int) time.Duration {
deadline, _ := ctx.Deadline()
return time.Until(deadline) / time.Duration(remaining)
}
func CreateStrategy(strategy string, servers []adapter.DNSTransport, logger logger.ContextLogger, timeout time.Duration) (ExchangeStrategy, error) {
if timeout <= 0 {
timeout = C.DNSTimeout
}
switch strategy { switch strategy {
case "parallel": case "parallel":
return parallelStrategy(servers, logger), nil return parallelStrategy(servers, logger, timeout), nil
case "", "sequential": case "", "sequential":
return sequentialStrategy(servers, logger), nil return sequentialStrategy(servers, logger, timeout), nil
default: default:
return nil, E.New("strategy not found: ", strategy) return nil, E.New("strategy not found: ", strategy)
} }

View File

@@ -2,6 +2,11 @@
icon: material/alert-decagram icon: material/alert-decagram
--- ---
#### 1.13.18
* Update naiveproxy to v150.0.7871.63-1
* Fixes and improvements
#### 1.13.16 #### 1.13.16
* Remove client metadata from AnyTLS requests by default **1** * Remove client metadata from AnyTLS requests by default **1**

View File

@@ -44,10 +44,13 @@
"i3": "<b 0xc70000000108...", "i3": "<b 0xc70000000108...",
"i4": "<b 0xc70000000108...", "i4": "<b 0xc70000000108...",
"i5": "<b 0xc70000000108...", "i5": "<b 0xc70000000108...",
"j1": "<b 0xc70000000108...", "header_protection_key": "QGg8AFRn6qKfTB7cT3FWH1WGx3np+OKzlNuQUrqIBmI=",
"j2": "<b 0xc70000000108...", "content_padding_addition": "50-100",
"j3": "<b 0xc70000000108...", "rekey_after_time": 120,
"itime": 50, "rekey_timeout": 5,
"reject_after_time": 180,
"keepalive_timeout": 10,
"max_handshake_attempts": 20
} }
} }
], ],

View File

@@ -34,7 +34,8 @@
// - "parallel": query all servers concurrently. Returns // - "parallel": query all servers concurrently. Returns
// the first successful response (cancelling the rest), or the last // the first successful response (cancelling the rest), or the last
// error if all servers failed. // error if all servers failed.
"strategy": "sequential" "strategy": "sequential",
"timeout": "10s" // overall budget for the whole fallback exchange
} }
], ],
"disable_cache": true, "disable_cache": true,

View File

@@ -19,11 +19,7 @@
"amnezia": { "amnezia": {
"jc": 120, "jc": 120,
"jmin": 23, "jmin": 23,
"jmax": 911, "jmax": 911
"h1": 1,
"h2": 2,
"h3": 3,
"h4": 4
}, },
"profile": { "profile": {
"detour": "direct" "detour": "direct"

View File

@@ -0,0 +1,53 @@
{
"log": {
"level": "error"
},
"dns": {
"servers": [
{
"type": "local",
"tag": "default"
}
]
},
"endpoints": [
{
"type": "vpn-client",
"tag": "vpn",
"address": "10.0.0.2",
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe",
"outbound": {
"type": "vless",
"tag": "vless-out",
"server": "0.0.0.0",
"server_port": 8000,
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
"network": "tcp"
}
}
],
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen_port": 7897
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct-out"
}
],
"route": {
"rules": [
{
"domain_suffix": ".internal",
"outbound": "vpn",
"override_gateway": "10.0.0.3"
}
],
"final": "direct-out",
"auto_detect_interface": true
}
}

View File

@@ -0,0 +1,40 @@
{
"log": {
"level": "error"
},
"dns": {
"servers": [
{
"type": "local",
"tag": "default"
}
]
},
"endpoints": [
{
"type": "vpn-client",
"tag": "vpn",
"address": "10.0.0.3",
"key": "3d74d616-2502-4c17-9cc3-92c366550f4f",
"outbound": {
"type": "vless",
"tag": "vless-out",
"server": "0.0.0.0",
"server_port": 8000,
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937",
"network": "tcp"
}
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct-out"
}
],
"route": {
"final": "direct-out",
"default_domain_resolver": "default",
"auto_detect_interface": true
}
}

View File

@@ -0,0 +1,61 @@
{
"log": {
"level": "error"
},
"dns": {
"servers": [
{
"type": "local",
"tag": "default"
}
]
},
"endpoints": [
{
"type": "vpn-server",
"tag": "vpn",
"address": "10.0.0.1",
"users": [
{
"address": "10.0.0.2",
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe"
},
{
"address": "10.0.0.3",
"key": "3d74d616-2502-4c17-9cc3-92c366550f4f"
}
],
"inbounds": [
{
"type": "vless",
"tag": "vless-in",
"listen": "0.0.0.0",
"listen_port": 8000,
"users": [
{
"name": "vless",
"uuid": "9b65b7e1-04c8-4717-8f45-2aa61fd25937"
}
]
}
]
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct-out"
}
],
"route": {
"rules": [
{
"source_ip_cidr": "10.0.0.0/24",
"outbound": "vpn"
}
],
"final": "direct-out",
"default_domain_resolver": "default",
"auto_detect_interface": true
}
}

View File

@@ -16,6 +16,7 @@
"tag": "vpn", "tag": "vpn",
"address": "10.0.0.2", "address": "10.0.0.2",
"key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe", "key": "1c9b2ccf-b0c0-4c26-868d-a55a4edad3fe",
"default_gateway": "10.0.0.3",
"outbound": { "outbound": {
"type": "vless", "type": "vless",
"tag": "vless-out", "tag": "vless-out",
@@ -40,13 +41,7 @@
} }
], ],
"route": { "route": {
"rules": [ "final": "vpn",
{
"outbound": "vpn",
"override_gateway": "10.0.0.3"
}
],
"final": "direct-out",
"auto_detect_interface": true "auto_detect_interface": true
} }
} }

View File

@@ -15,6 +15,7 @@
"type": "vpn-server", "type": "vpn-server",
"tag": "vpn", "tag": "vpn",
"address": "10.0.0.1", "address": "10.0.0.1",
"default_gateway": "10.0.0.2",
"users": [ "users": [
{ {
"address": "10.0.0.2", "address": "10.0.0.2",
@@ -47,8 +48,7 @@
"rules": [ "rules": [
{ {
"inbound": "vless-in", "inbound": "vless-in",
"outbound": "vpn", "outbound": "vpn"
"override_gateway": "10.0.0.2"
} }
], ],
"final": "direct-out", "final": "direct-out",

View File

@@ -15,6 +15,7 @@
"type": "vpn-server", "type": "vpn-server",
"tag": "vpn", "tag": "vpn",
"address": "10.0.0.1", "address": "10.0.0.1",
"default_gateway": "10.0.0.2",
"users": [ "users": [
{ {
"address": "10.0.0.2", "address": "10.0.0.2",
@@ -51,13 +52,7 @@
} }
], ],
"route": { "route": {
"rules": [ "final": "vpn",
{
"outbound": "vpn",
"override_gateway": "10.0.0.2"
}
],
"final": "direct-out",
"default_domain_resolver": "default", "default_domain_resolver": "default",
"auto_detect_interface": true "auto_detect_interface": true
} }

View File

@@ -26,23 +26,17 @@
"jc": 120, "jc": 120,
"jmin": 23, "jmin": 23,
"jmax": 911, "jmax": 911,
"s1": 1,
"s2": 2,
"s3": 3,
"s4": 4,
"h1": 1,
"h2": 2,
"h3": 3,
"h4": 4,
"i1": "<b 0xc70000000108...", "i1": "<b 0xc70000000108...",
"i2": "<b 0xc70000000108...", "i2": "<b 0xc70000000108...",
"i3": "<b 0xc70000000108...", "i3": "<b 0xc70000000108...",
"i4": "<b 0xc70000000108...", "i4": "<b 0xc70000000108...",
"i5": "<b 0xc70000000108...", "i5": "<b 0xc70000000108...",
"j1": "<b 0xc70000000108...", "content_padding_addition": "50-100",
"j2": "<b 0xc70000000108...", "rekey_after_time": 120,
"j3": "<b 0xc70000000108...", "rekey_timeout": 5,
"itime": 50 "reject_after_time": 180,
"keepalive_timeout": 10,
"max_handshake_attempts": 20
}, },
"profile": { "profile": {
"detour": "direct", "detour": "direct",

78
go.mod
View File

@@ -44,13 +44,13 @@ require (
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/cors v1.2.1 github.com/sagernet/cors v1.2.1
github.com/sagernet/cronet-go v0.0.0-20260712143338-d22f2ea3630e github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b
github.com/sagernet/cronet-go/all v0.0.0-20260712143338-d22f2ea3630e github.com/sagernet/cronet-go/all v0.0.0-20260807162344-ec9a39c5ba3b
github.com/sagernet/fswatch v0.1.2 github.com/sagernet/fswatch v0.1.2
github.com/sagernet/gomobile v0.1.12 github.com/sagernet/gomobile v0.1.12
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1 github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 github.com/sagernet/quic-go v0.59.0-sing-box-mod.4
github.com/sagernet/sing v0.8.12-0.20260726145744-ef2df370afca github.com/sagernet/sing v0.8.12
github.com/sagernet/sing-mux v0.3.5 github.com/sagernet/sing-mux v0.3.5
github.com/sagernet/sing-quic v0.6.4-0.20260803041914-d83826c306d7 github.com/sagernet/sing-quic v0.6.4-0.20260803041914-d83826c306d7
github.com/sagernet/sing-shadowsocks v0.2.8 github.com/sagernet/sing-shadowsocks v0.2.8
@@ -59,8 +59,8 @@ require (
github.com/sagernet/sing-tun v0.8.12-0.20260727151122-3a09076491df github.com/sagernet/sing-tun v0.8.12-0.20260727151122-3a09076491df
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
github.com/sagernet/smux v1.5.50-sing-box-mod.1 github.com/sagernet/smux v1.5.50-sing-box-mod.1
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.8 github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.9
github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20260224074747-506b7631853c github.com/sagernet/wireguard-go v0.0.4
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.0.2 github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.0.2
github.com/shtorm-7/workerpool v0.5.0 github.com/shtorm-7/workerpool v0.5.0
@@ -122,7 +122,7 @@ require (
go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gvisor.dev/gvisor v0.0.0-20260408064518-65a410b0d584 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/libc v1.72.0 // indirect modernc.org/libc v1.72.0 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect modernc.org/memory v1.11.0 // indirect
@@ -184,35 +184,35 @@ require (
github.com/prometheus-community/pro-bing v0.4.0 // indirect github.com/prometheus-community/pro-bing v0.4.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect
github.com/safchain/ethtool v0.3.0 // indirect github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260712142643-1e5048bd5587 // indirect github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-mod.2 // indirect github.com/sagernet/nftables v0.3.0-mod.2 // indirect
github.com/shtorm-7/rmux v1.0.0 // indirect github.com/shtorm-7/rmux v1.0.0 // indirect
@@ -246,9 +246,9 @@ require (
lukechampine.com/blake3 v1.4.1 lukechampine.com/blake3 v1.4.1
) )
replace github.com/sagernet/wireguard-go => github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.5.0 replace github.com/sagernet/wireguard-go => github.com/shtorm-7/wireguard-go v0.0.4-extended-1.5.1
replace github.com/sagernet/tailscale => github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2 replace github.com/sagernet/tailscale => github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.9-extended-1.0.2
replace github.com/ameshkov/dnscrypt/v2 => github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0 replace github.com/ameshkov/dnscrypt/v2 => github.com/shtorm-7/dnscrypt/v2 v2.4.0-extended-1.0.0
@@ -260,6 +260,6 @@ replace github.com/Diniboy1123/connect-ip-go => github.com/shtorm-7/connect-ip-g
replace github.com/shtorm-7/go-cache/v2 => github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.2.0 replace github.com/shtorm-7/go-cache/v2 => github.com/shtorm-7/go-cache/v2 v2.1.0-extended-1.2.0
replace github.com/sagernet/sing => github.com/shtorm-7/sing v0.8.10-extended-1.2.0 replace github.com/sagernet/sing => github.com/shtorm-7/sing v0.8.12-extended-1.2.0
replace github.com/sagernet/sing-mux => github.com/shtorm-7/sing-mux v0.3.4-extended-1.1.0 replace github.com/sagernet/sing-mux => github.com/shtorm-7/sing-mux v0.3.5-extended-1.0.0

142
go.sum
View File

@@ -351,68 +351,68 @@ 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/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 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI= github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/cronet-go v0.0.0-20260712143338-d22f2ea3630e h1:Y5mhsZrYuZ9jraIqg7hg1fw4zoVreae81fWvmozbCsQ= github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b h1:SfX10lRxbq9ixHm4OZ4/7q+TSpK4Gh0lEVrPaIAB/K0=
github.com/sagernet/cronet-go v0.0.0-20260712143338-d22f2ea3630e/go.mod h1:T/mwtrpC4JlWfScw73CmSBvHzIvc7BatQ1MhRr+cYNw= github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b/go.mod h1:T/mwtrpC4JlWfScw73CmSBvHzIvc7BatQ1MhRr+cYNw=
github.com/sagernet/cronet-go/all v0.0.0-20260712143338-d22f2ea3630e h1:rdNlS1dRSi7jQe/ingFf7QmV9ZCUZMbxAfHnBrdVW7g= github.com/sagernet/cronet-go/all v0.0.0-20260807162344-ec9a39c5ba3b h1:ilEtBdfFgdHmCY3q/Gzp3wRDILYAT8wOh+jVAEAT77U=
github.com/sagernet/cronet-go/all v0.0.0-20260712143338-d22f2ea3630e/go.mod h1:WNl4xfTNuR+f7SObmuBtrk0p4MhlmvuuiWYoty3U52E= github.com/sagernet/cronet-go/all v0.0.0-20260807162344-ec9a39c5ba3b/go.mod h1:Mr2MlNGQYovxvSPGo0+HEgXJ+uITwvPMtp7kdZO8T9w=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260712142643-1e5048bd5587 h1:ENmDXbGH92/jsMwhjIxK2a0URkA8ILC3npjqmTGj0Yc= github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260807161529-8d42107dcdfc h1:JxjIRahHFgA766NTMNoQH5FHcSjo3OVdQbqMuqqvOSQ=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw= github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:5xn/EZOO5LriSEih91thvTuR+gxb59jNxNQiB/KQPEc= github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:U1M7GkzYchNnahyGiSGD1xAHJXoCiae3E8DDPWaeliA=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM= github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260712142643-1e5048bd5587 h1:2/UN0LvWnAM0Sc9B/zE2qWylGrX2hQXhknyQyTlvz4k= github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260807161529-8d42107dcdfc h1:Vtm7UuE69wKPNUakYjlxscX16LkQTEX1Uh6qf2YZj3Y=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260712142643-1e5048bd5587/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc= github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:SDULc9o0HkneJD38H1G+HRLby69zfdtyQk5qaKtn/tU= github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:w48m0+ic4Y4pmW9ZIAo7o70fq/ioL1pgIKCggvvZiOU=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ= github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:hfcM9YccWN4O2LENHN16Jgm4g1/1PRouV0RmvvrS1f0= github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:1XkXOj47J9kPTBYZXIPraLG8V3vtQ2p6kI+EuLkNQVs=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs= github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:arY9CL3C7lwJfG2Cdz2ZLvHLzT1wnzCVyAomNER6CrY= github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:++dHyER9VY0KIfN18i0J2uQcp/DMySqVsUtSEso6mcU=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0= github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:HrtCf6KPJsW9KJ4L4T8HMW1vx+3xefLRLObpjZEtzF4= github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:Jd84+nEjqapOYqxbbRaYD9WsqV/+UacrPngpzT3JOW4=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0= github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:Inzzp4hyvcC0lawhndaK8iwEN0vGLDJLCThbaKZpksg= github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:2q/pUYTulaxitrk+Uuwu+nnPeJCwoWCI8YB0b5JU5Pg=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4= github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:qN6vqr9nFnZqjZXhLez+7tMcbm3FMfhaJRWNMlK3SLI= github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:KuR8ZjycdnaHpdlwER2BJcyQxFV89+Ci1MC7hQuDQtM=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo= github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260712142643-1e5048bd5587 h1:Zwzpw6555p3rWw844JxNZ+5iRqli6ZBOEtMH3qq1c7s= github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260807161529-8d42107dcdfc h1:5oWj5iZn7tg9wOC4VMltItUE87FDxubwICgtjSp+HAM=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ= github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260712142643-1e5048bd5587 h1:CwsGHfo1HwvEhA2Wnh3O8WxazN3d4Un44LXhiuIvA/w= github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260807161529-8d42107dcdfc h1:EZBIqNjit9xGM1w9xKcQVzU3gtyNdEcgXPfmeVuTinM=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU= github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:Pn6vsVFOJkj9q/XKBOZosfuDLB6luNMLkqq6YLUPXEk= github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:yOIHOVXOlmbvcs1Gm2xfcr84+mvAVN0zdXycfLgr3DE=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI= github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260712142643-1e5048bd5587 h1:vgRWcEr2jlgICj28XwDMXPQcy8mTob1ZcO+tPdiZjes= github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260807161529-8d42107dcdfc h1:4uVapCorUiSKJ2xTi8BNLOTq878kFv3l+6+aDeYpuiE=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ= github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260712142643-1e5048bd5587 h1:uiO62HvSAdRJR3d1Jc4duxWig3kkdwHBSQ3TmUFqw48= github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260807161529-8d42107dcdfc h1:fm2GA6JTp48rigyUtuzgu/G9WbMZ+o5QBylks1Noss0=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260712142643-1e5048bd5587/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0= github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:mXioRdq9h2YlIr9XGM51kXgIkToywwVfnGmJiaz/uzM= github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:kWx/l/TCx2hdUKnNn2uyKZAGkCeqm+vkhr84wZJkSgU=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s= github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260712142643-1e5048bd5587 h1:oOlfOL+sq0KNZRsk06+5KgvtKw2TEXl2EZnLakTx+WM= github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260807161529-8d42107dcdfc h1:XnHUep8+amHCWW3WWCD6/QAZYAXkcLUeCZqs0SnXp7M=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ= github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260712142643-1e5048bd5587 h1:UDCa0lYiSUXD4wbxA/G5pGPSXhJaesCQCh5IEiZKb3M= github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260807161529-8d42107dcdfc h1:F7rgwvKytTxeU3G3RpZlH0o77J6OmbZpxaUkTWk1+cE=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow= github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260712142643-1e5048bd5587 h1:Yg2Ut7mPs0GK4W6p9LDh8RrDOnhTe4YpvG/WeJyUqMo= github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260807161529-8d42107dcdfc h1:lpq6aztgg4Luspo4V4SresrCg7+bXlpZ45EfAwpWabg=
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk= github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk=
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260712142643-1e5048bd5587 h1:NtagC/YHvucD0Azh86aVghOk4z5f7oOAAy25Uw4knXQ= github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260807161529-8d42107dcdfc h1:o66baLwgg/aI14jCX6lrQ+1EPkn7RS4cFWJQRKXYXqM=
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E= github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E=
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260712142643-1e5048bd5587 h1:INzfLHBKjgJxoUDBeCaiB9hYqjhn5yfudkMz2phYypA= github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260807161529-8d42107dcdfc h1:7tSY8HPMvf/piEi0TywA/dQWyp1lAJH9U/qyn4+DKY8=
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260712142643-1e5048bd5587/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8= github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8=
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260712142643-1e5048bd5587 h1:IX5NCEV9nojHdjpOkxKVN4L5FUuA5nwawXlSTdFXjCE= github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260807161529-8d42107dcdfc h1:zgHBT1uv5UQ/GHVMn4j6NXelH7ukIo0IQQLsntV+VfA=
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260712142643-1e5048bd5587/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w= github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w=
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260712142643-1e5048bd5587 h1:pzgA94sR7kvrP+H86/to2oN3efYORQmh0Q3b6AyfRJQ= github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260807161529-8d42107dcdfc h1:HNzghnPzq2RK5TXjl5QIVPm44FKzw6pds1GQINAYD80=
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0= github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0=
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260712142643-1e5048bd5587 h1:nkS6jhF90E24PW37Xemhs6+hUM0l2iRcQcZFhPJL8R8= github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260807161529-8d42107dcdfc h1:xAaqyzbYqGPJbKCa6onmX7Bs2HXM1mvbEdMtdAiXplA=
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs= github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs=
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260712142643-1e5048bd5587 h1:EbydHlp6vWdqhb8e8zBPVJv/F3HPob+FgO6rsSdNSr0= github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260807161529-8d42107dcdfc h1:dHay8gOPaH4kwcTnxQeAEZaIw88Y0V6Gmef3HkdX31U=
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260712142643-1e5048bd5587/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc= github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:sSfVTswgqQZJqh9wTP0Acvvo5/qYARAoHegRYwZ+gyU= github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:NLAel/XTGYWExz5gpFzlsX+BZba3/6Wgz1xwOzux9r0=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4= github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:lHqqbALbKdJdq/1rcI4yyg3zvibHDw7wmhDYVbjy498= github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:/k71v3Hs2m6mpDTWVm2W6spZNd+do9U+1jBYTOIPj3g=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc= github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260712142643-1e5048bd5587 h1:23GyWjb58Nk9a7WXgRfFUiTrMD9kvYSRHx73AaBthYc= github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:HUB2CvXtgggtQNW5ix670s9Qd+vCiy5aFKEFJO5/UL8=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260712142643-1e5048bd5587/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc= github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260712142643-1e5048bd5587 h1:64EMjgVuZMD4TX5b7oWUfWRVp8aVl3hg4aWrriQKOWo= github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:rWdo0pRYdrGvZ0turVxLqsXsizj+AS/nivMHX0RHtLw=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8= github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260712142643-1e5048bd5587 h1:x8cvgMQUs0EgVwt3iT/isRQ9KImvXvPobLKO5bHTN4o= github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:+3Fe+RdrbyEGyi66Kt8BromMTFGVw6+8ZTEqKF4kCaU=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260712142643-1e5048bd5587/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw= github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
github.com/sagernet/fswatch v0.1.2 h1:/TT7k4mkce1qFPxamLO842WjqBgbTBiXP2mlUjp9PFk= github.com/sagernet/fswatch v0.1.2 h1:/TT7k4mkce1qFPxamLO842WjqBgbTBiXP2mlUjp9PFk=
github.com/sagernet/fswatch v0.1.2/go.mod h1:5BpGmpUQVd3Mc5r313HRpvADHRg3/rKn5QbwFteB880= github.com/sagernet/fswatch v0.1.2/go.mod h1:5BpGmpUQVd3Mc5r313HRpvADHRg3/rKn5QbwFteB880=
github.com/sagernet/gomobile v0.1.12 h1:XwzjZaclFF96deLqwAgK8gU3w0M2A8qxgDmhV+A0wjg= github.com/sagernet/gomobile v0.1.12 h1:XwzjZaclFF96deLqwAgK8gU3w0M2A8qxgDmhV+A0wjg=
@@ -449,16 +449,16 @@ github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0 h1:iBLll4ZZG8ULQcHWs6gGslZW
github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0/go.mod h1:3rvdhwdPABkwKBdvgMt3VwMn9uSq8hpoHRezZ5jRJU0= github.com/shtorm-7/mtg-multi v1.11.0-extended-1.0.0/go.mod h1:3rvdhwdPABkwKBdvgMt3VwMn9uSq8hpoHRezZ5jRJU0=
github.com/shtorm-7/rmux v1.0.0 h1:oNXshcZVUEDU9u5ylqh3VBFQwus5LFH5yZD9KMTkSSw= github.com/shtorm-7/rmux v1.0.0 h1:oNXshcZVUEDU9u5ylqh3VBFQwus5LFH5yZD9KMTkSSw=
github.com/shtorm-7/rmux v1.0.0/go.mod h1:6tVwUpgjMcCTi3iami7le2/rqhfR6aFPhwuyFzGNQcw= github.com/shtorm-7/rmux v1.0.0/go.mod h1:6tVwUpgjMcCTi3iami7le2/rqhfR6aFPhwuyFzGNQcw=
github.com/shtorm-7/sing v0.8.10-extended-1.2.0 h1:5yw9j0+P2QkRWvxBvb71wvNdpAlHmmpBv4hj2gqvass= github.com/shtorm-7/sing v0.8.12-extended-1.2.0 h1:/CYLFBi+Xrj0R6hFvYoqmDfcLe3WB6McZq5t9UvVhrc=
github.com/shtorm-7/sing v0.8.10-extended-1.2.0/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA= github.com/shtorm-7/sing v0.8.12-extended-1.2.0/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
github.com/shtorm-7/sing-mux v0.3.4-extended-1.1.0 h1:rSDID4on/Z9OherBpvsvKqyuj7dM2b4q+YBJFcpLGpI= github.com/shtorm-7/sing-mux v0.3.5-extended-1.0.0 h1:AfCNqpQlrdWnEd64wXN+cmoHlPMAszre+Wpkg/O8wvA=
github.com/shtorm-7/sing-mux v0.3.4-extended-1.1.0/go.mod h1:1qHy7cnbDQSY2OWL7CybproWC0t5Ia/DIuNHAzB6ZCA= github.com/shtorm-7/sing-mux v0.3.5-extended-1.0.0/go.mod h1:1qHy7cnbDQSY2OWL7CybproWC0t5Ia/DIuNHAzB6ZCA=
github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0 h1:OjvqZOhYSi9eIJGYC0iPDPqvgo/asujvhAGkFzbZ5/Y= github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0 h1:OjvqZOhYSi9eIJGYC0iPDPqvgo/asujvhAGkFzbZ5/Y=
github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY= github.com/shtorm-7/sing-vmess v0.2.8-extended-1.0.0/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2 h1:hSMjh97OszszOd8HrzpaYUQH9dWRRBluJCbwQyz8ZOk= github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.9-extended-1.0.2 h1:ho7bMq/aUFnNhFbdtrqlW3gyZAsnCtYMXEcyoSNo65Q=
github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.7-extended-1.0.2/go.mod h1:TYIIqO5sZpWq873rLIeO2usszSMUpR3h6WdqVVs65ug= github.com/shtorm-7/tailscale v1.92.4-sing-box-1.13-mod.9-extended-1.0.2/go.mod h1:rPn4ZaNbMdbCNBRj3bYCTFCHqB2LcxDgJJrFoaTavTc=
github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.5.0 h1:7YhTkPhDUK81OV9mRMH26Efobh0VCGlig/HgHAlsf5U= github.com/shtorm-7/wireguard-go v0.0.4-extended-1.5.1 h1:TJ8tinN4A2DRy33RiYiK3ST10Q93O2G/9ZabSUnb2iQ=
github.com/shtorm-7/wireguard-go v0.0.2-beta.1-extended-1.5.0/go.mod h1:Me2JlCDYHxnd0mnuX7L5LXAeDHCltI7vSKq3eTE6SVE= github.com/shtorm-7/wireguard-go v0.0.4-extended-1.5.1/go.mod h1:1VZmkU7yPAZVE0XKufUqqAhEY1RnWUcyZLmry85y+7A=
github.com/shtorm-7/workerpool v0.5.0 h1:NPZuNgyH0EUm4aQsTL09xR1iV+7GCFw6jX9Z4aAVp2s= github.com/shtorm-7/workerpool v0.5.0 h1:NPZuNgyH0EUm4aQsTL09xR1iV+7GCFw6jX9Z4aAVp2s=
github.com/shtorm-7/workerpool v0.5.0/go.mod h1:NI0pUZgmGu0BdKO9j3mct1DNZmgXbyTS9foorljdH6E= github.com/shtorm-7/workerpool v0.5.0/go.mod h1:NI0pUZgmGu0BdKO9j3mct1DNZmgXbyTS9foorljdH6E=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
@@ -668,8 +668,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20260408064518-65a410b0d584 h1:QyFROp5Ew7XZWKPtp8ap78z4gpY6xHpJIEdHgVA4bzA=
gvisor.dev/gvisor v0.0.0-20260408064518-65a410b0d584/go.mod h1:xQ2PWgHmWJA/Ph4i1q1jBm39BKhc3W0DXqWoDSyuBOY=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=

View File

@@ -429,4 +429,5 @@ type SDNSDNSServerOptions struct {
type FallbackDNSServerOptions struct { type FallbackDNSServerOptions struct {
Servers []string `json:"servers"` Servers []string `json:"servers"`
Strategy string `json:"strategy,omitempty"` Strategy string `json:"strategy,omitempty"`
Timeout badoption.Duration `json:"timeout,omitempty"`
} }

View File

@@ -11,7 +11,24 @@ type WARPEndpointOptions struct {
Workers int `json:"workers,omitempty"` Workers int `json:"workers,omitempty"`
PreallocatedBuffersPerPool uint32 `json:"preallocated_buffers_per_pool,omitempty"` PreallocatedBuffersPerPool uint32 `json:"preallocated_buffers_per_pool,omitempty"`
DisablePauses bool `json:"disable_pauses,omitempty"` DisablePauses bool `json:"disable_pauses,omitempty"`
Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"` Amnezia *WARPAmnezia `json:"amnezia,omitempty"`
Profile CloudflareProfile `json:"profile,omitempty"` Profile CloudflareProfile `json:"profile,omitempty"`
DialerOptions DialerOptions
} }
type WARPAmnezia struct {
JC int `json:"jc,omitempty"`
JMin int `json:"jmin,omitempty"`
JMax int `json:"jmax,omitempty"`
I1 string `json:"i1,omitempty"`
I2 string `json:"i2,omitempty"`
I3 string `json:"i3,omitempty"`
I4 string `json:"i4,omitempty"`
I5 string `json:"i5,omitempty"`
ContentPaddingAddition *badoption.Range[uint32] `json:"content_padding_addition,omitempty"`
RekeyAfterTime *badoption.Range[uint32] `json:"rekey_after_time,omitempty"`
RekeyTimeout *badoption.Range[uint32] `json:"rekey_timeout,omitempty"`
RejectAfterTime *badoption.Range[uint32] `json:"reject_after_time,omitempty"`
KeepaliveTimeout *badoption.Range[uint32] `json:"keepalive_timeout,omitempty"`
MaxHandshakeAttempts *badoption.Range[uint32] `json:"max_handshake_attempts,omitempty"`
}

View File

@@ -48,8 +48,11 @@ type WireGuardAmnezia struct {
I3 string `json:"i3,omitempty"` I3 string `json:"i3,omitempty"`
I4 string `json:"i4,omitempty"` I4 string `json:"i4,omitempty"`
I5 string `json:"i5,omitempty"` I5 string `json:"i5,omitempty"`
J1 string `json:"j1,omitempty"` HeaderProtectionKey string `json:"header_protection_key,omitempty"`
J2 string `json:"j2,omitempty"` ContentPaddingAddition *badoption.Range[uint32] `json:"content_padding_addition,omitempty"`
J3 string `json:"j3,omitempty"` RekeyAfterTime *badoption.Range[uint32] `json:"rekey_after_time,omitempty"`
ITime int64 `json:"itime,omitempty"` RekeyTimeout *badoption.Range[uint32] `json:"rekey_timeout,omitempty"`
RejectAfterTime *badoption.Range[uint32] `json:"reject_after_time,omitempty"`
KeepaliveTimeout *badoption.Range[uint32] `json:"keepalive_timeout,omitempty"`
MaxHandshakeAttempts *badoption.Range[uint32] `json:"max_handshake_attempts,omitempty"`
} }

View File

@@ -106,7 +106,12 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination
if err != nil { if err != nil {
return nil, err return nil, err
} }
return h.strategy.wrapConn(ctx, conn, adapter.ContextFrom(ctx), true) wrappedConn, err := h.strategy.wrapConn(ctx, conn, adapter.ContextFrom(ctx), true)
if err != nil {
conn.Close()
return nil, err
}
return wrappedConn, nil
} }
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
@@ -114,12 +119,17 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
if err != nil { if err != nil {
return nil, err return nil, err
} }
return h.strategy.wrapPacketConn(ctx, conn, adapter.ContextFrom(ctx), true) wrappedConn, err := h.strategy.wrapPacketConn(ctx, conn, adapter.ContextFrom(ctx), true)
if err != nil {
conn.Close()
return nil, err
}
return wrappedConn, nil
} }
func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
ctx = adapter.WithContext(ctx, &metadata) ctx = adapter.WithContext(ctx, &metadata)
conn, err := h.strategy.wrapConn(ctx, conn, &metadata, false) wrappedConn, err := h.strategy.wrapConn(ctx, conn, &metadata, false)
if err != nil { if err != nil {
h.logger.ErrorContext(ctx, err) h.logger.ErrorContext(ctx, err)
N.CloseOnHandshakeFailure(conn, onClose, err) N.CloseOnHandshakeFailure(conn, onClose, err)
@@ -127,7 +137,7 @@ func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata
} }
metadata.Inbound = h.Tag() metadata.Inbound = h.Tag()
metadata.InboundType = h.Type() metadata.InboundType = h.Type()
h.router.RouteConnectionEx(ctx, conn, metadata, onClose) h.router.RouteConnectionEx(ctx, wrappedConn, metadata, onClose)
return return
} }

View File

@@ -2,6 +2,7 @@ package bandwidth
import ( import (
"context" "context"
"io"
"net" "net"
"strconv" "strconv"
"sync" "sync"
@@ -191,7 +192,7 @@ func (s *UsersBandwidthStrategy) getStrategy(ctx context.Context, metadata *adap
} }
type bwConnEntry struct { type bwConnEntry struct {
conn net.Conn conn io.Closer
} }
type ManagerBandwidthStrategy struct { type ManagerBandwidthStrategy struct {
@@ -251,7 +252,25 @@ func (s *ManagerBandwidthStrategy) wrapPacketConn(ctx context.Context, conn net.
if !ok { if !ok {
return nil, E.New("user strategy not found: ", user) return nil, E.New("user strategy not found: ", user)
} }
return strategy.wrapPacketConn(ctx, conn, metadata, reverse) wrapped, err := strategy.wrapPacketConn(ctx, conn, metadata, reverse)
if err != nil {
return nil, err
}
entry := &bwConnEntry{conn: conn}
s.mtx.Lock()
s.conns[user] = append(s.conns[user], entry)
s.mtx.Unlock()
return onclose.NewPacketConn(wrapped, func() {
s.mtx.Lock()
entries := s.conns[user]
for i, e := range entries {
if e == entry {
s.conns[user] = append(entries[:i], entries[i+1:]...)
break
}
}
s.mtx.Unlock()
}), nil
} }
func (s *ManagerBandwidthStrategy) UpdateStrategies(strategies map[string]BandwidthStrategy) { func (s *ManagerBandwidthStrategy) UpdateStrategies(strategies map[string]BandwidthStrategy) {

View File

@@ -82,7 +82,12 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination
if err != nil { if err != nil {
return nil, err return nil, err
} }
return h.strategy.wrapConn(ctx, conn, adapter.ContextFrom(ctx), true) wrappedConn, err := h.strategy.wrapConn(ctx, conn, adapter.ContextFrom(ctx), true)
if err != nil {
conn.Close()
return nil, err
}
return wrappedConn, nil
} }
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
@@ -90,11 +95,16 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
if err != nil { if err != nil {
return nil, err return nil, err
} }
return h.strategy.wrapPacketConn(ctx, conn, adapter.ContextFrom(ctx), true) wrappedConn, err := h.strategy.wrapPacketConn(ctx, conn, adapter.ContextFrom(ctx), true)
if err != nil {
conn.Close()
return nil, err
}
return wrappedConn, nil
} }
func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
conn, err := h.strategy.wrapConn(ctx, conn, &metadata, false) wrappedConn, err := h.strategy.wrapConn(ctx, conn, &metadata, false)
if err != nil { if err != nil {
if err.Error() != "traffic limit exceeded" { if err.Error() != "traffic limit exceeded" {
h.logger.ErrorContext(ctx, err) h.logger.ErrorContext(ctx, err)
@@ -104,7 +114,7 @@ func (h *Outbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata
} }
metadata.Inbound = h.Tag() metadata.Inbound = h.Tag()
metadata.InboundType = h.Type() metadata.InboundType = h.Type()
h.router.RouteConnectionEx(ctx, conn, metadata, onClose) h.router.RouteConnectionEx(ctx, wrappedConn, metadata, onClose)
} }
func (h *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { func (h *Outbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {

View File

@@ -2,6 +2,7 @@ package traffic
import ( import (
"context" "context"
"io"
"net" "net"
"sync" "sync"
@@ -37,11 +38,11 @@ func NewDefaultWrapStrategy(limiterStrategy TrafficLimiterStrategy, connWrapper
func (s *DefaultWrapStrategy) wrapConn(ctx context.Context, conn net.Conn, metadata *adapter.InboundContext, reverse bool) (net.Conn, error) { func (s *DefaultWrapStrategy) wrapConn(ctx context.Context, conn net.Conn, metadata *adapter.InboundContext, reverse bool) (net.Conn, error) {
limiter, err := s.limiterStrategy.getLimiter(ctx, metadata) limiter, err := s.limiterStrategy.getLimiter(ctx, metadata)
if err != nil { if err != nil {
return conn, err return nil, err
} }
_, err = limiter.Reserve(0) _, err = limiter.Reserve(0)
if err != nil { if err != nil {
return conn, err return nil, err
} }
return s.connWrapper(ctx, conn, limiter, reverse), nil return s.connWrapper(ctx, conn, limiter, reverse), nil
} }
@@ -49,11 +50,11 @@ func (s *DefaultWrapStrategy) wrapConn(ctx context.Context, conn net.Conn, metad
func (s *DefaultWrapStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) { func (s *DefaultWrapStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) {
limiter, err := s.limiterStrategy.getLimiter(ctx, metadata) limiter, err := s.limiterStrategy.getLimiter(ctx, metadata)
if err != nil { if err != nil {
return conn, err return nil, err
} }
_, err = limiter.Reserve(0) _, err = limiter.Reserve(0)
if err != nil { if err != nil {
return conn, err return nil, err
} }
return s.packetConnWrapper(ctx, conn, limiter, reverse), nil return s.packetConnWrapper(ctx, conn, limiter, reverse), nil
} }
@@ -73,7 +74,7 @@ func (s *GlobalTrafficStrategy) getLimiter(ctx context.Context, metadata *adapte
} }
type connEntry struct { type connEntry struct {
conn net.Conn conn io.Closer
} }
type ManagerTrafficStrategy struct { type ManagerTrafficStrategy struct {
@@ -94,7 +95,7 @@ func (s *ManagerTrafficStrategy) wrapConn(ctx context.Context, conn net.Conn, me
if err != nil { if err != nil {
return nil, err return nil, err
} }
wrapped, err := strategy.wrapConn(ctx, conn, metadata, reverse) wrappedConn, err := strategy.wrapConn(ctx, conn, metadata, reverse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -102,7 +103,7 @@ func (s *ManagerTrafficStrategy) wrapConn(ctx context.Context, conn net.Conn, me
s.mtx.Lock() s.mtx.Lock()
s.conns[user] = append(s.conns[user], entry) s.conns[user] = append(s.conns[user], entry)
s.mtx.Unlock() s.mtx.Unlock()
return onclose.NewConn(wrapped, func() { return onclose.NewConn(wrappedConn, func() {
s.mtx.Lock() s.mtx.Lock()
entries := s.conns[user] entries := s.conns[user]
for i, e := range entries { for i, e := range entries {
@@ -116,11 +117,29 @@ func (s *ManagerTrafficStrategy) wrapConn(ctx context.Context, conn net.Conn, me
} }
func (s *ManagerTrafficStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) { func (s *ManagerTrafficStrategy) wrapPacketConn(ctx context.Context, conn net.PacketConn, metadata *adapter.InboundContext, reverse bool) (net.PacketConn, error) {
strategy, _, err := s.getStrategy(ctx, metadata) strategy, user, err := s.getStrategy(ctx, metadata)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return strategy.wrapPacketConn(ctx, conn, metadata, reverse) wrappedConn, err := strategy.wrapPacketConn(ctx, conn, metadata, reverse)
if err != nil {
return nil, err
}
entry := &connEntry{conn: conn}
s.mtx.Lock()
s.conns[user] = append(s.conns[user], entry)
s.mtx.Unlock()
return onclose.NewPacketConn(wrappedConn, func() {
s.mtx.Lock()
entries := s.conns[user]
for i, e := range entries {
if e == entry {
s.conns[user] = append(entries[:i], entries[i+1:]...)
break
}
}
s.mtx.Unlock()
}), nil
} }
func (s *ManagerTrafficStrategy) getStrategy(ctx context.Context, metadata *adapter.InboundContext) (TrafficStrategy, string, error) { func (s *ManagerTrafficStrategy) getStrategy(ctx context.Context, metadata *adapter.InboundContext) (TrafficStrategy, string, error) {

View File

@@ -16,6 +16,7 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service"
mieruclient "github.com/enfein/mieru/v3/apis/client" mieruclient "github.com/enfein/mieru/v3/apis/client"
mierucommon "github.com/enfein/mieru/v3/apis/common" mierucommon "github.com/enfein/mieru/v3/apis/common"
@@ -27,8 +28,11 @@ import (
type Outbound struct { type Outbound struct {
outbound.Adapter outbound.Adapter
ctx context.Context
dnsRouter adapter.DNSRouter
dialer N.Dialer dialer N.Dialer
logger log.ContextLogger logger log.ContextLogger
options option.MieruOutboundOptions
client mieruclient.Client client mieruclient.Client
} }
@@ -41,26 +45,42 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Outbound{
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeMieru, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
ctx: ctx,
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
dialer: outboundDialer,
logger: logger,
options: options,
}, nil
}
config, err := buildMieruClientConfig(options, mieruDialer{dialer: outboundDialer}) func (o *Outbound) Start(stage adapter.StartStage) error {
if stage != adapter.StartStatePostStart {
return nil
}
serverOptions := o.options
if M.IsDomainName(serverOptions.Server) {
addresses, err := o.dnsRouter.Lookup(o.ctx, serverOptions.Server, adapter.DNSQueryOptions{})
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to build mieru client config: %w", err) return fmt.Errorf("failed to resolve mieru server address: %w", err)
}
serverOptions.Server = addresses[0].String()
}
config, err := buildMieruClientConfig(serverOptions, mieruDialer{dialer: o.dialer})
if err != nil {
return fmt.Errorf("failed to build mieru client config: %w", err)
} }
c := mieruclient.NewClient() c := mieruclient.NewClient()
if err := c.Store(config); err != nil { if err := c.Store(config); err != nil {
return nil, fmt.Errorf("failed to store mieru client config: %w", err) return fmt.Errorf("failed to store mieru client config: %w", err)
} }
if err := c.Start(); err != nil { if err := c.Start(); err != nil {
return nil, fmt.Errorf("failed to start mieru client: %w", err) return fmt.Errorf("failed to start mieru client: %w", err)
} }
logger.NoticeContext(ctx, "mieru client is started") o.logger.NoticeContext(o.ctx, "mieru client is started")
o.client = c
return &Outbound{ return nil
Adapter: outbound.NewAdapterWithDialerOptions(C.TypeMieru, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
dialer: outboundDialer,
logger: logger,
client: c,
}, nil
} }
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

View File

@@ -27,9 +27,10 @@ import (
"github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route/rule" R "github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing-tun/ping" "github.com/sagernet/sing-tun/ping"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
@@ -60,6 +61,7 @@ import (
"github.com/sagernet/tailscale/wgengine/router" "github.com/sagernet/tailscale/wgengine/router"
"github.com/sagernet/tailscale/wgengine/wgcfg" "github.com/sagernet/tailscale/wgengine/wgcfg"
mDNS "github.com/miekg/dns"
"go4.org/netipx" "go4.org/netipx"
) )
@@ -96,6 +98,7 @@ type Endpoint struct {
routerCfg *router.Config routerCfg *router.Config
dnsCfg *tsDNS.Config dnsCfg *tsDNS.Config
routeDomains common.TypedValue[map[string]bool] routeDomains common.TypedValue[map[string]bool]
searchDomains atomic.Bool
routePrefixes atomic.Pointer[netipx.IPSet] routePrefixes atomic.Pointer[netipx.IPSet]
acceptRoutes bool acceptRoutes bool
@@ -260,6 +263,8 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
func (t *Endpoint) Start(stage adapter.StartStage) error { func (t *Endpoint) Start(stage adapter.StartStage) error {
switch stage { switch stage {
case adapter.StartStateInitialize:
t.server.PeerDNSQueryHandler = (*peerDNSQueryHandler)(t)
case adapter.StartStateStart: case adapter.StartStateStart:
return t.start() return t.start()
case adapter.StartStatePostStart: case adapter.StartStatePostStart:
@@ -678,9 +683,9 @@ func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destina
}, routeContext, timeout, false) }, routeContext, timeout, false)
if err != nil { if err != nil {
switch { switch {
case rule.IsBypassed(err): case R.IsBypassed(err):
err = nil err = nil
case rule.IsRejected(err): case R.IsRejected(err):
t.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString()) t.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
default: default:
if network == N.NetworkICMP { if network == N.NetworkICMP {
@@ -781,7 +786,11 @@ func (t *Endpoint) PreferredDomain(domain string) bool {
if routeDomains == nil { if routeDomains == nil {
return false return false
} }
return routeDomains[strings.ToLower(domain)] domain = strings.ToLower(domain)
if routeDomains[domain] {
return true
}
return !strings.Contains(domain, ".") && t.searchDomains.Load()
} }
func (t *Endpoint) PreferredAddress(address netip.Addr) bool { func (t *Endpoint) PreferredAddress(address netip.Addr) bool {
@@ -826,10 +835,14 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf
routeDomains[fqdn.WithoutTrailingDot()] = true routeDomains[fqdn.WithoutTrailingDot()] = true
} }
t.routeDomains.Store(routeDomains) t.routeDomains.Store(routeDomains)
t.searchDomains.Store(len(dnsCfg.SearchDomains) > 0)
var builder netipx.IPSetBuilder var builder netipx.IPSetBuilder
for _, peer := range cfg.Peers { for _, peer := range cfg.Peers {
for _, allowedIP := range peer.AllowedIPs { for _, allowedIP := range peer.AllowedIPs {
if allowedIP.Bits() == 0 {
continue
}
builder.AddPrefix(allowedIP) builder.AddPrefix(allowedIP)
} }
} }
@@ -888,3 +901,30 @@ func (c *dnsConfigurtor) GetBaseConfig() (tsDNS.OSConfig, error) {
func (c *dnsConfigurtor) Close() error { func (c *dnsConfigurtor) Close() error {
return nil return nil
} }
type peerDNSQueryHandler Endpoint
func (t *peerDNSQueryHandler) HandlePeerDNSQuery(ctx context.Context, query []byte, sourceAddress netip.AddrPort, allowName func(name string) bool) ([]byte, error) {
var message mDNS.Msg
err := message.Unpack(query)
if err != nil {
return nil, err
}
for _, question := range message.Question {
if allowName != nil && !allowName(question.Name) {
return dns.FixedResponseStatus(&message, mDNS.RcodeRefused).Pack()
}
}
var metadata adapter.InboundContext
metadata.Inbound = t.Tag()
metadata.InboundType = t.Type()
metadata.Source = M.SocksaddrFromNetIP(sourceAddress)
response, err := t.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), &message, adapter.DNSQueryOptions{})
if err != nil {
if !R.IsRejected(err) && !E.IsClosedOrCanceled(err) {
t.logger.ErrorContext(ctx, E.Cause(err, "process peer DNS query"))
}
return nil, err
}
return response.Pack()
}

View File

@@ -35,6 +35,7 @@ type ClientEndpoint struct {
logger logger.ContextLogger logger logger.ContextLogger
address IPv4 address IPv4
key uuid.UUID key uuid.UUID
defaultGateway IPv4
uotClient *uot.Client uotClient *uot.Client
} }
@@ -47,6 +48,13 @@ func NewClientEndpoint(ctx context.Context, router adapter.Router, logger log.Co
if err != nil { if err != nil {
return nil, err return nil, err
} }
defaultGateway := Loopback.As4()
if options.DefaultGateway.IsValid() {
if !options.DefaultGateway.Is4() {
return nil, E.New("invalid default_gateway: ", options.DefaultGateway)
}
defaultGateway = options.DefaultGateway.As4()
}
client := &ClientEndpoint{ client := &ClientEndpoint{
Adapter: outbound.NewAdapter(C.TypeVPNClient, tag, []string{N.NetworkTCP, N.NetworkUDP}, []string{}), Adapter: outbound.NewAdapter(C.TypeVPNClient, tag, []string{N.NetworkTCP, N.NetworkUDP}, []string{}),
ctx: ctx, ctx: ctx,
@@ -54,6 +62,7 @@ func NewClientEndpoint(ctx context.Context, router adapter.Router, logger log.Co
logger: logger, logger: logger,
address: address.As4(), address: address.As4(),
key: key, key: key,
defaultGateway: defaultGateway,
} }
outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx) outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
outbound, err := outboundRegistry.CreateOutbound(ctx, router, logger, options.Outbound.Tag, options.Outbound.Type, options.Outbound.Options) outbound, err := outboundRegistry.CreateOutbound(ctx, router, logger, options.Outbound.Tag, options.Outbound.Type, options.Outbound.Options)
@@ -102,7 +111,7 @@ func (c *ClientEndpoint) DialContext(ctx context.Context, network string, destin
if err != nil { if err != nil {
return nil, err return nil, err
} }
gateway := Loopback.As4() gateway := c.defaultGateway
if metadata := adapter.ContextFrom(ctx); metadata != nil { if metadata := adapter.ContextFrom(ctx); metadata != nil {
if metadata.Gateway != nil { if metadata.Gateway != nil {
gateway = metadata.Gateway.As4() gateway = metadata.Gateway.As4()

View File

@@ -37,6 +37,7 @@ type ServerEndpoint struct {
keys map[IPv4]uuid.UUID keys map[IPv4]uuid.UUID
conns map[IPv4]chan net.Conn conns map[IPv4]chan net.Conn
timeout time.Duration timeout time.Duration
defaultGateway *netip.Addr
uotClient *uot.Client uotClient *uot.Client
} }
@@ -51,6 +52,13 @@ func NewServerEndpoint(ctx context.Context, router adapter.Router, logger log.Co
router: sbUot.NewRouter(router, logger), router: sbUot.NewRouter(router, logger),
address: address.As4(), address: address.As4(),
} }
if options.DefaultGateway.IsValid() {
if !options.DefaultGateway.Is4() {
return nil, E.New("invalid default_gateway: ", options.DefaultGateway)
}
defaultGateway := options.DefaultGateway
server.defaultGateway = &defaultGateway
}
router = NewRouter(router, logger, server.connHandler) router = NewRouter(router, logger, server.connHandler)
inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx) inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
inbounds := make([]adapter.Inbound, len(options.Inbounds)) inbounds := make([]adapter.Inbound, len(options.Inbounds))
@@ -118,7 +126,9 @@ func (s *ServerEndpoint) DialContext(ctx context.Context, network string, destin
} }
} }
if gateway == nil { if gateway == nil {
if destination.IsIPv4() { if s.defaultGateway != nil {
gateway = s.defaultGateway
} else if destination.IsIPv4() {
gateway = &destination.Addr gateway = &destination.Addr
destination = M.Socksaddr{ destination = M.Socksaddr{
Addr: Loopback, Addr: Loopback,
@@ -127,7 +137,8 @@ func (s *ServerEndpoint) DialContext(ctx context.Context, network string, destin
} else { } else {
return nil, E.New("missing gateway") return nil, E.New("missing gateway")
} }
} else if destination.Addr.Compare(*gateway) == 0 { }
if destination.Addr.Compare(*gateway) == 0 {
destination = M.Socksaddr{ destination = M.Socksaddr{
Addr: Loopback, Addr: Loopback,
Port: destination.Port, Port: destination.Port,

View File

@@ -88,6 +88,25 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
} }
peer := config.Peers[0] peer := config.Peers[0]
hostParts := strings.Split(peer.Endpoint.Host, ":") hostParts := strings.Split(peer.Endpoint.Host, ":")
var amnezia *option.WireGuardAmnezia
if options.Amnezia != nil {
amnezia = &option.WireGuardAmnezia{
JC: options.Amnezia.JC,
JMin: options.Amnezia.JMin,
JMax: options.Amnezia.JMax,
I1: options.Amnezia.I1,
I2: options.Amnezia.I2,
I3: options.Amnezia.I3,
I4: options.Amnezia.I4,
I5: options.Amnezia.I5,
ContentPaddingAddition: options.Amnezia.ContentPaddingAddition,
RekeyAfterTime: options.Amnezia.RekeyAfterTime,
RekeyTimeout: options.Amnezia.RekeyTimeout,
RejectAfterTime: options.Amnezia.RejectAfterTime,
KeepaliveTimeout: options.Amnezia.KeepaliveTimeout,
MaxHandshakeAttempts: options.Amnezia.MaxHandshakeAttempts,
}
}
endpoint.endpoint, err = wireguard.NewEndpoint( endpoint.endpoint, err = wireguard.NewEndpoint(
ctx, ctx,
router, router,
@@ -101,7 +120,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
Workers: options.Workers, Workers: options.Workers,
PreallocatedBuffersPerPool: options.PreallocatedBuffersPerPool, PreallocatedBuffersPerPool: options.PreallocatedBuffersPerPool,
DisablePauses: options.DisablePauses, DisablePauses: options.DisablePauses,
Amnezia: options.Amnezia, Amnezia: amnezia,
DialerOptions: options.DialerOptions, DialerOptions: options.DialerOptions,
Address: badoption.Listable[netip.Prefix]{ Address: badoption.Listable[netip.Prefix]{

View File

@@ -93,10 +93,13 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
I3: options.Amnezia.I3, I3: options.Amnezia.I3,
I4: options.Amnezia.I4, I4: options.Amnezia.I4,
I5: options.Amnezia.I5, I5: options.Amnezia.I5,
J1: options.Amnezia.J1, HeaderProtectionKey: options.Amnezia.HeaderProtectionKey,
J2: options.Amnezia.J2, ContentPaddingAddition: options.Amnezia.ContentPaddingAddition,
J3: options.Amnezia.J3, RekeyAfterTime: options.Amnezia.RekeyAfterTime,
ITime: options.Amnezia.ITime, RekeyTimeout: options.Amnezia.RekeyTimeout,
RejectAfterTime: options.Amnezia.RejectAfterTime,
KeepaliveTimeout: options.Amnezia.KeepaliveTimeout,
MaxHandshakeAttempts: options.Amnezia.MaxHandshakeAttempts,
} }
} }
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{ wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{

View File

@@ -3,6 +3,7 @@ package xhttp
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -110,15 +111,18 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, sessio
if body != nil { if body != nil {
method = c.options.GetNormalizedUplinkHTTPMethod() // stream-up/one method = c.options.GetNormalizedUplinkHTTPMethod() // stream-up/one
} }
req, _ := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body) reqCtx, cancel := context.WithCancel(context.WithoutCancel(ctx))
req, _ := http.NewRequestWithContext(reqCtx, method, url, body)
FillStreamRequest(req, sessionId, "", c.options) FillStreamRequest(req, sessionId, "", c.options)
wrc = &WaitReadCloser{Wait: make(chan struct{})} wrc = &WaitReadCloser{Wait: make(chan struct{}), Cancel: cancel}
go func() { go func() {
resp, err := c.client.Do(req) var resp *http.Response
resp, err = c.client.Do(req)
if err != nil { if err != nil {
if !uploadOnly { // stream-down is enough if !uploadOnly && !errors.Is(err, context.Canceled) { // stream-down is enough
c.Close() c.Close()
} }
cancel()
gotConn.Close() gotConn.Close()
common.Close(body) common.Close(body)
wrc.Close() wrc.Close()
@@ -128,6 +132,7 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, sessio
io.Copy(io.Discard, resp.Body) io.Copy(io.Discard, resp.Body)
resp.Body.Close() // if it is called immediately, the upload will be interrupted also resp.Body.Close() // if it is called immediately, the upload will be interrupted also
common.Close(body) common.Close(body)
cancel()
wrc.Close() wrc.Close()
return return
} }
@@ -211,6 +216,7 @@ func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, sessio
type WaitReadCloser struct { type WaitReadCloser struct {
Wait chan struct{} Wait chan struct{}
Cancel context.CancelFunc
io.ReadCloser io.ReadCloser
} }
@@ -233,6 +239,9 @@ func (w *WaitReadCloser) Read(b []byte) (int, error) {
} }
func (w *WaitReadCloser) Close() error { func (w *WaitReadCloser) Close() error {
if w.Cancel != nil {
w.Cancel()
}
if w.ReadCloser != nil { if w.ReadCloser != nil {
return w.ReadCloser.Close() return w.ReadCloser.Close()
} }

View File

@@ -235,17 +235,30 @@ func (e *Endpoint) Start(resolve bool) error {
if e.options.Amnezia.I5 != "" { if e.options.Amnezia.I5 != "" {
ipcConf.WriteString("\ni5=" + e.options.Amnezia.I5) ipcConf.WriteString("\ni5=" + e.options.Amnezia.I5)
} }
if e.options.Amnezia.J1 != "" { if e.options.Amnezia.HeaderProtectionKey != "" {
ipcConf.WriteString("\nj1=" + e.options.Amnezia.J1) headerProtectionKeyBytes, err := base64.StdEncoding.DecodeString(e.options.Amnezia.HeaderProtectionKey)
if err != nil {
return E.Cause(err, "decode header protection key")
} }
if e.options.Amnezia.J2 != "" { ipcConf.WriteString("\nheader_protection_key=" + hex.EncodeToString(headerProtectionKeyBytes))
ipcConf.WriteString("\nj2=" + e.options.Amnezia.J2)
} }
if e.options.Amnezia.J3 != "" { if e.options.Amnezia.ContentPaddingAddition != nil {
ipcConf.WriteString("\nj3=" + e.options.Amnezia.J3) ipcConf.WriteString("\ncontent_padding_addition=" + e.options.Amnezia.ContentPaddingAddition.String())
} }
if e.options.Amnezia.ITime > 0 { if e.options.Amnezia.RekeyAfterTime != nil {
ipcConf.WriteString("\nitime=" + strconv.FormatInt(e.options.Amnezia.ITime, 10)) ipcConf.WriteString("\nrekey_after_time=" + e.options.Amnezia.RekeyAfterTime.String())
}
if e.options.Amnezia.RekeyTimeout != nil {
ipcConf.WriteString("\nrekey_timeout=" + e.options.Amnezia.RekeyTimeout.String())
}
if e.options.Amnezia.RejectAfterTime != nil {
ipcConf.WriteString("\nreject_after_time=" + e.options.Amnezia.RejectAfterTime.String())
}
if e.options.Amnezia.KeepaliveTimeout != nil {
ipcConf.WriteString("\nkeepalive_timeout=" + e.options.Amnezia.KeepaliveTimeout.String())
}
if e.options.Amnezia.MaxHandshakeAttempts != nil {
ipcConf.WriteString("\nmax_handshake_attempts=" + e.options.Amnezia.MaxHandshakeAttempts.String())
} }
} }
for _, peer := range e.peers { for _, peer := range e.peers {

View File

@@ -5,8 +5,8 @@ import (
"net/netip" "net/netip"
"time" "time"
"github.com/sagernet/sing/common/json/badoption"
tun "github.com/sagernet/sing-tun" tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/json/badoption"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -59,8 +59,11 @@ type AmneziaOptions struct {
I3 string I3 string
I4 string I4 string
I5 string I5 string
J1 string HeaderProtectionKey string
J2 string ContentPaddingAddition *badoption.Range[uint32]
J3 string RekeyAfterTime *badoption.Range[uint32]
ITime int64 RekeyTimeout *badoption.Range[uint32]
RejectAfterTime *badoption.Range[uint32]
KeepaliveTimeout *badoption.Range[uint32]
MaxHandshakeAttempts *badoption.Range[uint32]
} }