Compare commits

..

131 Commits

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

Signed-off-by: libtry486 <89328481+libtry486@users.noreply.github.com>
2025-07-07 14:08:22 +08:00
Alireza Ahmadi
69c0f424d9 Fix Outbound deadlock 2025-07-07 14:08:22 +08:00
世界
6e87e49690 documentation: Fix AnyTLS doc 2025-07-07 14:08:22 +08:00
anytls
638e5ad2be Add AnyTLS protocol 2025-07-07 14:08:21 +08:00
世界
366406a558 Migrate to stdlib ECH support 2025-07-07 14:08:21 +08:00
世界
4cf1e58eed Add fallback local DNS server for iOS 2025-07-07 14:08:21 +08:00
世界
5927d7affe Get darwin local DNS server from libresolv 2025-07-07 14:08:21 +08:00
世界
6f5dd46b32 Improve resolve action 2025-07-07 14:08:20 +08:00
世界
7d048a014f Add back port hopping to hysteria 1 2025-07-07 14:08:20 +08:00
xchacha20-poly1305
d51593e8a5 Remove single quotes of raw Moziila certs 2025-07-07 14:08:19 +08:00
世界
fc88d34edc Add Tailscale endpoint 2025-07-07 14:08:19 +08:00
世界
12a3fd3b60 Build legacy binaries with latest Go 2025-07-07 14:08:19 +08:00
世界
b69a381aa8 documentation: Remove outdated icons 2025-07-07 14:08:18 +08:00
世界
7f0419d34c documentation: Certificate store 2025-07-07 14:08:18 +08:00
世界
e758cbcd8c documentation: TLS fragment 2025-07-07 14:08:18 +08:00
世界
8b494f1c4b documentation: Outbound domain resolver 2025-07-07 14:08:17 +08:00
世界
0eaeef81fe documentation: Refactor DNS 2025-07-07 14:08:17 +08:00
世界
25ebabeb63 Add certificate store 2025-07-07 14:08:17 +08:00
世界
d2cb0ee4ae Add TLS fragment support 2025-07-07 14:08:17 +08:00
世界
c81a0ec4d7 refactor: Outbound domain resolver 2025-07-07 14:08:16 +08:00
世界
59fbf08651 refactor: DNS 2025-07-07 14:08:16 +08:00
世界
f792420a78 Bump version 2025-07-07 14:03:10 +08:00
世界
b8502759b5 Fix DNS reject check 2025-07-07 13:57:37 +08:00
世界
6f804adf39 Fix v2rayhttp crash 2025-07-03 21:48:10 +08:00
Kyson
36db31c55a documentation: Fix typo
Co-authored-by: chenqixin <chenqixin@bytedance.com>
2025-06-29 18:54:05 +08:00
世界
4dbbf59c82 Fix logger for acme 2025-06-29 18:44:40 +08:00
世界
832eb4458d release: Fix xcode version 2025-06-29 18:44:40 +08:00
dyhkwong
2cf989d306 Fix inbound with v2ray transport missing InboundOptions 2025-06-25 13:20:00 +08:00
世界
7d3ee29bd0 Also skip duplicate sniff for TCP 2025-06-21 12:57:27 +08:00
世界
cba0e46aba Fix log for rejected connections 2025-06-21 12:57:26 +08:00
世界
9b8ab3e61e Bump version 2025-06-19 11:57:44 +08:00
dyhkwong
47f18e823a Fix: macOS udp find process should use unspecified fallback
be8d63ba8f
2025-06-18 08:34:59 +08:00
40 changed files with 407 additions and 331 deletions

View File

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

View File

@@ -46,7 +46,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: Check input version
if: github.event_name == 'workflow_dispatch'
run: |-
@@ -109,7 +109,7 @@ jobs:
if: ${{ ! matrix.legacy_go }}
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: Cache Legacy Go
if: matrix.require_legacy_go
id: cache-legacy-go
@@ -294,7 +294,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: Setup Android NDK
id: setup-ndk
uses: nttld/setup-ndk@v1
@@ -374,7 +374,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: Setup Android NDK
id: setup-ndk
uses: nttld/setup-ndk@v1
@@ -472,15 +472,15 @@ jobs:
if: matrix.if
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: Setup Xcode stable
if: matrix.if && github.ref == 'refs/heads/main-next'
run: |-
sudo xcode-select -s /Applications/Xcode_16.2.app
sudo xcode-select -s /Applications/Xcode_16.4.app
- name: Setup Xcode beta
if: matrix.if && github.ref == 'refs/heads/dev-next'
run: |-
sudo xcode-select -s /Applications/Xcode_16.2.app
sudo xcode-select -s /Applications/Xcode_16.4.app
- name: Set tag
if: matrix.if
run: |-
@@ -615,7 +615,7 @@ jobs:
path: 'dist'
upload:
name: Upload builds
if: always() && github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone')
if: "!failure() && github.event_name == 'workflow_dispatch' && (inputs.build == 'All' || inputs.build == 'Binary' || inputs.build == 'Android' || inputs.build == 'Apple' || inputs.build == 'macOS-standalone')"
runs-on: ubuntu-latest
needs:
- calculate_version

View File

@@ -28,7 +28,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:

View File

@@ -25,7 +25,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: Check input version
if: github.event_name == 'workflow_dispatch'
run: |-
@@ -66,7 +66,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ^1.24.4
go-version: ^1.24
- name: Setup Android NDK
if: matrix.os == 'android'
uses: nttld/setup-ndk@v1

View File

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

View File

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

View File

@@ -108,6 +108,16 @@ upload_ios_app_store:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath build/SFI.xcarchive -exportOptionsPlist SFI/Upload.plist -allowProvisioningUpdates
export_ios_ipa:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath build/SFI.xcarchive -exportOptionsPlist SFI/Export.plist -allowProvisioningUpdates -exportPath build/SFI && \
cp build/SFI/sing-box.ipa dist/SFI.ipa
upload_ios_ipa:
cd dist && \
cp SFI.ipa "SFI-${VERSION}.ipa" && \
ghr --replace --draft --prerelease "v${VERSION}" "SFI-${VERSION}.ipa"
release_ios: build_ios upload_ios_app_store
build_macos:
@@ -175,6 +185,16 @@ upload_tvos_app_store:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath "build/SFT.xcarchive" -exportOptionsPlist SFI/Upload.plist -allowProvisioningUpdates
export_tvos_ipa:
cd ../sing-box-for-apple && \
xcodebuild -exportArchive -archivePath "build/SFT.xcarchive" -exportOptionsPlist SFI/Export.plist -allowProvisioningUpdates -exportPath build/SFT && \
cp build/SFT/sing-box.ipa dist/SFT.ipa
upload_tvos_ipa:
cd dist && \
cp SFT.ipa "SFT-${VERSION}.ipa" && \
ghr --replace --draft --prerelease "v${VERSION}" "SFT-${VERSION}.ipa"
release_tvos: build_tvos upload_tvos_app_store
update_apple_version:
@@ -225,8 +245,8 @@ lib:
go run ./cmd/internal/build_libbox -target ios
lib_install:
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.6
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.6
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.7
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.7
docs:
venv/bin/mkdocs serve

