mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-16 05:39:08 +10:00
Compare commits
11 Commits
v1.8.10
...
v1.9.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02862cbc6a | ||
|
|
fef9c4f606 | ||
|
|
5ffae06bf2 | ||
|
|
36c89165f2 | ||
|
|
57fcea326b | ||
|
|
de4e9553b1 | ||
|
|
8fc2e1e7ea | ||
|
|
c65db38379 | ||
|
|
76dd983a45 | ||
|
|
16cae78411 | ||
|
|
bd0ccafd40 |
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -65,12 +65,6 @@ body:
|
||||
For Apple platform clients, please check `Settings - View Service Log` for crash logs.
|
||||
For the Android client, please check the `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` file for crash logs.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: supporter
|
||||
attributes:
|
||||
label: Supporter
|
||||
options:
|
||||
- label: I am a [sponsor](https://github.com/sponsors/nekohasekai/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Integrity requirements
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
@@ -65,12 +65,6 @@ body:
|
||||
对于 Apple 平台图形客户端程序,请检查 `Settings - View Service Log` 以导出崩溃日志。
|
||||
对于 Android 图形客户端程序,请检查 `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` 文件以导出崩溃日志。
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: supporter
|
||||
attributes:
|
||||
label: 支持我们
|
||||
options:
|
||||
- label: 我已经 [赞助](https://github.com/sponsors/nekohasekai/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 完整性要求
|
||||
|
||||
14
.github/update_clients.sh
vendored
14
.github/update_clients.sh
vendored
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PROJECTS=$(dirname "$0")/../..
|
||||
|
||||
function updateClient() {
|
||||
pushd clients/$1
|
||||
git fetch
|
||||
git reset FETCH_HEAD --hard
|
||||
popd
|
||||
git add clients/$1
|
||||
}
|
||||
|
||||
updateClient "apple"
|
||||
updateClient "android"
|
||||
2
.github/workflows/debug.yml
vendored
2
.github/workflows/debug.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
||||
~/go/pkg/mod
|
||||
key: go120-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: make ci_build_go120
|
||||
run: make ci_build
|
||||
build_go121:
|
||||
name: Debug build (Go 1.21)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -3,7 +3,7 @@ name: Build Docker Images
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- released
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@@ -26,11 +26,15 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.22
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=30m
|
||||
|
||||
29
.github/workflows/linux.yml
vendored
29
.github/workflows/linux.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: Release to Linux repository
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
- name: Publish release
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release -f .goreleaser.fury.yaml --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +0,0 @@
|
||||
[submodule "clients/apple"]
|
||||
path = clients/apple
|
||||
url = https://github.com/SagerNet/sing-box-for-apple.git
|
||||
[submodule "clients/android"]
|
||||
path = clients/android
|
||||
url = https://github.com/SagerNet/sing-box-for-android.git
|
||||
@@ -1,80 +0,0 @@
|
||||
project_name: sing-box
|
||||
builds:
|
||||
- id: main
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_ech
|
||||
- with_utls
|
||||
- with_reality_server
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
targets:
|
||||
- linux_386
|
||||
- linux_amd64_v1
|
||||
- linux_arm64
|
||||
- linux_arm_7
|
||||
- linux_s390x
|
||||
- linux_riscv64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
snapshot:
|
||||
name_template: "{{ .Version }}.{{ .ShortCommit }}"
|
||||
nfpms:
|
||||
- &template
|
||||
id: package
|
||||
package_name: sing-box
|
||||
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
builds:
|
||||
- main
|
||||
vendor: sagernet
|
||||
homepage: https://sing-box.sagernet.org/
|
||||
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||
description: The universal proxy platform.
|
||||
license: GPLv3 or later
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
priority: extra
|
||||
contents:
|
||||
- src: release/config/config.json
|
||||
dst: /etc/sing-box/config.json
|
||||
type: config
|
||||
- src: release/config/sing-box.service
|
||||
dst: /usr/lib/systemd/system/sing-box.service
|
||||
- src: release/config/sing-box@.service
|
||||
dst: /usr/lib/systemd/system/sing-box@.service
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
conflicts:
|
||||
- sing-box-beta
|
||||
- id: package_beta
|
||||
<<: *template
|
||||
package_name: sing-box-beta
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
conflicts:
|
||||
- sing-box
|
||||
release:
|
||||
disable: true
|
||||
furies:
|
||||
- account: sagernet
|
||||
ids:
|
||||
- package
|
||||
skip: |-
|
||||
{{ eq .Prerelease "" }}
|
||||
- account: sagernet
|
||||
ids:
|
||||
- package_beta
|
||||
skip: |-
|
||||
{{ ne .Prerelease "" }}
|
||||
@@ -1,11 +1,14 @@
|
||||
project_name: sing-box
|
||||
builds:
|
||||
- &template
|
||||
id: main
|
||||
- id: main
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
asmflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
gcflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||
tags:
|
||||
@@ -27,35 +30,65 @@ builds:
|
||||
- linux_arm64
|
||||
- linux_arm_7
|
||||
- linux_s390x
|
||||
- linux_riscv64
|
||||
- windows_amd64_v1
|
||||
- windows_amd64_v3
|
||||
- windows_386
|
||||
- windows_arm64
|
||||
- darwin_amd64_v1
|
||||
- darwin_amd64_v3
|
||||
- darwin_arm64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
- id: legacy
|
||||
<<: *template
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
asmflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
gcflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_ech
|
||||
- with_utls
|
||||
- with_reality_server
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOROOT={{ .Env.GOPATH }}/go1.20.14
|
||||
gobinary: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
|
||||
- GOROOT=/nix/store/kg6i737jjqs923jcijnm003h68c1dghj-go-1.20.11/share/go
|
||||
gobinary: /nix/store/kg6i737jjqs923jcijnm003h68c1dghj-go-1.20.11/bin/go
|
||||
targets:
|
||||
- windows_amd64_v1
|
||||
- windows_386
|
||||
- darwin_amd64_v1
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
- id: android
|
||||
<<: *template
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
asmflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
gcflags:
|
||||
- all=-trimpath={{.Env.GOPATH}}
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_ech
|
||||
- with_utls
|
||||
- with_reality_server
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
overrides:
|
||||
@@ -63,8 +96,8 @@ builds:
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
env:
|
||||
- CC=armv7a-linux-androideabi21-clang
|
||||
- CXX=armv7a-linux-androideabi21-clang++
|
||||
- CC=armv7a-linux-androideabi19-clang
|
||||
- CXX=armv7a-linux-androideabi19-clang++
|
||||
- goos: android
|
||||
goarch: arm64
|
||||
env:
|
||||
@@ -73,8 +106,8 @@ builds:
|
||||
- goos: android
|
||||
goarch: 386
|
||||
env:
|
||||
- CC=i686-linux-android21-clang
|
||||
- CXX=i686-linux-android21-clang++
|
||||
- CC=i686-linux-android19-clang
|
||||
- CXX=i686-linux-android19-clang++
|
||||
- goos: android
|
||||
goarch: amd64
|
||||
goamd64: v1
|
||||
@@ -86,11 +119,11 @@ builds:
|
||||
- android_arm64
|
||||
- android_386
|
||||
- android_amd64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
snapshot:
|
||||
name_template: "{{ .Version }}.{{ .ShortCommit }}"
|
||||
archives:
|
||||
- &template
|
||||
id: archive
|
||||
- id: archive
|
||||
builds:
|
||||
- main
|
||||
- android
|
||||
@@ -103,16 +136,20 @@ archives:
|
||||
- LICENSE
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
- id: archive-legacy
|
||||
<<: *template
|
||||
builds:
|
||||
- legacy
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- LICENSE
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-legacy'
|
||||
nfpms:
|
||||
- id: package
|
||||
package_name: sing-box
|
||||
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
builds:
|
||||
- main
|
||||
vendor: sagernet
|
||||
homepage: https://sing-box.sagernet.org/
|
||||
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||
@@ -128,25 +165,11 @@ nfpms:
|
||||
dst: /etc/sing-box/config.json
|
||||
type: config
|
||||
- src: release/config/sing-box.service
|
||||
dst: /usr/lib/systemd/system/sing-box.service
|
||||
dst: /etc/systemd/system/sing-box.service
|
||||
- src: release/config/sing-box@.service
|
||||
dst: /usr/lib/systemd/system/sing-box@.service
|
||||
dst: /etc/systemd/system/sing-box@.service
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
deb:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
rpm:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
overrides:
|
||||
deb:
|
||||
conflicts:
|
||||
- sing-box-beta
|
||||
rpm:
|
||||
conflicts:
|
||||
- sing-box-beta
|
||||
|
||||
source:
|
||||
enabled: false
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}.source'
|
||||
@@ -160,10 +183,6 @@ release:
|
||||
github:
|
||||
owner: SagerNet
|
||||
name: sing-box
|
||||
name_template: '{{ if .IsSnapshot }}{{ nightly }}{{ else }}{{ .Version }}{{ end }}'
|
||||
draft: true
|
||||
prerelease: auto
|
||||
mode: replace
|
||||
ids:
|
||||
- archive
|
||||
- package
|
||||
skip_upload: true
|
||||
mode: replace
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
COPY . /go/src/github.com/sagernet/sing-box
|
||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||
|
||||
32
Makefile
32
Makefile
@@ -1,9 +1,8 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS_GO118 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api
|
||||
TAGS_GO120 = with_quic,with_utls
|
||||
TAGS_GO121 = with_ech
|
||||
TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121)
|
||||
TAGS_GO120 = with_quic,with_ech,with_utls
|
||||
TAGS ?= $(TAGS_GO118),$(TAGS_GO120)
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server
|
||||
|
||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||
@@ -15,7 +14,7 @@ MAIN_PARAMS = $(PARAMS) -tags $(TAGS)
|
||||
MAIN = ./cmd/sing-box
|
||||
PREFIX ?= $(shell go env GOPATH)
|
||||
|
||||
.PHONY: test release docs build
|
||||
.PHONY: test release docs
|
||||
|
||||
build:
|
||||
go build $(MAIN_PARAMS) $(MAIN)
|
||||
@@ -24,10 +23,6 @@ ci_build_go118:
|
||||
go build $(PARAMS) $(MAIN)
|
||||
go build $(PARAMS) -tags "$(TAGS_GO118)" $(MAIN)
|
||||
|
||||
ci_build_go120:
|
||||
go build $(PARAMS) $(MAIN)
|
||||
go build $(PARAMS) -tags "$(TAGS_GO118),$(TAGS_GO120)" $(MAIN)
|
||||
|
||||
ci_build:
|
||||
go build $(PARAMS) $(MAIN)
|
||||
go build $(MAIN_PARAMS) $(MAIN)
|
||||
@@ -64,22 +59,12 @@ proto_install:
|
||||
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
|
||||
release:
|
||||
go run ./cmd/internal/build goreleaser release --clean --skip publish
|
||||
go run ./cmd/internal/build goreleaser release --clean --skip-publish || exit 1
|
||||
mkdir dist/release
|
||||
mv dist/*.tar.gz \
|
||||
dist/*.zip \
|
||||
dist/*.deb \
|
||||
dist/*.rpm \
|
||||
dist/*_amd64.pkg.tar.zst \
|
||||
dist/*_amd64v3.pkg.tar.zst \
|
||||
dist/*_arm64.pkg.tar.zst \
|
||||
dist/release
|
||||
mv dist/*.tar.gz dist/*.zip dist/*.deb dist/*.rpm dist/*.pkg.tar.zst dist/release
|
||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release
|
||||
rm -r dist/release
|
||||
|
||||
release_repo:
|
||||
go run ./cmd/internal/build goreleaser release -f .goreleaser.fury.yaml --clean
|
||||
|
||||
release_install:
|
||||
go install -v github.com/goreleaser/goreleaser@latest
|
||||
go install -v github.com/tcnksm/ghr@latest
|
||||
@@ -88,12 +73,11 @@ update_android_version:
|
||||
go run ./cmd/internal/update_android_version
|
||||
|
||||
build_android:
|
||||
cd ../sing-box-for-android && ./gradlew :app:clean :app:assemblePlayRelease :app:assembleOtherRelease && ./gradlew --stop
|
||||
cd ../sing-box-for-android && ./gradlew :app:assemblePlayRelease && ./gradlew --stop
|
||||
|
||||
upload_android:
|
||||
mkdir -p dist/release_android
|
||||
cp ../sing-box-for-android/app/build/outputs/apk/play/release/*.apk dist/release_android
|
||||
cp ../sing-box-for-android/app/build/outputs/apk/other/release/*-universal.apk dist/release_android
|
||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release_android
|
||||
rm -rf dist/release_android
|
||||
|
||||
@@ -194,8 +178,8 @@ lib:
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib_install:
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.3
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.3
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.1
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.1
|
||||
|
||||
docs:
|
||||
mkdocs serve
|
||||
|
||||
@@ -51,11 +51,13 @@ type InboundContext struct {
|
||||
|
||||
// rule cache
|
||||
|
||||
IPCIDRMatchSource bool
|
||||
SourceAddressMatch bool
|
||||
SourcePortMatch bool
|
||||
DestinationAddressMatch bool
|
||||
DestinationPortMatch bool
|
||||
IPCIDRMatchSource bool
|
||||
SourceAddressMatch bool
|
||||
SourcePortMatch bool
|
||||
DestinationAddressMatch bool
|
||||
DestinationPortMatch bool
|
||||
DidMatch bool
|
||||
IgnoreDestinationIPCIDRMatch bool
|
||||
}
|
||||
|
||||
func (c *InboundContext) ResetRuleCache() {
|
||||
|
||||
@@ -33,8 +33,6 @@ type Router interface {
|
||||
|
||||
RuleSet(tag string) (RuleSet, bool)
|
||||
|
||||
NeedWIFIState() bool
|
||||
|
||||
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
|
||||
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||
@@ -86,6 +84,9 @@ type DNSRule interface {
|
||||
Rule
|
||||
DisableCache() bool
|
||||
RewriteTTL() *uint32
|
||||
ClientSubnet() *netip.Addr
|
||||
WithAddressLimit() bool
|
||||
MatchAddressLimit(metadata *InboundContext) bool
|
||||
}
|
||||
|
||||
type RuleSet interface {
|
||||
@@ -99,6 +100,7 @@ type RuleSet interface {
|
||||
type RuleSetMetadata struct {
|
||||
ContainsProcessRule bool
|
||||
ContainsWIFIRule bool
|
||||
ContainsIPCIDRRule bool
|
||||
}
|
||||
|
||||
type RuleSetStartContext interface {
|
||||
|
||||
Submodule clients/android deleted from 1a7b9dc6fd
Submodule clients/apple deleted from db8cbfa865
@@ -46,13 +46,13 @@ var (
|
||||
|
||||
func init() {
|
||||
sharedFlags = append(sharedFlags, "-trimpath")
|
||||
sharedFlags = append(sharedFlags, "-buildvcs=false")
|
||||
sharedFlags = append(sharedFlags, "-ldflags")
|
||||
currentTag, err := build_shared.ReadTag()
|
||||
if err != nil {
|
||||
currentTag = "unknown"
|
||||
}
|
||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
||||
sharedFlags = append(sharedFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
||||
debugFlags = append(debugFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
||||
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api")
|
||||
iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack")
|
||||
|
||||
@@ -11,9 +11,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -30,7 +28,7 @@ func FindSDK() {
|
||||
}
|
||||
for _, path := range searchPath {
|
||||
path = os.ExpandEnv(path)
|
||||
if rw.FileExists(filepath.Join(path, "licenses", "android-sdk-license")) {
|
||||
if rw.FileExists(path + "/licenses/android-sdk-license") {
|
||||
androidSDKPath = path
|
||||
break
|
||||
}
|
||||
@@ -42,14 +40,6 @@ func FindSDK() {
|
||||
log.Fatal("android NDK not found")
|
||||
}
|
||||
|
||||
javaVersion, err := shell.Exec("java", "--version").ReadOutput()
|
||||
if err != nil {
|
||||
log.Fatal(E.Cause(err, "check java version"))
|
||||
}
|
||||
if !strings.Contains(javaVersion, "openjdk 17") {
|
||||
log.Fatal("java version should be openjdk 17")
|
||||
}
|
||||
|
||||
os.Setenv("ANDROID_HOME", androidSDKPath)
|
||||
os.Setenv("ANDROID_SDK_HOME", androidSDKPath)
|
||||
os.Setenv("ANDROID_NDK_HOME", androidNDKPath)
|
||||
@@ -58,13 +48,11 @@ func FindSDK() {
|
||||
}
|
||||
|
||||
func findNDK() bool {
|
||||
const fixedVersion = "26.2.11394342"
|
||||
const versionFile = "source.properties"
|
||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.FileExists(filepath.Join(fixedPath, versionFile)) {
|
||||
androidNDKPath = fixedPath
|
||||
if rw.FileExists(androidSDKPath + "/ndk/25.1.8937393") {
|
||||
androidNDKPath = androidSDKPath + "/ndk/25.1.8937393"
|
||||
return true
|
||||
}
|
||||
ndkVersions, err := os.ReadDir(filepath.Join(androidSDKPath, "ndk"))
|
||||
ndkVersions, err := os.ReadDir(androidSDKPath + "/ndk")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -85,10 +73,8 @@ func findNDK() bool {
|
||||
return true
|
||||
})
|
||||
for _, versionName := range versionNames {
|
||||
currentNDKPath := filepath.Join(androidSDKPath, "ndk", versionName)
|
||||
if rw.FileExists(filepath.Join(androidSDKPath, versionFile)) {
|
||||
androidNDKPath = currentNDKPath
|
||||
log.Warn("reproducibility warning: using NDK version " + versionName + " instead of " + fixedVersion)
|
||||
if rw.FileExists(androidSDKPath + "/ndk/" + versionName) {
|
||||
androidNDKPath = androidSDKPath + "/ndk/" + versionName
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -99,12 +85,13 @@ var GoBinPath string
|
||||
|
||||
func FindMobile() {
|
||||
goBin := filepath.Join(build.Default.GOPATH, "bin")
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
if !rw.FileExists(filepath.Join(goBin, "gobind.exe")) {
|
||||
log.Fatal("missing gomobile installation")
|
||||
if !rw.FileExists(goBin + "/" + "gobind.exe") {
|
||||
log.Fatal("missing gomobile.exe installation")
|
||||
}
|
||||
} else {
|
||||
if !rw.FileExists(filepath.Join(goBin, "gobind")) {
|
||||
if !rw.FileExists(goBin + "/" + "gobind") {
|
||||
log.Fatal("missing gomobile installation")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -19,46 +18,34 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
common.Must(os.Chdir(androidPath))
|
||||
localProps := common.Must1(os.ReadFile("version.properties"))
|
||||
localProps := common.Must1(os.ReadFile("local.properties"))
|
||||
var propsList [][]string
|
||||
for _, propLine := range strings.Split(string(localProps), "\n") {
|
||||
propsList = append(propsList, strings.Split(propLine, "="))
|
||||
}
|
||||
var (
|
||||
versionUpdated bool
|
||||
goVersionUpdated bool
|
||||
)
|
||||
for _, propPair := range propsList {
|
||||
switch propPair[0] {
|
||||
case "VERSION_NAME":
|
||||
if propPair[1] != newVersion.String() {
|
||||
versionUpdated = true
|
||||
propPair[1] = newVersion.String()
|
||||
log.Info("updated version to ", newVersion.String())
|
||||
}
|
||||
case "GO_VERSION":
|
||||
if propPair[1] != runtime.Version() {
|
||||
goVersionUpdated = true
|
||||
propPair[1] = runtime.Version()
|
||||
log.Info("updated Go version to ", runtime.Version())
|
||||
if propPair[0] == "VERSION_NAME" {
|
||||
if propPair[1] == newVersion.String() {
|
||||
log.Info("version not changed")
|
||||
return
|
||||
}
|
||||
propPair[1] = newVersion.String()
|
||||
log.Info("updated version to ", newVersion.String())
|
||||
}
|
||||
}
|
||||
if !(versionUpdated || goVersionUpdated) {
|
||||
log.Info("version not changed")
|
||||
return
|
||||
}
|
||||
for _, propPair := range propsList {
|
||||
switch propPair[0] {
|
||||
case "VERSION_CODE":
|
||||
versionCode := common.Must1(strconv.ParseInt(propPair[1], 10, 64))
|
||||
propPair[1] = strconv.Itoa(int(versionCode + 1))
|
||||
log.Info("updated version code to ", propPair[1])
|
||||
case "RELEASE_NOTES":
|
||||
propPair[1] = "sing-box " + newVersion.String()
|
||||
}
|
||||
}
|
||||
var newProps []string
|
||||
for _, propPair := range propsList {
|
||||
newProps = append(newProps, strings.Join(propPair, "="))
|
||||
}
|
||||
common.Must(os.WriteFile("version.properties", []byte(strings.Join(newProps, "\n")), 0o644))
|
||||
common.Must(os.WriteFile("local.properties", []byte(strings.Join(newProps, "\n")), 0o644))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package badtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
@@ -18,20 +20,32 @@ import (
|
||||
var _ N.ReadWaiter = (*ReadWaitConn)(nil)
|
||||
|
||||
type ReadWaitConn struct {
|
||||
*tls.STDConn
|
||||
halfAccess *sync.Mutex
|
||||
rawInput *bytes.Buffer
|
||||
input *bytes.Reader
|
||||
hand *bytes.Buffer
|
||||
readWaitOptions N.ReadWaitOptions
|
||||
tls.Conn
|
||||
halfAccess *sync.Mutex
|
||||
rawInput *bytes.Buffer
|
||||
input *bytes.Reader
|
||||
hand *bytes.Buffer
|
||||
readWaitOptions N.ReadWaitOptions
|
||||
tlsReadRecord func() error
|
||||
tlsHandlePostHandshakeMessage func() error
|
||||
}
|
||||
|
||||
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
||||
stdConn, isSTDConn := conn.(*tls.STDConn)
|
||||
if !isSTDConn {
|
||||
var (
|
||||
loaded bool
|
||||
tlsReadRecord func() error
|
||||
tlsHandlePostHandshakeMessage func() error
|
||||
)
|
||||
for _, tlsCreator := range tlsRegistry {
|
||||
loaded, tlsReadRecord, tlsHandlePostHandshakeMessage = tlsCreator(conn)
|
||||
if loaded {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !loaded {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(stdConn))
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||
rawHalfConn := rawConn.FieldByName("in")
|
||||
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
||||
return nil, E.New("badtls: invalid half conn")
|
||||
@@ -57,11 +71,13 @@ func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
||||
}
|
||||
hand := (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
|
||||
return &ReadWaitConn{
|
||||
STDConn: stdConn,
|
||||
halfAccess: halfAccess,
|
||||
rawInput: rawInput,
|
||||
input: input,
|
||||
hand: hand,
|
||||
Conn: conn,
|
||||
halfAccess: halfAccess,
|
||||
rawInput: rawInput,
|
||||
input: input,
|
||||
hand: hand,
|
||||
tlsReadRecord: tlsReadRecord,
|
||||
tlsHandlePostHandshakeMessage: tlsHandlePostHandshakeMessage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -71,19 +87,19 @@ func (c *ReadWaitConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy
|
||||
}
|
||||
|
||||
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||
err = c.Handshake()
|
||||
err = c.HandshakeContext(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.halfAccess.Lock()
|
||||
defer c.halfAccess.Unlock()
|
||||
for c.input.Len() == 0 {
|
||||
err = tlsReadRecord(c.STDConn)
|
||||
err = c.tlsReadRecord()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for c.hand.Len() > 0 {
|
||||
err = tlsHandlePostHandshakeMessage(c.STDConn)
|
||||
err = c.tlsHandlePostHandshakeMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -100,7 +116,7 @@ func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
|
||||
// recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
|
||||
c.rawInput.Bytes()[0] == 21 {
|
||||
_ = tlsReadRecord(c.STDConn)
|
||||
_ = c.tlsReadRecord()
|
||||
// return n, err // will be io.EOF on closeNotify
|
||||
}
|
||||
|
||||
@@ -109,11 +125,27 @@ func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||
}
|
||||
|
||||
func (c *ReadWaitConn) Upstream() any {
|
||||
return c.STDConn
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
//go:linkname tlsReadRecord crypto/tls.(*Conn).readRecord
|
||||
func tlsReadRecord(c *tls.STDConn) error
|
||||
var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error)
|
||||
|
||||
//go:linkname tlsHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
||||
func tlsHandlePostHandshakeMessage(c *tls.STDConn) error
|
||||
func init() {
|
||||
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||
tlsConn, loaded := conn.(*tls.STDConn)
|
||||
if !loaded {
|
||||
return
|
||||
}
|
||||
return true, func() error {
|
||||
return stdTLSReadRecord(tlsConn)
|
||||
}, func() error {
|
||||
return stdTLSHandlePostHandshakeMessage(tlsConn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//go:linkname stdTLSReadRecord crypto/tls.(*Conn).readRecord
|
||||
func stdTLSReadRecord(c *tls.STDConn) error
|
||||
|
||||
//go:linkname stdTLSHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
||||
func stdTLSHandlePostHandshakeMessage(c *tls.STDConn) error
|
||||
|
||||
31
common/badtls/read_wait_ech.go
Normal file
31
common/badtls/read_wait_ech.go
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build go1.21 && !without_badtls && with_ech
|
||||
|
||||
package badtls
|
||||
|
||||
import (
|
||||
"net"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/sagernet/cloudflare-tls"
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||
tlsConn, loaded := common.Cast[*tls.Conn](conn)
|
||||
if !loaded {
|
||||
return
|
||||
}
|
||||
return true, func() error {
|
||||
return echReadRecord(tlsConn)
|
||||
}, func() error {
|
||||
return echHandlePostHandshakeMessage(tlsConn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//go:linkname echReadRecord github.com/sagernet/cloudflare-tls.(*Conn).readRecord
|
||||
func echReadRecord(c *tls.Conn) error
|
||||
|
||||
//go:linkname echHandlePostHandshakeMessage github.com/sagernet/cloudflare-tls.(*Conn).handlePostHandshakeMessage
|
||||
func echHandlePostHandshakeMessage(c *tls.Conn) error
|
||||
31
common/badtls/read_wait_utls.go
Normal file
31
common/badtls/read_wait_utls.go
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build go1.21 && !without_badtls && with_utls
|
||||
|
||||
package badtls
|
||||
|
||||
import (
|
||||
"net"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/utls"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||
tlsConn, loaded := common.Cast[*tls.UConn](conn)
|
||||
if !loaded {
|
||||
return
|
||||
}
|
||||
return true, func() error {
|
||||
return utlsReadRecord(tlsConn.Conn)
|
||||
}, func() error {
|
||||
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//go:linkname utlsReadRecord github.com/sagernet/utls.(*Conn).readRecord
|
||||
func utlsReadRecord(c *tls.Conn) error
|
||||
|
||||
//go:linkname utlsHandlePostHandshakeMessage github.com/sagernet/utls.(*Conn).handlePostHandshakeMessage
|
||||
func utlsHandlePostHandshakeMessage(c *tls.Conn) error
|
||||
@@ -223,7 +223,7 @@ func getExecPathFromPID(pid uint32) (string, error) {
|
||||
r1, _, err := syscall.SyscallN(
|
||||
procQueryFullProcessImageNameW.Addr(),
|
||||
uintptr(h),
|
||||
uintptr(1),
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&size)),
|
||||
)
|
||||
|
||||
5
constant/quic.go
Normal file
5
constant/quic.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build with_quic
|
||||
|
||||
package constant
|
||||
|
||||
const WithQUIC = true
|
||||
5
constant/quic_stub.go
Normal file
5
constant/quic_stub.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build !with_quic
|
||||
|
||||
package constant
|
||||
|
||||
const WithQUIC = false
|
||||
@@ -2,25 +2,40 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.8.10
|
||||
#### 1.9.0-alpha.2
|
||||
|
||||
* Add support for `client-subnet` DNS options **1**
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.9
|
||||
**1**:
|
||||
|
||||
* Fixes and improvements
|
||||
See [DNS](/configuration/dns), [DNS Server](/configuration/dns/server) and [DNS Rules](/configuration/dns/rule).
|
||||
|
||||
#### 1.8.8
|
||||
Since this feature makes the scenario mentioned in `alpha.1` no longer leak DNS requests,
|
||||
the [Client example](/manual/proxy/client#traffic-bypass-usage-for-chinese-users) has been updated.
|
||||
|
||||
* Fixes and improvements
|
||||
#### 1.9.0-alpha.1
|
||||
|
||||
#### 1.8.7
|
||||
* `domain_suffix` behavior update **1**
|
||||
* `process_path` format update on Windows **2**
|
||||
* Add address filter DNS rule items **3**
|
||||
|
||||
* Fixes and improvements
|
||||
**1**:
|
||||
|
||||
#### 1.8.6
|
||||
See [Migration](/migration/#domain_suffix-behavior-update).
|
||||
|
||||
* Fixes and improvements
|
||||
**2**:
|
||||
|
||||
See [Migration](/migration/#process_path-format-update-on-windows).
|
||||
|
||||
**3**:
|
||||
|
||||
The new DNS feature allows you to more precisely bypass Chinese websites via **DNS leaks**. Do not use plain local DNS
|
||||
if using this method.
|
||||
|
||||
See [Address Filter Fields](/configuration/dns/rule#address-filter-fields).
|
||||
|
||||
[Client example](/manual/proxy/client#traffic-bypass-usage-for-chinese-users) updated.
|
||||
|
||||
#### 1.8.5
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ platform-specific function implementation, such as TUN transparent proxy impleme
|
||||
* [Play Store](https://play.google.com/store/apps/details?id=io.nekohasekai.sfa)
|
||||
* [Play Store (Beta)](https://play.google.com/apps/testing/io.nekohasekai.sfa)
|
||||
* [GitHub Releases](https://github.com/SagerNet/sing-box/releases)
|
||||
* [F-Droid](https://f-droid.org/packages/io.nekohasekai.sfa/) (Unified signature via reproducible builds)
|
||||
|
||||
## :material-source-repository: Source code
|
||||
|
||||
|
||||
@@ -6,9 +6,3 @@ icon: material/security
|
||||
|
||||
sing-box and official graphics clients do not collect or share personal data,
|
||||
and the data generated by the software is always on your device.
|
||||
|
||||
## Android
|
||||
|
||||
If your configuration contains `wifi_ssid` or `wifi_bssid` routing rules,
|
||||
sing-box uses the location permission in the background
|
||||
to get information about the connected Wi-Fi network to make them work.
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
# DNS
|
||||
|
||||
### Structure
|
||||
@@ -13,6 +21,7 @@
|
||||
"disable_expire": false,
|
||||
"independent_cache": false,
|
||||
"reverse_mapping": false,
|
||||
"client_subnet": "",
|
||||
"fakeip": {}
|
||||
}
|
||||
}
|
||||
@@ -21,8 +30,8 @@
|
||||
|
||||
### Fields
|
||||
|
||||
| Key | Format |
|
||||
|----------|--------------------------------|
|
||||
| Key | Format |
|
||||
|----------|---------------------------------|
|
||||
| `server` | List of [DNS Server](./server/) |
|
||||
| `rules` | List of [DNS Rule](./rule/) |
|
||||
| `fakeip` | [FakeIP](./fakeip/) |
|
||||
@@ -60,6 +69,10 @@ Stores a reverse mapping of IP addresses after responding to a DNS query in orde
|
||||
Since this process relies on the act of resolving domain names by an application before making a request, it can be
|
||||
problematic in environments such as macOS, where DNS is proxied and cached by the system.
|
||||
|
||||
#### fakeip
|
||||
#### client_subnet
|
||||
|
||||
[FakeIP](./fakeip/) settings.
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
|
||||
Can be overrides by `servers.[].client_subnet` or `rules.[].client_subnet`.
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
# DNS
|
||||
|
||||
### 结构
|
||||
@@ -13,6 +21,7 @@
|
||||
"disable_expire": false,
|
||||
"independent_cache": false,
|
||||
"reverse_mapping": false,
|
||||
"client_subnet": "",
|
||||
"fakeip": {}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +67,14 @@
|
||||
|
||||
由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
可以被 `servers.[].client_subnet` 或 `rules.[].client_subnet` 覆盖。
|
||||
|
||||
#### fakeip
|
||||
|
||||
[FakeIP](./fakeip/) 设置。
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [geoip](#geoip)
|
||||
:material-plus: [ip_cidr](#ip_cidr)
|
||||
:material-plus: [ip_is_private](#ip_is_private)
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -53,11 +60,19 @@ icon: material/alert-decagram
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_ip_is_private": false,
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_is_private": false,
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
@@ -107,7 +122,8 @@ icon: material/alert-decagram
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
@@ -115,7 +131,8 @@ icon: material/alert-decagram
|
||||
"rules": [],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -266,8 +283,6 @@ Match Clash mode.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
<!-- md:version 1.7.0-beta.4 -->
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
@@ -312,6 +327,40 @@ Disable cache and save cache in this query.
|
||||
|
||||
Rewrite TTL in DNS responses.
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
|
||||
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
|
||||
### Address Filter Fields
|
||||
|
||||
Only takes effect for IP address requests. When the query results do not match the address filtering rule items, the current rule will be skipped.
|
||||
|
||||
!!! note ""
|
||||
|
||||
`ip_cidr` items in included rule sets also takes effect as an address filtering field.
|
||||
|
||||
#### geoip
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Match GeoIP with query response.
|
||||
|
||||
#### ip_cidr
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Match IP CIDR with query response.
|
||||
|
||||
#### ip_is_private
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Match private IP with query response.
|
||||
|
||||
### Logical Fields
|
||||
|
||||
#### type
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [geoip](#geoip)
|
||||
:material-plus: [ip_cidr](#ip_cidr)
|
||||
:material-plus: [ip_is_private](#ip_is_private)
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -53,10 +60,19 @@ icon: material/alert-decagram
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24"
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_ip_is_private": false,
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_is_private": false,
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
@@ -105,14 +121,16 @@ icon: material/alert-decagram
|
||||
"direct"
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": false
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"server": "local",
|
||||
"disable_cache": false
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -307,6 +325,40 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
重写 DNS 回应中的 TTL。
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。
|
||||
|
||||
### 地址筛选字段
|
||||
|
||||
仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
||||
|
||||
!!! note ""
|
||||
|
||||
引用的规则集中的 `ip_cidr` 项也作为地址筛选字段生效。
|
||||
|
||||
#### geoip
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
与查询响应匹配 GeoIP。
|
||||
|
||||
#### ip_cidr
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
与查询相应匹配 IP CIDR。
|
||||
|
||||
#### ip_is_private
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
与查询响应匹配非公开 IP。
|
||||
|
||||
### 逻辑字段
|
||||
|
||||
#### type
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
@@ -5,17 +13,17 @@
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://dns.google",
|
||||
"address_resolver": "local",
|
||||
"address_strategy": "prefer_ipv4",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "direct"
|
||||
"tag": "",
|
||||
"address": "",
|
||||
"address_resolver": "",
|
||||
"address_strategy": "",
|
||||
"strategy": "",
|
||||
"detour": "",
|
||||
"client_subnet": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Fields
|
||||
@@ -80,10 +88,20 @@ Default domain strategy for resolving the domain names.
|
||||
|
||||
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
||||
|
||||
Take no effect if override by other settings.
|
||||
Take no effect if overridden by other settings.
|
||||
|
||||
#### detour
|
||||
|
||||
Tag of an outbound for connecting to the dns server.
|
||||
|
||||
Default outbound will be used if empty.
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
|
||||
Can be overrides by `rules.[].client_subnet`.
|
||||
|
||||
Will overrides `dns.client_subnet`.
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
@@ -5,17 +13,17 @@
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://dns.google",
|
||||
"address_resolver": "local",
|
||||
"address_strategy": "prefer_ipv4",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "direct"
|
||||
"tag": "",
|
||||
"address": "",
|
||||
"address_resolver": "",
|
||||
"address_strategy": "",
|
||||
"strategy": "",
|
||||
"detour": "",
|
||||
"client_subnet": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 字段
|
||||
@@ -87,3 +95,13 @@ DNS 服务器的地址。
|
||||
用于连接到 DNS 服务器的出站的标签。
|
||||
|
||||
如果为空,将使用默认出站。
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
可以被 `rules.[].client_subnet` 覆盖。
|
||||
|
||||
将覆盖 `dns.client_subnet`。
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
### Structure
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
### 结构
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-delete-alert: [store_mode](#store_mode)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-delete-alert: [store_mode](#store_mode)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# Experimental
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# 实验性
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# Route
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# 路由
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
### Structure
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# Rule Set
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# Source Format
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-alert-decagram: [utls](#utls)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-alert-decagram: [utls](#utls)
|
||||
|
||||
@@ -32,6 +32,8 @@ suffers from a number of problems, including lack of maintenance, inaccurate rul
|
||||
sing-box 1.8.0 introduces [Rule Set](/configuration/rule-set/), which can completely replace Geosite,
|
||||
check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
Geosite,即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案,存在着大量问题,包括缺少维护、规则不准确、管理困难。
|
||||
|
||||
## 1.6.0
|
||||
|
||||
The following features will be marked deprecated in 1.5.0 and removed entirely in 1.6.0.
|
||||
|
||||
@@ -23,8 +23,7 @@ Since sing-box 1.5.0:
|
||||
Since sing-box 1.8.0:
|
||||
|
||||
* Go 1.18.5 - ~
|
||||
* Go 1.20.0 - ~ with tag `with_quic`, or `with_utls` enabled
|
||||
* Go 1.21.0 - ~ with tag `with_ech` enabled
|
||||
* Go 1.20.0 - ~ with tag `with_quic`, `with_ech`, or `with_utls` enabled
|
||||
|
||||
You can download and install Go from: https://go.dev/doc/install, latest version is recommended.
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ sing-box 1.4.0 前:
|
||||
从 sing-box 1.8.0:
|
||||
|
||||
* Go 1.18.5 - ~
|
||||
* Go 1.20.0 - ~ 如果启用构建标记 `with_quic` 或 `with_utls`
|
||||
* Go 1.20.1 - ~ 如果启用构建标记 `with_ech`
|
||||
* Go 1.20.0 - ~ 如果启用构建标记 `with_quic`、`with_ech` 或 `with_utls`
|
||||
|
||||
您可以从 https://go.dev/doc/install 下载并安装 Go,推荐使用最新版本。
|
||||
|
||||
|
||||
@@ -4,35 +4,6 @@ icon: material/package
|
||||
|
||||
# Package Manager
|
||||
|
||||
## :material-tram: Repository Installation
|
||||
|
||||
=== ":material-debian: Debian / APT"
|
||||
|
||||
```bash
|
||||
sudo curl -fsSL https://deb.sagernet.org/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
||||
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
||||
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: Redhat / DNF"
|
||||
|
||||
```bash
|
||||
sudo dnf -y install dnf-plugins-core
|
||||
sudo dnf config-manager --add-repo https://sing-box.app/rpm.repo
|
||||
sudo dnf install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: CentOS / YUM"
|
||||
|
||||
```bash
|
||||
sudo yum install -y yum-utils
|
||||
sudo yum-config-manager --add-repo https://sing-box.app/rpm.repo
|
||||
sudo yum install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
## :material-download-box: Manual Installation
|
||||
|
||||
=== ":material-debian: Debian / DEB"
|
||||
|
||||
@@ -4,35 +4,6 @@ icon: material/package
|
||||
|
||||
# 包管理器
|
||||
|
||||
## :material-tram: 仓库安装
|
||||
|
||||
=== ":material-debian: Debian / APT"
|
||||
|
||||
```bash
|
||||
sudo curl -fsSL https://deb.sagernet.org/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
||||
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
||||
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: Redhat / DNF"
|
||||
|
||||
```bash
|
||||
sudo dnf -y install dnf-plugins-core
|
||||
sudo dnf config-manager --add-repo https://sing-box.app/rpm.repo
|
||||
sudo dnf install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: CentOS / YUM"
|
||||
|
||||
```bash
|
||||
sudo yum install -y yum-utils
|
||||
sudo yum-config-manager --add-repo https://sing-box.app/rpm.repo
|
||||
sudo yum install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
## :material-download-box: 手动安装
|
||||
|
||||
=== ":material-debian: Debian / DEB"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
[sing-box]
|
||||
name=sing-box
|
||||
baseurl=https://rpm.sagernet.org/
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://deb.sagernet.org/gpg.key
|
||||
@@ -290,52 +290,6 @@ flowchart TB
|
||||
|
||||
=== ":material-dns: DNS rules"
|
||||
|
||||
!!! info
|
||||
|
||||
DNS rules are optional if FakeIP is used.
|
||||
|
||||
```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"
|
||||
},
|
||||
{
|
||||
"geosite": "geolocation-cn",
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-dns: DNS rules (1.8.0+)"
|
||||
|
||||
!!! info
|
||||
|
||||
DNS rules are optional if FakeIP is used.
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
@@ -382,74 +336,144 @@ flowchart TB
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-router-network: Route rules"
|
||||
=== ":material-dns: DNS rules (1.9.0+)"
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
=== ":material-shield-off: With DNS Leaks"
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "or",
|
||||
"rules": [
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"protocol": "dns"
|
||||
"tag": "google",
|
||||
"address": "tls://8.8.8.8"
|
||||
},
|
||||
{
|
||||
"port": 53
|
||||
"tag": "local",
|
||||
"address": "https://223.5.5.5/dns-query",
|
||||
"detour": "direct"
|
||||
}
|
||||
],
|
||||
"outbound": "dns"
|
||||
},
|
||||
{
|
||||
"geoip": "private",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Direct",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Global",
|
||||
"outbound": "default"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "or",
|
||||
"rules": [
|
||||
{
|
||||
"port": 853
|
||||
"outbound": "any",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"network": "udp",
|
||||
"port": 443
|
||||
"clash_mode": "Direct",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"protocol": "stun"
|
||||
"clash_mode": "Global",
|
||||
"server": "google"
|
||||
},
|
||||
{
|
||||
"rule_set": "geosite-geolocation-cn",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Default",
|
||||
"server": "google"
|
||||
},
|
||||
{
|
||||
"rule_set": "geoip-cn",
|
||||
"server": "local"
|
||||
}
|
||||
],
|
||||
"outbound": "block"
|
||||
]
|
||||
},
|
||||
{
|
||||
"geosite": "geolocation-cn",
|
||||
"outbound": "direct"
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geoip-cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs"
|
||||
}
|
||||
]
|
||||
},
|
||||
"experimental": {
|
||||
"clash_api": {
|
||||
"default_mode": "Leak"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-router-network: Route rules (1.8.0+)"
|
||||
=== ":material-security: Without DNS Leaks (1.9.0-alpha.2+)"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://8.8.8.8"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "https://223.5.5.5/dns-query",
|
||||
"detour": "direct"
|
||||
}
|
||||
],
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"rule_set": "geoip-cn",
|
||||
"server": "google",
|
||||
"client_subnet": "114.114.114.114" // Any China client IP address
|
||||
}
|
||||
]
|
||||
},
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geoip-cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs"
|
||||
}
|
||||
]
|
||||
},
|
||||
"experimental": {
|
||||
"clash_api": {
|
||||
"default_mode": "Leak"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-router-network: Route rules"
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
icon: material/arrange-bring-forward
|
||||
---
|
||||
|
||||
## 1.9.0
|
||||
|
||||
!!! warning "Unstable"
|
||||
|
||||
This version is still under development, and the following migration guide may be changed in the future.
|
||||
|
||||
### `domain_suffix` behavior update
|
||||
|
||||
For historical reasons, sing-box's `domain_suffix` rule matches literal prefixes instead of the same as other projects.
|
||||
|
||||
sing-box 1.9.0 modifies the behavior of `domain_suffix`: If the rule value is prefixed with `.`,
|
||||
the behavior is unchanged, otherwise it matches `(domain|.+\.domain)` instead.
|
||||
|
||||
### `process_path` format update on Windows
|
||||
|
||||
The `process_path` rule of sing-box is inherited from Clash,
|
||||
the original code uses the local system's path format (e.g. `\Device\HarddiskVolume1\folder\program.exe`),
|
||||
but when the device has multiple disks, the HarddiskVolume serial number is not stable.
|
||||
|
||||
sing-box 1.9.0 make QueryFullProcessImageNameW output a Win32 path (such as `C:\folder\program.exe`),
|
||||
which will disrupt the existing `process_path` use cases in Windows.
|
||||
|
||||
## 1.8.0
|
||||
|
||||
### :material-close-box: Migrate cache file from Clash API to independent options
|
||||
|
||||
@@ -2,6 +2,27 @@
|
||||
icon: material/arrange-bring-forward
|
||||
---
|
||||
|
||||
## 1.9.0
|
||||
|
||||
!!! warning "不稳定的"
|
||||
|
||||
该版本仍在开发中,迁移指南可能将在未来更改。
|
||||
|
||||
### `domain_suffix` 行为更新
|
||||
|
||||
由于历史原因,sing-box 的 `domain_suffix` 规则匹配字面前缀,而不与其他项目相同。
|
||||
|
||||
sing-box 1.9.0 修改了 `domain_suffix` 的行为:如果规则值以 `.` 为前缀则行为不变,否则改为匹配 `(domain|.+\.domain)`。
|
||||
|
||||
### 对 Windows 上 `process_path` 格式的更新
|
||||
|
||||
sing-box 的 `process_path` 规则继承自Clash,
|
||||
原始代码使用本地系统的路径格式(例如 `\Device\HarddiskVolume1\folder\program.exe`),
|
||||
但是当设备有多个硬盘时,该 HarddiskVolume 系列号并不稳定。
|
||||
|
||||
sing-box 1.9.0 使 QueryFullProcessImageNameW 输出 Win32 路径(如 `C:\folder\program.exe`),
|
||||
这将会破坏现有的 Windows `process_path` 用例。
|
||||
|
||||
## 1.8.0
|
||||
|
||||
### :material-close-box: 将缓存文件从 Clash API 迁移到独立选项
|
||||
|
||||
@@ -3,6 +3,7 @@ package experimental
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -27,11 +28,26 @@ func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.O
|
||||
}
|
||||
|
||||
func CalculateClashModeList(options option.Options) []string {
|
||||
var clashMode []string
|
||||
clashMode = append(clashMode, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...)
|
||||
clashMode = append(clashMode, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...)
|
||||
clashMode = common.FilterNotDefault(common.Uniq(clashMode))
|
||||
return clashMode
|
||||
var clashModes []string
|
||||
clashModes = append(clashModes, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...)
|
||||
clashModes = append(clashModes, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...)
|
||||
clashModes = common.FilterNotDefault(common.Uniq(clashModes))
|
||||
predefinedOrder := []string{
|
||||
"Rule", "Global", "Direct",
|
||||
}
|
||||
var newClashModes []string
|
||||
for _, mode := range clashModes {
|
||||
if !common.Contains(predefinedOrder, mode) {
|
||||
newClashModes = append(newClashModes, mode)
|
||||
}
|
||||
}
|
||||
sort.Strings(newClashModes)
|
||||
for _, mode := range predefinedOrder {
|
||||
if common.Contains(clashModes, mode) {
|
||||
newClashModes = append(newClashModes, mode)
|
||||
}
|
||||
}
|
||||
return newClashModes
|
||||
}
|
||||
|
||||
func extraClashModeFromRule(rules []option.Rule) []string {
|
||||
|
||||
@@ -4,7 +4,6 @@ const (
|
||||
CommandLog int32 = iota
|
||||
CommandStatus
|
||||
CommandServiceReload
|
||||
CommandServiceClose
|
||||
CommandCloseConnections
|
||||
CommandGroup
|
||||
CommandSelectOutbound
|
||||
|
||||
@@ -30,6 +30,7 @@ func (c *CommandClient) SetClashMode(newMode string) error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
newMode, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -60,6 +61,7 @@ func (c *CommandClient) handleModeConn(conn net.Conn) {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleModeConn(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
ctx := connKeepAlive(conn)
|
||||
for s.service == nil {
|
||||
select {
|
||||
@@ -71,6 +73,7 @@ func (s *CommandServer) handleModeConn(conn net.Conn) error {
|
||||
}
|
||||
clashServer := s.service.instance.Router().ClashServer()
|
||||
if clashServer == nil {
|
||||
defer conn.Close()
|
||||
return binary.Write(conn, binary.BigEndian, uint16(0))
|
||||
}
|
||||
err := writeClashModeList(conn, clashServer)
|
||||
|
||||
@@ -58,13 +58,7 @@ func (c *CommandClient) handleGroupConn(conn net.Conn) {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
||||
var interval int64
|
||||
err := binary.Read(conn, binary.BigEndian, &interval)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read interval")
|
||||
}
|
||||
ticker := time.NewTicker(time.Duration(interval))
|
||||
defer ticker.Stop()
|
||||
defer conn.Close()
|
||||
ctx := connKeepAlive(conn)
|
||||
for {
|
||||
service := s.service
|
||||
@@ -82,7 +76,7 @@ func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
case <-time.After(2 * time.Second):
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -280,6 +274,7 @@ func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -44,41 +44,3 @@ func (s *CommandServer) handleServiceReload(conn net.Conn) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandClient) ServiceClose() error {
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceClose))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hasError bool
|
||||
err = binary.Read(conn, binary.BigEndian, &hasError)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return E.New(errorMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleServiceClose(conn net.Conn) error {
|
||||
rErr := s.service.Close()
|
||||
s.handler.PostServiceClose()
|
||||
err := binary.Write(conn, binary.BigEndian, rErr != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rErr != nil {
|
||||
return rw.WriteVString(conn, rErr.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -31,6 +31,7 @@ func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) erro
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -37,7 +37,6 @@ type CommandServer struct {
|
||||
|
||||
type CommandServerHandler interface {
|
||||
ServiceReload() error
|
||||
PostServiceClose()
|
||||
GetSystemProxyStatus() *SystemProxyStatus
|
||||
SetSystemProxyEnabled(isEnabled bool) error
|
||||
}
|
||||
@@ -59,19 +58,16 @@ func (s *CommandServer) SetService(newService *BoxService) {
|
||||
if newService != nil {
|
||||
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
|
||||
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
|
||||
s.savedLines.Init()
|
||||
select {
|
||||
case s.logReset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
s.service = newService
|
||||
s.notifyURLTestUpdate()
|
||||
}
|
||||
|
||||
func (s *CommandServer) ResetLog() {
|
||||
s.savedLines.Init()
|
||||
select {
|
||||
case s.logReset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) notifyURLTestUpdate() {
|
||||
select {
|
||||
case s.urlTestUpdate <- struct{}{}:
|
||||
@@ -156,8 +152,6 @@ func (s *CommandServer) handleConnection(conn net.Conn) error {
|
||||
return s.handleStatusConn(conn)
|
||||
case CommandServiceReload:
|
||||
return s.handleServiceReload(conn)
|
||||
case CommandServiceClose:
|
||||
return s.handleServiceClose(conn)
|
||||
case CommandCloseConnections:
|
||||
return s.handleCloseConnections(conn)
|
||||
case CommandGroup:
|
||||
|
||||
@@ -35,6 +35,7 @@ func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleGetSystemProxyStatus(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
status := s.handler.GetSystemProxyStatus()
|
||||
err := binary.Write(conn, binary.BigEndian, status.Available)
|
||||
if err != nil {
|
||||
@@ -67,6 +68,7 @@ func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSetSystemProxyEnabled(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
var isEnabled bool
|
||||
err := binary.Read(conn, binary.BigEndian, &isEnabled)
|
||||
if err != nil {
|
||||
|
||||
@@ -33,6 +33,7 @@ func (c *CommandClient) URLTest(groupTag string) error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -9,9 +9,7 @@ import (
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
@@ -25,9 +23,11 @@ type LocalDNSTransport interface {
|
||||
|
||||
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
||||
if transport == nil {
|
||||
dns.RegisterTransport([]string{"local"}, dns.CreateLocalTransport)
|
||||
dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
return dns.NewLocalTransport(options), nil
|
||||
})
|
||||
} else {
|
||||
dns.RegisterTransport([]string{"local"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
return &platformLocalDNSTransport{
|
||||
iif: transport,
|
||||
}, nil
|
||||
|
||||
@@ -3,17 +3,14 @@ package libbox
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
runtimeDebug "runtime/debug"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -75,25 +72,11 @@ func (s *BoxService) Start() error {
|
||||
}
|
||||
|
||||
func (s *BoxService) Close() error {
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
go func() {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-time.After(C.DefaultStopFatalTimeout):
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
s.cancel()
|
||||
s.urlTestHistoryStorage.Close()
|
||||
return s.instance.Close()
|
||||
}
|
||||
|
||||
func (s *BoxService) NeedWIFIState() bool {
|
||||
return s.instance.Router().NeedWIFIState()
|
||||
}
|
||||
|
||||
var (
|
||||
_ platform.Interface = (*platformInterfaceWrapper)(nil)
|
||||
_ log.PlatformWriter = (*platformInterfaceWrapper)(nil)
|
||||
|
||||
@@ -3,6 +3,8 @@ package libbox
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
type servicePauseFields struct {
|
||||
@@ -13,28 +15,38 @@ type servicePauseFields struct {
|
||||
func (s *BoxService) Pause() {
|
||||
s.pauseAccess.Lock()
|
||||
defer s.pauseAccess.Unlock()
|
||||
|
||||
if s.pauseTimer != nil {
|
||||
s.pauseTimer.Stop()
|
||||
log.Debug("pause() reconfigured")
|
||||
}
|
||||
|
||||
s.pauseTimer = time.AfterFunc(time.Minute, s.pause)
|
||||
}
|
||||
|
||||
func (s *BoxService) pause() {
|
||||
s.pauseAccess.Lock()
|
||||
defer s.pauseAccess.Unlock()
|
||||
|
||||
s.pauseManager.DevicePause()
|
||||
_ = s.instance.Router().ResetNetwork()
|
||||
s.pauseTimer = nil
|
||||
|
||||
log.Debug("pause()")
|
||||
}
|
||||
|
||||
func (s *BoxService) Wake() {
|
||||
_ = s.instance.Router().ResetNetwork()
|
||||
s.pauseAccess.Lock()
|
||||
defer s.pauseAccess.Unlock()
|
||||
|
||||
if s.pauseTimer != nil {
|
||||
s.pauseTimer.Stop()
|
||||
s.pauseTimer = nil
|
||||
log.Debug("pause() ignored")
|
||||
return
|
||||
}
|
||||
|
||||
s.pauseManager.DeviceWake()
|
||||
_ = s.instance.Router().ResetNetwork()
|
||||
log.Debug("wake()")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
_ "github.com/sagernet/sing-box/include"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
28
go.mod
28
go.mod
@@ -8,11 +8,11 @@ require (
|
||||
github.com/cloudflare/circl v1.3.7
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/go-chi/chi/v5 v5.0.11
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1
|
||||
github.com/libdns/alidns v1.0.3
|
||||
github.com/libdns/cloudflare v0.1.0
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
@@ -22,18 +22,18 @@ require (
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||
github.com/sagernet/gomobile v0.1.3
|
||||
github.com/sagernet/gomobile v0.1.1
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
|
||||
github.com/sagernet/quic-go v0.40.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.3.6
|
||||
github.com/sagernet/sing-dns v0.1.14
|
||||
github.com/sagernet/sing v0.3.1-0.20240105061852-782bc05c5573
|
||||
github.com/sagernet/sing-dns v0.1.13-0.20240209104932-6a377c9272fb
|
||||
github.com/sagernet/sing-mux v0.2.0
|
||||
github.com/sagernet/sing-quic v0.1.8
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.2.5
|
||||
github.com/sagernet/sing-tun v0.2.1
|
||||
github.com/sagernet/sing-vmess v0.1.8
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
||||
@@ -41,15 +41,15 @@ require (
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.uber.org/zap v1.27.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.uber.org/zap v1.26.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.22.0
|
||||
golang.org/x/sys v0.18.0
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/sys v0.17.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
google.golang.org/grpc v1.61.0
|
||||
google.golang.org/protobuf v1.32.0
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
|
||||
@@ -91,7 +91,7 @@ require (
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
|
||||
60
go.sum
60
go.sum
@@ -19,8 +19,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
|
||||
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
|
||||
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
@@ -49,8 +49,8 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1 h1:L3pm9Kf2G6gJVYawz2SrI5QnV1wzHYbqmKnSHHXJAb8=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20240204152450-ca2dc33955c1/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
@@ -98,8 +98,8 @@ 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/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
|
||||
github.com/sagernet/gomobile v0.1.3 h1:ohjIb1Ou2+1558PnZour3od69suSuvkdSVOlO1tC4B8=
|
||||
github.com/sagernet/gomobile v0.1.3/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||
github.com/sagernet/gomobile v0.1.1 h1:3vihRGyUfFTToHMeeak0UK6/ldt2MV2bcWKFi2VyECU=
|
||||
github.com/sagernet/gomobile v0.1.1/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE=
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
@@ -109,10 +109,10 @@ github.com/sagernet/quic-go v0.40.1/go.mod h1:CcKTpzTAISxrM4PA5M20/wYuz9Tj6Tx4Dw
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.3.6 h1:dsEdYLKBQlrxUfw1a92x0VdPvR1/BOxQ+HIMyaoEJsQ=
|
||||
github.com/sagernet/sing v0.3.6/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
|
||||
github.com/sagernet/sing-dns v0.1.14 h1:kxE/Ik3jMXmD3sXsdt9MgrNzLFWt64mghV+MQqzyf40=
|
||||
github.com/sagernet/sing-dns v0.1.14/go.mod h1:AA+vZMNovuPN5i/sPnfF6756Nq94nzb5nXodMWbta5w=
|
||||
github.com/sagernet/sing v0.3.1-0.20240105061852-782bc05c5573 h1:1wGN3eNanp8r+Y3bNBys3ZnAVF5gdtDoDwtosMZEbgA=
|
||||
github.com/sagernet/sing v0.3.1-0.20240105061852-782bc05c5573/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||
github.com/sagernet/sing-dns v0.1.13-0.20240209104932-6a377c9272fb h1:jjlaJ9GMjTB4OoS8taQ1gh0oMBnWke72qSteaMzecPU=
|
||||
github.com/sagernet/sing-dns v0.1.13-0.20240209104932-6a377c9272fb/go.mod h1:IxOqfSb6Zt6UVCy8fJpDxb2XxqzHUytNqeOuJfaiLu8=
|
||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
||||
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||
github.com/sagernet/sing-quic v0.1.8 h1:G4iBXAKIII+uTzd55oZ/9cAQswGjlvHh/0yKMQioDS0=
|
||||
@@ -123,8 +123,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.2.5 h1:DtXyS2weCc87bXe4GQkLkW7OyhXvKpBi2ppPpH7aHJM=
|
||||
github.com/sagernet/sing-tun v0.2.5/go.mod h1:GtKY1Sr2CCWLHPjVj9GZpBFZ/KoXOzVBSxXvmCCPxT4=
|
||||
github.com/sagernet/sing-tun v0.2.1 h1:xZ/MkQbAX4yuOXq/s1pfhWa0lK1Ld7t4gOpSUbRl1Rs=
|
||||
github.com/sagernet/sing-tun v0.2.1/go.mod h1:w1HTPPEL575m+Ob3GOIWaTs3ZrTdgLIwoldce/p3WPU=
|
||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
@@ -147,8 +147,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
@@ -159,25 +159,25 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
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-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -188,10 +188,10 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
@@ -204,14 +204,14 @@ golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
|
||||
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
|
||||
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -116,7 +116,6 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
@@ -130,7 +129,6 @@ func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
func (h *Hysteria) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
|
||||
@@ -127,7 +127,6 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
@@ -141,7 +140,6 @@ func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata a
|
||||
func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -109,8 +108,8 @@ func (n *Naive) Start() error {
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
err := n.configureHTTP3Listener()
|
||||
if !include.WithQUIC && len(n.network) > 1 {
|
||||
log.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
if !C.WithQUIC && len(n.network) > 1 {
|
||||
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -98,7 +98,6 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
@@ -112,7 +111,6 @@ func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapte
|
||||
func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"dhcp"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
dns.RegisterTransport([]string{"dhcp"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
//go:build with_quic
|
||||
|
||||
package include
|
||||
|
||||
import (
|
||||
_ "github.com/sagernet/sing-box/transport/v2rayquic"
|
||||
_ "github.com/sagernet/sing-dns/quic"
|
||||
)
|
||||
|
||||
const WithQUIC = true
|
||||
@@ -16,8 +16,6 @@ import (
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
const WithQUIC = false
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"quic", "h3"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
|
||||
21
include/tz_android.go
Normal file
21
include/tz_android.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// kanged from https://github.com/golang/mobile/blob/c713f31d574bb632a93f169b2cc99c9e753fef0e/app/android.go#L89
|
||||
|
||||
package include
|
||||
|
||||
// #include <time.h>
|
||||
import "C"
|
||||
import "time"
|
||||
|
||||
func init() {
|
||||
var currentT C.time_t
|
||||
var currentTM C.struct_tm
|
||||
C.time(¤tT)
|
||||
C.localtime_r(¤tT, ¤tTM)
|
||||
tzOffset := int(currentTM.tm_gmtoff)
|
||||
tz := C.GoString(currentTM.tm_zone)
|
||||
time.Local = time.FixedZone(tz, tzOffset)
|
||||
}
|
||||
30
include/tz_ios.go
Normal file
30
include/tz_ios.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package include
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
#import <Foundation/Foundation.h>
|
||||
const char* getSystemTimeZone() {
|
||||
NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
|
||||
NSString *timeZoneName = [timeZone description];
|
||||
return [timeZoneName UTF8String];
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tzDescription := C.GoString(C.getSystemTimeZone())
|
||||
if len(tzDescription) == 0 {
|
||||
return
|
||||
}
|
||||
location, err := time.LoadLocation(strings.Split(tzDescription, " ")[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
time.Local = location
|
||||
}
|
||||
@@ -133,6 +133,7 @@ nav:
|
||||
- WireGuard: configuration/outbound/wireguard.md
|
||||
- Hysteria: configuration/outbound/hysteria.md
|
||||
- ShadowTLS: configuration/outbound/shadowtls.md
|
||||
- ShadowsocksR: configuration/outbound/shadowsocksr.md
|
||||
- VLESS: configuration/outbound/vless.md
|
||||
- TUIC: configuration/outbound/tuic.md
|
||||
- Hysteria2: configuration/outbound/hysteria2.md
|
||||
@@ -235,4 +236,4 @@ plugins:
|
||||
|
||||
Manual: 手册
|
||||
reconfigure_material: true
|
||||
reconfigure_search: true
|
||||
reconfigure_search: true
|
||||
@@ -19,6 +19,7 @@ type DNSServerOptions struct {
|
||||
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSClientOptions struct {
|
||||
@@ -26,6 +27,7 @@ type DNSClientOptions struct {
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSFakeIPOptions struct {
|
||||
|
||||
@@ -77,6 +77,9 @@ type DefaultDNSRule struct {
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
@@ -97,6 +100,7 @@ type DefaultDNSRule struct {
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
func (r DefaultDNSRule) IsValid() bool {
|
||||
@@ -105,16 +109,18 @@ func (r DefaultDNSRule) IsValid() bool {
|
||||
defaultValue.Server = r.Server
|
||||
defaultValue.DisableCache = r.DisableCache
|
||||
defaultValue.RewriteTTL = r.RewriteTTL
|
||||
defaultValue.ClientSubnet = r.ClientSubnet
|
||||
return !reflect.DeepEqual(r, defaultValue)
|
||||
}
|
||||
|
||||
type LogicalDNSRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
func (r LogicalDNSRule) IsValid() bool {
|
||||
|
||||
@@ -270,8 +270,7 @@ func truncateDNSMessage(response *mDNS.Msg, maxLen int) *mDNS.Msg {
|
||||
if responseLen <= maxLen {
|
||||
return response
|
||||
}
|
||||
newResponse := *response
|
||||
response = &newResponse
|
||||
response = response.Copy()
|
||||
for len(response.Answer) > 0 && responseLen > maxLen {
|
||||
response.Answer = response.Answer[:len(response.Answer)-1]
|
||||
response.Truncated = true
|
||||
|
||||
@@ -222,7 +222,20 @@ func NewRouter(
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
}
|
||||
}
|
||||
transport, err := dns.CreateTransport(tag, ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address)
|
||||
var clientSubnet netip.Addr
|
||||
if server.ClientSubnet != nil {
|
||||
clientSubnet = server.ClientSubnet.Build()
|
||||
} else if dnsOptions.ClientSubnet != nil {
|
||||
clientSubnet = dnsOptions.ClientSubnet.Build()
|
||||
}
|
||||
transport, err := dns.CreateTransport(dns.TransportOptions{
|
||||
Context: ctx,
|
||||
Logger: logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")),
|
||||
Name: tag,
|
||||
Dialer: detour,
|
||||
Address: server.Address,
|
||||
ClientSubnet: clientSubnet,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||
}
|
||||
@@ -262,7 +275,11 @@ func NewRouter(
|
||||
}
|
||||
if defaultTransport == nil {
|
||||
if len(transports) == 0 {
|
||||
transports = append(transports, dns.NewLocalTransport("local", N.SystemDialer))
|
||||
transports = append(transports, common.Must1(dns.CreateTransport(dns.TransportOptions{
|
||||
Context: ctx,
|
||||
Name: "local",
|
||||
Dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{})),
|
||||
})))
|
||||
}
|
||||
defaultTransport = transports[0]
|
||||
}
|
||||
@@ -560,12 +577,13 @@ func (r *Router) Start() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needWIFIStateFromRuleSet || r.needWIFIState) && r.platformInterface != nil {
|
||||
if needWIFIStateFromRuleSet || r.needWIFIState {
|
||||
monitor.Start("initialize WIFI state")
|
||||
r.needWIFIState = true
|
||||
r.interfaceMonitor.RegisterCallback(func(_ int) {
|
||||
r.updateWIFIState()
|
||||
})
|
||||
if r.platformInterface != nil && r.interfaceMonitor != nil {
|
||||
r.interfaceMonitor.RegisterCallback(func(_ int) {
|
||||
r.updateWIFIState()
|
||||
})
|
||||
}
|
||||
r.updateWIFIState()
|
||||
monitor.Finish()
|
||||
}
|
||||
@@ -715,10 +733,6 @@ func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
|
||||
return ruleSet, loaded
|
||||
}
|
||||
|
||||
func (r *Router) NeedWIFIState() bool {
|
||||
return r.needWIFIState
|
||||
}
|
||||
|
||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if r.pauseManager.IsDevicePaused() {
|
||||
return E.New("reject connection to ", metadata.Destination, " while device paused")
|
||||
@@ -1182,10 +1196,6 @@ func (r *Router) updateWIFIState() {
|
||||
state := r.platformInterface.ReadWIFIState()
|
||||
if state != r.wifiState {
|
||||
r.wifiState = state
|
||||
if state.SSID == "" && state.BSSID == "" {
|
||||
r.logger.Info("updated WIFI state: disconnected")
|
||||
} else {
|
||||
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
|
||||
}
|
||||
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package route
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/cache"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -37,41 +37,54 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) {
|
||||
return domain, loaded
|
||||
}
|
||||
|
||||
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool) (context.Context, dns.Transport, dns.DomainStrategy) {
|
||||
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) {
|
||||
metadata := adapter.ContextFrom(ctx)
|
||||
if metadata == nil {
|
||||
panic("no context")
|
||||
}
|
||||
for i, rule := range r.dnsRules {
|
||||
metadata.ResetRuleCache()
|
||||
if rule.Match(metadata) {
|
||||
detour := rule.Outbound()
|
||||
transport, loaded := r.transportMap[detour]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
||||
continue
|
||||
}
|
||||
if _, isFakeIP := transport.(adapter.FakeIPTransport); isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "match[", i, "] ", rule.String(), " => ", detour)
|
||||
if rule.DisableCache() {
|
||||
ctx = dns.ContextWithDisableCache(ctx, true)
|
||||
}
|
||||
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
|
||||
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL)
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
return ctx, transport, domainStrategy
|
||||
} else {
|
||||
return ctx, transport, r.defaultDomainStrategy
|
||||
if index < len(r.dnsRules) {
|
||||
dnsRules := r.dnsRules
|
||||
if index != -1 {
|
||||
dnsRules = dnsRules[index+1:]
|
||||
}
|
||||
for ruleIndex, rule := range dnsRules {
|
||||
metadata.ResetRuleCache()
|
||||
if rule.Match(metadata) {
|
||||
detour := rule.Outbound()
|
||||
transport, loaded := r.transportMap[detour]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
||||
continue
|
||||
}
|
||||
if _, isFakeIP := transport.(adapter.FakeIPTransport); isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
displayRuleIndex := ruleIndex
|
||||
if index != -1 {
|
||||
displayRuleIndex += index + 1
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", detour)
|
||||
if rule.DisableCache() {
|
||||
ctx = dns.ContextWithDisableCache(ctx, true)
|
||||
}
|
||||
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
|
||||
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL)
|
||||
}
|
||||
if clientSubnet := rule.ClientSubnet(); clientSubnet != nil {
|
||||
ctx = dns.ContextWithClientSubnet(ctx, *clientSubnet)
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
return ctx, transport, domainStrategy, rule, ruleIndex
|
||||
} else {
|
||||
return ctx, transport, r.defaultDomainStrategy, rule, ruleIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
|
||||
return ctx, r.defaultTransport, domainStrategy
|
||||
return ctx, r.defaultTransport, domainStrategy, nil, -1
|
||||
} else {
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy, nil, -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +99,8 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
)
|
||||
response, cached = r.dnsClient.ExchangeCache(ctx, message)
|
||||
if !cached {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
var metadata *adapter.InboundContext
|
||||
ctx, metadata = adapter.AppendContext(ctx)
|
||||
if len(message.Question) > 0 {
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
@@ -97,17 +111,47 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
}
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
ctx, transport, strategy := r.matchDNS(ctx, true)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
response, err = r.dnsClient.Exchange(ctx, transport, message, strategy)
|
||||
if err != nil && len(message.Question) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||
var (
|
||||
transport dns.Transport
|
||||
strategy dns.DomainStrategy
|
||||
rule adapter.DNSRule
|
||||
ruleIndex int
|
||||
)
|
||||
ruleIndex = -1
|
||||
for {
|
||||
var (
|
||||
dnsCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
addressLimit bool
|
||||
)
|
||||
|
||||
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex)
|
||||
dnsCtx, cancel = context.WithTimeout(dnsCtx, C.DNSTimeout)
|
||||
if rule != nil && rule.WithAddressLimit() && isAddressQuery(message) {
|
||||
addressLimit = true
|
||||
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, strategy, func(response *mDNS.Msg) bool {
|
||||
metadata.DestinationAddresses, _ = dns.MessageToAddresses(response)
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
})
|
||||
} else {
|
||||
addressLimit = false
|
||||
response, err = r.dnsClient.Exchange(dnsCtx, transport, message, strategy)
|
||||
}
|
||||
cancel()
|
||||
if err != nil {
|
||||
if errors.Is(err, dns.ErrResponseRejected) {
|
||||
r.dnsLogger.DebugContext(ctx, E.Cause(err, "response rejected for ", formatQuestion(message.Question[0].String())))
|
||||
} else if len(message.Question) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||
} else {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for <empty query>"))
|
||||
}
|
||||
}
|
||||
if !addressLimit || err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(message.Question) > 0 && response != nil {
|
||||
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)
|
||||
}
|
||||
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
||||
for _, answer := range response.Answer {
|
||||
switch record := answer.(type) {
|
||||
@@ -125,22 +169,57 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Domain = domain
|
||||
ctx, transport, transportStrategy := r.matchDNS(ctx, false)
|
||||
if strategy == dns.DomainStrategyAsIS {
|
||||
strategy = transportStrategy
|
||||
var (
|
||||
transport dns.Transport
|
||||
transportStrategy dns.DomainStrategy
|
||||
rule adapter.DNSRule
|
||||
ruleIndex int
|
||||
resultAddrs []netip.Addr
|
||||
err error
|
||||
)
|
||||
ruleIndex = -1
|
||||
for {
|
||||
var (
|
||||
dnsCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
addressLimit bool
|
||||
)
|
||||
metadata.ResetRuleCache()
|
||||
metadata.DestinationAddresses = nil
|
||||
dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex)
|
||||
if strategy == dns.DomainStrategyAsIS {
|
||||
strategy = transportStrategy
|
||||
}
|
||||
dnsCtx, cancel = context.WithTimeout(dnsCtx, C.DNSTimeout)
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
addressLimit = true
|
||||
resultAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, strategy, func(responseAddrs []netip.Addr) bool {
|
||||
metadata.DestinationAddresses = responseAddrs
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
})
|
||||
} else {
|
||||
addressLimit = false
|
||||
resultAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, strategy)
|
||||
}
|
||||
cancel()
|
||||
if err != nil {
|
||||
if errors.Is(err, dns.ErrResponseRejected) {
|
||||
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain)
|
||||
} else {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
||||
}
|
||||
} else if len(resultAddrs) == 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
|
||||
err = dns.RCodeNameError
|
||||
}
|
||||
if !addressLimit || err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
addrs, err := r.dnsClient.Lookup(ctx, transport, domain, strategy)
|
||||
if len(addrs) > 0 {
|
||||
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(addrs), " "))
|
||||
} else if err != nil {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
||||
} else {
|
||||
r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
|
||||
err = dns.RCodeNameError
|
||||
if len(resultAddrs) > 0 {
|
||||
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(resultAddrs), " "))
|
||||
}
|
||||
return addrs, err
|
||||
return resultAddrs, err
|
||||
}
|
||||
|
||||
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
|
||||
@@ -154,10 +233,13 @@ func (r *Router) ClearDNSCache() {
|
||||
}
|
||||
}
|
||||
|
||||
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []mDNS.RR) {
|
||||
for _, answer := range answers {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", mDNS.Type(answer.Header().Rrtype).String(), " ", formatQuestion(answer.String()))
|
||||
func isAddressQuery(message *mDNS.Msg) bool {
|
||||
for _, question := range message.Question {
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func fqdnToDomain(fqdn string) string {
|
||||
|
||||
@@ -97,3 +97,7 @@ func isWIFIDNSRule(rule option.DefaultDNSRule) bool {
|
||||
func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool {
|
||||
return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
|
||||
}
|
||||
|
||||
func isIPCIDRHeadlessRule(rule option.DefaultHeadlessRule) bool {
|
||||
return len(rule.IPCIDR) > 0 || rule.IPSet != nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ type abstractDefaultRule struct {
|
||||
sourceAddressItems []RuleItem
|
||||
sourcePortItems []RuleItem
|
||||
destinationAddressItems []RuleItem
|
||||
destinationIPCIDRItems []RuleItem
|
||||
destinationPortItems []RuleItem
|
||||
allItems []RuleItem
|
||||
ruleSetItem RuleItem
|
||||
@@ -64,6 +65,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
if len(r.sourceAddressItems) > 0 && !metadata.SourceAddressMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.sourceAddressItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.SourceAddressMatch = true
|
||||
@@ -73,6 +75,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
if len(r.sourcePortItems) > 0 && !metadata.SourcePortMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.sourcePortItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.SourcePortMatch = true
|
||||
@@ -82,6 +85,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 && !metadata.DestinationAddressMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.destinationAddressItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationAddressMatch = true
|
||||
@@ -90,7 +94,18 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if !metadata.IgnoreDestinationIPCIDRMatch && len(r.destinationIPCIDRItems) > 0 && !metadata.DestinationAddressMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.destinationIPCIDRItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationAddressMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationPortItems) > 0 && !metadata.DestinationPortMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.destinationPortItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationPortMatch = true
|
||||
@@ -100,6 +115,9 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
for _, item := range r.items {
|
||||
if _, isRuleSet := item.(*RuleSetItem); !isRuleSet {
|
||||
metadata.DidMatch = true
|
||||
}
|
||||
if !item.Match(metadata) {
|
||||
return r.invert
|
||||
}
|
||||
@@ -113,7 +131,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
return r.invert
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 && !metadata.DestinationAddressMatch {
|
||||
if ((!metadata.IgnoreDestinationIPCIDRMatch && len(r.destinationIPCIDRItems) > 0) || len(r.destinationAddressItems) > 0) && !metadata.DestinationAddressMatch {
|
||||
return r.invert
|
||||
}
|
||||
|
||||
@@ -121,6 +139,10 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
return r.invert
|
||||
}
|
||||
|
||||
if !metadata.DidMatch {
|
||||
return false
|
||||
}
|
||||
|
||||
return !r.invert
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
}
|
||||
if len(options.GeoIP) > 0 {
|
||||
item := NewGeoIPItem(router, logger, false, options.GeoIP)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
@@ -130,12 +130,12 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "ipcidr")
|
||||
}
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.IPIsPrivate {
|
||||
item := NewIPIsPrivateItem(false)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
@@ -37,6 +40,7 @@ type DefaultDNSRule struct {
|
||||
abstractDefaultRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Addr
|
||||
}
|
||||
|
||||
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
||||
@@ -47,6 +51,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
},
|
||||
disableCache: options.DisableCache,
|
||||
rewriteTTL: options.RewriteTTL,
|
||||
clientSubnet: (*netip.Addr)(options.ClientSubnet),
|
||||
}
|
||||
if len(options.Inbound) > 0 {
|
||||
item := NewInboundRule(options.Inbound)
|
||||
@@ -111,6 +116,11 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.GeoIP) > 0 {
|
||||
item := NewGeoIPItem(router, logger, false, options.GeoIP)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
||||
if err != nil {
|
||||
@@ -119,11 +129,24 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.IPCIDR) > 0 {
|
||||
item, err := NewIPCIDRItem(false, options.IPCIDR)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "ip_cidr")
|
||||
}
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.SourceIPIsPrivate {
|
||||
item := NewIPIsPrivateItem(true)
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.IPIsPrivate {
|
||||
item := NewIPIsPrivateItem(false)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
item := NewPortItem(true, options.SourcePort)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
@@ -211,12 +234,45 @@ func (r *DefaultDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) ClientSubnet() *netip.Addr {
|
||||
return r.clientSubnet
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) WithAddressLimit() bool {
|
||||
if len(r.destinationIPCIDRItems) > 0 {
|
||||
return true
|
||||
}
|
||||
for _, rawRule := range r.items {
|
||||
ruleSet, isRuleSet := rawRule.(*RuleSetItem)
|
||||
if !isRuleSet {
|
||||
continue
|
||||
}
|
||||
if ruleSet.ContainsIPCIDRRule() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) Match(metadata *adapter.InboundContext) bool {
|
||||
metadata.IgnoreDestinationIPCIDRMatch = true
|
||||
defer func() {
|
||||
metadata.IgnoreDestinationIPCIDRMatch = false
|
||||
}()
|
||||
return r.abstractDefaultRule.Match(metadata)
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) MatchAddressLimit(metadata *adapter.InboundContext) bool {
|
||||
return r.abstractDefaultRule.Match(metadata)
|
||||
}
|
||||
|
||||
var _ adapter.DNSRule = (*LogicalDNSRule)(nil)
|
||||
|
||||
type LogicalDNSRule struct {
|
||||
abstractLogicalRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Addr
|
||||
}
|
||||
|
||||
func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
|
||||
@@ -254,3 +310,51 @@ func (r *LogicalDNSRule) DisableCache() bool {
|
||||
func (r *LogicalDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) ClientSubnet() *netip.Addr {
|
||||
return r.clientSubnet
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) WithAddressLimit() bool {
|
||||
for _, rawRule := range r.rules {
|
||||
switch rule := rawRule.(type) {
|
||||
case *DefaultDNSRule:
|
||||
if rule.WithAddressLimit() {
|
||||
return true
|
||||
}
|
||||
case *LogicalDNSRule:
|
||||
if rule.WithAddressLimit() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) Match(metadata *adapter.InboundContext) bool {
|
||||
if r.mode == C.LogicalTypeAnd {
|
||||
return common.All(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).Match(metadata)
|
||||
}) != r.invert
|
||||
} else {
|
||||
return common.Any(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).Match(metadata)
|
||||
}) != r.invert
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) MatchAddressLimit(metadata *adapter.InboundContext) bool {
|
||||
if r.mode == C.LogicalTypeAnd {
|
||||
return common.All(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).MatchAddressLimit(metadata)
|
||||
}) != r.invert
|
||||
} else {
|
||||
return common.Any(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).MatchAddressLimit(metadata)
|
||||
}) != r.invert
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +80,11 @@ func NewDefaultHeadlessRule(router adapter.Router, options option.DefaultHeadles
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "ipcidr")
|
||||
}
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
} else if options.IPSet != nil {
|
||||
item := NewRawIPCIDRItem(false, options.IPSet)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user