Compare commits
55 Commits
v1.8.4
...
v1.9.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0463a2182 | ||
|
|
cd71e29440 | ||
|
|
e471255511 | ||
|
|
7f96e2c9b5 | ||
|
|
63da3feb4a | ||
|
|
4349dc7f10 | ||
|
|
626f2b8bc0 | ||
|
|
3d62227684 | ||
|
|
da11b8e49f | ||
|
|
7cb8a3898c | ||
|
|
3f642f08b6 | ||
|
|
825963737f | ||
|
|
d0e1fb1c03 | ||
|
|
ca53d40396 | ||
|
|
5a61ba1987 | ||
|
|
9690fd03ef | ||
|
|
2b93b74d38 | ||
|
|
dd52c26ae1 | ||
|
|
f288e3898b | ||
|
|
1bc893a73a | ||
|
|
7359fdf195 | ||
|
|
02b7041de6 | ||
|
|
96ac931b11 | ||
|
|
3077a82650 | ||
|
|
de998c5119 | ||
|
|
d32c30c4b7 | ||
|
|
4823023806 | ||
|
|
bb355d17b2 | ||
|
|
aaf30bf92b | ||
|
|
f8c400cffc | ||
|
|
3c24411e14 | ||
|
|
4a44aa3c21 | ||
|
|
8db2ae0c83 | ||
|
|
80d1aebcb7 | ||
|
|
5583e01c99 | ||
|
|
bca0b86549 | ||
|
|
8332878cdc | ||
|
|
d0ba69ad22 | ||
|
|
31b8834427 | ||
|
|
d0f7a59e9b | ||
|
|
71e7d517a8 | ||
|
|
e6885e9967 | ||
|
|
e2090923db | ||
|
|
46be319976 | ||
|
|
b27bc45cf2 | ||
|
|
3d735281f4 | ||
|
|
8760a0d94d | ||
|
|
2239b59933 | ||
|
|
425a63f59d | ||
|
|
b85725c009 | ||
|
|
17aebc56c1 | ||
|
|
f76b21b02c | ||
|
|
704545a2ec | ||
|
|
dc7b7afc06 | ||
|
|
e478d3c2dc |
14
.github/update_clients.sh
vendored
Executable file
14
.github/update_clients.sh
vendored
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/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"
|
||||||
60
.github/workflows/debug.yml
vendored
60
.github/workflows/debug.yml
vendored
@@ -25,22 +25,10 @@ jobs:
|
|||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Get latest go version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
echo go_version=$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') >> $GITHUB_OUTPUT
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ steps.version.outputs.go_version }}
|
go-version: ^1.22
|
||||||
- name: Add cache to Go proxy
|
|
||||||
run: |
|
|
||||||
version=`git rev-parse HEAD`
|
|
||||||
mkdir build
|
|
||||||
pushd build
|
|
||||||
go mod init build
|
|
||||||
go get -v github.com/sagernet/sing-box@$version
|
|
||||||
popd
|
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Run Test
|
- name: Run Test
|
||||||
run: |
|
run: |
|
||||||
@@ -56,9 +44,9 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.18.10
|
go-version: ~1.18
|
||||||
- name: Cache go module
|
- name: Cache go module
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
@@ -76,13 +64,33 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: 1.20.7
|
go-version: ~1.20
|
||||||
- name: Cache go module
|
- name: Cache go module
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/pkg/mod
|
~/go/pkg/mod
|
||||||
key: go118-${{ hashFiles('**/go.sum') }}
|
key: go120-${{ hashFiles('**/go.sum') }}
|
||||||
|
- name: Run Test
|
||||||
|
run: make ci_build_go120
|
||||||
|
build_go121:
|
||||||
|
name: Debug build (Go 1.21)
|
||||||
|
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.21
|
||||||
|
- name: Cache go module
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: go121-${{ hashFiles('**/go.sum') }}
|
||||||
- name: Run Test
|
- name: Run Test
|
||||||
run: make ci_build
|
run: make ci_build
|
||||||
cross:
|
cross:
|
||||||
@@ -188,8 +196,7 @@ jobs:
|
|||||||
- name: freebsd-arm64
|
- name: freebsd-arm64
|
||||||
goos: freebsd
|
goos: freebsd
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
|
fail-fast: true
|
||||||
fail-fast: false
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
GOOS: ${{ matrix.goos }}
|
GOOS: ${{ matrix.goos }}
|
||||||
@@ -204,19 +211,10 @@ jobs:
|
|||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Get latest go version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
echo go_version=$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') >> $GITHUB_OUTPUT
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ steps.version.outputs.go_version }}
|
go-version: ^1.21
|
||||||
- name: Build
|
- name: Build
|
||||||
id: build
|
id: build
|
||||||
run: make
|
run: make
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: sing-box-${{ matrix.name }}
|
|
||||||
path: sing-box*
|
|
||||||
22
.github/workflows/docker.yml
vendored
22
.github/workflows/docker.yml
vendored
@@ -1,9 +1,10 @@
|
|||||||
name: Build Docker Images
|
name: Build Docker Images
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
release:
|
||||||
inputs:
|
types:
|
||||||
tag:
|
- released
|
||||||
description: "The tag version you want to build"
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -25,15 +26,6 @@ jobs:
|
|||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/sagernet/sing-box
|
images: ghcr.io/sagernet/sing-box
|
||||||
- name: Get tag to build
|
|
||||||
id: tag
|
|
||||||
run: |
|
|
||||||
echo "latest=ghcr.io/sagernet/sing-box:latest" >> $GITHUB_OUTPUT
|
|
||||||
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
|
|
||||||
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "versioned=ghcr.io/sagernet/sing-box:${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
- name: Build and release Docker images
|
- name: Build and release Docker images
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
@@ -42,6 +34,6 @@ jobs:
|
|||||||
build-args: |
|
build-args: |
|
||||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
|
||||||
tags: |
|
tags: |
|
||||||
${{ steps.tag.outputs.latest }}
|
ghcr.io/sagernet/sing-box:latest
|
||||||
${{ steps.tag.outputs.versioned }}
|
ghcr.io/sagernet/sing-box:${{ github.ref_name }}
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
10
.github/workflows/lint.yml
vendored
10
.github/workflows/lint.yml
vendored
@@ -25,16 +25,16 @@ jobs:
|
|||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Get latest go version
|
- name: Setup Go
|
||||||
id: version
|
uses: actions/setup-go@v5
|
||||||
run: |
|
with:
|
||||||
echo go_version=$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') >> $GITHUB_OUTPUT
|
go-version: ^1.22
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ${{ steps.version.outputs.go_version }}
|
go-version: ${{ steps.version.outputs.go_version }}
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v4
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
|
|||||||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[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
|
||||||
@@ -5,10 +5,6 @@ builds:
|
|||||||
flags:
|
flags:
|
||||||
- -v
|
- -v
|
||||||
- -trimpath
|
- -trimpath
|
||||||
asmflags:
|
|
||||||
- all=-trimpath={{.Env.GOPATH}}
|
|
||||||
gcflags:
|
|
||||||
- all=-trimpath={{.Env.GOPATH}}
|
|
||||||
ldflags:
|
ldflags:
|
||||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||||
tags:
|
tags:
|
||||||
@@ -30,6 +26,7 @@ builds:
|
|||||||
- linux_arm64
|
- linux_arm64
|
||||||
- linux_arm_7
|
- linux_arm_7
|
||||||
- linux_s390x
|
- linux_s390x
|
||||||
|
- linux_riscv64
|
||||||
- windows_amd64_v1
|
- windows_amd64_v1
|
||||||
- windows_amd64_v3
|
- windows_amd64_v3
|
||||||
- windows_386
|
- windows_386
|
||||||
@@ -43,10 +40,6 @@ builds:
|
|||||||
flags:
|
flags:
|
||||||
- -v
|
- -v
|
||||||
- -trimpath
|
- -trimpath
|
||||||
asmflags:
|
|
||||||
- all=-trimpath={{.Env.GOPATH}}
|
|
||||||
gcflags:
|
|
||||||
- all=-trimpath={{.Env.GOPATH}}
|
|
||||||
ldflags:
|
ldflags:
|
||||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||||
tags:
|
tags:
|
||||||
@@ -54,7 +47,6 @@ builds:
|
|||||||
- with_quic
|
- with_quic
|
||||||
- with_dhcp
|
- with_dhcp
|
||||||
- with_wireguard
|
- with_wireguard
|
||||||
- with_ech
|
|
||||||
- with_utls
|
- with_utls
|
||||||
- with_reality_server
|
- with_reality_server
|
||||||
- with_acme
|
- with_acme
|
||||||
@@ -73,10 +65,6 @@ builds:
|
|||||||
flags:
|
flags:
|
||||||
- -v
|
- -v
|
||||||
- -trimpath
|
- -trimpath
|
||||||
asmflags:
|
|
||||||
- all=-trimpath={{.Env.GOPATH}}
|
|
||||||
gcflags:
|
|
||||||
- all=-trimpath={{.Env.GOPATH}}
|
|
||||||
ldflags:
|
ldflags:
|
||||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||||
tags:
|
tags:
|
||||||
@@ -96,8 +84,8 @@ builds:
|
|||||||
goarch: arm
|
goarch: arm
|
||||||
goarm: 7
|
goarm: 7
|
||||||
env:
|
env:
|
||||||
- CC=armv7a-linux-androideabi19-clang
|
- CC=armv7a-linux-androideabi21-clang
|
||||||
- CXX=armv7a-linux-androideabi19-clang++
|
- CXX=armv7a-linux-androideabi21-clang++
|
||||||
- goos: android
|
- goos: android
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
env:
|
env:
|
||||||
@@ -106,8 +94,8 @@ builds:
|
|||||||
- goos: android
|
- goos: android
|
||||||
goarch: 386
|
goarch: 386
|
||||||
env:
|
env:
|
||||||
- CC=i686-linux-android19-clang
|
- CC=i686-linux-android21-clang
|
||||||
- CXX=i686-linux-android19-clang++
|
- CXX=i686-linux-android21-clang++
|
||||||
- goos: android
|
- goos: android
|
||||||
goarch: amd64
|
goarch: amd64
|
||||||
goamd64: v1
|
goamd64: v1
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
|
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
COPY . /go/src/github.com/sagernet/sing-box
|
COPY . /go/src/github.com/sagernet/sing-box
|
||||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||||
|
|||||||
25
Makefile
25
Makefile
@@ -1,8 +1,9 @@
|
|||||||
NAME = sing-box
|
NAME = sing-box
|
||||||
COMMIT = $(shell git rev-parse --short HEAD)
|
COMMIT = $(shell git rev-parse --short HEAD)
|
||||||
TAGS_GO118 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api
|
TAGS_GO118 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api
|
||||||
TAGS_GO120 = with_quic,with_ech,with_utls
|
TAGS_GO120 = with_quic,with_utls
|
||||||
TAGS ?= $(TAGS_GO118),$(TAGS_GO120)
|
TAGS_GO121 = with_ech
|
||||||
|
TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121)
|
||||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server
|
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server
|
||||||
|
|
||||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||||
@@ -23,6 +24,10 @@ ci_build_go118:
|
|||||||
go build $(PARAMS) $(MAIN)
|
go build $(PARAMS) $(MAIN)
|
||||||
go build $(PARAMS) -tags "$(TAGS_GO118)" $(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:
|
ci_build:
|
||||||
go build $(PARAMS) $(MAIN)
|
go build $(PARAMS) $(MAIN)
|
||||||
go build $(MAIN_PARAMS) $(MAIN)
|
go build $(MAIN_PARAMS) $(MAIN)
|
||||||
@@ -61,7 +66,14 @@ proto_install:
|
|||||||
release:
|
release:
|
||||||
go run ./cmd/internal/build goreleaser release --clean --skip-publish || exit 1
|
go run ./cmd/internal/build goreleaser release --clean --skip-publish || exit 1
|
||||||
mkdir dist/release
|
mkdir dist/release
|
||||||
mv dist/*.tar.gz dist/*.zip dist/*.deb dist/*.rpm dist/*.pkg.tar.zst 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
|
||||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release
|
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release
|
||||||
rm -r dist/release
|
rm -r dist/release
|
||||||
|
|
||||||
@@ -73,11 +85,12 @@ update_android_version:
|
|||||||
go run ./cmd/internal/update_android_version
|
go run ./cmd/internal/update_android_version
|
||||||
|
|
||||||
build_android:
|
build_android:
|
||||||
cd ../sing-box-for-android && ./gradlew :app:assemblePlayRelease && ./gradlew --stop
|
cd ../sing-box-for-android && ./gradlew :app:assemblePlayRelease && ./gradlew :app:assembleOtherRelease && ./gradlew --stop
|
||||||
|
|
||||||
upload_android:
|
upload_android:
|
||||||
mkdir -p dist/release_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/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
|
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release_android
|
||||||
rm -rf dist/release_android
|
rm -rf dist/release_android
|
||||||
|
|
||||||
@@ -178,8 +191,8 @@ lib:
|
|||||||
go run ./cmd/internal/build_libbox -target ios
|
go run ./cmd/internal/build_libbox -target ios
|
||||||
|
|
||||||
lib_install:
|
lib_install:
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.1
|
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.3
|
||||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.1
|
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.1.3
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
mkdocs serve
|
mkdocs serve
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
|
"github.com/sagernet/sing-dns"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
)
|
)
|
||||||
@@ -30,6 +31,9 @@ type CacheFile interface {
|
|||||||
StoreFakeIP() bool
|
StoreFakeIP() bool
|
||||||
FakeIPStorage
|
FakeIPStorage
|
||||||
|
|
||||||
|
StoreRDRC() bool
|
||||||
|
dns.RDRCStore
|
||||||
|
|
||||||
LoadMode() string
|
LoadMode() string
|
||||||
StoreMode(mode string) error
|
StoreMode(mode string) error
|
||||||
LoadSelected(group string) string
|
LoadSelected(group string) string
|
||||||
|
|||||||
@@ -51,11 +51,13 @@ type InboundContext struct {
|
|||||||
|
|
||||||
// rule cache
|
// rule cache
|
||||||
|
|
||||||
IPCIDRMatchSource bool
|
IPCIDRMatchSource bool
|
||||||
SourceAddressMatch bool
|
SourceAddressMatch bool
|
||||||
SourcePortMatch bool
|
SourcePortMatch bool
|
||||||
DestinationAddressMatch bool
|
DestinationAddressMatch bool
|
||||||
DestinationPortMatch bool
|
DestinationPortMatch bool
|
||||||
|
DidMatch bool
|
||||||
|
IgnoreDestinationIPCIDRMatch bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *InboundContext) ResetRuleCache() {
|
func (c *InboundContext) ResetRuleCache() {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ type Router interface {
|
|||||||
|
|
||||||
RuleSet(tag string) (RuleSet, bool)
|
RuleSet(tag string) (RuleSet, bool)
|
||||||
|
|
||||||
|
NeedWIFIState() bool
|
||||||
|
|
||||||
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
|
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
|
||||||
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
||||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||||
@@ -84,6 +86,9 @@ type DNSRule interface {
|
|||||||
Rule
|
Rule
|
||||||
DisableCache() bool
|
DisableCache() bool
|
||||||
RewriteTTL() *uint32
|
RewriteTTL() *uint32
|
||||||
|
ClientSubnet() *netip.Addr
|
||||||
|
WithAddressLimit() bool
|
||||||
|
MatchAddressLimit(metadata *InboundContext) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleSet interface {
|
type RuleSet interface {
|
||||||
@@ -97,6 +102,7 @@ type RuleSet interface {
|
|||||||
type RuleSetMetadata struct {
|
type RuleSetMetadata struct {
|
||||||
ContainsProcessRule bool
|
ContainsProcessRule bool
|
||||||
ContainsWIFIRule bool
|
ContainsWIFIRule bool
|
||||||
|
ContainsIPCIDRRule bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleSetStartContext interface {
|
type RuleSetStartContext interface {
|
||||||
|
|||||||
1
clients/android
Submodule
1
clients/android
Submodule
Submodule clients/android added at c8db11f062
1
clients/apple
Submodule
1
clients/apple
Submodule
Submodule clients/apple added at 45b4e58b3f
@@ -46,13 +46,13 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
sharedFlags = append(sharedFlags, "-trimpath")
|
sharedFlags = append(sharedFlags, "-trimpath")
|
||||||
sharedFlags = append(sharedFlags, "-ldflags")
|
sharedFlags = append(sharedFlags, "-buildvcs=false")
|
||||||
currentTag, err := build_shared.ReadTag()
|
currentTag, err := build_shared.ReadTag()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
currentTag = "unknown"
|
currentTag = "unknown"
|
||||||
}
|
}
|
||||||
sharedFlags = append(sharedFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
||||||
debugFlags = append(debugFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
debugFlags = append(debugFlags, "-ldflags", "-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")
|
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")
|
iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack")
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
"github.com/sagernet/sing/common/shell"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -28,7 +30,7 @@ func FindSDK() {
|
|||||||
}
|
}
|
||||||
for _, path := range searchPath {
|
for _, path := range searchPath {
|
||||||
path = os.ExpandEnv(path)
|
path = os.ExpandEnv(path)
|
||||||
if rw.FileExists(path + "/licenses/android-sdk-license") {
|
if rw.FileExists(filepath.Join(path, "licenses", "android-sdk-license")) {
|
||||||
androidSDKPath = path
|
androidSDKPath = path
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -40,6 +42,14 @@ func FindSDK() {
|
|||||||
log.Fatal("android NDK not found")
|
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_HOME", androidSDKPath)
|
||||||
os.Setenv("ANDROID_SDK_HOME", androidSDKPath)
|
os.Setenv("ANDROID_SDK_HOME", androidSDKPath)
|
||||||
os.Setenv("ANDROID_NDK_HOME", androidNDKPath)
|
os.Setenv("ANDROID_NDK_HOME", androidNDKPath)
|
||||||
@@ -48,11 +58,13 @@ func FindSDK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findNDK() bool {
|
func findNDK() bool {
|
||||||
if rw.FileExists(androidSDKPath + "/ndk/25.1.8937393") {
|
const fixedVersion = "26.2.11394342"
|
||||||
androidNDKPath = androidSDKPath + "/ndk/25.1.8937393"
|
const versionFile = "source.properties"
|
||||||
|
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.FileExists(filepath.Join(fixedPath, versionFile)) {
|
||||||
|
androidNDKPath = fixedPath
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
ndkVersions, err := os.ReadDir(androidSDKPath + "/ndk")
|
ndkVersions, err := os.ReadDir(filepath.Join(androidSDKPath, "ndk"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -73,8 +85,10 @@ func findNDK() bool {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
for _, versionName := range versionNames {
|
for _, versionName := range versionNames {
|
||||||
if rw.FileExists(androidSDKPath + "/ndk/" + versionName) {
|
currentNDKPath := filepath.Join(androidSDKPath, "ndk", versionName)
|
||||||
androidNDKPath = androidSDKPath + "/ndk/" + versionName
|
if rw.FileExists(filepath.Join(androidSDKPath, versionFile)) {
|
||||||
|
androidNDKPath = currentNDKPath
|
||||||
|
log.Warn("reproducibility warning: using NDK version " + versionName + " instead of " + fixedVersion)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,8 +99,14 @@ var GoBinPath string
|
|||||||
|
|
||||||
func FindMobile() {
|
func FindMobile() {
|
||||||
goBin := filepath.Join(build.Default.GOPATH, "bin")
|
goBin := filepath.Join(build.Default.GOPATH, "bin")
|
||||||
if !rw.FileExists(goBin + "/" + "gobind") {
|
if runtime.GOOS == "windows" {
|
||||||
log.Fatal("missing gomobile installation")
|
if !rw.FileExists(filepath.Join(goBin, "gobind.exe")) {
|
||||||
|
log.Fatal("missing gomobile installation")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !rw.FileExists(filepath.Join(goBin, "gobind")) {
|
||||||
|
log.Fatal("missing gomobile installation")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GoBinPath = goBin
|
GoBinPath = goBin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -18,34 +19,46 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
common.Must(os.Chdir(androidPath))
|
common.Must(os.Chdir(androidPath))
|
||||||
localProps := common.Must1(os.ReadFile("local.properties"))
|
localProps := common.Must1(os.ReadFile("version.properties"))
|
||||||
var propsList [][]string
|
var propsList [][]string
|
||||||
for _, propLine := range strings.Split(string(localProps), "\n") {
|
for _, propLine := range strings.Split(string(localProps), "\n") {
|
||||||
propsList = append(propsList, strings.Split(propLine, "="))
|
propsList = append(propsList, strings.Split(propLine, "="))
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
|
versionUpdated bool
|
||||||
|
goVersionUpdated bool
|
||||||
|
)
|
||||||
for _, propPair := range propsList {
|
for _, propPair := range propsList {
|
||||||
if propPair[0] == "VERSION_NAME" {
|
switch propPair[0] {
|
||||||
if propPair[1] == newVersion.String() {
|
case "VERSION_NAME":
|
||||||
log.Info("version not changed")
|
if propPair[1] != newVersion.String() {
|
||||||
return
|
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())
|
||||||
}
|
}
|
||||||
propPair[1] = newVersion.String()
|
|
||||||
log.Info("updated version to ", newVersion.String())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !(versionUpdated || goVersionUpdated) {
|
||||||
|
log.Info("version not changed")
|
||||||
|
return
|
||||||
|
}
|
||||||
for _, propPair := range propsList {
|
for _, propPair := range propsList {
|
||||||
switch propPair[0] {
|
switch propPair[0] {
|
||||||
case "VERSION_CODE":
|
case "VERSION_CODE":
|
||||||
versionCode := common.Must1(strconv.ParseInt(propPair[1], 10, 64))
|
versionCode := common.Must1(strconv.ParseInt(propPair[1], 10, 64))
|
||||||
propPair[1] = strconv.Itoa(int(versionCode + 1))
|
propPair[1] = strconv.Itoa(int(versionCode + 1))
|
||||||
log.Info("updated version code to ", propPair[1])
|
log.Info("updated version code to ", propPair[1])
|
||||||
case "RELEASE_NOTES":
|
|
||||||
propPair[1] = "sing-box " + newVersion.String()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var newProps []string
|
var newProps []string
|
||||||
for _, propPair := range propsList {
|
for _, propPair := range propsList {
|
||||||
newProps = append(newProps, strings.Join(propPair, "="))
|
newProps = append(newProps, strings.Join(propPair, "="))
|
||||||
}
|
}
|
||||||
common.Must(os.WriteFile("local.properties", []byte(strings.Join(newProps, "\n")), 0o644))
|
common.Must(os.WriteFile("version.properties", []byte(strings.Join(newProps, "\n")), 0o644))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ package badtls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -18,20 +20,32 @@ import (
|
|||||||
var _ N.ReadWaiter = (*ReadWaitConn)(nil)
|
var _ N.ReadWaiter = (*ReadWaitConn)(nil)
|
||||||
|
|
||||||
type ReadWaitConn struct {
|
type ReadWaitConn struct {
|
||||||
*tls.STDConn
|
tls.Conn
|
||||||
halfAccess *sync.Mutex
|
halfAccess *sync.Mutex
|
||||||
rawInput *bytes.Buffer
|
rawInput *bytes.Buffer
|
||||||
input *bytes.Reader
|
input *bytes.Reader
|
||||||
hand *bytes.Buffer
|
hand *bytes.Buffer
|
||||||
readWaitOptions N.ReadWaitOptions
|
readWaitOptions N.ReadWaitOptions
|
||||||
|
tlsReadRecord func() error
|
||||||
|
tlsHandlePostHandshakeMessage func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
||||||
stdConn, isSTDConn := conn.(*tls.STDConn)
|
var (
|
||||||
if !isSTDConn {
|
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
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
rawConn := reflect.Indirect(reflect.ValueOf(stdConn))
|
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||||
rawHalfConn := rawConn.FieldByName("in")
|
rawHalfConn := rawConn.FieldByName("in")
|
||||||
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
||||||
return nil, E.New("badtls: invalid half conn")
|
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()))
|
hand := (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
|
||||||
return &ReadWaitConn{
|
return &ReadWaitConn{
|
||||||
STDConn: stdConn,
|
Conn: conn,
|
||||||
halfAccess: halfAccess,
|
halfAccess: halfAccess,
|
||||||
rawInput: rawInput,
|
rawInput: rawInput,
|
||||||
input: input,
|
input: input,
|
||||||
hand: hand,
|
hand: hand,
|
||||||
|
tlsReadRecord: tlsReadRecord,
|
||||||
|
tlsHandlePostHandshakeMessage: tlsHandlePostHandshakeMessage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,19 +87,19 @@ func (c *ReadWaitConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||||
err = c.Handshake()
|
err = c.HandshakeContext(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.halfAccess.Lock()
|
c.halfAccess.Lock()
|
||||||
defer c.halfAccess.Unlock()
|
defer c.halfAccess.Unlock()
|
||||||
for c.input.Len() == 0 {
|
for c.input.Len() == 0 {
|
||||||
err = tlsReadRecord(c.STDConn)
|
err = c.tlsReadRecord()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for c.hand.Len() > 0 {
|
for c.hand.Len() > 0 {
|
||||||
err = tlsHandlePostHandshakeMessage(c.STDConn)
|
err = c.tlsHandlePostHandshakeMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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 &&
|
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
|
||||||
// recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
|
// recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
|
||||||
c.rawInput.Bytes()[0] == 21 {
|
c.rawInput.Bytes()[0] == 21 {
|
||||||
_ = tlsReadRecord(c.STDConn)
|
_ = c.tlsReadRecord()
|
||||||
// return n, err // will be io.EOF on closeNotify
|
// 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 {
|
func (c *ReadWaitConn) Upstream() any {
|
||||||
return c.STDConn
|
return c.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:linkname tlsReadRecord crypto/tls.(*Conn).readRecord
|
var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error)
|
||||||
func tlsReadRecord(c *tls.STDConn) error
|
|
||||||
|
|
||||||
//go:linkname tlsHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
func init() {
|
||||||
func tlsHandlePostHandshakeMessage(c *tls.STDConn) error
|
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(
|
r1, _, err := syscall.SyscallN(
|
||||||
procQueryFullProcessImageNameW.Addr(),
|
procQueryFullProcessImageNameW.Addr(),
|
||||||
uintptr(h),
|
uintptr(h),
|
||||||
uintptr(1),
|
uintptr(0),
|
||||||
uintptr(unsafe.Pointer(&buf[0])),
|
uintptr(unsafe.Pointer(&buf[0])),
|
||||||
uintptr(unsafe.Pointer(&size)),
|
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,6 +2,129 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### 1.9.0-beta.7
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-beta.6
|
||||||
|
|
||||||
|
* Fix address filter DNS rule items **1**
|
||||||
|
* Fix DNS outbound responding with wrong data
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
Fixed an issue where address filter DNS rule was incorrectly rejected under certain circumstances.
|
||||||
|
If you have enabled `store_rdrc` to save results, consider clearing the cache file.
|
||||||
|
|
||||||
|
#### 1.8.7
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.15
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.14
|
||||||
|
|
||||||
|
* Improve DNS truncate behavior
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.13
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.8.6
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.12
|
||||||
|
|
||||||
|
* Handle Windows power events
|
||||||
|
* Always disable cache for fake-ip DNS transport if `dns.independent_cache` disabled
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.11
|
||||||
|
|
||||||
|
* Fix missing `rule_set_ipcidr_match_source` item in DNS rules **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [DNS Rule](/configuration/dns/rule/).
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.10
|
||||||
|
|
||||||
|
* Add `bypass_domain` and `search_domain` platform HTTP proxy options **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [TUN](/configuration/inbound/tun) inbound.
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.8
|
||||||
|
|
||||||
|
* Add rejected DNS response cache support **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
The new feature allows you to cache the check results of
|
||||||
|
[Address filter DNS rule items](/configuration/dns/rule/#address-filter-fields) until expiration.
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.7
|
||||||
|
|
||||||
|
* Update gVisor to 20240206.0
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.6
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.3
|
||||||
|
|
||||||
|
* Update `quic-go` to v0.41.0
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.2
|
||||||
|
|
||||||
|
* Add support for `client-subnet` DNS options **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [DNS](/configuration/dns), [DNS Server](/configuration/dns/server) and [DNS Rules](/configuration/dns/rule).
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### 1.9.0-alpha.1
|
||||||
|
|
||||||
|
* `domain_suffix` behavior update **1**
|
||||||
|
* `process_path` format update on Windows **2**
|
||||||
|
* Add address filter DNS rule items **3**
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [Migration](/migration/#domain_suffix-behavior-update).
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.8.4
|
#### 1.8.4
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
@@ -96,8 +219,8 @@ Also, starting with this release, uTLS requires at least Go 1.20.
|
|||||||
|
|
||||||
**11**:
|
**11**:
|
||||||
|
|
||||||
Updated `cloudflare-tls`, `gomobile`, `smux`, `tfo-go` and `wireguard-go` to latest, `quic-go` to `0.40.1` and `gvisor` to `20231204.0`
|
Updated `cloudflare-tls`, `gomobile`, `smux`, `tfo-go` and `wireguard-go` to latest, `quic-go` to `0.40.1` and `gvisor`
|
||||||
|
to `20231204.0`
|
||||||
|
|
||||||
#### 1.8.0-rc.11
|
#### 1.8.0-rc.11
|
||||||
|
|
||||||
@@ -347,7 +470,7 @@ see [TCP Brutal](/configuration/shared/tcp-brutal/) for details.
|
|||||||
|
|
||||||
**5**:
|
**5**:
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
#### 1.7.0-rc.3
|
#### 1.7.0-rc.3
|
||||||
|
|
||||||
@@ -384,7 +507,7 @@ Only supported in graphical clients on Android and iOS.
|
|||||||
|
|
||||||
**1**:
|
**1**:
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
#### 1.7.0-beta.3
|
#### 1.7.0-beta.3
|
||||||
|
|
||||||
|
|||||||
@@ -6,3 +6,9 @@ icon: material/security
|
|||||||
|
|
||||||
sing-box and official graphics clients do not collect or share personal data,
|
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.
|
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
|
# DNS
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
@@ -13,6 +21,7 @@
|
|||||||
"disable_expire": false,
|
"disable_expire": false,
|
||||||
"independent_cache": false,
|
"independent_cache": false,
|
||||||
"reverse_mapping": false,
|
"reverse_mapping": false,
|
||||||
|
"client_subnet": "",
|
||||||
"fakeip": {}
|
"fakeip": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,8 +30,8 @@
|
|||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
|
|
||||||
| Key | Format |
|
| Key | Format |
|
||||||
|----------|--------------------------------|
|
|----------|---------------------------------|
|
||||||
| `server` | List of [DNS Server](./server/) |
|
| `server` | List of [DNS Server](./server/) |
|
||||||
| `rules` | List of [DNS Rule](./rule/) |
|
| `rules` | List of [DNS Rule](./rule/) |
|
||||||
| `fakeip` | [FakeIP](./fakeip/) |
|
| `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
|
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.
|
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
|
# DNS
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
@@ -13,6 +21,7 @@
|
|||||||
"disable_expire": false,
|
"disable_expire": false,
|
||||||
"independent_cache": false,
|
"independent_cache": false,
|
||||||
"reverse_mapping": false,
|
"reverse_mapping": false,
|
||||||
|
"client_subnet": "",
|
||||||
"fakeip": {}
|
"fakeip": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,6 +67,14 @@
|
|||||||
|
|
||||||
由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。
|
由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||||
|
|
||||||
|
可以被 `servers.[].client_subnet` 或 `rules.[].client_subnet` 覆盖。
|
||||||
|
|
||||||
#### fakeip
|
#### fakeip
|
||||||
|
|
||||||
[FakeIP](./fakeip/) 设置。
|
[FakeIP](./fakeip/) 设置。
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
---
|
---
|
||||||
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)
|
||||||
|
:material-plus: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
:material-plus: [rule_set](#rule_set)
|
:material-plus: [rule_set](#rule_set)
|
||||||
@@ -53,11 +61,19 @@ icon: material/alert-decagram
|
|||||||
"source_geoip": [
|
"source_geoip": [
|
||||||
"private"
|
"private"
|
||||||
],
|
],
|
||||||
|
"geoip": [
|
||||||
|
"cn"
|
||||||
|
],
|
||||||
"source_ip_cidr": [
|
"source_ip_cidr": [
|
||||||
"10.0.0.0/24",
|
"10.0.0.0/24",
|
||||||
"192.168.0.1"
|
"192.168.0.1"
|
||||||
],
|
],
|
||||||
"source_ip_is_private": false,
|
"source_ip_is_private": false,
|
||||||
|
"ip_cidr": [
|
||||||
|
"10.0.0.0/24",
|
||||||
|
"192.168.0.1"
|
||||||
|
],
|
||||||
|
"ip_is_private": false,
|
||||||
"source_port": [
|
"source_port": [
|
||||||
12345
|
12345
|
||||||
],
|
],
|
||||||
@@ -101,13 +117,15 @@ icon: material/alert-decagram
|
|||||||
"geoip-cn",
|
"geoip-cn",
|
||||||
"geosite-cn"
|
"geosite-cn"
|
||||||
],
|
],
|
||||||
|
"rule_set_ipcidr_match_source": false,
|
||||||
"invert": false,
|
"invert": false,
|
||||||
"outbound": [
|
"outbound": [
|
||||||
"direct"
|
"direct"
|
||||||
],
|
],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false,
|
"disable_cache": false,
|
||||||
"rewrite_ttl": 100
|
"rewrite_ttl": 100,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "logical",
|
"type": "logical",
|
||||||
@@ -115,7 +133,8 @@ icon: material/alert-decagram
|
|||||||
"rules": [],
|
"rules": [],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false,
|
"disable_cache": false,
|
||||||
"rewrite_ttl": 100
|
"rewrite_ttl": 100,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -266,11 +285,9 @@ Match Clash mode.
|
|||||||
|
|
||||||
#### wifi_ssid
|
#### wifi_ssid
|
||||||
|
|
||||||
<!-- md:version 1.7.0-beta.4 -->
|
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi SSID.
|
Match WiFi SSID.
|
||||||
|
|
||||||
@@ -278,7 +295,7 @@ Match WiFi SSID.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi BSSID.
|
Match WiFi BSSID.
|
||||||
|
|
||||||
@@ -288,6 +305,12 @@ Match WiFi BSSID.
|
|||||||
|
|
||||||
Match [Rule Set](/configuration/route/#rule_set).
|
Match [Rule Set](/configuration/route/#rule_set).
|
||||||
|
|
||||||
|
#### rule_set_ipcidr_match_source
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.9.0"
|
||||||
|
|
||||||
|
Make `ipcidr` in rule sets match the source IP.
|
||||||
|
|
||||||
#### invert
|
#### invert
|
||||||
|
|
||||||
Invert match result.
|
Invert match result.
|
||||||
@@ -312,6 +335,44 @@ Disable cache and save cache in this query.
|
|||||||
|
|
||||||
Rewrite TTL in DNS responses.
|
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.
|
||||||
|
|
||||||
|
!!! info ""
|
||||||
|
|
||||||
|
`ip_cidr` items in included rule sets also takes effect as an address filtering field.
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
Enable `experimental.cache_file.store_rdrc` to cache results.
|
||||||
|
|
||||||
|
#### 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
|
### Logical Fields
|
||||||
|
|
||||||
#### type
|
#### type
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
---
|
---
|
||||||
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)
|
||||||
|
:material-plus: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [rule_set](#rule_set)
|
:material-plus: [rule_set](#rule_set)
|
||||||
@@ -53,10 +61,19 @@ icon: material/alert-decagram
|
|||||||
"source_geoip": [
|
"source_geoip": [
|
||||||
"private"
|
"private"
|
||||||
],
|
],
|
||||||
|
"geoip": [
|
||||||
|
"cn"
|
||||||
|
],
|
||||||
"source_ip_cidr": [
|
"source_ip_cidr": [
|
||||||
"10.0.0.0/24"
|
"10.0.0.0/24",
|
||||||
|
"192.168.0.1"
|
||||||
],
|
],
|
||||||
"source_ip_is_private": false,
|
"source_ip_is_private": false,
|
||||||
|
"ip_cidr": [
|
||||||
|
"10.0.0.0/24",
|
||||||
|
"192.168.0.1"
|
||||||
|
],
|
||||||
|
"ip_is_private": false,
|
||||||
"source_port": [
|
"source_port": [
|
||||||
12345
|
12345
|
||||||
],
|
],
|
||||||
@@ -100,19 +117,22 @@ icon: material/alert-decagram
|
|||||||
"geoip-cn",
|
"geoip-cn",
|
||||||
"geosite-cn"
|
"geosite-cn"
|
||||||
],
|
],
|
||||||
|
"rule_set_ipcidr_match_source": false,
|
||||||
"invert": false,
|
"invert": false,
|
||||||
"outbound": [
|
"outbound": [
|
||||||
"direct"
|
"direct"
|
||||||
],
|
],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false
|
"disable_cache": false,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "logical",
|
"type": "logical",
|
||||||
"mode": "and",
|
"mode": "and",
|
||||||
"rules": [],
|
"rules": [],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false
|
"disable_cache": false,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -265,7 +285,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 iOS 的图形客户端中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi SSID。
|
匹配 WiFi SSID。
|
||||||
|
|
||||||
@@ -273,7 +293,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 iOS 的图形客户端中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi BSSID。
|
匹配 WiFi BSSID。
|
||||||
|
|
||||||
@@ -283,6 +303,12 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
|||||||
|
|
||||||
匹配[规则集](/zh/configuration/route/#rule_set)。
|
匹配[规则集](/zh/configuration/route/#rule_set)。
|
||||||
|
|
||||||
|
#### rule_set_ipcidr_match_source
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
使规则集中的 `ipcidr` 规则匹配源 IP。
|
||||||
|
|
||||||
#### invert
|
#### invert
|
||||||
|
|
||||||
反选匹配结果。
|
反选匹配结果。
|
||||||
@@ -307,6 +333,44 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
|||||||
|
|
||||||
重写 DNS 回应中的 TTL。
|
重写 DNS 回应中的 TTL。
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||||
|
|
||||||
|
将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。
|
||||||
|
|
||||||
|
### 地址筛选字段
|
||||||
|
|
||||||
|
仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
||||||
|
|
||||||
|
!!! info ""
|
||||||
|
|
||||||
|
引用的规则集中的 `ip_cidr` 项也作为地址筛选字段生效。
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
启用 `experimental.cache_file.store_rdrc` 以缓存结果。
|
||||||
|
|
||||||
|
#### geoip
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
与查询响应匹配 GeoIP。
|
||||||
|
|
||||||
|
#### ip_cidr
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
与查询相应匹配 IP CIDR。
|
||||||
|
|
||||||
|
#### ip_is_private
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
与查询响应匹配非公开 IP。
|
||||||
|
|
||||||
### 逻辑字段
|
### 逻辑字段
|
||||||
|
|
||||||
#### type
|
#### type
|
||||||
@@ -319,4 +383,4 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
|||||||
|
|
||||||
#### rules
|
#### rules
|
||||||
|
|
||||||
包括的规则。
|
包括的规则。
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.9.0"
|
||||||
|
|
||||||
|
:material-plus: [client_subnet](#client_subnet)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -5,17 +13,17 @@
|
|||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"tag": "google",
|
"tag": "",
|
||||||
"address": "tls://dns.google",
|
"address": "",
|
||||||
"address_resolver": "local",
|
"address_resolver": "",
|
||||||
"address_strategy": "prefer_ipv4",
|
"address_strategy": "",
|
||||||
"strategy": "ipv4_only",
|
"strategy": "",
|
||||||
"detour": "direct"
|
"detour": "",
|
||||||
|
"client_subnet": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
@@ -80,10 +88,20 @@ Default domain strategy for resolving the domain names.
|
|||||||
|
|
||||||
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
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
|
#### detour
|
||||||
|
|
||||||
Tag of an outbound for connecting to the dns server.
|
Tag of an outbound for connecting to the dns server.
|
||||||
|
|
||||||
Default outbound will be used if empty.
|
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
|
```json
|
||||||
@@ -5,17 +13,17 @@
|
|||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"tag": "google",
|
"tag": "",
|
||||||
"address": "tls://dns.google",
|
"address": "",
|
||||||
"address_resolver": "local",
|
"address_resolver": "",
|
||||||
"address_strategy": "prefer_ipv4",
|
"address_strategy": "",
|
||||||
"strategy": "ipv4_only",
|
"strategy": "",
|
||||||
"detour": "direct"
|
"detour": "",
|
||||||
|
"client_subnet": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
@@ -87,3 +95,13 @@ DNS 服务器的地址。
|
|||||||
用于连接到 DNS 服务器的出站的标签。
|
用于连接到 DNS 服务器的出站的标签。
|
||||||
|
|
||||||
如果为空,将使用默认出站。
|
如果为空,将使用默认出站。
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||||
|
|
||||||
|
可以被 `rules.[].client_subnet` 覆盖。
|
||||||
|
|
||||||
|
将覆盖 `dns.client_subnet`。
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ icon: material/new-box
|
|||||||
|
|
||||||
!!! question "Since sing-box 1.8.0"
|
!!! question "Since sing-box 1.8.0"
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.9.0"
|
||||||
|
|
||||||
|
:material-plus: [store_rdrc](#store_rdrc)
|
||||||
|
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -11,7 +16,9 @@ icon: material/new-box
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"path": "",
|
"path": "",
|
||||||
"cache_id": "",
|
"cache_id": "",
|
||||||
"store_fakeip": false
|
"store_fakeip": false,
|
||||||
|
"store_rdrc": false,
|
||||||
|
"rdrc_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -29,6 +36,23 @@ Path to the cache file.
|
|||||||
|
|
||||||
#### cache_id
|
#### cache_id
|
||||||
|
|
||||||
Identifier in cache file.
|
Identifier in the cache file
|
||||||
|
|
||||||
If not empty, configuration specified data will use a separate store keyed by it.
|
If not empty, configuration specified data will use a separate store keyed by it.
|
||||||
|
|
||||||
|
#### store_fakeip
|
||||||
|
|
||||||
|
Store fakeip in the cache file
|
||||||
|
|
||||||
|
#### store_rdrc
|
||||||
|
|
||||||
|
Store rejected DNS response cache in the cache file
|
||||||
|
|
||||||
|
The check results of [Address filter DNS rule items](/configuration/dns/rule/#address-filter-fields)
|
||||||
|
will be cached until expiration.
|
||||||
|
|
||||||
|
#### rdrc_timeout
|
||||||
|
|
||||||
|
Timeout of rejected DNS response cache.
|
||||||
|
|
||||||
|
`7d` is used by default.
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ icon: material/new-box
|
|||||||
|
|
||||||
!!! question "自 sing-box 1.8.0 起"
|
!!! question "自 sing-box 1.8.0 起"
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.9.0 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [store_rdrc](#store_rdrc)
|
||||||
|
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -11,7 +16,9 @@ icon: material/new-box
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"path": "",
|
"path": "",
|
||||||
"cache_id": "",
|
"cache_id": "",
|
||||||
"store_fakeip": false
|
"store_fakeip": false,
|
||||||
|
"store_rdrc": false,
|
||||||
|
"rdrc_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -30,3 +37,19 @@ icon: material/new-box
|
|||||||
缓存文件中的标识符。
|
缓存文件中的标识符。
|
||||||
|
|
||||||
如果不为空,配置特定的数据将使用由其键控的单独存储。
|
如果不为空,配置特定的数据将使用由其键控的单独存储。
|
||||||
|
|
||||||
|
#### store_fakeip
|
||||||
|
|
||||||
|
将 fakeip 存储在缓存文件中。
|
||||||
|
|
||||||
|
#### store_rdrc
|
||||||
|
|
||||||
|
将拒绝的 DNS 响应缓存存储在缓存文件中。
|
||||||
|
|
||||||
|
[地址筛选 DNS 规则项](/zh/configuration/dns/rule/#_3) 的检查结果将被缓存至过期。
|
||||||
|
|
||||||
|
#### rdrc_timeout
|
||||||
|
|
||||||
|
拒绝的 DNS 响应缓存超时。
|
||||||
|
|
||||||
|
默认使用 `7d`。
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
:material-delete-alert: [store_mode](#store_mode)
|
:material-delete-alert: [store_mode](#store_mode)
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-delete-alert: [store_mode](#store_mode)
|
:material-delete-alert: [store_mode](#store_mode)
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
# Experimental
|
# Experimental
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
# 实验性
|
# 实验性
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ No authentication required if empty.
|
|||||||
|
|
||||||
!!! warning ""
|
!!! warning ""
|
||||||
|
|
||||||
To work on Android and iOS without privileges, use tun.platform.http_proxy instead.
|
To work on Android and Apple platforms without privileges, use tun.platform.http_proxy instead.
|
||||||
|
|
||||||
Automatically set system proxy configuration when start and clean up when stop.
|
Automatically set system proxy configuration when start and clean up when stop.
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ No authentication required if empty.
|
|||||||
|
|
||||||
!!! warning ""
|
!!! warning ""
|
||||||
|
|
||||||
To work on Android and iOS without privileges, use tun.platform.http_proxy instead.
|
To work on Android and Apple platforms without privileges, use tun.platform.http_proxy instead.
|
||||||
|
|
||||||
Automatically set system proxy configuration when start and clean up when stop.
|
Automatically set system proxy configuration when start and clean up when stop.
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
---
|
---
|
||||||
icon: material/alert-decagram
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.9.0"
|
||||||
|
|
||||||
|
:material-plus: [platform.http_proxy.bypass_domain](#platformhttp_proxybypass_domain)
|
||||||
|
:material-plus: [platform.http_proxy.match_domain](#platformhttp_proxymatch_domain)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
:material-plus: [gso](#gso)
|
:material-plus: [gso](#gso)
|
||||||
@@ -73,7 +78,9 @@ icon: material/alert-decagram
|
|||||||
"http_proxy": {
|
"http_proxy": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 8080
|
"server_port": 8080,
|
||||||
|
"bypass_domain": [],
|
||||||
|
"match_domain": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -260,6 +267,38 @@ Platform-specific settings, provided by client applications.
|
|||||||
|
|
||||||
System HTTP proxy settings.
|
System HTTP proxy settings.
|
||||||
|
|
||||||
|
#### platform.http_proxy.enabled
|
||||||
|
|
||||||
|
Enable system HTTP proxy.
|
||||||
|
|
||||||
|
#### platform.http_proxy.server
|
||||||
|
|
||||||
|
==Required==
|
||||||
|
|
||||||
|
HTTP proxy server address.
|
||||||
|
|
||||||
|
#### platform.http_proxy.server_port
|
||||||
|
|
||||||
|
==Required==
|
||||||
|
|
||||||
|
HTTP proxy server port.
|
||||||
|
|
||||||
|
#### platform.http_proxy.bypass_domain
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
On Apple platforms, `bypass_domain` items matches hostname **suffixes**.
|
||||||
|
|
||||||
|
Hostnames that bypass the HTTP proxy.
|
||||||
|
|
||||||
|
#### platform.http_proxy.match_domain
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
Only supported in graphical clients on Apple platforms.
|
||||||
|
|
||||||
|
Hostnames that use the HTTP proxy.
|
||||||
|
|
||||||
### Listen Fields
|
### Listen Fields
|
||||||
|
|
||||||
See [Listen Fields](/configuration/shared/listen/) for details.
|
See [Listen Fields](/configuration/shared/listen/) for details.
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
---
|
---
|
||||||
icon: material/alert-decagram
|
icon: material/new-box
|
||||||
---
|
---
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.9.0 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [platform.http_proxy.bypass_domain](#platformhttp_proxybypass_domain)
|
||||||
|
:material-plus: [platform.http_proxy.match_domain](#platformhttp_proxymatch_domain)
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [gso](#gso)
|
:material-plus: [gso](#gso)
|
||||||
@@ -73,7 +78,9 @@ icon: material/alert-decagram
|
|||||||
"http_proxy": {
|
"http_proxy": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 8080
|
"server_port": 8080,
|
||||||
|
"bypass_domain": [],
|
||||||
|
"match_domain": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -257,6 +264,38 @@ TCP/IP 栈。
|
|||||||
|
|
||||||
系统 HTTP 代理设置。
|
系统 HTTP 代理设置。
|
||||||
|
|
||||||
|
##### platform.http_proxy.enabled
|
||||||
|
|
||||||
|
启用系统 HTTP 代理。
|
||||||
|
|
||||||
|
##### platform.http_proxy.server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
系统 HTTP 代理服务器地址。
|
||||||
|
|
||||||
|
##### platform.http_proxy.server_port
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
系统 HTTP 代理服务器端口。
|
||||||
|
|
||||||
|
##### platform.http_proxy.bypass_domain
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
在 Apple 平台,`bypass_domain` 项匹配主机名 **后缀**.
|
||||||
|
|
||||||
|
绕过代理的主机名列表。
|
||||||
|
|
||||||
|
##### platform.http_proxy.match_domain
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
仅在 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
|
代理的主机名列表。
|
||||||
|
|
||||||
### 监听字段
|
### 监听字段
|
||||||
|
|
||||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
:material-plus: [gso](#gso)
|
:material-plus: [gso](#gso)
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [gso](#gso)
|
:material-plus: [gso](#gso)
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
# Route
|
# Route
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
# 路由
|
# 路由
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
:material-plus: [rule_set](#rule_set)
|
:material-plus: [rule_set](#rule_set)
|
||||||
@@ -109,6 +105,7 @@ icon: material/alert-decagram
|
|||||||
"geoip-cn",
|
"geoip-cn",
|
||||||
"geosite-cn"
|
"geosite-cn"
|
||||||
],
|
],
|
||||||
|
"rule_set_ipcidr_match_source": false,
|
||||||
"invert": false,
|
"invert": false,
|
||||||
"outbound": "direct"
|
"outbound": "direct"
|
||||||
},
|
},
|
||||||
@@ -284,7 +281,7 @@ Match Clash mode.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi SSID.
|
Match WiFi SSID.
|
||||||
|
|
||||||
@@ -292,7 +289,7 @@ Match WiFi SSID.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi BSSID.
|
Match WiFi BSSID.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [rule_set](#rule_set)
|
:material-plus: [rule_set](#rule_set)
|
||||||
@@ -107,6 +103,7 @@ icon: material/alert-decagram
|
|||||||
"geoip-cn",
|
"geoip-cn",
|
||||||
"geosite-cn"
|
"geosite-cn"
|
||||||
],
|
],
|
||||||
|
"rule_set_ipcidr_match_source": false,
|
||||||
"invert": false,
|
"invert": false,
|
||||||
"outbound": "direct"
|
"outbound": "direct"
|
||||||
},
|
},
|
||||||
@@ -282,7 +279,7 @@ icon: material/alert-decagram
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 iOS 的图形客户端中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi SSID。
|
匹配 WiFi SSID。
|
||||||
|
|
||||||
@@ -290,7 +287,7 @@ icon: material/alert-decagram
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 iOS 的图形客户端中支持。
|
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||||
|
|
||||||
匹配 WiFi BSSID。
|
匹配 WiFi BSSID。
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
!!! question "Since sing-box 1.8.0"
|
!!! question "Since sing-box 1.8.0"
|
||||||
@@ -128,7 +124,7 @@ Match source IP CIDR.
|
|||||||
|
|
||||||
!!! info ""
|
!!! info ""
|
||||||
|
|
||||||
`ip_cidr` is an alias for `source_ip_cidr` when the Rule Set is used in DNS rules or `rule_set_ipcidr_match_source` enabled in route rules.
|
`ip_cidr` is an alias for `source_ip_cidr` when `rule_set_ipcidr_match_source` enabled in route/DNS rules.
|
||||||
|
|
||||||
Match IP CIDR.
|
Match IP CIDR.
|
||||||
|
|
||||||
@@ -172,7 +168,7 @@ Match android package name.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi SSID.
|
Match WiFi SSID.
|
||||||
|
|
||||||
@@ -180,7 +176,7 @@ Match WiFi SSID.
|
|||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and Apple platforms.
|
||||||
|
|
||||||
Match WiFi BSSID.
|
Match WiFi BSSID.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
# Rule Set
|
# Rule Set
|
||||||
|
|
||||||
!!! question "Since sing-box 1.8.0"
|
!!! question "Since sing-box 1.8.0"
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
# Source Format
|
# Source Format
|
||||||
|
|
||||||
!!! question "Since sing-box 1.8.0"
|
!!! question "Since sing-box 1.8.0"
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
:material-alert-decagram: [utls](#utls)
|
:material-alert-decagram: [utls](#utls)
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/alert-decagram
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-alert-decagram: [utls](#utls)
|
:material-alert-decagram: [utls](#utls)
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ 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,
|
sing-box 1.8.0 introduces [Rule Set](/configuration/rule-set/), which can completely replace Geosite,
|
||||||
check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||||
|
|
||||||
Geosite,即由 V2Ray 维护的 domain-list-community 项目,作为早期流量绕过解决方案,存在着大量问题,包括缺少维护、规则不准确、管理困难。
|
|
||||||
|
|
||||||
## 1.6.0
|
## 1.6.0
|
||||||
|
|
||||||
The following features will be marked deprecated in 1.5.0 and removed entirely in 1.6.0.
|
The following features will be marked deprecated in 1.5.0 and removed entirely in 1.6.0.
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ Since sing-box 1.5.0:
|
|||||||
Since sing-box 1.8.0:
|
Since sing-box 1.8.0:
|
||||||
|
|
||||||
* Go 1.18.5 - ~
|
* Go 1.18.5 - ~
|
||||||
* Go 1.20.0 - ~ with tag `with_quic`, `with_ech`, or `with_utls` enabled
|
* Go 1.20.0 - ~ with tag `with_quic`, or `with_utls` enabled
|
||||||
|
* Go 1.21.0 - ~ with tag `with_ech` enabled
|
||||||
|
|
||||||
You can download and install Go from: https://go.dev/doc/install, latest version is recommended.
|
You can download and install Go from: https://go.dev/doc/install, latest version is recommended.
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ sing-box 1.4.0 前:
|
|||||||
从 sing-box 1.8.0:
|
从 sing-box 1.8.0:
|
||||||
|
|
||||||
* Go 1.18.5 - ~
|
* Go 1.18.5 - ~
|
||||||
* Go 1.20.0 - ~ 如果启用构建标记 `with_quic`、`with_ech` 或 `with_utls`
|
* Go 1.20.0 - ~ 如果启用构建标记 `with_quic` 或 `with_utls`
|
||||||
|
* Go 1.20.1 - ~ 如果启用构建标记 `with_ech`
|
||||||
|
|
||||||
您可以从 https://go.dev/doc/install 下载并安装 Go,推荐使用最新版本。
|
您可以从 https://go.dev/doc/install 下载并安装 Go,推荐使用最新版本。
|
||||||
|
|
||||||
|
|||||||
@@ -290,52 +290,6 @@ flowchart TB
|
|||||||
|
|
||||||
=== ":material-dns: DNS rules"
|
=== ":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
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
@@ -382,74 +336,135 @@ flowchart TB
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== ":material-router-network: Route rules"
|
=== ":material-dns: DNS rules (1.9.0+)"
|
||||||
|
|
||||||
```json
|
=== ":material-shield-off: With DNS Leaks"
|
||||||
{
|
|
||||||
"outbounds": [
|
```json
|
||||||
{
|
{
|
||||||
"type": "direct",
|
"dns": {
|
||||||
"tag": "direct"
|
"servers": [
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "block",
|
|
||||||
"tag": "block"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"route": {
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"type": "logical",
|
|
||||||
"mode": "or",
|
|
||||||
"rules": [
|
|
||||||
{
|
{
|
||||||
"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": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"port": 853
|
"outbound": "any",
|
||||||
|
"server": "local"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"network": "udp",
|
"clash_mode": "Direct",
|
||||||
"port": 443
|
"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"
|
|
||||||
},
|
},
|
||||||
{
|
"route": {
|
||||||
"geosite": "geolocation-cn",
|
"rule_set": [
|
||||||
"outbound": "direct"
|
{
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== ":material-router-network: Route rules"
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,28 @@
|
|||||||
icon: material/arrange-bring-forward
|
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
|
## 1.8.0
|
||||||
|
|
||||||
### :material-close-box: Migrate cache file from Clash API to independent options
|
### :material-close-box: Migrate cache file from Clash API to independent options
|
||||||
|
|||||||
@@ -2,6 +2,27 @@
|
|||||||
icon: material/arrange-bring-forward
|
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
|
## 1.8.0
|
||||||
|
|
||||||
### :material-close-box: 将缓存文件从 Clash API 迁移到独立选项
|
### :material-close-box: 将缓存文件从 Clash API 迁移到独立选项
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ var (
|
|||||||
string(bucketExpand),
|
string(bucketExpand),
|
||||||
string(bucketMode),
|
string(bucketMode),
|
||||||
string(bucketRuleSet),
|
string(bucketRuleSet),
|
||||||
|
string(bucketRDRC),
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheIDDefault = []byte("default")
|
cacheIDDefault = []byte("default")
|
||||||
@@ -37,17 +38,25 @@ var (
|
|||||||
var _ adapter.CacheFile = (*CacheFile)(nil)
|
var _ adapter.CacheFile = (*CacheFile)(nil)
|
||||||
|
|
||||||
type CacheFile struct {
|
type CacheFile struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
path string
|
path string
|
||||||
cacheID []byte
|
cacheID []byte
|
||||||
storeFakeIP bool
|
storeFakeIP bool
|
||||||
|
storeRDRC bool
|
||||||
|
rdrcTimeout time.Duration
|
||||||
DB *bbolt.DB
|
DB *bbolt.DB
|
||||||
saveAccess sync.RWMutex
|
saveMetadataTimer *time.Timer
|
||||||
|
saveFakeIPAccess sync.RWMutex
|
||||||
saveDomain map[netip.Addr]string
|
saveDomain map[netip.Addr]string
|
||||||
saveAddress4 map[string]netip.Addr
|
saveAddress4 map[string]netip.Addr
|
||||||
saveAddress6 map[string]netip.Addr
|
saveAddress6 map[string]netip.Addr
|
||||||
saveMetadataTimer *time.Timer
|
saveRDRCAccess sync.RWMutex
|
||||||
|
saveRDRC map[saveRDRCCacheKey]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type saveRDRCCacheKey struct {
|
||||||
|
TransportName string
|
||||||
|
QuestionName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||||
@@ -61,14 +70,25 @@ func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
|||||||
if options.CacheID != "" {
|
if options.CacheID != "" {
|
||||||
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
|
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
|
||||||
}
|
}
|
||||||
|
var rdrcTimeout time.Duration
|
||||||
|
if options.StoreRDRC {
|
||||||
|
if options.RDRCTimeout > 0 {
|
||||||
|
rdrcTimeout = time.Duration(options.RDRCTimeout)
|
||||||
|
} else {
|
||||||
|
rdrcTimeout = 7 * 24 * time.Hour
|
||||||
|
}
|
||||||
|
}
|
||||||
return &CacheFile{
|
return &CacheFile{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
path: filemanager.BasePath(ctx, path),
|
path: filemanager.BasePath(ctx, path),
|
||||||
cacheID: cacheIDBytes,
|
cacheID: cacheIDBytes,
|
||||||
storeFakeIP: options.StoreFakeIP,
|
storeFakeIP: options.StoreFakeIP,
|
||||||
|
storeRDRC: options.StoreRDRC,
|
||||||
|
rdrcTimeout: rdrcTimeout,
|
||||||
saveDomain: make(map[netip.Addr]string),
|
saveDomain: make(map[netip.Addr]string),
|
||||||
saveAddress4: make(map[string]netip.Addr),
|
saveAddress4: make(map[string]netip.Addr),
|
||||||
saveAddress6: make(map[string]netip.Addr),
|
saveAddress6: make(map[string]netip.Addr),
|
||||||
|
saveRDRC: make(map[saveRDRCCacheKey]bool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,34 +89,34 @@ func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
|
func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
|
||||||
c.saveAccess.Lock()
|
c.saveFakeIPAccess.Lock()
|
||||||
c.saveDomain[address] = domain
|
c.saveDomain[address] = domain
|
||||||
if address.Is4() {
|
if address.Is4() {
|
||||||
c.saveAddress4[domain] = address
|
c.saveAddress4[domain] = address
|
||||||
} else {
|
} else {
|
||||||
c.saveAddress6[domain] = address
|
c.saveAddress6[domain] = address
|
||||||
}
|
}
|
||||||
c.saveAccess.Unlock()
|
c.saveFakeIPAccess.Unlock()
|
||||||
go func() {
|
go func() {
|
||||||
err := c.FakeIPStore(address, domain)
|
err := c.FakeIPStore(address, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn("save FakeIP address pair: ", err)
|
logger.Warn("save FakeIP cache: ", err)
|
||||||
}
|
}
|
||||||
c.saveAccess.Lock()
|
c.saveFakeIPAccess.Lock()
|
||||||
delete(c.saveDomain, address)
|
delete(c.saveDomain, address)
|
||||||
if address.Is4() {
|
if address.Is4() {
|
||||||
delete(c.saveAddress4, domain)
|
delete(c.saveAddress4, domain)
|
||||||
} else {
|
} else {
|
||||||
delete(c.saveAddress6, domain)
|
delete(c.saveAddress6, domain)
|
||||||
}
|
}
|
||||||
c.saveAccess.Unlock()
|
c.saveFakeIPAccess.Unlock()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
||||||
c.saveAccess.RLock()
|
c.saveFakeIPAccess.RLock()
|
||||||
cachedDomain, cached := c.saveDomain[address]
|
cachedDomain, cached := c.saveDomain[address]
|
||||||
c.saveAccess.RUnlock()
|
c.saveFakeIPAccess.RUnlock()
|
||||||
if cached {
|
if cached {
|
||||||
return cachedDomain, true
|
return cachedDomain, true
|
||||||
}
|
}
|
||||||
@@ -137,13 +137,13 @@ func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bo
|
|||||||
cachedAddress netip.Addr
|
cachedAddress netip.Addr
|
||||||
cached bool
|
cached bool
|
||||||
)
|
)
|
||||||
c.saveAccess.RLock()
|
c.saveFakeIPAccess.RLock()
|
||||||
if !isIPv6 {
|
if !isIPv6 {
|
||||||
cachedAddress, cached = c.saveAddress4[domain]
|
cachedAddress, cached = c.saveAddress4[domain]
|
||||||
} else {
|
} else {
|
||||||
cachedAddress, cached = c.saveAddress6[domain]
|
cachedAddress, cached = c.saveAddress6[domain]
|
||||||
}
|
}
|
||||||
c.saveAccess.RUnlock()
|
c.saveFakeIPAccess.RUnlock()
|
||||||
if cached {
|
if cached {
|
||||||
return cachedAddress, true
|
return cachedAddress, true
|
||||||
}
|
}
|
||||||
|
|||||||
101
experimental/cachefile/rdrc.go
Normal file
101
experimental/cachefile/rdrc.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package cachefile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/bbolt"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bucketRDRC = []byte("rdrc")
|
||||||
|
|
||||||
|
func (c *CacheFile) StoreRDRC() bool {
|
||||||
|
return c.storeRDRC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) RDRCTimeout() time.Duration {
|
||||||
|
return c.rdrcTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) LoadRDRC(transportName string, qName string) (rejected bool) {
|
||||||
|
c.saveRDRCAccess.RLock()
|
||||||
|
rejected, cached := c.saveRDRC[saveRDRCCacheKey{transportName, qName}]
|
||||||
|
c.saveRDRCAccess.RUnlock()
|
||||||
|
if cached {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var deleteCache bool
|
||||||
|
err := c.DB.View(func(tx *bbolt.Tx) error {
|
||||||
|
bucket := c.bucket(tx, bucketRDRC)
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bucket = bucket.Bucket([]byte(transportName))
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
content := bucket.Get([]byte(qName))
|
||||||
|
if content == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
expiresAt := time.Unix(int64(binary.BigEndian.Uint64(content)), 0)
|
||||||
|
if time.Now().After(expiresAt) {
|
||||||
|
deleteCache = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rejected = true
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if deleteCache {
|
||||||
|
c.DB.Update(func(tx *bbolt.Tx) error {
|
||||||
|
bucket := c.bucket(tx, bucketRDRC)
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bucket = bucket.Bucket([]byte(transportName))
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return bucket.Delete([]byte(qName))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SaveRDRC(transportName string, qName string) error {
|
||||||
|
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
||||||
|
bucket, err := c.createBucket(tx, bucketRDRC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bucket, err = bucket.CreateBucketIfNotExists([]byte(transportName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
expiresAt := buf.Get(8)
|
||||||
|
defer buf.Put(expiresAt)
|
||||||
|
binary.BigEndian.PutUint64(expiresAt, uint64(time.Now().Add(c.rdrcTimeout).Unix()))
|
||||||
|
return bucket.Put([]byte(qName), expiresAt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SaveRDRCAsync(transportName string, qName string, logger logger.Logger) {
|
||||||
|
saveKey := saveRDRCCacheKey{transportName, qName}
|
||||||
|
c.saveRDRCAccess.Lock()
|
||||||
|
c.saveRDRC[saveKey] = true
|
||||||
|
c.saveRDRCAccess.Unlock()
|
||||||
|
go func() {
|
||||||
|
err := c.SaveRDRC(transportName, qName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("save RDRC: ", err)
|
||||||
|
}
|
||||||
|
c.saveRDRCAccess.Lock()
|
||||||
|
delete(c.saveRDRC, saveKey)
|
||||||
|
c.saveRDRCAccess.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package experimental
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
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 {
|
func CalculateClashModeList(options option.Options) []string {
|
||||||
var clashMode []string
|
var clashModes []string
|
||||||
clashMode = append(clashMode, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...)
|
clashModes = append(clashModes, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...)
|
||||||
clashMode = append(clashMode, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...)
|
clashModes = append(clashModes, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...)
|
||||||
clashMode = common.FilterNotDefault(common.Uniq(clashMode))
|
clashModes = common.FilterNotDefault(common.Uniq(clashModes))
|
||||||
return clashMode
|
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 {
|
func extraClashModeFromRule(rules []option.Rule) []string {
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ func (c *CommandClient) SetClashMode(newMode string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
|
func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
|
||||||
defer conn.Close()
|
|
||||||
newMode, err := rw.ReadVString(conn)
|
newMode, err := rw.ReadVString(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -61,7 +60,6 @@ func (c *CommandClient) handleModeConn(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleModeConn(conn net.Conn) error {
|
func (s *CommandServer) handleModeConn(conn net.Conn) error {
|
||||||
defer conn.Close()
|
|
||||||
ctx := connKeepAlive(conn)
|
ctx := connKeepAlive(conn)
|
||||||
for s.service == nil {
|
for s.service == nil {
|
||||||
select {
|
select {
|
||||||
@@ -73,7 +71,6 @@ func (s *CommandServer) handleModeConn(conn net.Conn) error {
|
|||||||
}
|
}
|
||||||
clashServer := s.service.instance.Router().ClashServer()
|
clashServer := s.service.instance.Router().ClashServer()
|
||||||
if clashServer == nil {
|
if clashServer == nil {
|
||||||
defer conn.Close()
|
|
||||||
return binary.Write(conn, binary.BigEndian, uint16(0))
|
return binary.Write(conn, binary.BigEndian, uint16(0))
|
||||||
}
|
}
|
||||||
err := writeClashModeList(conn, clashServer)
|
err := writeClashModeList(conn, clashServer)
|
||||||
|
|||||||
@@ -58,7 +58,13 @@ func (c *CommandClient) handleGroupConn(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
||||||
defer conn.Close()
|
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()
|
||||||
ctx := connKeepAlive(conn)
|
ctx := connKeepAlive(conn)
|
||||||
for {
|
for {
|
||||||
service := s.service
|
service := s.service
|
||||||
@@ -76,7 +82,7 @@ func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case <-time.After(2 * time.Second):
|
case <-ticker.C:
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -274,7 +280,6 @@ func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
||||||
defer conn.Close()
|
|
||||||
groupTag, err := rw.ReadVString(conn)
|
groupTag, err := rw.ReadVString(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
|
func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
|
||||||
defer conn.Close()
|
|
||||||
groupTag, err := rw.ReadVString(conn)
|
groupTag, err := rw.ReadVString(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleGetSystemProxyStatus(conn net.Conn) error {
|
func (s *CommandServer) handleGetSystemProxyStatus(conn net.Conn) error {
|
||||||
defer conn.Close()
|
|
||||||
status := s.handler.GetSystemProxyStatus()
|
status := s.handler.GetSystemProxyStatus()
|
||||||
err := binary.Write(conn, binary.BigEndian, status.Available)
|
err := binary.Write(conn, binary.BigEndian, status.Available)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -68,7 +67,6 @@ func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleSetSystemProxyEnabled(conn net.Conn) error {
|
func (s *CommandServer) handleSetSystemProxyEnabled(conn net.Conn) error {
|
||||||
defer conn.Close()
|
|
||||||
var isEnabled bool
|
var isEnabled bool
|
||||||
err := binary.Read(conn, binary.BigEndian, &isEnabled)
|
err := binary.Read(conn, binary.BigEndian, &isEnabled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ func (c *CommandClient) URLTest(groupTag string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||||
defer conn.Close()
|
|
||||||
groupTag, err := rw.ReadVString(conn)
|
groupTag, err := rw.ReadVString(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ import (
|
|||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/common/task"
|
"github.com/sagernet/sing/common/task"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
@@ -25,9 +23,11 @@ type LocalDNSTransport interface {
|
|||||||
|
|
||||||
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
||||||
if transport == nil {
|
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 {
|
} 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{
|
return &platformLocalDNSTransport{
|
||||||
iif: transport,
|
iif: transport,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -97,6 +97,17 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
|
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
|
||||||
|
if interfaceName == "" || interfaceIndex32 == -1 {
|
||||||
|
m.defaultInterfaceName = ""
|
||||||
|
m.defaultInterfaceIndex = -1
|
||||||
|
m.access.Lock()
|
||||||
|
callbacks := m.callbacks.Array()
|
||||||
|
m.access.Unlock()
|
||||||
|
for _, callback := range callbacks {
|
||||||
|
callback(tun.EventNoRoute)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
if m.iif.UsePlatformInterfaceGetter() {
|
if m.iif.UsePlatformInterfaceGetter() {
|
||||||
err = m.updateInterfacesPlatform()
|
err = m.updateInterfacesPlatform()
|
||||||
@@ -110,28 +121,6 @@ func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName s
|
|||||||
m.logger.Error(E.Cause(err, "update interfaces"))
|
m.logger.Error(E.Cause(err, "update interfaces"))
|
||||||
}
|
}
|
||||||
interfaceIndex := int(interfaceIndex32)
|
interfaceIndex := int(interfaceIndex32)
|
||||||
if interfaceName == "" {
|
|
||||||
for _, netIf := range m.networkAddresses {
|
|
||||||
if netIf.interfaceIndex == interfaceIndex {
|
|
||||||
interfaceName = netIf.interfaceName
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if interfaceIndex == -1 {
|
|
||||||
for _, netIf := range m.networkAddresses {
|
|
||||||
if netIf.interfaceName == interfaceName {
|
|
||||||
interfaceIndex = netIf.interfaceIndex
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if interfaceName == "" {
|
|
||||||
m.logger.Error(E.New("invalid interface name for ", interfaceIndex))
|
|
||||||
return
|
|
||||||
} else if interfaceIndex == -1 {
|
|
||||||
m.logger.Error(E.New("invalid interface index for ", interfaceName))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
|
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ type BoxService struct {
|
|||||||
instance *box.Box
|
instance *box.Box
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
urlTestHistoryStorage *urltest.HistoryStorage
|
urlTestHistoryStorage *urltest.HistoryStorage
|
||||||
|
|
||||||
|
servicePauseFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||||
@@ -75,14 +77,8 @@ func (s *BoxService) Close() error {
|
|||||||
return s.instance.Close()
|
return s.instance.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BoxService) Sleep() {
|
func (s *BoxService) NeedWIFIState() bool {
|
||||||
s.pauseManager.DevicePause()
|
return s.instance.Router().NeedWIFIState()
|
||||||
_ = s.instance.Router().ResetNetwork()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BoxService) Wake() {
|
|
||||||
s.pauseManager.DeviceWake()
|
|
||||||
_ = s.instance.Router().ResetNetwork()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
45
experimental/libbox/service_pause.go
Normal file
45
experimental/libbox/service_pause.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type servicePauseFields struct {
|
||||||
|
pauseAccess sync.Mutex
|
||||||
|
pauseTimer *time.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoxService) Pause() {
|
||||||
|
s.pauseAccess.Lock()
|
||||||
|
defer s.pauseAccess.Unlock()
|
||||||
|
|
||||||
|
if s.pauseTimer != nil {
|
||||||
|
s.pauseTimer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoxService) Wake() {
|
||||||
|
s.pauseAccess.Lock()
|
||||||
|
defer s.pauseAccess.Unlock()
|
||||||
|
|
||||||
|
if s.pauseTimer != nil {
|
||||||
|
s.pauseTimer.Stop()
|
||||||
|
s.pauseTimer = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.pauseManager.DeviceWake()
|
||||||
|
_ = s.instance.Router().ResetNetwork()
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/common/humanize"
|
"github.com/sagernet/sing-box/common/humanize"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
_ "github.com/sagernet/sing-box/include"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ type TunOptions interface {
|
|||||||
IsHTTPProxyEnabled() bool
|
IsHTTPProxyEnabled() bool
|
||||||
GetHTTPProxyServer() string
|
GetHTTPProxyServer() string
|
||||||
GetHTTPProxyServerPort() int32
|
GetHTTPProxyServerPort() int32
|
||||||
|
GetHTTPProxyBypassDomain() StringIterator
|
||||||
|
GetHTTPProxyMatchDomain() StringIterator
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoutePrefix struct {
|
type RoutePrefix struct {
|
||||||
@@ -156,3 +158,11 @@ func (o *tunOptions) GetHTTPProxyServer() string {
|
|||||||
func (o *tunOptions) GetHTTPProxyServerPort() int32 {
|
func (o *tunOptions) GetHTTPProxyServerPort() int32 {
|
||||||
return int32(o.TunPlatformOptions.HTTPProxy.ServerPort)
|
return int32(o.TunPlatformOptions.HTTPProxy.ServerPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetHTTPProxyBypassDomain() StringIterator {
|
||||||
|
return newIterator(o.TunPlatformOptions.HTTPProxy.BypassDomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetHTTPProxyMatchDomain() StringIterator {
|
||||||
|
return newIterator(o.TunPlatformOptions.HTTPProxy.MatchDomain)
|
||||||
|
}
|
||||||
|
|||||||
34
go.mod
34
go.mod
@@ -8,7 +8,7 @@ require (
|
|||||||
github.com/cloudflare/circl v1.3.7
|
github.com/cloudflare/circl v1.3.7
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/go-chi/chi/v5 v5.0.11
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/gofrs/uuid/v5 v5.0.0
|
github.com/gofrs/uuid/v5 v5.0.0
|
||||||
@@ -22,18 +22,18 @@ require (
|
|||||||
github.com/oschwald/maxminddb-golang v1.12.0
|
github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||||
github.com/sagernet/gomobile v0.1.1
|
github.com/sagernet/gomobile v0.1.3
|
||||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
|
github.com/sagernet/gvisor v0.0.0-20240214044702-a3d61928a32f
|
||||||
github.com/sagernet/quic-go v0.40.1
|
github.com/sagernet/quic-go v0.41.0-beta.2
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.3.0
|
github.com/sagernet/sing v0.3.4-beta.1
|
||||||
github.com/sagernet/sing-dns v0.1.12
|
github.com/sagernet/sing-dns v0.2.0-beta.14
|
||||||
github.com/sagernet/sing-mux v0.2.0
|
github.com/sagernet/sing-mux v0.2.0
|
||||||
github.com/sagernet/sing-quic v0.1.7
|
github.com/sagernet/sing-quic v0.1.9-beta.1
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4
|
github.com/sagernet/sing-shadowtls v0.1.4
|
||||||
github.com/sagernet/sing-tun v0.2.0
|
github.com/sagernet/sing-tun v0.2.4-beta.1
|
||||||
github.com/sagernet/sing-vmess v0.1.8
|
github.com/sagernet/sing-vmess v0.1.8
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
||||||
@@ -42,13 +42,13 @@ require (
|
|||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
go.uber.org/zap v1.26.0
|
go.uber.org/zap v1.27.0
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.18.0
|
golang.org/x/crypto v0.19.0
|
||||||
golang.org/x/net v0.20.0
|
golang.org/x/net v0.21.0
|
||||||
golang.org/x/sys v0.16.0
|
golang.org/x/sys v0.17.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
google.golang.org/grpc v1.60.1
|
google.golang.org/grpc v1.62.0
|
||||||
google.golang.org/protobuf v1.32.0
|
google.golang.org/protobuf v1.32.0
|
||||||
howett.net/plist v1.0.1
|
howett.net/plist v1.0.1
|
||||||
)
|
)
|
||||||
@@ -86,12 +86,12 @@ require (
|
|||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
golang.org/x/mod v0.15.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.17.0 // indirect
|
golang.org/x/tools v0.18.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/blake3 v1.2.1 // indirect
|
lukechampine.com/blake3 v1.2.1 // indirect
|
||||||
|
|||||||
76
go.sum
76
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/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 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
|
||||||
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
||||||
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
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/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||||
@@ -42,7 +42,7 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
|
|||||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
@@ -98,33 +98,35 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
|
|||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
|
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/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
|
||||||
github.com/sagernet/gomobile v0.1.1 h1:3vihRGyUfFTToHMeeak0UK6/ldt2MV2bcWKFi2VyECU=
|
github.com/sagernet/gomobile v0.1.3 h1:ohjIb1Ou2+1558PnZour3od69suSuvkdSVOlO1tC4B8=
|
||||||
github.com/sagernet/gomobile v0.1.1/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
github.com/sagernet/gomobile v0.1.3/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-20240214044702-a3d61928a32f h1:7hj/CcCkUiC6gfhX4D+QNyodmhfurW2L2Q4qzJ1bPnI=
|
||||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw=
|
github.com/sagernet/gvisor v0.0.0-20240214044702-a3d61928a32f/go.mod h1:bLmnT/4M4+yKB6F7JtRsbUr+YJ64yXwFIygjyYDFQzQ=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.40.1 h1:qLeTIJR0d0JWRmDWo346nLsVN6EWihd1kalJYPEd0TM=
|
github.com/sagernet/quic-go v0.41.0-beta.2 h1:NtFC1Ief+SYJkfRq68D1OEqZQTNh2jYSpyRLhjT+m6U=
|
||||||
github.com/sagernet/quic-go v0.40.1/go.mod h1:CcKTpzTAISxrM4PA5M20/wYuz9Tj6Tx4DwGbNl9UQrU=
|
github.com/sagernet/quic-go v0.41.0-beta.2/go.mod h1:X10Mf9DVHuSEReOLov/XuflD13MVLH3WtppVVFnSItU=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
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/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.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8=
|
github.com/sagernet/sing v0.3.4-beta.1 h1:y1jKu9u11drNO00ycJ6KOZd1axQdozU92MdhWuAmWnU=
|
||||||
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
github.com/sagernet/sing v0.3.4-beta.1/go.mod h1:qHySJ7u8po9DABtMYEkNBcOumx7ZZJf/fbv2sfTkNHE=
|
||||||
github.com/sagernet/sing-dns v0.1.12 h1:1HqZ+ln+Rezx/aJMStaS0d7oPeX2EobSV1NT537kyj4=
|
github.com/sagernet/sing-dns v0.2.0-beta.14 h1:/lelsvJa5S/BWokuYyeCkYdi3hf7DlKGdgsKuamJ9V8=
|
||||||
github.com/sagernet/sing-dns v0.1.12/go.mod h1:rx/DTOisneQpCgNQ4jbFU/JNEtnz0lYcHXenlVzpjEU=
|
github.com/sagernet/sing-dns v0.2.0-beta.14/go.mod h1:gfs585rEu+ZgsXJJiecEIK5avrF5SYlCAbFfZ1B66hs=
|
||||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
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-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||||
github.com/sagernet/sing-quic v0.1.7 h1:SC45rAnvQ9BuyO0V186OdDScMBitmZo0XcM9LBYRUW8=
|
github.com/sagernet/sing-quic v0.1.9-beta.1 h1:rCmgLUq2d4EA643EAvjbfUYYVMPCss0GpmS4pJCT2Lw=
|
||||||
github.com/sagernet/sing-quic v0.1.7/go.mod h1:wUg1Z6AGKeJguruZo0lhrie3dqPiRCKaidLAbK2ttEs=
|
github.com/sagernet/sing-quic v0.1.9-beta.1/go.mod h1:F4AXCZiwtRtYdLUTjVMO6elTpA/lLJe17sFlHhHmDVw=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
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 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||||
github.com/sagernet/sing-tun v0.2.0 h1:/WloeRTWvwKIuiIvY+HVJaaZsTGb3XqJXZUbn6wVhz4=
|
github.com/sagernet/sing-tun v0.2.4-0.20240228052322-427e1a732caa h1:6d/j3790DkkkoH7vo9RVfv9mDlj8tP9dbrc1Dr0Z/20=
|
||||||
github.com/sagernet/sing-tun v0.2.0/go.mod h1:vJHzPAbwFUHxdFHUFQlH+Fb4rT3K4/SHODdMLU1rrQI=
|
github.com/sagernet/sing-tun v0.2.4-0.20240228052322-427e1a732caa/go.mod h1:lkefC8gty7FTuzz9ZoNASueutIhClEz7LHjFK3BLGco=
|
||||||
|
github.com/sagernet/sing-tun v0.2.4-beta.1 h1:npx/TwmVqsGZxw5aX08oRHpFBaIw3VSgR/CVfz1BA1A=
|
||||||
|
github.com/sagernet/sing-tun v0.2.4-beta.1/go.mod h1:lkefC8gty7FTuzz9ZoNASueutIhClEz7LHjFK3BLGco=
|
||||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
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/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
@@ -159,25 +161,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/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 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
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/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
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.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
||||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.15.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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
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/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -188,10 +190,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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
@@ -199,15 +201,15 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
||||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
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=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0=
|
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-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
|
||||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
"github.com/sagernet/sing-box/common/uot"
|
"github.com/sagernet/sing-box/common/uot"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/include"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
@@ -109,8 +108,8 @@ func (n *Naive) Start() error {
|
|||||||
|
|
||||||
if common.Contains(n.network, N.NetworkUDP) {
|
if common.Contains(n.network, N.NetworkUDP) {
|
||||||
err := n.configureHTTP3Listener()
|
err := n.configureHTTP3Listener()
|
||||||
if !include.WithQUIC && len(n.network) > 1 {
|
if !C.WithQUIC && len(n.network) > 1 {
|
||||||
log.Warn(E.Cause(err, "naive http3 disabled"))
|
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,12 @@
|
|||||||
package include
|
package include
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
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`)
|
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,3 @@ import (
|
|||||||
_ "github.com/sagernet/sing-box/transport/v2rayquic"
|
_ "github.com/sagernet/sing-box/transport/v2rayquic"
|
||||||
_ "github.com/sagernet/sing-dns/quic"
|
_ "github.com/sagernet/sing-dns/quic"
|
||||||
)
|
)
|
||||||
|
|
||||||
const WithQUIC = true
|
|
||||||
|
|||||||
@@ -11,15 +11,12 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/v2ray"
|
"github.com/sagernet/sing-box/transport/v2ray"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
const WithQUIC = false
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
dns.RegisterTransport([]string{"quic", "h3"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
dns.RegisterTransport([]string{"quic", "h3"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||||
return nil, C.ErrQUICNotIncluded
|
return nil, C.ErrQUICNotIncluded
|
||||||
})
|
})
|
||||||
v2ray.RegisterQUICConstructor(
|
v2ray.RegisterQUICConstructor(
|
||||||
|
|||||||
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,7 +133,6 @@ nav:
|
|||||||
- WireGuard: configuration/outbound/wireguard.md
|
- WireGuard: configuration/outbound/wireguard.md
|
||||||
- Hysteria: configuration/outbound/hysteria.md
|
- Hysteria: configuration/outbound/hysteria.md
|
||||||
- ShadowTLS: configuration/outbound/shadowtls.md
|
- ShadowTLS: configuration/outbound/shadowtls.md
|
||||||
- ShadowsocksR: configuration/outbound/shadowsocksr.md
|
|
||||||
- VLESS: configuration/outbound/vless.md
|
- VLESS: configuration/outbound/vless.md
|
||||||
- TUIC: configuration/outbound/tuic.md
|
- TUIC: configuration/outbound/tuic.md
|
||||||
- Hysteria2: configuration/outbound/hysteria2.md
|
- Hysteria2: configuration/outbound/hysteria2.md
|
||||||
@@ -236,4 +235,4 @@ plugins:
|
|||||||
|
|
||||||
Manual: 手册
|
Manual: 手册
|
||||||
reconfigure_material: true
|
reconfigure_material: true
|
||||||
reconfigure_search: true
|
reconfigure_search: true
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type DNSServerOptions struct {
|
|||||||
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
||||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||||
Detour string `json:"detour,omitempty"`
|
Detour string `json:"detour,omitempty"`
|
||||||
|
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSClientOptions struct {
|
type DNSClientOptions struct {
|
||||||
@@ -26,6 +27,7 @@ type DNSClientOptions struct {
|
|||||||
DisableCache bool `json:"disable_cache,omitempty"`
|
DisableCache bool `json:"disable_cache,omitempty"`
|
||||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||||
|
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSFakeIPOptions struct {
|
type DNSFakeIPOptions struct {
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ type ExperimentalOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CacheFileOptions struct {
|
type CacheFileOptions struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
CacheID string `json:"cache_id,omitempty"`
|
CacheID string `json:"cache_id,omitempty"`
|
||||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||||
|
StoreRDRC bool `json:"store_rdrc,omitempty"`
|
||||||
|
RDRCTimeout Duration `json:"rdrc_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClashAPIOptions struct {
|
type ClashAPIOptions struct {
|
||||||
|
|||||||
@@ -65,38 +65,43 @@ func (r DNSRule) IsValid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DefaultDNSRule struct {
|
type DefaultDNSRule struct {
|
||||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||||
IPVersion int `json:"ip_version,omitempty"`
|
IPVersion int `json:"ip_version,omitempty"`
|
||||||
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||||
Network Listable[string] `json:"network,omitempty"`
|
Network Listable[string] `json:"network,omitempty"`
|
||||||
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
||||||
Protocol Listable[string] `json:"protocol,omitempty"`
|
Protocol Listable[string] `json:"protocol,omitempty"`
|
||||||
Domain Listable[string] `json:"domain,omitempty"`
|
Domain Listable[string] `json:"domain,omitempty"`
|
||||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
GeoIP Listable[string] `json:"geoip,omitempty"`
|
||||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
|
||||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||||
Port Listable[uint16] `json:"port,omitempty"`
|
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
Port Listable[uint16] `json:"port,omitempty"`
|
||||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||||
User Listable[string] `json:"user,omitempty"`
|
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||||
ClashMode string `json:"clash_mode,omitempty"`
|
User Listable[string] `json:"user,omitempty"`
|
||||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||||
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
ClashMode string `json:"clash_mode,omitempty"`
|
||||||
Invert bool `json:"invert,omitempty"`
|
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||||
Server string `json:"server,omitempty"`
|
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||||
DisableCache bool `json:"disable_cache,omitempty"`
|
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
||||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
RuleSetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,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 DefaultDNSRule) IsValid() bool {
|
func (r DefaultDNSRule) IsValid() bool {
|
||||||
@@ -105,16 +110,18 @@ func (r DefaultDNSRule) IsValid() bool {
|
|||||||
defaultValue.Server = r.Server
|
defaultValue.Server = r.Server
|
||||||
defaultValue.DisableCache = r.DisableCache
|
defaultValue.DisableCache = r.DisableCache
|
||||||
defaultValue.RewriteTTL = r.RewriteTTL
|
defaultValue.RewriteTTL = r.RewriteTTL
|
||||||
|
defaultValue.ClientSubnet = r.ClientSubnet
|
||||||
return !reflect.DeepEqual(r, defaultValue)
|
return !reflect.DeepEqual(r, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogicalDNSRule struct {
|
type LogicalDNSRule struct {
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
Rules []DNSRule `json:"rules,omitempty"`
|
Rules []DNSRule `json:"rules,omitempty"`
|
||||||
Invert bool `json:"invert,omitempty"`
|
Invert bool `json:"invert,omitempty"`
|
||||||
Server string `json:"server,omitempty"`
|
Server string `json:"server,omitempty"`
|
||||||
DisableCache bool `json:"disable_cache,omitempty"`
|
DisableCache bool `json:"disable_cache,omitempty"`
|
||||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||||
|
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LogicalDNSRule) IsValid() bool {
|
func (r LogicalDNSRule) IsValid() bool {
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ type TunPlatformOptions struct {
|
|||||||
type HTTPProxyOptions struct {
|
type HTTPProxyOptions struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
ServerOptions
|
ServerOptions
|
||||||
|
BypassDomain Listable[string] `json:"bypass_domain,omitempty"`
|
||||||
|
MatchDomain Listable[string] `json:"match_domain,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn = h.loopBack.NewPacketConn(conn)
|
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn))
|
||||||
if originDestination != destination {
|
if originDestination != destination {
|
||||||
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
|
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
type loopBackDetector struct {
|
type loopBackDetector struct {
|
||||||
@@ -40,7 +41,7 @@ func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *loopBackDetector) NewPacketConn(conn net.PacketConn) net.PacketConn {
|
func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn) N.NetPacketConn {
|
||||||
connAddr := M.AddrPortFromNet(conn.LocalAddr())
|
connAddr := M.AddrPortFromNet(conn.LocalAddr())
|
||||||
if !connAddr.IsValid() {
|
if !connAddr.IsValid() {
|
||||||
return conn
|
return conn
|
||||||
@@ -48,7 +49,7 @@ func (l *loopBackDetector) NewPacketConn(conn net.PacketConn) net.PacketConn {
|
|||||||
l.packetConnAccess.Lock()
|
l.packetConnAccess.Lock()
|
||||||
l.packetConnMap[connAddr] = true
|
l.packetConnMap[connAddr] = true
|
||||||
l.packetConnAccess.Unlock()
|
l.packetConnAccess.Unlock()
|
||||||
return &loopBackDetectPacketWrapper{PacketConn: conn, detector: l, connAddr: connAddr}
|
return &loopBackDetectPacketWrapper{NetPacketConn: conn, detector: l, connAddr: connAddr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *loopBackDetector) CheckConn(connAddr netip.AddrPort) bool {
|
func (l *loopBackDetector) CheckConn(connAddr netip.AddrPort) bool {
|
||||||
@@ -92,7 +93,7 @@ func (w *loopBackDetectWrapper) Upstream() any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type loopBackDetectPacketWrapper struct {
|
type loopBackDetectPacketWrapper struct {
|
||||||
net.PacketConn
|
N.NetPacketConn
|
||||||
detector *loopBackDetector
|
detector *loopBackDetector
|
||||||
connAddr netip.AddrPort
|
connAddr netip.AddrPort
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
@@ -104,7 +105,7 @@ func (w *loopBackDetectPacketWrapper) Close() error {
|
|||||||
delete(w.detector.packetConnMap, w.connAddr)
|
delete(w.detector.packetConnMap, w.connAddr)
|
||||||
w.detector.packetConnAccess.Unlock()
|
w.detector.packetConnAccess.Unlock()
|
||||||
})
|
})
|
||||||
return w.PacketConn.Close()
|
return w.NetPacketConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *loopBackDetectPacketWrapper) ReaderReplaceable() bool {
|
func (w *loopBackDetectPacketWrapper) ReaderReplaceable() bool {
|
||||||
@@ -116,7 +117,7 @@ func (w *loopBackDetectPacketWrapper) WriterReplaceable() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *loopBackDetectPacketWrapper) Upstream() any {
|
func (w *loopBackDetectPacketWrapper) Upstream() any {
|
||||||
return w.PacketConn
|
return w.NetPacketConn
|
||||||
}
|
}
|
||||||
|
|
||||||
type abstractUDPConn interface {
|
type abstractUDPConn interface {
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.Pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
metadata.Destination = M.Socksaddr{}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
|
||||||
for {
|
for {
|
||||||
err := d.handleConnection(ctx, conn, metadata)
|
err := d.handleConnection(ctx, conn, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -98,6 +98,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
metadata.Destination = M.Socksaddr{}
|
||||||
var reader N.PacketReader = conn
|
var reader N.PacketReader = conn
|
||||||
var counters []N.CountFunc
|
var counters []N.CountFunc
|
||||||
var cachedPackets []*N.PacketBuffer
|
var cachedPackets []*N.PacketBuffer
|
||||||
@@ -111,14 +112,11 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created {
|
if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created {
|
||||||
readWaiter.InitializeReadWaiter(N.ReadWaitOptions{
|
readWaiter.InitializeReadWaiter(N.ReadWaitOptions{})
|
||||||
MTU: dns.FixedPacketSize,
|
|
||||||
})
|
|
||||||
return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata)
|
return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
|
||||||
fastClose, cancel := common.ContextWithCancelCause(ctx)
|
fastClose, cancel := common.ContextWithCancelCause(ctx)
|
||||||
timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
|
timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
|
||||||
var group task.Group
|
var group task.Group
|
||||||
@@ -167,15 +165,11 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
timeout.Update()
|
timeout.Update()
|
||||||
responseBuffer := buf.NewPacket()
|
responseBuffer, err := dns.TruncateDNSMessage(&message, response, 1024)
|
||||||
responseBuffer.Resize(1024, 0)
|
|
||||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
cancel(err)
|
||||||
responseBuffer.Release()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
responseBuffer.Truncate(len(n))
|
|
||||||
err = conn.WritePacket(responseBuffer, destination)
|
err = conn.WritePacket(responseBuffer, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
cancel(err)
|
||||||
@@ -241,15 +235,11 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
timeout.Update()
|
timeout.Update()
|
||||||
responseBuffer := buf.NewPacket()
|
responseBuffer, err := dns.TruncateDNSMessage(&message, response, 1024)
|
||||||
responseBuffer.Resize(1024, 0)
|
|
||||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
cancel(err)
|
||||||
responseBuffer.Release()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
responseBuffer.Truncate(len(n))
|
|
||||||
err = conn.WritePacket(responseBuffer, destination)
|
err = conn.WritePacket(responseBuffer, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
cancel(err)
|
||||||
|
|||||||
@@ -125,7 +125,18 @@ func (s *URLTest) CheckOutbounds() {
|
|||||||
|
|
||||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
s.group.Touch()
|
s.group.Touch()
|
||||||
outbound := s.group.Select(network)
|
var outbound adapter.Outbound
|
||||||
|
switch N.NetworkName(network) {
|
||||||
|
case N.NetworkTCP:
|
||||||
|
outbound = s.group.selectedOutboundTCP
|
||||||
|
case N.NetworkUDP:
|
||||||
|
outbound = s.group.selectedOutboundUDP
|
||||||
|
default:
|
||||||
|
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||||
|
}
|
||||||
|
if outbound == nil {
|
||||||
|
outbound, _ = s.group.Select(network)
|
||||||
|
}
|
||||||
if outbound == nil {
|
if outbound == nil {
|
||||||
return nil, E.New("missing supported outbound")
|
return nil, E.New("missing supported outbound")
|
||||||
}
|
}
|
||||||
@@ -140,7 +151,10 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
|||||||
|
|
||||||
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
s.group.Touch()
|
s.group.Touch()
|
||||||
outbound := s.group.Select(N.NetworkUDP)
|
outbound := s.group.selectedOutboundUDP
|
||||||
|
if outbound == nil {
|
||||||
|
outbound, _ = s.group.Select(N.NetworkUDP)
|
||||||
|
}
|
||||||
if outbound == nil {
|
if outbound == nil {
|
||||||
return nil, E.New("missing supported outbound")
|
return nil, E.New("missing supported outbound")
|
||||||
}
|
}
|
||||||
@@ -271,7 +285,7 @@ func (g *URLTestGroup) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) Select(network string) adapter.Outbound {
|
func (g *URLTestGroup) Select(network string) (adapter.Outbound, bool) {
|
||||||
var minDelay uint16
|
var minDelay uint16
|
||||||
var minTime time.Time
|
var minTime time.Time
|
||||||
var minOutbound adapter.Outbound
|
var minOutbound adapter.Outbound
|
||||||
@@ -294,11 +308,11 @@ func (g *URLTestGroup) Select(network string) adapter.Outbound {
|
|||||||
if !common.Contains(detour.Network(), network) {
|
if !common.Contains(detour.Network(), network) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
minOutbound = detour
|
return detour, false
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
return minOutbound
|
return minOutbound, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) loopCheck() {
|
func (g *URLTestGroup) loopCheck() {
|
||||||
@@ -382,14 +396,12 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) performUpdateCheck() {
|
func (g *URLTestGroup) performUpdateCheck() {
|
||||||
outbound := g.Select(N.NetworkTCP)
|
|
||||||
var updated bool
|
var updated bool
|
||||||
if outbound != nil && outbound != g.selectedOutboundTCP {
|
if outbound, exists := g.Select(N.NetworkTCP); outbound != nil && (g.selectedOutboundTCP == nil || (exists && outbound != g.selectedOutboundTCP)) {
|
||||||
g.selectedOutboundTCP = outbound
|
g.selectedOutboundTCP = outbound
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
outbound = g.Select(N.NetworkUDP)
|
if outbound, exists := g.Select(N.NetworkUDP); outbound != nil && (g.selectedOutboundUDP == nil || (exists && outbound != g.selectedOutboundUDP)) {
|
||||||
if outbound != nil && outbound != g.selectedOutboundUDP {
|
|
||||||
g.selectedOutboundUDP = outbound
|
g.selectedOutboundUDP = outbound
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|||||||
109
route/router.go
109
route/router.go
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ import (
|
|||||||
serviceNTP "github.com/sagernet/sing/common/ntp"
|
serviceNTP "github.com/sagernet/sing/common/ntp"
|
||||||
"github.com/sagernet/sing/common/task"
|
"github.com/sagernet/sing/common/task"
|
||||||
"github.com/sagernet/sing/common/uot"
|
"github.com/sagernet/sing/common/uot"
|
||||||
|
"github.com/sagernet/sing/common/winpowrprof"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
"github.com/sagernet/sing/service/pause"
|
"github.com/sagernet/sing/service/pause"
|
||||||
)
|
)
|
||||||
@@ -68,6 +70,7 @@ type Router struct {
|
|||||||
geositeCache map[string]adapter.Rule
|
geositeCache map[string]adapter.Rule
|
||||||
needFindProcess bool
|
needFindProcess bool
|
||||||
dnsClient *dns.Client
|
dnsClient *dns.Client
|
||||||
|
dnsIndependentCache bool
|
||||||
defaultDomainStrategy dns.DomainStrategy
|
defaultDomainStrategy dns.DomainStrategy
|
||||||
dnsRules []adapter.DNSRule
|
dnsRules []adapter.DNSRule
|
||||||
ruleSets []adapter.RuleSet
|
ruleSets []adapter.RuleSet
|
||||||
@@ -85,6 +88,7 @@ type Router struct {
|
|||||||
networkMonitor tun.NetworkUpdateMonitor
|
networkMonitor tun.NetworkUpdateMonitor
|
||||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||||
packageManager tun.PackageManager
|
packageManager tun.PackageManager
|
||||||
|
powerListener winpowrprof.EventListener
|
||||||
processSearcher process.Searcher
|
processSearcher process.Searcher
|
||||||
timeService *ntp.Service
|
timeService *ntp.Service
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
@@ -120,6 +124,7 @@ func NewRouter(
|
|||||||
geositeOptions: common.PtrValueOrDefault(options.Geosite),
|
geositeOptions: common.PtrValueOrDefault(options.Geosite),
|
||||||
geositeCache: make(map[string]adapter.Rule),
|
geositeCache: make(map[string]adapter.Rule),
|
||||||
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
|
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
|
||||||
|
dnsIndependentCache: dnsOptions.IndependentCache,
|
||||||
defaultDetour: options.Final,
|
defaultDetour: options.Final,
|
||||||
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
|
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
|
||||||
autoDetectInterface: options.AutoDetectInterface,
|
autoDetectInterface: options.AutoDetectInterface,
|
||||||
@@ -136,7 +141,17 @@ func NewRouter(
|
|||||||
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
|
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
|
||||||
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
|
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
|
||||||
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
|
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
|
||||||
Logger: router.dnsLogger,
|
RDRC: func() dns.RDRCStore {
|
||||||
|
cacheFile := service.FromContext[adapter.CacheFile](ctx)
|
||||||
|
if cacheFile == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !cacheFile.StoreRDRC() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cacheFile
|
||||||
|
},
|
||||||
|
Logger: router.dnsLogger,
|
||||||
})
|
})
|
||||||
for i, ruleOptions := range options.Rules {
|
for i, ruleOptions := range options.Rules {
|
||||||
routeRule, err := NewRule(router, router.logger, ruleOptions, true)
|
routeRule, err := NewRule(router, router.logger, ruleOptions, true)
|
||||||
@@ -222,7 +237,20 @@ func NewRouter(
|
|||||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
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 {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||||
}
|
}
|
||||||
@@ -262,7 +290,12 @@ func NewRouter(
|
|||||||
}
|
}
|
||||||
if defaultTransport == nil {
|
if defaultTransport == nil {
|
||||||
if len(transports) == 0 {
|
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",
|
||||||
|
Address: "local",
|
||||||
|
Dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{})),
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
defaultTransport = transports[0]
|
defaultTransport = transports[0]
|
||||||
}
|
}
|
||||||
@@ -321,6 +354,14 @@ func NewRouter(
|
|||||||
router.interfaceMonitor = interfaceMonitor
|
router.interfaceMonitor = interfaceMonitor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
powerListener, err := winpowrprof.NewEventListener(router.notifyWindowsPowerEvent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "initialize power listener")
|
||||||
|
}
|
||||||
|
router.powerListener = powerListener
|
||||||
|
}
|
||||||
|
|
||||||
if ntpOptions.Enabled {
|
if ntpOptions.Enabled {
|
||||||
timeService, err := ntp.NewService(ctx, router, logFactory.NewLogger("ntp"), ntpOptions)
|
timeService, err := ntp.NewService(ctx, router, logFactory.NewLogger("ntp"), ntpOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -415,6 +456,9 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) Outbounds() []adapter.Outbound {
|
func (r *Router) Outbounds() []adapter.Outbound {
|
||||||
|
if !r.started {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return r.outbounds
|
return r.outbounds
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,13 +601,22 @@ func (r *Router) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if needWIFIStateFromRuleSet || r.needWIFIState {
|
|
||||||
monitor.Start("initialize WIFI state")
|
if r.powerListener != nil {
|
||||||
if r.platformInterface != nil && r.interfaceMonitor != nil {
|
monitor.Start("start power listener")
|
||||||
r.interfaceMonitor.RegisterCallback(func(_ int) {
|
err := r.powerListener.Start()
|
||||||
r.updateWIFIState()
|
monitor.Finish()
|
||||||
})
|
if err != nil {
|
||||||
|
return E.Cause(err, "start power listener")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needWIFIStateFromRuleSet || r.needWIFIState) && r.platformInterface != nil {
|
||||||
|
monitor.Start("initialize WIFI state")
|
||||||
|
r.needWIFIState = true
|
||||||
|
r.interfaceMonitor.RegisterCallback(func(_ int) {
|
||||||
|
r.updateWIFIState()
|
||||||
|
})
|
||||||
r.updateWIFIState()
|
r.updateWIFIState()
|
||||||
monitor.Finish()
|
monitor.Finish()
|
||||||
}
|
}
|
||||||
@@ -576,6 +629,11 @@ func (r *Router) Start() error {
|
|||||||
return E.Cause(err, "initialize rule[", i, "]")
|
return E.Cause(err, "initialize rule[", i, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monitor.Start("initialize DNS client")
|
||||||
|
r.dnsClient.Start()
|
||||||
|
monitor.Finish()
|
||||||
|
|
||||||
for i, rule := range r.dnsRules {
|
for i, rule := range r.dnsRules {
|
||||||
monitor.Start("initialize DNS rule[", i, "]")
|
monitor.Start("initialize DNS rule[", i, "]")
|
||||||
err := rule.Start()
|
err := rule.Start()
|
||||||
@@ -655,6 +713,13 @@ func (r *Router) Close() error {
|
|||||||
})
|
})
|
||||||
monitor.Finish()
|
monitor.Finish()
|
||||||
}
|
}
|
||||||
|
if r.powerListener != nil {
|
||||||
|
monitor.Start("close power listener")
|
||||||
|
err = E.Append(err, r.powerListener.Close(), func(err error) error {
|
||||||
|
return E.Cause(err, "close power listener")
|
||||||
|
})
|
||||||
|
monitor.Finish()
|
||||||
|
}
|
||||||
if r.timeService != nil {
|
if r.timeService != nil {
|
||||||
monitor.Start("close time service")
|
monitor.Start("close time service")
|
||||||
err = E.Append(err, r.timeService.Close(), func(err error) error {
|
err = E.Append(err, r.timeService.Close(), func(err error) error {
|
||||||
@@ -713,6 +778,10 @@ func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
|
|||||||
return ruleSet, loaded
|
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 {
|
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
if r.pauseManager.IsDevicePaused() {
|
if r.pauseManager.IsDevicePaused() {
|
||||||
return E.New("reject connection to ", metadata.Destination, " while device paused")
|
return E.New("reject connection to ", metadata.Destination, " while device paused")
|
||||||
@@ -1176,6 +1245,26 @@ func (r *Router) updateWIFIState() {
|
|||||||
state := r.platformInterface.ReadWIFIState()
|
state := r.platformInterface.ReadWIFIState()
|
||||||
if state != r.wifiState {
|
if state != r.wifiState {
|
||||||
r.wifiState = state
|
r.wifiState = state
|
||||||
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) notifyWindowsPowerEvent(event int) {
|
||||||
|
switch event {
|
||||||
|
case winpowrprof.EVENT_SUSPEND:
|
||||||
|
r.pauseManager.DevicePause()
|
||||||
|
_ = r.ResetNetwork()
|
||||||
|
case winpowrprof.EVENT_RESUME:
|
||||||
|
if !r.pauseManager.IsDevicePaused() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case winpowrprof.EVENT_RESUME_AUTOMATIC:
|
||||||
|
r.pauseManager.DeviceWake()
|
||||||
|
_ = r.ResetNetwork()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package route
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common/cache"
|
"github.com/sagernet/sing/common/cache"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -37,41 +37,55 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) {
|
|||||||
return domain, loaded
|
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)
|
metadata := adapter.ContextFrom(ctx)
|
||||||
if metadata == nil {
|
if metadata == nil {
|
||||||
panic("no context")
|
panic("no context")
|
||||||
}
|
}
|
||||||
for i, rule := range r.dnsRules {
|
if index < len(r.dnsRules) {
|
||||||
metadata.ResetRuleCache()
|
dnsRules := r.dnsRules
|
||||||
if rule.Match(metadata) {
|
if index != -1 {
|
||||||
detour := rule.Outbound()
|
dnsRules = dnsRules[index+1:]
|
||||||
transport, loaded := r.transportMap[detour]
|
}
|
||||||
if !loaded {
|
for ruleIndex, rule := range dnsRules {
|
||||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
metadata.ResetRuleCache()
|
||||||
continue
|
if rule.Match(metadata) {
|
||||||
}
|
detour := rule.Outbound()
|
||||||
if _, isFakeIP := transport.(adapter.FakeIPTransport); isFakeIP && !allowFakeIP {
|
transport, loaded := r.transportMap[detour]
|
||||||
continue
|
if !loaded {
|
||||||
}
|
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
||||||
r.dnsLogger.DebugContext(ctx, "match[", i, "] ", rule.String(), " => ", detour)
|
continue
|
||||||
if rule.DisableCache() {
|
}
|
||||||
ctx = dns.ContextWithDisableCache(ctx, true)
|
_, isFakeIP := transport.(adapter.FakeIPTransport)
|
||||||
}
|
if isFakeIP && !allowFakeIP {
|
||||||
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
|
continue
|
||||||
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL)
|
}
|
||||||
}
|
displayRuleIndex := ruleIndex
|
||||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
if index != -1 {
|
||||||
return ctx, transport, domainStrategy
|
displayRuleIndex += index + 1
|
||||||
} else {
|
}
|
||||||
return ctx, transport, r.defaultDomainStrategy
|
r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", detour)
|
||||||
|
if (isFakeIP && !r.dnsIndependentCache) || 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 {
|
if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
|
||||||
return ctx, r.defaultTransport, domainStrategy
|
return ctx, r.defaultTransport, domainStrategy, nil, -1
|
||||||
} else {
|
} else {
|
||||||
return ctx, r.defaultTransport, r.defaultDomainStrategy
|
return ctx, r.defaultTransport, r.defaultDomainStrategy, nil, -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,13 +94,15 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
|||||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
response *mDNS.Msg
|
response *mDNS.Msg
|
||||||
cached bool
|
cached bool
|
||||||
err error
|
transport dns.Transport
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
response, cached = r.dnsClient.ExchangeCache(ctx, message)
|
response, cached = r.dnsClient.ExchangeCache(ctx, message)
|
||||||
if !cached {
|
if !cached {
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
var metadata *adapter.InboundContext
|
||||||
|
ctx, metadata = adapter.AppendContext(ctx)
|
||||||
if len(message.Question) > 0 {
|
if len(message.Question) > 0 {
|
||||||
metadata.QueryType = message.Question[0].Qtype
|
metadata.QueryType = message.Question[0].Qtype
|
||||||
switch metadata.QueryType {
|
switch metadata.QueryType {
|
||||||
@@ -97,50 +113,130 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
|||||||
}
|
}
|
||||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||||
}
|
}
|
||||||
ctx, transport, strategy := r.matchDNS(ctx, true)
|
var (
|
||||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
strategy dns.DomainStrategy
|
||||||
defer cancel()
|
rule adapter.DNSRule
|
||||||
response, err = r.dnsClient.Exchange(ctx, transport, message, strategy)
|
ruleIndex int
|
||||||
if err != nil && len(message.Question) > 0 {
|
)
|
||||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
ruleIndex = -1
|
||||||
}
|
for {
|
||||||
}
|
var (
|
||||||
if len(message.Question) > 0 && response != nil {
|
dnsCtx context.Context
|
||||||
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)
|
cancel context.CancelFunc
|
||||||
}
|
addressLimit bool
|
||||||
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
)
|
||||||
for _, answer := range response.Answer {
|
|
||||||
switch record := answer.(type) {
|
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex)
|
||||||
case *mDNS.A:
|
dnsCtx, cancel = context.WithTimeout(dnsCtx, C.DNSTimeout)
|
||||||
r.dnsReverseMapping.Save(M.AddrFromIP(record.A), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl))
|
if rule != nil && rule.WithAddressLimit() && isAddressQuery(message) {
|
||||||
case *mDNS.AAAA:
|
addressLimit = true
|
||||||
r.dnsReverseMapping.Save(M.AddrFromIP(record.AAAA), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl))
|
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.ErrResponseRejectedCached) {
|
||||||
|
r.dnsLogger.DebugContext(ctx, E.Cause(err, "response rejected for ", formatQuestion(message.Question[0].String())), " (cached)")
|
||||||
|
} else 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return response, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
||||||
|
if _, isFakeIP := transport.(adapter.FakeIPTransport); !isFakeIP {
|
||||||
|
for _, answer := range response.Answer {
|
||||||
|
switch record := answer.(type) {
|
||||||
|
case *mDNS.A:
|
||||||
|
r.dnsReverseMapping.Save(M.AddrFromIP(record.A), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl))
|
||||||
|
case *mDNS.AAAA:
|
||||||
|
r.dnsReverseMapping.Save(M.AddrFromIP(record.AAAA), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
|
func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
|
||||||
|
var (
|
||||||
|
responseAddrs []netip.Addr
|
||||||
|
cached bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
responseAddrs, cached = r.dnsClient.LookupCache(ctx, domain, strategy)
|
||||||
|
if cached {
|
||||||
|
return responseAddrs, nil
|
||||||
|
}
|
||||||
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
|
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
metadata.Domain = domain
|
metadata.Domain = domain
|
||||||
ctx, transport, transportStrategy := r.matchDNS(ctx, false)
|
var (
|
||||||
if strategy == dns.DomainStrategyAsIS {
|
transport dns.Transport
|
||||||
strategy = transportStrategy
|
transportStrategy dns.DomainStrategy
|
||||||
|
rule adapter.DNSRule
|
||||||
|
ruleIndex int
|
||||||
|
)
|
||||||
|
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
|
||||||
|
responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, strategy, func(responseAddrs []netip.Addr) bool {
|
||||||
|
metadata.DestinationAddresses = responseAddrs
|
||||||
|
return rule.MatchAddressLimit(metadata)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addressLimit = false
|
||||||
|
responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, strategy)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, dns.ErrResponseRejectedCached) {
|
||||||
|
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)")
|
||||||
|
} else 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(responseAddrs) == 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)
|
if len(responseAddrs) > 0 {
|
||||||
defer cancel()
|
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " "))
|
||||||
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
|
|
||||||
}
|
}
|
||||||
return addrs, err
|
return responseAddrs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
|
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
|
||||||
@@ -154,10 +250,13 @@ func (r *Router) ClearDNSCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []mDNS.RR) {
|
func isAddressQuery(message *mDNS.Msg) bool {
|
||||||
for _, answer := range answers {
|
for _, question := range message.Question {
|
||||||
logger.InfoContext(ctx, "exchanged ", domain, " ", mDNS.Type(answer.Header().Rrtype).String(), " ", formatQuestion(answer.String()))
|
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func fqdnToDomain(fqdn string) string {
|
func fqdnToDomain(fqdn string) string {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func isGeoIPRule(rule option.DefaultRule) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isGeoIPDNSRule(rule option.DefaultDNSRule) bool {
|
func isGeoIPDNSRule(rule option.DefaultDNSRule) bool {
|
||||||
return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode)
|
return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) || len(rule.GeoIP) > 0 && common.Any(rule.GeoIP, notPrivateNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGeositeRule(rule option.DefaultRule) bool {
|
func isGeositeRule(rule option.DefaultRule) bool {
|
||||||
@@ -97,3 +97,7 @@ func isWIFIDNSRule(rule option.DefaultDNSRule) bool {
|
|||||||
func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool {
|
func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool {
|
||||||
return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
|
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
|
sourceAddressItems []RuleItem
|
||||||
sourcePortItems []RuleItem
|
sourcePortItems []RuleItem
|
||||||
destinationAddressItems []RuleItem
|
destinationAddressItems []RuleItem
|
||||||
|
destinationIPCIDRItems []RuleItem
|
||||||
destinationPortItems []RuleItem
|
destinationPortItems []RuleItem
|
||||||
allItems []RuleItem
|
allItems []RuleItem
|
||||||
ruleSetItem RuleItem
|
ruleSetItem RuleItem
|
||||||
@@ -64,6 +65,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(r.sourceAddressItems) > 0 && !metadata.SourceAddressMatch {
|
if len(r.sourceAddressItems) > 0 && !metadata.SourceAddressMatch {
|
||||||
|
metadata.DidMatch = true
|
||||||
for _, item := range r.sourceAddressItems {
|
for _, item := range r.sourceAddressItems {
|
||||||
if item.Match(metadata) {
|
if item.Match(metadata) {
|
||||||
metadata.SourceAddressMatch = true
|
metadata.SourceAddressMatch = true
|
||||||
@@ -73,6 +75,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(r.sourcePortItems) > 0 && !metadata.SourcePortMatch {
|
if len(r.sourcePortItems) > 0 && !metadata.SourcePortMatch {
|
||||||
|
metadata.DidMatch = true
|
||||||
for _, item := range r.sourcePortItems {
|
for _, item := range r.sourcePortItems {
|
||||||
if item.Match(metadata) {
|
if item.Match(metadata) {
|
||||||
metadata.SourcePortMatch = true
|
metadata.SourcePortMatch = true
|
||||||
@@ -82,6 +85,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(r.destinationAddressItems) > 0 && !metadata.DestinationAddressMatch {
|
if len(r.destinationAddressItems) > 0 && !metadata.DestinationAddressMatch {
|
||||||
|
metadata.DidMatch = true
|
||||||
for _, item := range r.destinationAddressItems {
|
for _, item := range r.destinationAddressItems {
|
||||||
if item.Match(metadata) {
|
if item.Match(metadata) {
|
||||||
metadata.DestinationAddressMatch = true
|
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 {
|
if len(r.destinationPortItems) > 0 && !metadata.DestinationPortMatch {
|
||||||
|
metadata.DidMatch = true
|
||||||
for _, item := range r.destinationPortItems {
|
for _, item := range r.destinationPortItems {
|
||||||
if item.Match(metadata) {
|
if item.Match(metadata) {
|
||||||
metadata.DestinationPortMatch = true
|
metadata.DestinationPortMatch = true
|
||||||
@@ -100,6 +115,9 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range r.items {
|
for _, item := range r.items {
|
||||||
|
if _, isRuleSet := item.(*RuleSetItem); !isRuleSet {
|
||||||
|
metadata.DidMatch = true
|
||||||
|
}
|
||||||
if !item.Match(metadata) {
|
if !item.Match(metadata) {
|
||||||
return r.invert
|
return r.invert
|
||||||
}
|
}
|
||||||
@@ -113,7 +131,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
|||||||
return r.invert
|
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
|
return r.invert
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +139,10 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
|||||||
return r.invert
|
return r.invert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !metadata.DidMatch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return !r.invert
|
return !r.invert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
|||||||
}
|
}
|
||||||
if len(options.GeoIP) > 0 {
|
if len(options.GeoIP) > 0 {
|
||||||
item := NewGeoIPItem(router, logger, false, options.GeoIP)
|
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)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.SourceIPCIDR) > 0 {
|
if len(options.SourceIPCIDR) > 0 {
|
||||||
@@ -130,12 +130,12 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "ipcidr")
|
return nil, E.Cause(err, "ipcidr")
|
||||||
}
|
}
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if options.IPIsPrivate {
|
if options.IPIsPrivate {
|
||||||
item := NewIPIsPrivateItem(false)
|
item := NewIPIsPrivateItem(false)
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.SourcePort) > 0 {
|
if len(options.SourcePort) > 0 {
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,6 +40,7 @@ type DefaultDNSRule struct {
|
|||||||
abstractDefaultRule
|
abstractDefaultRule
|
||||||
disableCache bool
|
disableCache bool
|
||||||
rewriteTTL *uint32
|
rewriteTTL *uint32
|
||||||
|
clientSubnet *netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
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,
|
disableCache: options.DisableCache,
|
||||||
rewriteTTL: options.RewriteTTL,
|
rewriteTTL: options.RewriteTTL,
|
||||||
|
clientSubnet: (*netip.Addr)(options.ClientSubnet),
|
||||||
}
|
}
|
||||||
if len(options.Inbound) > 0 {
|
if len(options.Inbound) > 0 {
|
||||||
item := NewInboundRule(options.Inbound)
|
item := NewInboundRule(options.Inbound)
|
||||||
@@ -111,6 +116,11 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
|||||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||||
rule.allItems = append(rule.allItems, 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 {
|
if len(options.SourceIPCIDR) > 0 {
|
||||||
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -119,11 +129,24 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
|||||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||||
rule.allItems = append(rule.allItems, 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 {
|
if options.SourceIPIsPrivate {
|
||||||
item := NewIPIsPrivateItem(true)
|
item := NewIPIsPrivateItem(true)
|
||||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||||
rule.allItems = append(rule.allItems, 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 {
|
if len(options.SourcePort) > 0 {
|
||||||
item := NewPortItem(true, options.SourcePort)
|
item := NewPortItem(true, options.SourcePort)
|
||||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||||
@@ -196,7 +219,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
|||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.RuleSet) > 0 {
|
if len(options.RuleSet) > 0 {
|
||||||
item := NewRuleSetItem(router, options.RuleSet, false)
|
item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource)
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
@@ -211,12 +234,45 @@ func (r *DefaultDNSRule) RewriteTTL() *uint32 {
|
|||||||
return r.rewriteTTL
|
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)
|
var _ adapter.DNSRule = (*LogicalDNSRule)(nil)
|
||||||
|
|
||||||
type LogicalDNSRule struct {
|
type LogicalDNSRule struct {
|
||||||
abstractLogicalRule
|
abstractLogicalRule
|
||||||
disableCache bool
|
disableCache bool
|
||||||
rewriteTTL *uint32
|
rewriteTTL *uint32
|
||||||
|
clientSubnet *netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
|
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 {
|
func (r *LogicalDNSRule) RewriteTTL() *uint32 {
|
||||||
return r.rewriteTTL
|
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 {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "ipcidr")
|
return nil, E.Cause(err, "ipcidr")
|
||||||
}
|
}
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
} else if options.IPSet != nil {
|
} else if options.IPSet != nil {
|
||||||
item := NewRawIPCIDRItem(false, options.IPSet)
|
item := NewRawIPCIDRItem(false, options.IPSet)
|
||||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.SourcePort) > 0 {
|
if len(options.SourcePort) > 0 {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func NewRawIPCIDRItem(isSource bool, ipSet *netipx.IPSet) *IPCIDRItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
|
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
|
||||||
if r.isSource || metadata.QueryType != 0 || metadata.IPCIDRMatchSource {
|
if r.isSource || metadata.IPCIDRMatchSource {
|
||||||
return r.ipSet.Contains(metadata.Source.Addr)
|
return r.ipSet.Contains(metadata.Source.Addr)
|
||||||
} else {
|
} else {
|
||||||
if metadata.Destination.IsIP() {
|
if metadata.Destination.IsIP() {
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
|
|||||||
description += " "
|
description += " "
|
||||||
}
|
}
|
||||||
if dsLen == 1 {
|
if dsLen == 1 {
|
||||||
description += "domainSuffix=" + domainSuffixes[0]
|
description += "domain_suffix=" + domainSuffixes[0]
|
||||||
} else if dsLen > 3 {
|
} else if dsLen > 3 {
|
||||||
description += "domainSuffix=[" + strings.Join(domainSuffixes[:3], " ") + "...]"
|
description += "domain_suffix=[" + strings.Join(domainSuffixes[:3], " ") + "...]"
|
||||||
} else {
|
} else {
|
||||||
description += "domainSuffix=[" + strings.Join(domainSuffixes, " ") + "]"
|
description += "domain_suffix=[" + strings.Join(domainSuffixes, " ") + "]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &DomainItem{
|
return &DomainItem{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
)
|
)
|
||||||
@@ -13,7 +14,7 @@ var _ RuleItem = (*RuleSetItem)(nil)
|
|||||||
type RuleSetItem struct {
|
type RuleSetItem struct {
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
tagList []string
|
tagList []string
|
||||||
setList []adapter.HeadlessRule
|
setList []adapter.RuleSet
|
||||||
ipcidrMatchSource bool
|
ipcidrMatchSource bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +47,12 @@ func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RuleSetItem) ContainsIPCIDRRule() bool {
|
||||||
|
return common.Any(r.setList, func(ruleSet adapter.RuleSet) bool {
|
||||||
|
return ruleSet.Metadata().ContainsIPCIDRRule
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RuleSetItem) String() string {
|
func (r *RuleSetItem) String() string {
|
||||||
if len(r.tagList) == 1 {
|
if len(r.tagList) == 1 {
|
||||||
return F.ToString("rule_set=", r.tagList[0])
|
return F.ToString("rule_set=", r.tagList[0])
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ func NewLocalRuleSet(router adapter.Router, options option.RuleSet) (*LocalRuleS
|
|||||||
var metadata adapter.RuleSetMetadata
|
var metadata adapter.RuleSetMetadata
|
||||||
metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
||||||
metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
||||||
|
metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
|
||||||
return &LocalRuleSet{rules, metadata}, nil
|
return &LocalRuleSet{rules, metadata}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user