View File

@@ -53,11 +53,11 @@ type InboundContext struct {
// sniffer
Protocol string
Domain string
Client string
SniffContext any
PacketSniffError error
Protocol string
Domain string
Client string
SniffContext any
SniffError error
// cache

View File

@@ -177,7 +177,7 @@ func publishTestflight(ctx context.Context) error {
}
log.Info(string(platform), " ", tag, " publish")
response, err := client.TestFlight.AddBuildsToBetaGroup(ctx, groupID, []string{build.ID})
if response != nil && response.StatusCode == http.StatusUnprocessableEntity {
if response != nil && (response.StatusCode == http.StatusUnprocessableEntity || response.StatusCode == http.StatusNotFound) {
log.Info("waiting for process")
time.Sleep(15 * time.Second)
continue

View File

@@ -16,15 +16,17 @@ import (
)
var (
debugEnabled bool
target string
platform string
debugEnabled bool
target string
platform string
withTailscale bool
)
func init() {
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
flag.StringVar(&target, "target", "android", "target platform")
flag.StringVar(&platform, "platform", "", "specify platform")
flag.BoolVar(&withTailscale, "with-tailscale", false, "build tailscale for iOS and tvOS")
}
func main() {
@@ -44,8 +46,9 @@ var (
sharedFlags []string
debugFlags []string
sharedTags []string
iosTags []string
darwinTags []string
memcTags []string
notMemcTags []string
debugTags []string
)
@@ -60,8 +63,9 @@ func init() {
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack")
iosTags = append(iosTags, "with_dhcp", "with_low_memory")
darwinTags = append(darwinTags, "with_dhcp")
memcTags = append(memcTags, "with_tailscale")
notMemcTags = append(notMemcTags, "with_low_memory")
debugTags = append(debugTags, "debug")
}
@@ -151,7 +155,10 @@ func buildApple() {
"-v",
"-target", bindTarget,
"-libname=box",
"-tags-macos=" + strings.Join(memcTags, ","),
"-tags-not-macos=with_low_memory",
}
if !withTailscale {
args = append(args, "-tags-macos="+strings.Join(memcTags, ","))
}
if !debugEnabled {
@@ -160,7 +167,10 @@ func buildApple() {
args = append(args, debugFlags...)
}
tags := append(sharedTags, iosTags...)
tags := append(sharedTags, darwinTags...)
if withTailscale {
tags = append(tags, memcTags...)
}
if debugEnabled {
tags = append(tags, debugTags...)
}

View File

@@ -40,12 +40,7 @@ parseLine:
if ruleLine == "" {
continue
}
if strings.Contains(ruleLine, "!") {
continue
}
if strings.Contains(ruleLine, "#") {
ignoredLines++
logger.Debug("ignored unsupported cosmetic filter: ", ruleLine)
if strings.HasPrefix(ruleLine, "!") || strings.HasPrefix(ruleLine, "#") {
continue
}
originRuleLine := ruleLine
@@ -101,7 +96,7 @@ parseLine:
}
if !ignored {
ignoredLines++
logger.Debug("ignored unsupported rule with modifier: ", paramParts[0], ": ", ruleLine)
logger.Debug("ignored unsupported rule with modifier: ", paramParts[0], ": ", originRuleLine)
continue parseLine
}
}
@@ -129,37 +124,35 @@ parseLine:
ruleLine = ruleLine[1 : len(ruleLine)-1]
if ignoreIPCIDRRegexp(ruleLine) {
ignoredLines++
logger.Debug("ignored unsupported rule with IPCIDR regexp: ", ruleLine)
logger.Debug("ignored unsupported rule with IPCIDR regexp: ", originRuleLine)
continue
}
isRegexp = true
} else {
if strings.Contains(ruleLine, "://") {
ruleLine = common.SubstringAfter(ruleLine, "://")
isSuffix = true
}
if strings.Contains(ruleLine, "/") {
ignoredLines++
logger.Debug("ignored unsupported rule with path: ", ruleLine)
logger.Debug("ignored unsupported rule with path: ", originRuleLine)
continue
}
if strings.Contains(ruleLine, "?") || strings.Contains(ruleLine, "&") {
ignoredLines++
logger.Debug("ignored unsupported rule with query: ", ruleLine)
logger.Debug("ignored unsupported rule with query: ", originRuleLine)
continue
}
if strings.Contains(ruleLine, "[") || strings.Contains(ruleLine, "]") {
if strings.Contains(ruleLine, "[") || strings.Contains(ruleLine, "]") ||
strings.Contains(ruleLine, "(") || strings.Contains(ruleLine, ")") ||
strings.Contains(ruleLine, "!") || strings.Contains(ruleLine, "#") {
ignoredLines++
logger.Debug("ignored unsupported cosmetic filter: ", ruleLine)
continue
}
if strings.Contains(ruleLine, "(") || strings.Contains(ruleLine, ")") {
ignoredLines++
logger.Debug("ignored unsupported cosmetic filter: ", ruleLine)
logger.Debug("ignored unsupported cosmetic filter: ", originRuleLine)
continue
}
if strings.Contains(ruleLine, "~") {
ignoredLines++
logger.Debug("ignored unsupported rule modifier: ", ruleLine)
logger.Debug("ignored unsupported rule modifier: ", originRuleLine)
continue
}
var domainCheck string
@@ -178,13 +171,13 @@ parseLine:
_, ipErr := parseADGuardIPCIDRLine(ruleLine)
if ipErr == nil {
ignoredLines++
logger.Debug("ignored unsupported rule with IPCIDR: ", ruleLine)
logger.Debug("ignored unsupported rule with IPCIDR: ", originRuleLine)
continue
}
if M.ParseSocksaddr(domainCheck).Port != 0 {
logger.Debug("ignored unsupported rule with port: ", ruleLine)
logger.Debug("ignored unsupported rule with port: ", originRuleLine)
} else {
logger.Debug("ignored unsupported rule with invalid domain: ", ruleLine)
logger.Debug("ignored unsupported rule with invalid domain: ", originRuleLine)
}
ignoredLines++
continue
@@ -302,7 +295,9 @@ parseLine:
},
}
}
logger.Info("parsed rules: ", len(ruleLines), "/", len(ruleLines)+ignoredLines)
if ignoredLines > 0 {
logger.Info("parsed rules: ", len(ruleLines), "/", len(ruleLines)+ignoredLines)
}
return []option.HeadlessRule{currentRule}, nil
}
@@ -413,11 +408,9 @@ func ignoreIPCIDRRegexp(ruleLine string) bool {
ruleLine = ruleLine[13:]
} else if strings.HasPrefix(ruleLine, "^") {
ruleLine = ruleLine[1:]
} else {
return false
}
_, parseErr := strconv.ParseUint(common.SubstringBefore(ruleLine, "\\."), 10, 8)
return parseErr == nil
return common.Error(strconv.ParseUint(common.SubstringBefore(ruleLine, "\\."), 10, 8)) == nil ||
common.Error(strconv.ParseUint(common.SubstringBefore(ruleLine, "."), 10, 8)) == nil
}
func parseAdGuardHostLine(ruleLine string) (string, error) {

View File

@@ -76,6 +76,8 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
// rup8(sizeof(xtcpcb_n))
itemSize += 208
}
var fallbackUDPProcess string
// skip the first xinpgen(24 bytes) block
for i := 24; i+itemSize <= len(buf); i += itemSize {
// offset of xinpcb_n and xsocket_n
@@ -90,10 +92,12 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
flag := buf[inp+44]
var srcIP netip.Addr
srcIsIPv4 := false
switch {
case flag&0x1 > 0 && isIPv4:
// ipv4
srcIP = netip.AddrFrom4(*(*[4]byte)(buf[inp+76 : inp+80]))
srcIsIPv4 = true
case flag&0x2 > 0 && !isIPv4:
// ipv6
srcIP = netip.AddrFrom16(*(*[16]byte)(buf[inp+64 : inp+80]))
@@ -101,13 +105,21 @@ func findProcessName(network string, ip netip.Addr, port int) (string, error) {
continue
}
if ip != srcIP {
continue
if ip == srcIP {
// xsocket_n.so_last_pid
pid := readNativeUint32(buf[so+68 : so+72])
return getExecPathFromPID(pid)
}
// xsocket_n.so_last_pid
pid := readNativeUint32(buf[so+68 : so+72])
return getExecPathFromPID(pid)
// udp packet connection may be not equal with srcIP
if network == N.NetworkUDP && srcIP.IsUnspecified() && isIPv4 == srcIsIPv4 {
pid := readNativeUint32(buf[so+68 : so+72])
fallbackUDPProcess, _ = getExecPathFromPID(pid)
}
}
if network == N.NetworkUDP && len(fallbackUDPProcess) > 0 {
return fallbackUDPProcess, nil
}
return "", ErrNotFound

View File

@@ -5,13 +5,13 @@ package tls
import (
"context"
"crypto/tls"
"os"
"strings"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/caddyserver/certmagic"
"github.com/libdns/alidns"
@@ -37,7 +37,38 @@ func (w *acmeWrapper) Close() error {
return nil
}
func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Config, adapter.SimpleLifecycle, error) {
type acmeLogWriter struct {
logger logger.Logger
}
func (w *acmeLogWriter) Write(p []byte) (n int, err error) {
logLine := strings.ReplaceAll(string(p), " ", ": ")
switch {
case strings.HasPrefix(logLine, "error: "):
w.logger.Error(logLine[7:])
case strings.HasPrefix(logLine, "warn: "):
w.logger.Warn(logLine[6:])
case strings.HasPrefix(logLine, "info: "):
w.logger.Info(logLine[6:])
case strings.HasPrefix(logLine, "debug: "):
w.logger.Debug(logLine[7:])
default:
w.logger.Debug(logLine)
}
return len(p), nil
}
func (w *acmeLogWriter) Sync() error {
return nil
}
func encoderConfig() zapcore.EncoderConfig {
config := zap.NewProductionEncoderConfig()
config.TimeKey = zapcore.OmitKey
return config
}
func startACME(ctx context.Context, logger logger.Logger, options option.InboundACMEOptions) (*tls.Config, adapter.SimpleLifecycle, error) {
var acmeServer string
switch options.Provider {
case "", "letsencrypt":
@@ -58,14 +89,15 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
} else {
storage = certmagic.Default.Storage
}
zapLogger := zap.New(zapcore.NewCore(
zapcore.NewConsoleEncoder(encoderConfig()),
&acmeLogWriter{logger: logger},
zap.DebugLevel,
))
config := &certmagic.Config{
DefaultServerName: options.DefaultServerName,
Storage: storage,
Logger: zap.New(zapcore.NewCore(
zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()),
os.Stderr,
zap.InfoLevel,
)),
Logger: zapLogger,
}
acmeConfig := certmagic.ACMEIssuer{
CA: acmeServer,
@@ -75,7 +107,7 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
DisableTLSALPNChallenge: options.DisableTLSALPNChallenge,
AltHTTPPort: int(options.AlternativeHTTPPort),
AltTLSALPNPort: int(options.AlternativeTLSPort),
Logger: config.Logger,
Logger: zapLogger,
}
if dnsOptions := options.DNS01Challenge; dnsOptions != nil && dnsOptions.Provider != "" {
var solver certmagic.DNS01Solver
@@ -103,6 +135,7 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
GetConfigForCert: func(certificate certmagic.Certificate) (*certmagic.Config, error) {
return config, nil
},
Logger: zapLogger,
})
config = certmagic.New(cache, *config)
var tlsConfig *tls.Config

View File

@@ -9,8 +9,9 @@ import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
)
func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Config, adapter.SimpleLifecycle, error) {
func startACME(ctx context.Context, logger logger.Logger, options option.InboundACMEOptions) (*tls.Config, adapter.SimpleLifecycle, error) {
return nil, nil, E.New(`ACME is not included in this build, rebuild with -tags with_acme`)
}

View File

@@ -169,7 +169,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
var err error
if options.ACME != nil && len(options.ACME.Domain) > 0 {
//nolint:staticcheck
tlsConfig, acmeService, err = startACME(ctx, common.PtrValueOrDefault(options.ACME))
tlsConfig, acmeService, err = startACME(ctx, logger, common.PtrValueOrDefault(options.ACME))
if err != nil {
return nil, err
}

View File

@@ -195,8 +195,13 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
}
}*/
if responseChecker != nil {
addr, addrErr := MessageToAddresses(response)
if addrErr != nil || !responseChecker(addr) {
var rejected bool
if !(response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError) {
rejected = true
} else {
rejected = !responseChecker(MessageToAddresses(response))
}
if rejected {
if c.rdrc != nil {
c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger)
}
@@ -420,7 +425,10 @@ func (c *Client) lookupToExchange(ctx context.Context, transport adapter.DNSTran
if err != nil {
return nil, err
}
return MessageToAddresses(response)
if response.Rcode != dns.RcodeSuccess {
return nil, RcodeError(response.Rcode)
}
return MessageToAddresses(response), nil
}
func (c *Client) questionCache(question dns.Question, transport adapter.DNSTransport) ([]netip.Addr, error) {
@@ -428,7 +436,10 @@ func (c *Client) questionCache(question dns.Question, transport adapter.DNSTrans
if response == nil {
return nil, ErrNotCached
}
return MessageToAddresses(response)
if response.Rcode != dns.RcodeSuccess {
return nil, RcodeError(response.Rcode)
}
return MessageToAddresses(response), nil
}
func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransport) (*dns.Msg, int) {
@@ -505,10 +516,7 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
}
}
func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) {
if response.Rcode != dns.RcodeSuccess {
return nil, RcodeError(response.Rcode)
}
func MessageToAddresses(response *dns.Msg) []netip.Addr {
addresses := make([]netip.Addr, 0, len(response.Answer))
for _, rawAnswer := range response.Answer {
switch answer := rawAnswer.(type) {
@@ -524,7 +532,7 @@ func MessageToAddresses(response *dns.Msg) ([]netip.Addr, error) {
}
}
}
return addresses, nil
return addresses
}
func wrapError(err error) error {

View File

@@ -3,11 +3,15 @@ package transport
import (
"bytes"
"context"
"errors"
"io"
"net"
"net/http"
"net/url"
"os"
"strconv"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
@@ -39,11 +43,13 @@ func RegisterHTTPS(registry *dns.TransportRegistry) {
type HTTPSTransport struct {
dns.TransportAdapter
logger logger.ContextLogger
dialer N.Dialer
destination *url.URL
headers http.Header
transport *http.Transport
logger logger.ContextLogger
dialer N.Dialer
destination *url.URL
headers http.Header
transportAccess sync.Mutex
transport *http.Transport
transportResetAt time.Time
}
func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteHTTPSDNSServerOptions) (adapter.DNSTransport, error) {
@@ -161,12 +167,33 @@ func (t *HTTPSTransport) Start(stage adapter.StartStage) error {
}
func (t *HTTPSTransport) Close() error {
t.transportAccess.Lock()
defer t.transportAccess.Unlock()
t.transport.CloseIdleConnections()
t.transport = t.transport.Clone()
return nil
}
func (t *HTTPSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
startAt := time.Now()
response, err := t.exchange(ctx, message)
if err != nil {
if errors.Is(err, os.ErrDeadlineExceeded) {
t.transportAccess.Lock()
defer t.transportAccess.Unlock()
if t.transportResetAt.After(startAt) {
return nil, err
}
t.transport.CloseIdleConnections()
t.transport = t.transport.Clone()
t.transportResetAt = time.Now()
}
return nil, err
}
return response, nil
}
func (t *HTTPSTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
exMessage := *message
exMessage.Id = 0
exMessage.Compress = true

View File

@@ -3,7 +3,6 @@ package local
import (
"context"
"math/rand"
"net/netip"
"time"
"github.com/sagernet/sing-box/adapter"
@@ -91,9 +90,9 @@ func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfi
startRacer := func(ctx context.Context, fqdn string) {
response, err := t.tryOneName(ctx, systemConfig, fqdn, message)
if err == nil {
var addresses []netip.Addr
addresses, err = dns.MessageToAddresses(response)
if err == nil && len(addresses) == 0 {
if response.Rcode != mDNS.RcodeSuccess {
err = dns.RcodeError(response.Rcode)
} else if len(dns.MessageToAddresses(response)) == 0 {
err = E.New(fqdn, ": empty result")
}
}

View File

@@ -20,7 +20,8 @@ import (
)
func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
if C.res_init() != 0 {
var state C.res_state
if C.res_ninit(state) != 0 {
return &dnsConfig{
servers: defaultNS,
search: dnsDefaultSearch(),
@@ -33,10 +34,10 @@ func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
conf := &dnsConfig{
ndots: 1,
timeout: 5 * time.Second,
attempts: int(C._res.retry),
attempts: int(state.retry),
}
for i := 0; i < int(C._res.nscount); i++ {
ns := C._res.nsaddr_list[i]
for i := 0; i < int(state.nscount); i++ {
ns := state.nsaddr_list[i]
addr := C.inet_ntoa(ns.sin_addr)
if addr == nil {
continue
@@ -44,7 +45,7 @@ func dnsReadConfig(_ context.Context, _ string) *dnsConfig {
conf.servers = append(conf.servers, C.GoString(addr))
}
for i := 0; ; i++ {
search := C._res.dnsrch[i]
search := state.dnsrch[i]
if search == nil {
break
}

View File

@@ -2,10 +2,34 @@
icon: material/alert-decagram
---
#### 1.12.0-beta.26
#### 1.12.0-beta.33
* Add firewalld compatibility for auto redirect
* Fixes and improvements
### 1.11.15
* Fixes and improvements
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
violated the rules (TestFlight users are not affected)._
#### 1.12.0-beta.32
* Improve tun performance on Apple platforms **1**
* Fixes and improvements
**1**:
We have significantly improved the performance of tun inbound on Apple platforms, especially in the gVisor stack.
### 1.11.14
* Fixes and improvements
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we
violated the rules (TestFlight users are not affected)._
#### 1.12.0-beta.24
* Allow `tls_fragment` and `tls_record_fragment` to be enabled together **1**

View File

@@ -25,7 +25,7 @@ icon: material/new-box
| 类型 | 格式 |
|-------------|---------------------------|
| `wireguard` | [WireGuard](./wiregaurd/) |
| `wireguard` | [WireGuard](./wireguard/) |
| `tailscale` | [Tailscale](./tailscale/) |
#### tag

View File

@@ -59,7 +59,7 @@
{
"external_controller": "0.0.0.0:9090",
"external_ui": "dashboard"
// external_ui_download_detour: "direct"
// "external_ui_download_detour": "direct"
}
```

View File

@@ -59,7 +59,7 @@
{
"external_controller": "0.0.0.0:9090",
"external_ui": "dashboard"
// external_ui_download_detour: "direct"
// "external_ui_download_detour": "direct"
}
```

View File

@@ -64,7 +64,7 @@ icon: material/new-box
"auto_redirect_input_mark": "0x2023",
"auto_redirect_output_mark": "0x2024",
"loopback_address": [
"10.0.7.1"
"10.7.0.1"
],
"strict_route": true,
"route_address": [
@@ -284,7 +284,7 @@ Connection output mark used by `auto_redirect`.
Loopback addresses make TCP connections to the specified address connect to the source address.
Setting option value to `10.0.7.1` achieves the same behavior as SideStore/StosVPN.
Setting option value to `10.7.0.1` achieves the same behavior as SideStore/StosVPN.
When `auto_redirect` is enabled, the same behavior can be achieved for LAN devices (not just local) as a gateway.

View File

@@ -64,7 +64,7 @@ icon: material/new-box
"auto_redirect_input_mark": "0x2023",
"auto_redirect_output_mark": "0x2024",
"loopback_address": [
"10.0.7.1"
"10.7.0.1"
],
"strict_route": true,
"route_address": [
@@ -283,7 +283,7 @@ tun 接口的 IPv6 前缀。
环回地址是用于使指向指定地址的 TCP 连接连接到来源地址的。
将选项值设置为 `10.0.7.1` 可实现与 SideStore/StosVPN 相同的行为。
将选项值设置为 `10.7.0.1` 可实现与 SideStore/StosVPN 相同的行为。
当启用 `auto_redirect` 时,可以作为网关为局域网设备(而不仅仅是本地)实现相同的行为。

View File

@@ -94,18 +94,13 @@ flowchart TB
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
"type": "tls",
"server": "8.8.8.8"
},
{
"tag": "local",
"address": "223.5.5.5",
"detour": "direct"
}
],
"rules": [
{
"outbound": "any",
"server": "local"
"type": "udp",
"server": "223.5.5.5"
}
],
"strategy": "ipv4_only"
@@ -115,7 +110,8 @@ flowchart TB
"type": "tun",
"inet4_address": "172.19.0.1/30",
"auto_route": true,
"strict_route": false
// "auto_redirect": true, // On linux
"strict_route": true
}
],
"outbounds": [
@@ -123,25 +119,23 @@ flowchart TB
{
"type": "direct",
"tag": "direct"
},
{
"type": "dns",
"tag": "dns-out"
}
],
"route": {
"rules": [
{
"protocol": "dns",
"outbound": "dns-out"
"action": "sniff"
},
{
"geoip": [
"private"
],
"protocol": "dns",
"action": "hijack-dns"
},
{
"ip_is_private": true,
"outbound": "direct"
}
],
"default_domain_resolver": "local",
"auto_detect_interface": true
}
}
@@ -155,18 +149,13 @@ flowchart TB
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
"type": "tls",
"server": "8.8.8.8"
},
{
"tag": "local",
"address": "223.5.5.5",
"detour": "direct"
}
],
"rules": [
{
"outbound": "any",
"server": "local"
"type": "udp",
"server": "223.5.5.5"
}
]
},
@@ -176,7 +165,8 @@ flowchart TB
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"auto_route": true,
"strict_route": false
// "auto_redirect": true, // On linux
"strict_route": true
}
],
"outbounds": [
@@ -184,25 +174,23 @@ flowchart TB
{
"type": "direct",
"tag": "direct"
},
{
"type": "dns",
"tag": "dns-out"
}
],
"route": {
"rules": [
{
"protocol": "dns",
"outbound": "dns-out"
"action": "sniff"
},
{
"geoip": [
"private"
],
"protocol": "dns",
"action": "hijack-dns"
},
{
"ip_is_private": true,
"outbound": "direct"
}
],
"default_domain_resolver": "local",
"auto_detect_interface": true
}
}
@@ -216,23 +204,22 @@ flowchart TB
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
"type": "tls",
"server": "8.8.8.8"
},
{
"tag": "local",
"address": "223.5.5.5",
"detour": "direct"
"type": "udp",
"server": "223.5.5.5"
},
{
"tag": "remote",
"address": "fakeip"
"type": "fakeip",
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18"
}
],
"rules": [
{
"outbound": "any",
"server": "local"
},
{
"query_type": [
"A",
@@ -241,11 +228,6 @@ flowchart TB
"server": "remote"
}
],
"fakeip": {
"enabled": true,
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18"
},
"independent_cache": true
},
"inbounds": [
@@ -254,6 +236,7 @@ flowchart TB
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126",
"auto_route": true,
// "auto_redirect": true, // On linux
"strict_route": true
}
],
@@ -262,25 +245,23 @@ flowchart TB
{
"type": "direct",
"tag": "direct"
},
{
"type": "dns",
"tag": "dns-out"
}
],
"route": {
"rules": [
{
"protocol": "dns",
"outbound": "dns-out"
"action": "sniff"
},
{
"geoip": [
"private"
],
"protocol": "dns",
"action": "hijack-dns"
},
{
"ip_is_private": true,
"outbound": "direct"
}
],
"default_domain_resolver": "local",
"auto_detect_interface": true
}
}
@@ -290,54 +271,6 @@ flowchart TB
=== ":material-dns: DNS rules"
```json
{
"dns": {
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
},
{
"tag": "local",
"address": "223.5.5.5",
"detour": "direct"
}
],
"rules": [
{
"outbound": "any",
"server": "local"
},
{
"clash_mode": "Direct",
"server": "local"
},
{
"clash_mode": "Global",
"server": "google"
},
{
"rule_set": "geosite-geolocation-cn",
"server": "local"
}
]
},
"route": {
"rule_set": [
{
"type": "remote",
"tag": "geosite-geolocation-cn",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-cn.srs"
}
]
}
}
```
=== ":material-dns: DNS rules (Enhanced, but slower) (1.9.0+)"
=== ":material-shield-off: With DNS leaks"
```json
@@ -346,35 +279,20 @@ flowchart TB
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
"type": "tls",
"server": "8.8.8.8"
},
{
"tag": "local",
"address": "https://223.5.5.5/dns-query",
"detour": "direct"
"type": "https",
"server": "223.5.5.5"
}
],
"rules": [
{
"outbound": "any",
"server": "local"
},
{
"clash_mode": "Direct",
"server": "local"
},
{
"clash_mode": "Global",
"server": "google"
},
{
"rule_set": "geosite-geolocation-cn",
"server": "local"
},
{
"clash_mode": "Default",
"server": "google"
},
{
"type": "logical",
"mode": "and",
@@ -392,6 +310,7 @@ flowchart TB
]
},
"route": {
"default_domain_resolver": "local",
"rule_set": [
{
"type": "remote",
@@ -425,35 +344,24 @@ flowchart TB
}
```
=== ":material-security: Without DNS leaks, but slower (1.9.0-alpha.2+)"
=== ":material-security: Without DNS leaks, but slower"
```json
{
"dns": {
"servers": [
{
"tag": "google",
"address": "tls://8.8.8.8"
"type": "tls",
"server": "8.8.8.8"
},
{
"tag": "local",
"address": "https://223.5.5.5/dns-query",
"detour": "direct"
"type": "https",
"server": "223.5.5.5"
}
],
"rules": [
{
"outbound": "any",
"server": "local"
},
{
"clash_mode": "Direct",
"server": "local"
},
{
"clash_mode": "Global",
"server": "google"
},
{
"rule_set": "geosite-geolocation-cn",
"server": "local"
@@ -476,6 +384,7 @@ flowchart TB
]
},
"route": {
"default_domain_resolver": "local",
"rule_set": [
{
"type": "remote",
@@ -517,14 +426,13 @@ flowchart TB
{
"type": "direct",
"tag": "direct"
},
{
"type": "block",
"tag": "block"
}
],
"route": {
"rules": [
{
"action": "sniff"
},
{
"type": "logical",
"mode": "or",
@@ -536,20 +444,12 @@ flowchart TB
"port": 53
}
],
"outbound": "dns"
"action": "hijack-dns"
},
{
"ip_is_private": true,
"outbound": "direct"
},
{
"clash_mode": "Direct",
"outbound": "direct"
},
{
"clash_mode": "Global",
"outbound": "default"
},
{
"type": "logical",
"mode": "or",
@@ -565,12 +465,23 @@ flowchart TB
"protocol": "stun"
}
],
"outbound": "block"
"action": "reject"
},
{
"rule_set": [
"geoip-cn",
"geosite-geolocation-cn"
"rule_set": "geosite-geolocation-cn",
"outbound": "direct"
},
{
"type": "logical",
"mode": "and",
"rules": [
{
"rule_set": "geoip-cn"
},
{
"rule_set": "geosite-geolocation-!cn",
"invert": true
}
],
"outbound": "direct"
}
@@ -591,4 +502,4 @@ flowchart TB
]
}
}
```
```

18
go.mod
View File

@@ -25,16 +25,16 @@ require (
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/cors v1.2.1
github.com/sagernet/fswatch v0.1.1
github.com/sagernet/gomobile v0.1.6
github.com/sagernet/gomobile v0.1.7
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
github.com/sagernet/quic-go v0.52.0-beta.1
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b
github.com/sagernet/sing v0.6.12-0.20250704043954-da981379f151
github.com/sagernet/sing-mux v0.3.2
github.com/sagernet/sing-quic v0.5.0-beta.2
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.6.9-0.20250617062442-df4458520f26
github.com/sagernet/sing-tun v0.6.10-0.20250707094843-b2e2674d73e5
github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88
github.com/sagernet/smux v1.5.34-mod.2
github.com/sagernet/tailscale v1.80.3-mod.5
@@ -45,10 +45,10 @@ require (
github.com/vishvananda/netns v0.0.5
go.uber.org/zap v1.27.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.38.0
golang.org/x/crypto v0.39.0
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
golang.org/x/mod v0.24.0
golang.org/x/net v0.40.0
golang.org/x/mod v0.25.0
golang.org/x/net v0.41.0
golang.org/x/sys v0.33.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
google.golang.org/grpc v1.72.0
@@ -107,7 +107,7 @@ require (
github.com/quic-go/qpack v0.5.1 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/nftables v0.3.0-mod.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
@@ -123,9 +123,9 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.33.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect

36
go.sum
View File

@@ -157,19 +157,19 @@ 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/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.6 h1:JkR1ToKOrdoiwULte4pYS5HYdPBzl2N+JNuuwVuLs0k=
github.com/sagernet/gomobile v0.1.6/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
github.com/sagernet/gomobile v0.1.7 h1:I9jCJZTH0weP5MsuydvYHX5QfN/r6Fe8ptAIj1+SJVg=
github.com/sagernet/gomobile v0.1.7/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb h1:pprQtDqNgqXkRsXn+0E8ikKOemzmum8bODjSfDene38=
github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
github.com/sagernet/nftables v0.3.0-mod.1 h1:OMe+qoEAx8EipYAQbD2FI5erVvKmTS9+cYhdpg+vezY=
github.com/sagernet/nftables v0.3.0-mod.1/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b h1:ZjTCYPb5f7aHdf1UpUvE22dVmf7BL8eQ/zLZhjgh7Wo=
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.6.12-0.20250704043954-da981379f151 h1:UCiQ1d/t5Y9uKAL9ir3i06+ClqS93OGGG8oqB82RMCE=
github.com/sagernet/sing v0.6.12-0.20250704043954-da981379f151/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
github.com/sagernet/sing-quic v0.5.0-beta.2 h1:j7KAbBuGmsKwSxVAQL5soJ+wDqxim4/llK2kxB0hSKk=
@@ -180,8 +180,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
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.6.9-0.20250617062442-df4458520f26 h1:N9yAoqtQlawlJzLONjdQvIO3GJLEg9tZBfyKToSi0cM=
github.com/sagernet/sing-tun v0.6.9-0.20250617062442-df4458520f26/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-tun v0.6.10-0.20250707094843-b2e2674d73e5 h1:JHa9vyTie1FbWGofPt4TEpysl7tBeEoiQDtwVK0Scqg=
github.com/sagernet/sing-tun v0.6.10-0.20250707094843-b2e2674d73e5/go.mod h1:c/7Blmaw8GRL4JPvoajBfwUfdzoa2KCMtAnq5Q9AjA0=
github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88 h1:0pVm8sPOel+BoiCddW3pV3cKDKEaSioVTYDdTSKjyFI=
github.com/sagernet/sing-vmess v0.2.4-0.20250605032146-38cc72672c88/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
@@ -263,21 +263,21 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wus
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -293,8 +293,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@@ -130,9 +130,15 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
deprecated.Report(ctx, deprecated.OptionTUNGSO)
}
platformInterface := service.FromContext[platform.Interface](ctx)
tunMTU := options.MTU
if tunMTU == 0 {
tunMTU = 9000
if platformInterface != nil && platformInterface.UnderNetworkExtension() {
// In Network Extension, when MTU exceeds 4064 (4096-UTUN_IF_HEADROOM_SIZE), the performance of tun will drop significantly, which may be a system bug.
tunMTU = 4064
} else {
tunMTU = 9000
}
}
var udpTimeout time.Duration
if options.UDPTimeout != 0 {
@@ -208,7 +214,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
},
udpTimeout: udpTimeout,
stack: options.Stack,
platformInterface: service.FromContext[platform.Interface](ctx),
platformInterface: platformInterface,
platformOptions: common.PtrValueOrDefault(options.Platform),
}
for _, routeAddressSet := range options.RouteAddressSet {

View File

@@ -205,6 +205,10 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
var metadata adapter.InboundContext
metadata.Source = source
metadata.Destination = destination
//nolint:staticcheck
metadata.InboundDetour = h.listener.ListenOptions().Detour
//nolint:staticcheck
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
}

View File

@@ -219,6 +219,10 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
var metadata adapter.InboundContext
metadata.Source = source
metadata.Destination = destination
//nolint:staticcheck
metadata.InboundDetour = h.listener.ListenOptions().Detour
//nolint:staticcheck
metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
}

View File

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

View File

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

View File

@@ -2,7 +2,6 @@ package route
import (
"context"
"errors"
"net"
"time"
@@ -10,7 +9,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
dnsOutbound "github.com/sagernet/sing-box/protocol/dns"
"github.com/sagernet/sing-tun"
R "github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
@@ -65,7 +64,7 @@ func (r *Router) hijackDNSPacket(ctx context.Context, conn N.PacketConn, packetB
func ExchangeDNSPacket(ctx context.Context, router adapter.DNSRouter, logger logger.ContextLogger, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) {
err := exchangeDNSPacket(ctx, router, conn, buffer, metadata, destination)
if err != nil && !errors.Is(err, tun.ErrDrop) && !E.IsClosedOrCanceled(err) {
if err != nil && !R.IsRejected(err) && !E.IsClosedOrCanceled(err) {
logger.ErrorContext(ctx, E.Cause(err, "process DNS packet"))
}
}

View File

@@ -15,7 +15,7 @@ import (
"github.com/sagernet/sing-box/common/sniff"
C "github.com/sagernet/sing-box/constant"
"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-mux"
"github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common"
@@ -49,7 +49,7 @@ func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata
err := r.routeConnection(ctx, conn, metadata, onClose)
if err != nil {
N.CloseOnHandshakeFailure(conn, onClose, err)
if E.IsClosedOrCanceled(err) {
if E.IsClosedOrCanceled(err) || R.IsRejected(err) {
r.logger.DebugContext(ctx, "connection closed: ", err)
} else {
r.logger.ErrorContext(ctx, err)
@@ -99,7 +99,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
var selectedOutbound adapter.Outbound
if selectedRule != nil {
switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute:
case *R.RuleActionRoute:
var loaded bool
selectedOutbound, loaded = r.outbound.Outbound(action.Outbound)
if !loaded {
@@ -110,10 +110,10 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
buf.ReleaseMulti(buffers)
return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag())
}
case *rule.RuleActionReject:
case *R.RuleActionReject:
buf.ReleaseMulti(buffers)
return action.Error(ctx)
case *rule.RuleActionHijackDNS:
case *R.RuleActionHijackDNS:
for _, buffer := range buffers {
conn = bufio.NewCachedConn(conn, buffer)
}
@@ -151,7 +151,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
}))
if err != nil {
conn.Close()
if E.IsClosedOrCanceled(err) {
if E.IsClosedOrCanceled(err) || R.IsRejected(err) {
r.logger.DebugContext(ctx, "connection closed: ", err)
} else {
r.logger.ErrorContext(ctx, err)
@@ -168,7 +168,7 @@ func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn,
err := r.routePacketConnection(ctx, conn, metadata, onClose)
if err != nil {
N.CloseOnHandshakeFailure(conn, onClose, err)
if E.IsClosedOrCanceled(err) {
if E.IsClosedOrCanceled(err) || R.IsRejected(err) {
r.logger.DebugContext(ctx, "connection closed: ", err)
} else {
r.logger.ErrorContext(ctx, err)
@@ -214,7 +214,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
var selectReturn bool
if selectedRule != nil {
switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute:
case *R.RuleActionRoute:
var loaded bool
selectedOutbound, loaded = r.outbound.Outbound(action.Outbound)
if !loaded {
@@ -225,10 +225,10 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
N.ReleaseMultiPacketBuffer(packetBuffers)
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag())
}
case *rule.RuleActionReject:
case *R.RuleActionReject:
N.ReleaseMultiPacketBuffer(packetBuffers)
return action.Error(ctx)
case *rule.RuleActionHijackDNS:
case *R.RuleActionHijackDNS:
return r.hijackDNSPacket(ctx, conn, packetBuffers, metadata, onClose)
}
}
@@ -266,7 +266,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext) error {
if selectedRule == nil {
return nil
}
rejectAction, isReject := selectedRule.Action().(*rule.RuleActionReject)
rejectAction, isReject := selectedRule.Action().(*R.RuleActionReject)
if !isReject {
return nil
}
@@ -342,7 +342,7 @@ func (r *Router) matchRule(
//nolint:staticcheck
if metadata.InboundOptions != common.DefaultValue[option.InboundOptions]() {
if !preMatch && metadata.InboundOptions.SniffEnabled {
newBuffer, newPackerBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{
newBuffer, newPackerBuffers, newErr := r.actionSniff(ctx, metadata, &R.RuleActionSniff{
OverrideDestination: metadata.InboundOptions.SniffOverrideDestination,
Timeout: time.Duration(metadata.InboundOptions.SniffTimeout),
}, inputConn, inputPacketConn, nil)
@@ -357,7 +357,7 @@ func (r *Router) matchRule(
}
}
if C.DomainStrategy(metadata.InboundOptions.DomainStrategy) != C.DomainStrategyAsIS {
fatalErr = r.actionResolve(ctx, metadata, &rule.RuleActionResolve{
fatalErr = r.actionResolve(ctx, metadata, &R.RuleActionResolve{
Strategy: C.DomainStrategy(metadata.InboundOptions.DomainStrategy),
})
if fatalErr != nil {
@@ -394,11 +394,11 @@ match:
}
}
}
var routeOptions *rule.RuleActionRouteOptions
var routeOptions *R.RuleActionRouteOptions
switch action := currentRule.Action().(type) {
case *rule.RuleActionRoute:
case *R.RuleActionRoute:
routeOptions = &action.RuleActionRouteOptions
case *rule.RuleActionRouteOptions:
case *R.RuleActionRouteOptions:
routeOptions = action
}
if routeOptions != nil {
@@ -451,7 +451,7 @@ match:
}
}
switch action := currentRule.Action().(type) {
case *rule.RuleActionSniff:
case *R.RuleActionSniff:
if !preMatch {
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn, buffers)
if newErr != nil {
@@ -468,7 +468,7 @@ match:
selectedRuleIndex = currentRuleIndex
break match
}
case *rule.RuleActionResolve:
case *R.RuleActionResolve:
fatalErr = r.actionResolve(ctx, metadata, action)
if fatalErr != nil {
return
@@ -488,7 +488,7 @@ match:
}
func (r *Router) actionSniff(
ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionSniff,
ctx context.Context, metadata *adapter.InboundContext, action *R.RuleActionSniff,
inputConn net.Conn, inputPacketConn N.PacketConn, inputBuffers []*buf.Buffer,
) (buffer *buf.Buffer, packetBuffers []*N.PacketBuffer, fatalErr error) {
if sniff.Skip(metadata) {
@@ -501,6 +501,9 @@ func (r *Router) actionSniff(
if inputConn != nil {
if len(action.StreamSniffers) == 0 && len(action.PacketSniffers) > 0 {
return
} else if metadata.SniffError != nil && !errors.Is(metadata.SniffError, sniff.ErrNeedMoreData) {
r.logger.DebugContext(ctx, "packet sniff skipped due to previous error: ", metadata.SniffError)
return
}
var streamSniffers []sniff.StreamSniffer
if len(action.StreamSniffers) > 0 {
@@ -525,6 +528,7 @@ func (r *Router) actionSniff(
action.Timeout,
streamSniffers...,
)
metadata.SniffError = err
if err == nil {
//goland:noinspection GoDeprecation
if action.OverrideDestination && M.IsDomainName(metadata.Domain) {
@@ -549,8 +553,8 @@ func (r *Router) actionSniff(
} else if inputPacketConn != nil {
if len(action.PacketSniffers) == 0 && len(action.StreamSniffers) > 0 {
return
} else if metadata.PacketSniffError != nil && !errors.Is(metadata.PacketSniffError, sniff.ErrNeedMoreData) {
r.logger.DebugContext(ctx, "packet sniff skipped due to previous error: ", metadata.PacketSniffError)
} else if metadata.SniffError != nil && !errors.Is(metadata.SniffError, sniff.ErrNeedMoreData) {
r.logger.DebugContext(ctx, "packet sniff skipped due to previous error: ", metadata.SniffError)
return
}
var packetSniffers []sniff.PacketSniffer
@@ -598,7 +602,7 @@ func (r *Router) actionSniff(
return
}
} else {
if len(packetBuffers) > 0 || metadata.PacketSniffError != nil {
if len(packetBuffers) > 0 || metadata.SniffError != nil {
err = sniff.PeekPacket(
ctx,
metadata,
@@ -618,7 +622,7 @@ func (r *Router) actionSniff(
Destination: destination,
}
packetBuffers = append(packetBuffers, packetBuffer)
metadata.PacketSniffError = err
metadata.SniffError = err
if errors.Is(err, sniff.ErrNeedMoreData) {
// TODO: replace with generic message when there are more multi-packet protocols
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
@@ -649,7 +653,7 @@ func (r *Router) actionSniff(
return
}
func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionResolve) error {
func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *R.RuleActionResolve) error {
if metadata.Destination.IsFqdn() {
var transport adapter.DNSTransport
if action.Server != "" {

View File

@@ -2,6 +2,7 @@ package rule
import (
"context"
"errors"
"net/netip"
"strings"
"sync"
@@ -284,6 +285,23 @@ func (r *RuleActionDirect) String() string {
return "direct" + r.description
}
type RejectedError struct {
Cause error
}
func (r *RejectedError) Error() string {
return "rejected"
}
func (r *RejectedError) Unwrap() error {
return r.Cause
}
func IsRejected(err error) bool {
var rejected *RejectedError
return errors.As(err, &rejected)
}
type RuleActionReject struct {
Method string
NoDrop bool
@@ -307,9 +325,9 @@ func (r *RuleActionReject) Error(ctx context.Context) error {
var returnErr error
switch r.Method {
case C.RuleActionRejectMethodDefault:
returnErr = syscall.ECONNREFUSED
returnErr = &RejectedError{syscall.ECONNREFUSED}
case C.RuleActionRejectMethodDrop:
return tun.ErrDrop
return &RejectedError{tun.ErrDrop}
default:
panic(F.ToString("unknown reject method: ", r.Method))
}
@@ -327,7 +345,7 @@ func (r *RuleActionReject) Error(ctx context.Context) error {
if ctx != nil {
r.logger.DebugContext(ctx, "dropped due to flooding")
}
return tun.ErrDrop
return &RejectedError{tun.ErrDrop}
}
return returnErr
}

View File

@@ -31,6 +31,9 @@ type HTTPConn struct {
}
func NewHTTP1Conn(conn net.Conn, request *http.Request) *HTTPConn {
if request.Header.Get("Host") == "" {
request.Header.Set("Host", request.Host)
}
return &HTTPConn{
Conn: conn,
request: request,
@@ -89,9 +92,6 @@ func (c *HTTPConn) writeRequest(payload []byte) error {
if err != nil {
return err
}
if c.request.Header.Get("Host") == "" {
c.request.Header.Set("Host", c.request.Host)
}
for key, value := range c.request.Header {
_, err = writer.Write([]byte(F.ToString(key, ": ", strings.Join(value, ", "), CRLF)))
if err != nil {