mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-16 13:44:41 +10:00
Compare commits
43 Commits
v1.9.0-bet
...
v1.8.12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ae1afef44 | ||
|
|
a5e2a4073b | ||
|
|
b6cb3948a3 | ||
|
|
7b0f5061dc | ||
|
|
76f20482f7 | ||
|
|
e735a5bdc8 | ||
|
|
70381e93c8 | ||
|
|
07a40716e8 | ||
|
|
5fea5956db | ||
|
|
d20a389043 | ||
|
|
4a4180bde5 | ||
|
|
7ecb6daabb | ||
|
|
712bdd9ae5 | ||
|
|
a3b74591a7 | ||
|
|
2f4abc6523 | ||
|
|
965ab075d9 | ||
|
|
ed2f8b9637 | ||
|
|
0f71ce5120 | ||
|
|
f8085ab111 | ||
|
|
f61b272cbf | ||
|
|
59d437b9d2 | ||
|
|
a7338fdc2b | ||
|
|
d88860928e | ||
|
|
20a2e38f47 | ||
|
|
acd438be23 | ||
|
|
e27fb51b54 | ||
|
|
adc38b26eb | ||
|
|
7e943e743a | ||
|
|
ceffcc0ad2 | ||
|
|
fdc451f7c6 | ||
|
|
b48c471e6a | ||
|
|
4b1fabd007 | ||
|
|
2b5eb1c59e | ||
|
|
e2d3862e64 | ||
|
|
4f5e7b974d | ||
|
|
21dedddd93 | ||
|
|
e02502bec0 | ||
|
|
ba67633ee8 | ||
|
|
7fd9abe802 | ||
|
|
78a5f59202 | ||
|
|
8d0da685d2 | ||
|
|
e6644f784e | ||
|
|
2b93b74d38 |
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -65,6 +65,12 @@ body:
|
||||
For Apple platform clients, please check `Settings - View Service Log` for crash logs.
|
||||
For the Android client, please check the `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` file for crash logs.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: supporter
|
||||
attributes:
|
||||
label: Supporter
|
||||
options:
|
||||
- label: I am a [sponsor](https://github.com/sponsors/nekohasekai/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Integrity requirements
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
@@ -65,6 +65,12 @@ body:
|
||||
对于 Apple 平台图形客户端程序,请检查 `Settings - View Service Log` 以导出崩溃日志。
|
||||
对于 Android 图形客户端程序,请检查 `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` 文件以导出崩溃日志。
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: supporter
|
||||
attributes:
|
||||
label: 支持我们
|
||||
options:
|
||||
- label: 我已经 [赞助](https://github.com/sponsors/nekohasekai/)
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 完整性要求
|
||||
|
||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -29,10 +29,6 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
with:
|
||||
|
||||
37
.github/workflows/linux.yml
vendored
Normal file
37
.github/workflows/linux.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Release to Linux repository
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
- name: Extract signing key
|
||||
run: |-
|
||||
mkdir -p $HOME/.gnupg
|
||||
cat > $HOME/.gnupg/sagernet.key <<EOF
|
||||
${{ secrets.GPG_KEY }}
|
||||
EOF
|
||||
- name: Publish release
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release -f .goreleaser.fury.yaml --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
|
||||
NFPM_KEY_PATH: ${{ env.Home }}/.gnupg/sagernet.key
|
||||
NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
87
.goreleaser.fury.yaml
Normal file
87
.goreleaser.fury.yaml
Normal file
@@ -0,0 +1,87 @@
|
||||
project_name: sing-box
|
||||
builds:
|
||||
- id: main
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_ech
|
||||
- with_utls
|
||||
- with_reality_server
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
targets:
|
||||
- linux_386
|
||||
- linux_amd64_v1
|
||||
- linux_arm64
|
||||
- linux_arm_7
|
||||
- linux_s390x
|
||||
- linux_riscv64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
snapshot:
|
||||
name_template: "{{ .Version }}.{{ .ShortCommit }}"
|
||||
nfpms:
|
||||
- &template
|
||||
id: package
|
||||
package_name: sing-box
|
||||
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
builds:
|
||||
- main
|
||||
vendor: sagernet
|
||||
homepage: https://sing-box.sagernet.org/
|
||||
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||
description: The universal proxy platform.
|
||||
license: GPLv3 or later
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
priority: extra
|
||||
contents:
|
||||
- src: release/config/config.json
|
||||
dst: /etc/sing-box/config.json
|
||||
type: config
|
||||
- src: release/config/sing-box.service
|
||||
dst: /usr/lib/systemd/system/sing-box.service
|
||||
- src: release/config/sing-box@.service
|
||||
dst: /usr/lib/systemd/system/sing-box@.service
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
deb:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
fields:
|
||||
Bugs: https://github.com/SagerNet/sing-box/issues
|
||||
rpm:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
conflicts:
|
||||
- sing-box-beta
|
||||
- id: package_beta
|
||||
<<: *template
|
||||
package_name: sing-box-beta
|
||||
file_name_template: '{{ .ProjectName }}-beta_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
conflicts:
|
||||
- sing-box
|
||||
release:
|
||||
disable: true
|
||||
furies:
|
||||
- account: sagernet
|
||||
ids:
|
||||
- package
|
||||
disable: "{{ not (not .Prerelease) }}"
|
||||
- account: sagernet
|
||||
ids:
|
||||
- package_beta
|
||||
disable: "{{ not .Prerelease }}"
|
||||
@@ -1,6 +1,7 @@
|
||||
project_name: sing-box
|
||||
builds:
|
||||
- id: main
|
||||
- &template
|
||||
id: main
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
@@ -32,16 +33,10 @@ builds:
|
||||
- windows_386
|
||||
- windows_arm64
|
||||
- darwin_amd64_v1
|
||||
- darwin_amd64_v3
|
||||
- darwin_arm64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
- id: legacy
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||
<<: *template
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
@@ -53,30 +48,14 @@ builds:
|
||||
- with_clash_api
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOROOT=/nix/store/kg6i737jjqs923jcijnm003h68c1dghj-go-1.20.11/share/go
|
||||
gobinary: /nix/store/kg6i737jjqs923jcijnm003h68c1dghj-go-1.20.11/bin/go
|
||||
- GOROOT={{ .Env.GOPATH }}/go1.20.14
|
||||
gobinary: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
|
||||
targets:
|
||||
- windows_amd64_v1
|
||||
- windows_386
|
||||
- darwin_amd64_v1
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
- id: android
|
||||
main: ./cmd/sing-box
|
||||
flags:
|
||||
- -v
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
|
||||
tags:
|
||||
- with_gvisor
|
||||
- with_quic
|
||||
- with_dhcp
|
||||
- with_wireguard
|
||||
- with_ech
|
||||
- with_utls
|
||||
- with_reality_server
|
||||
- with_acme
|
||||
- with_clash_api
|
||||
<<: *template
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
overrides:
|
||||
@@ -107,11 +86,11 @@ builds:
|
||||
- android_arm64
|
||||
- android_386
|
||||
- android_amd64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
snapshot:
|
||||
name_template: "{{ .Version }}.{{ .ShortCommit }}"
|
||||
archives:
|
||||
- id: archive
|
||||
- &template
|
||||
id: archive
|
||||
builds:
|
||||
- main
|
||||
- android
|
||||
@@ -124,20 +103,16 @@ archives:
|
||||
- LICENSE
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
- id: archive-legacy
|
||||
<<: *template
|
||||
builds:
|
||||
- legacy
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
wrap_in_directory: true
|
||||
files:
|
||||
- LICENSE
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}-legacy'
|
||||
nfpms:
|
||||
- id: package
|
||||
package_name: sing-box
|
||||
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
builds:
|
||||
- main
|
||||
vendor: sagernet
|
||||
homepage: https://sing-box.sagernet.org/
|
||||
maintainer: nekohasekai <contact-git@sekai.icu>
|
||||
@@ -153,11 +128,27 @@ nfpms:
|
||||
dst: /etc/sing-box/config.json
|
||||
type: config
|
||||
- src: release/config/sing-box.service
|
||||
dst: /etc/systemd/system/sing-box.service
|
||||
dst: /usr/lib/systemd/system/sing-box.service
|
||||
- src: release/config/sing-box@.service
|
||||
dst: /etc/systemd/system/sing-box@.service
|
||||
dst: /usr/lib/systemd/system/sing-box@.service
|
||||
- src: LICENSE
|
||||
dst: /usr/share/licenses/sing-box/LICENSE
|
||||
deb:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
fields:
|
||||
Bugs: https://github.com/SagerNet/sing-box/issues
|
||||
rpm:
|
||||
signature:
|
||||
key_file: "{{ .Env.NFPM_KEY_PATH }}"
|
||||
overrides:
|
||||
deb:
|
||||
conflicts:
|
||||
- sing-box-beta
|
||||
rpm:
|
||||
conflicts:
|
||||
- sing-box-beta
|
||||
|
||||
source:
|
||||
enabled: false
|
||||
name_template: '{{ .ProjectName }}-{{ .Version }}.source'
|
||||
@@ -171,6 +162,10 @@ release:
|
||||
github:
|
||||
owner: SagerNet
|
||||
name: sing-box
|
||||
name_template: '{{ if .IsSnapshot }}{{ nightly }}{{ else }}{{ .Version }}{{ end }}'
|
||||
draft: true
|
||||
mode: replace
|
||||
prerelease: auto
|
||||
mode: replace
|
||||
ids:
|
||||
- archive
|
||||
- package
|
||||
skip_upload: true
|
||||
10
Makefile
10
Makefile
@@ -15,7 +15,7 @@ MAIN_PARAMS = $(PARAMS) -tags $(TAGS)
|
||||
MAIN = ./cmd/sing-box
|
||||
PREFIX ?= $(shell go env GOPATH)
|
||||
|
||||
.PHONY: test release docs
|
||||
.PHONY: test release docs build
|
||||
|
||||
build:
|
||||
go build $(MAIN_PARAMS) $(MAIN)
|
||||
@@ -64,7 +64,7 @@ proto_install:
|
||||
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
|
||||
release:
|
||||
go run ./cmd/internal/build goreleaser release --clean --skip-publish || exit 1
|
||||
go run ./cmd/internal/build goreleaser release --clean --skip publish
|
||||
mkdir dist/release
|
||||
mv dist/*.tar.gz \
|
||||
dist/*.zip \
|
||||
@@ -77,15 +77,17 @@ release:
|
||||
ghr --replace --draft --prerelease -p 3 "v${VERSION}" dist/release
|
||||
rm -r dist/release
|
||||
|
||||
release_repo:
|
||||
go run ./cmd/internal/build goreleaser release -f .goreleaser.fury.yaml --clean
|
||||
|
||||
release_install:
|
||||
go install -v github.com/goreleaser/goreleaser@latest
|
||||
go install -v github.com/tcnksm/ghr@latest
|
||||
|
||||
update_android_version:
|
||||
go run ./cmd/internal/update_android_version
|
||||
|
||||
build_android:
|
||||
cd ../sing-box-for-android && ./gradlew :app:assemblePlayRelease && ./gradlew :app:assembleOtherRelease && ./gradlew --stop
|
||||
cd ../sing-box-for-android && ./gradlew :app:clean :app:assemblePlayRelease :app:assembleOtherRelease && ./gradlew --stop
|
||||
|
||||
upload_android:
|
||||
mkdir -p dist/release_android
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-dns"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
@@ -31,9 +30,6 @@ type CacheFile interface {
|
||||
StoreFakeIP() bool
|
||||
FakeIPStorage
|
||||
|
||||
StoreRDRC() bool
|
||||
dns.RDRCStore
|
||||
|
||||
LoadMode() string
|
||||
StoreMode(mode string) error
|
||||
LoadSelected(group string) string
|
||||
|
||||
@@ -51,13 +51,11 @@ type InboundContext struct {
|
||||
|
||||
// rule cache
|
||||
|
||||
IPCIDRMatchSource bool
|
||||
SourceAddressMatch bool
|
||||
SourcePortMatch bool
|
||||
DestinationAddressMatch bool
|
||||
DestinationPortMatch bool
|
||||
DidMatch bool
|
||||
IgnoreDestinationIPCIDRMatch bool
|
||||
IPCIDRMatchSource bool
|
||||
SourceAddressMatch bool
|
||||
SourcePortMatch bool
|
||||
DestinationAddressMatch bool
|
||||
DestinationPortMatch bool
|
||||
}
|
||||
|
||||
func (c *InboundContext) ResetRuleCache() {
|
||||
|
||||
@@ -86,9 +86,6 @@ type DNSRule interface {
|
||||
Rule
|
||||
DisableCache() bool
|
||||
RewriteTTL() *uint32
|
||||
ClientSubnet() *netip.Addr
|
||||
WithAddressLimit() bool
|
||||
MatchAddressLimit(metadata *InboundContext) bool
|
||||
}
|
||||
|
||||
type RuleSet interface {
|
||||
@@ -102,7 +99,6 @@ type RuleSet interface {
|
||||
type RuleSetMetadata struct {
|
||||
ContainsProcessRule bool
|
||||
ContainsWIFIRule bool
|
||||
ContainsIPCIDRRule bool
|
||||
}
|
||||
|
||||
type RuleSetStartContext interface {
|
||||
|
||||
4
box.go
4
box.go
@@ -235,7 +235,7 @@ func (s *Box) Start() error {
|
||||
}
|
||||
|
||||
func (s *Box) preStart() error {
|
||||
monitor := taskmonitor.New(s.logger, C.DefaultStartTimeout)
|
||||
monitor := taskmonitor.New(s.logger, C.StartTimeout)
|
||||
monitor.Start("start logger")
|
||||
err := s.logFactory.Start()
|
||||
monitor.Finish()
|
||||
@@ -331,7 +331,7 @@ func (s *Box) Close() error {
|
||||
default:
|
||||
close(s.done)
|
||||
}
|
||||
monitor := taskmonitor.New(s.logger, C.DefaultStopTimeout)
|
||||
monitor := taskmonitor.New(s.logger, C.StopTimeout)
|
||||
var errors error
|
||||
for serviceName, service := range s.postServices {
|
||||
monitor.Start("close ", serviceName)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Box) startOutbounds() error {
|
||||
monitor := taskmonitor.New(s.logger, C.DefaultStartTimeout)
|
||||
monitor := taskmonitor.New(s.logger, C.StartTimeout)
|
||||
outboundTags := make(map[adapter.Outbound]string)
|
||||
outbounds := make(map[string]adapter.Outbound)
|
||||
for i, outboundToStart := range s.outbounds {
|
||||
|
||||
Submodule clients/android updated: c8db11f062...288a6f34db
Submodule clients/apple updated: 45b4e58b3f...1c5bc23a25
@@ -199,7 +199,7 @@ func run() error {
|
||||
}
|
||||
|
||||
func closeMonitor(ctx context.Context) {
|
||||
time.Sleep(C.DefaultStopFatalTimeout)
|
||||
time.Sleep(C.FatalStopTimeout)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
@@ -4,8 +4,6 @@ package badtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
@@ -20,32 +18,20 @@ import (
|
||||
var _ N.ReadWaiter = (*ReadWaitConn)(nil)
|
||||
|
||||
type ReadWaitConn struct {
|
||||
tls.Conn
|
||||
halfAccess *sync.Mutex
|
||||
rawInput *bytes.Buffer
|
||||
input *bytes.Reader
|
||||
hand *bytes.Buffer
|
||||
readWaitOptions N.ReadWaitOptions
|
||||
tlsReadRecord func() error
|
||||
tlsHandlePostHandshakeMessage func() error
|
||||
*tls.STDConn
|
||||
halfAccess *sync.Mutex
|
||||
rawInput *bytes.Buffer
|
||||
input *bytes.Reader
|
||||
hand *bytes.Buffer
|
||||
readWaitOptions N.ReadWaitOptions
|
||||
}
|
||||
|
||||
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
||||
var (
|
||||
loaded bool
|
||||
tlsReadRecord func() error
|
||||
tlsHandlePostHandshakeMessage func() error
|
||||
)
|
||||
for _, tlsCreator := range tlsRegistry {
|
||||
loaded, tlsReadRecord, tlsHandlePostHandshakeMessage = tlsCreator(conn)
|
||||
if loaded {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !loaded {
|
||||
stdConn, isSTDConn := conn.(*tls.STDConn)
|
||||
if !isSTDConn {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(stdConn))
|
||||
rawHalfConn := rawConn.FieldByName("in")
|
||||
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
||||
return nil, E.New("badtls: invalid half conn")
|
||||
@@ -71,13 +57,11 @@ func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
||||
}
|
||||
hand := (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
|
||||
return &ReadWaitConn{
|
||||
Conn: conn,
|
||||
halfAccess: halfAccess,
|
||||
rawInput: rawInput,
|
||||
input: input,
|
||||
hand: hand,
|
||||
tlsReadRecord: tlsReadRecord,
|
||||
tlsHandlePostHandshakeMessage: tlsHandlePostHandshakeMessage,
|
||||
STDConn: stdConn,
|
||||
halfAccess: halfAccess,
|
||||
rawInput: rawInput,
|
||||
input: input,
|
||||
hand: hand,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -87,19 +71,19 @@ func (c *ReadWaitConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy
|
||||
}
|
||||
|
||||
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||
err = c.HandshakeContext(context.Background())
|
||||
err = c.Handshake()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.halfAccess.Lock()
|
||||
defer c.halfAccess.Unlock()
|
||||
for c.input.Len() == 0 {
|
||||
err = c.tlsReadRecord()
|
||||
err = tlsReadRecord(c.STDConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for c.hand.Len() > 0 {
|
||||
err = c.tlsHandlePostHandshakeMessage()
|
||||
err = tlsHandlePostHandshakeMessage(c.STDConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -116,7 +100,7 @@ func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
|
||||
// recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
|
||||
c.rawInput.Bytes()[0] == 21 {
|
||||
_ = c.tlsReadRecord()
|
||||
_ = tlsReadRecord(c.STDConn)
|
||||
// return n, err // will be io.EOF on closeNotify
|
||||
}
|
||||
|
||||
@@ -125,27 +109,11 @@ func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||
}
|
||||
|
||||
func (c *ReadWaitConn) Upstream() any {
|
||||
return c.Conn
|
||||
return c.STDConn
|
||||
}
|
||||
|
||||
var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error)
|
||||
//go:linkname tlsReadRecord crypto/tls.(*Conn).readRecord
|
||||
func tlsReadRecord(c *tls.STDConn) error
|
||||
|
||||
func init() {
|
||||
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||
tlsConn, loaded := conn.(*tls.STDConn)
|
||||
if !loaded {
|
||||
return
|
||||
}
|
||||
return true, func() error {
|
||||
return stdTLSReadRecord(tlsConn)
|
||||
}, func() error {
|
||||
return stdTLSHandlePostHandshakeMessage(tlsConn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//go:linkname stdTLSReadRecord crypto/tls.(*Conn).readRecord
|
||||
func stdTLSReadRecord(c *tls.STDConn) error
|
||||
|
||||
//go:linkname stdTLSHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
||||
func stdTLSHandlePostHandshakeMessage(c *tls.STDConn) error
|
||||
//go:linkname tlsHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
||||
func tlsHandlePostHandshakeMessage(c *tls.STDConn) error
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
//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
|
||||
@@ -1,31 +0,0 @@
|
||||
//go:build go1.21 && !without_badtls && with_utls
|
||||
|
||||
package badtls
|
||||
|
||||
import (
|
||||
"net"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/utls"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||
tlsConn, loaded := common.Cast[*tls.UConn](conn)
|
||||
if !loaded {
|
||||
return
|
||||
}
|
||||
return true, func() error {
|
||||
return utlsReadRecord(tlsConn.Conn)
|
||||
}, func() error {
|
||||
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//go:linkname utlsReadRecord github.com/sagernet/utls.(*Conn).readRecord
|
||||
func utlsReadRecord(c *tls.Conn) error
|
||||
|
||||
//go:linkname utlsHandlePostHandshakeMessage github.com/sagernet/utls.(*Conn).handlePostHandshakeMessage
|
||||
func utlsHandlePostHandshakeMessage(c *tls.Conn) error
|
||||
@@ -223,7 +223,7 @@ func getExecPathFromPID(pid uint32) (string, error) {
|
||||
r1, _, err := syscall.SyscallN(
|
||||
procQueryFullProcessImageNameW.Addr(),
|
||||
uintptr(h),
|
||||
uintptr(0),
|
||||
uintptr(1),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&size)),
|
||||
)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//go:build with_quic
|
||||
|
||||
package constant
|
||||
|
||||
const WithQUIC = true
|
||||
@@ -1,5 +0,0 @@
|
||||
//go:build !with_quic
|
||||
|
||||
package constant
|
||||
|
||||
const WithQUIC = false
|
||||
@@ -3,15 +3,16 @@ package constant
|
||||
import "time"
|
||||
|
||||
const (
|
||||
TCPTimeout = 5 * time.Second
|
||||
ReadPayloadTimeout = 300 * time.Millisecond
|
||||
DNSTimeout = 10 * time.Second
|
||||
QUICTimeout = 30 * time.Second
|
||||
STUNTimeout = 15 * time.Second
|
||||
UDPTimeout = 5 * time.Minute
|
||||
DefaultURLTestInterval = 3 * time.Minute
|
||||
DefaultURLTestIdleTimeout = 30 * time.Minute
|
||||
DefaultStartTimeout = 10 * time.Second
|
||||
DefaultStopTimeout = 5 * time.Second
|
||||
DefaultStopFatalTimeout = 10 * time.Second
|
||||
TCPTimeout = 5 * time.Second
|
||||
ReadPayloadTimeout = 300 * time.Millisecond
|
||||
DNSTimeout = 10 * time.Second
|
||||
QUICTimeout = 30 * time.Second
|
||||
STUNTimeout = 15 * time.Second
|
||||
UDPTimeout = 5 * time.Minute
|
||||
DefaultURLTestInterval = 3 * time.Minute
|
||||
DefaultURLTestIdleTimeout = 30 * time.Minute
|
||||
StartTimeout = 10 * time.Second
|
||||
StopTimeout = 5 * time.Second
|
||||
FatalStopTimeout = 10 * time.Second
|
||||
FakeIPMetadataSaveInterval = 10 * time.Second
|
||||
)
|
||||
|
||||
@@ -2,7 +2,29 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.9.0-beta.4
|
||||
#### 1.8.12
|
||||
|
||||
* Now we have official APT and DNF repositories **1**
|
||||
* Fix packet MTU for QUIC protocols
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Including stable and beta versions, see https://sing-box.sagernet.org/installation/package-manager/
|
||||
|
||||
#### 1.8.11
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.10
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.9
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.8.8
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
@@ -10,106 +32,10 @@ icon: material/alert-decagram
|
||||
|
||||
* 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
|
||||
@@ -459,7 +385,7 @@ see [TCP Brutal](/configuration/shared/tcp-brutal/) for details.
|
||||
|
||||
**5**:
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
#### 1.7.0-rc.3
|
||||
|
||||
@@ -496,7 +422,7 @@ Only supported in graphical clients on Android and Apple platforms.
|
||||
|
||||
**1**:
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
#### 1.7.0-beta.3
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ platform-specific function implementation, such as TUN transparent proxy impleme
|
||||
* [Play Store](https://play.google.com/store/apps/details?id=io.nekohasekai.sfa)
|
||||
* [Play Store (Beta)](https://play.google.com/apps/testing/io.nekohasekai.sfa)
|
||||
* [GitHub Releases](https://github.com/SagerNet/sing-box/releases)
|
||||
* [F-Droid](https://f-droid.org/packages/io.nekohasekai.sfa/) (Unified signature via reproducible builds)
|
||||
|
||||
## :material-source-repository: Source code
|
||||
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
# DNS
|
||||
|
||||
### Structure
|
||||
@@ -21,7 +13,6 @@ icon: material/new-box
|
||||
"disable_expire": false,
|
||||
"independent_cache": false,
|
||||
"reverse_mapping": false,
|
||||
"client_subnet": "",
|
||||
"fakeip": {}
|
||||
}
|
||||
}
|
||||
@@ -30,8 +21,8 @@ icon: material/new-box
|
||||
|
||||
### Fields
|
||||
|
||||
| Key | Format |
|
||||
|----------|---------------------------------|
|
||||
| Key | Format |
|
||||
|----------|--------------------------------|
|
||||
| `server` | List of [DNS Server](./server/) |
|
||||
| `rules` | List of [DNS Rule](./rule/) |
|
||||
| `fakeip` | [FakeIP](./fakeip/) |
|
||||
@@ -69,10 +60,6 @@ Stores a reverse mapping of IP addresses after responding to a DNS query in orde
|
||||
Since this process relies on the act of resolving domain names by an application before making a request, it can be
|
||||
problematic in environments such as macOS, where DNS is proxied and cached by the system.
|
||||
|
||||
#### client_subnet
|
||||
#### fakeip
|
||||
|
||||
!!! 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`.
|
||||
[FakeIP](./fakeip/) settings.
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
# DNS
|
||||
|
||||
### 结构
|
||||
@@ -21,7 +13,6 @@ icon: material/new-box
|
||||
"disable_expire": false,
|
||||
"independent_cache": false,
|
||||
"reverse_mapping": false,
|
||||
"client_subnet": "",
|
||||
"fakeip": {}
|
||||
}
|
||||
}
|
||||
@@ -67,14 +58,6 @@ icon: material/new-box
|
||||
|
||||
由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
可以被 `servers.[].client_subnet` 或 `rules.[].client_subnet` 覆盖。
|
||||
|
||||
#### fakeip
|
||||
|
||||
[FakeIP](./fakeip/) 设置。
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! 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"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -61,19 +53,11 @@ icon: material/new-box
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_ip_is_private": false,
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_is_private": false,
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
@@ -117,15 +101,13 @@ icon: material/new-box
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"invert": false,
|
||||
"outbound": [
|
||||
"direct"
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"rewrite_ttl": 100
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
@@ -133,8 +115,7 @@ icon: material/new-box
|
||||
"rules": [],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"rewrite_ttl": 100,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"rewrite_ttl": 100
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -285,9 +266,11 @@ Match Clash mode.
|
||||
|
||||
#### wifi_ssid
|
||||
|
||||
<!-- md:version 1.7.0-beta.4 -->
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
Match WiFi SSID.
|
||||
|
||||
@@ -295,7 +278,7 @@ Match WiFi SSID.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
Match WiFi BSSID.
|
||||
|
||||
@@ -305,12 +288,6 @@ Match WiFi BSSID.
|
||||
|
||||
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 match result.
|
||||
@@ -335,44 +312,6 @@ Disable cache and save cache in this query.
|
||||
|
||||
Rewrite TTL in DNS responses.
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
|
||||
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
|
||||
### Address Filter Fields
|
||||
|
||||
Only takes effect for IP address requests. When the query results do not match the address filtering rule items, the current rule will be skipped.
|
||||
|
||||
!!! 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
|
||||
|
||||
#### type
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! 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 中的更改"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -61,19 +53,10 @@ icon: material/new-box
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
"10.0.0.0/24"
|
||||
],
|
||||
"source_ip_is_private": false,
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_is_private": false,
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
@@ -117,22 +100,19 @@ icon: material/new-box
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"invert": false,
|
||||
"outbound": [
|
||||
"direct"
|
||||
],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"disable_cache": false
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"server": "local",
|
||||
"disable_cache": false,
|
||||
"client_subnet": "127.0.0.1"
|
||||
"disable_cache": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -285,7 +265,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
仅在 Android 与 iOS 的图形客户端中支持。
|
||||
|
||||
匹配 WiFi SSID。
|
||||
|
||||
@@ -293,7 +273,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
仅在 Android 与 iOS 的图形客户端中支持。
|
||||
|
||||
匹配 WiFi BSSID。
|
||||
|
||||
@@ -303,12 +283,6 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
匹配[规则集](/zh/configuration/route/#rule_set)。
|
||||
|
||||
#### rule_set_ipcidr_match_source
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
使规则集中的 `ipcidr` 规则匹配源 IP。
|
||||
|
||||
#### invert
|
||||
|
||||
反选匹配结果。
|
||||
@@ -333,44 +307,6 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
重写 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
|
||||
@@ -383,4 +319,4 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
#### rules
|
||||
|
||||
包括的规则。
|
||||
包括的规则。
|
||||
@@ -1,11 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
@@ -13,17 +5,17 @@ icon: material/new-box
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "",
|
||||
"address": "",
|
||||
"address_resolver": "",
|
||||
"address_strategy": "",
|
||||
"strategy": "",
|
||||
"detour": "",
|
||||
"client_subnet": ""
|
||||
"tag": "google",
|
||||
"address": "tls://dns.google",
|
||||
"address_resolver": "local",
|
||||
"address_strategy": "prefer_ipv4",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "direct"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Fields
|
||||
@@ -88,20 +80,10 @@ Default domain strategy for resolving the domain names.
|
||||
|
||||
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
||||
|
||||
Take no effect if overridden by other settings.
|
||||
Take no effect if override by other settings.
|
||||
|
||||
#### detour
|
||||
|
||||
Tag of an outbound for connecting to the dns server.
|
||||
|
||||
Default outbound will be used if empty.
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||
|
||||
Can be overrides by `rules.[].client_subnet`.
|
||||
|
||||
Will overrides `dns.client_subnet`.
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [client_subnet](#client_subnet)
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
@@ -13,17 +5,17 @@ icon: material/new-box
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "",
|
||||
"address": "",
|
||||
"address_resolver": "",
|
||||
"address_strategy": "",
|
||||
"strategy": "",
|
||||
"detour": "",
|
||||
"client_subnet": ""
|
||||
"tag": "google",
|
||||
"address": "tls://dns.google",
|
||||
"address_resolver": "local",
|
||||
"address_strategy": "prefer_ipv4",
|
||||
"strategy": "ipv4_only",
|
||||
"detour": "direct"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 字段
|
||||
@@ -95,13 +87,3 @@ DNS 服务器的地址。
|
||||
用于连接到 DNS 服务器的出站的标签。
|
||||
|
||||
如果为空,将使用默认出站。
|
||||
|
||||
#### client_subnet
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||
|
||||
可以被 `rules.[].client_subnet` 覆盖。
|
||||
|
||||
将覆盖 `dns.client_subnet`。
|
||||
|
||||
@@ -4,11 +4,6 @@ icon: material/new-box
|
||||
|
||||
!!! 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
|
||||
|
||||
```json
|
||||
@@ -16,9 +11,7 @@ icon: material/new-box
|
||||
"enabled": true,
|
||||
"path": "",
|
||||
"cache_id": "",
|
||||
"store_fakeip": false,
|
||||
"store_rdrc": false,
|
||||
"rdrc_timeout": ""
|
||||
"store_fakeip": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -36,23 +29,6 @@ Path to the cache file.
|
||||
|
||||
#### cache_id
|
||||
|
||||
Identifier in the cache file
|
||||
Identifier in cache file.
|
||||
|
||||
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,11 +4,6 @@ icon: material/new-box
|
||||
|
||||
!!! 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
|
||||
@@ -16,9 +11,7 @@ icon: material/new-box
|
||||
"enabled": true,
|
||||
"path": "",
|
||||
"cache_id": "",
|
||||
"store_fakeip": false,
|
||||
"store_rdrc": false,
|
||||
"rdrc_timeout": ""
|
||||
"store_fakeip": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -37,19 +30,3 @@ icon: material/new-box
|
||||
缓存文件中的标识符。
|
||||
|
||||
如果不为空,配置特定的数据将使用由其键控的单独存储。
|
||||
|
||||
#### store_fakeip
|
||||
|
||||
将 fakeip 存储在缓存文件中。
|
||||
|
||||
#### store_rdrc
|
||||
|
||||
将拒绝的 DNS 响应缓存存储在缓存文件中。
|
||||
|
||||
[地址筛选 DNS 规则项](/zh/configuration/dns/rule/#_3) 的检查结果将被缓存至过期。
|
||||
|
||||
#### rdrc_timeout
|
||||
|
||||
拒绝的 DNS 响应缓存超时。
|
||||
|
||||
默认使用 `7d`。
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-delete-alert: [store_mode](#store_mode)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-delete-alert: [store_mode](#store_mode)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# Experimental
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# 实验性
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
@@ -42,6 +42,6 @@ No authentication required if empty.
|
||||
|
||||
!!! warning ""
|
||||
|
||||
To work on Android and Apple platforms without privileges, use tun.platform.http_proxy instead.
|
||||
To work on Android and iOS without privileges, use tun.platform.http_proxy instead.
|
||||
|
||||
Automatically set system proxy configuration when start and clean up when stop.
|
||||
|
||||
@@ -39,6 +39,6 @@ No authentication required if empty.
|
||||
|
||||
!!! warning ""
|
||||
|
||||
To work on Android and Apple platforms without privileges, use tun.platform.http_proxy instead.
|
||||
To work on Android and iOS without privileges, use tun.platform.http_proxy instead.
|
||||
|
||||
Automatically set system proxy configuration when start and clean up when stop.
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! 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"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
@@ -78,9 +73,7 @@ icon: material/new-box
|
||||
"http_proxy": {
|
||||
"enabled": false,
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080,
|
||||
"bypass_domain": [],
|
||||
"match_domain": []
|
||||
"server_port": 8080
|
||||
}
|
||||
},
|
||||
|
||||
@@ -267,38 +260,6 @@ Platform-specific settings, provided by client applications.
|
||||
|
||||
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
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen/) for details.
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! 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 中的更改"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
@@ -78,9 +73,7 @@ icon: material/new-box
|
||||
"http_proxy": {
|
||||
"enabled": false,
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080,
|
||||
"bypass_domain": [],
|
||||
"match_domain": []
|
||||
"server_port": 8080
|
||||
}
|
||||
},
|
||||
|
||||
@@ -264,38 +257,6 @@ TCP/IP 栈。
|
||||
|
||||
系统 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/)。
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [gso](#gso)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# Route
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
# 路由
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -105,7 +109,6 @@
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"invert": false,
|
||||
"outbound": "direct"
|
||||
},
|
||||
@@ -281,7 +284,7 @@ Match Clash mode.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
Match WiFi SSID.
|
||||
|
||||
@@ -289,7 +292,7 @@ Match WiFi SSID.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
Match WiFi BSSID.
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -103,7 +107,6 @@
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"invert": false,
|
||||
"outbound": "direct"
|
||||
},
|
||||
@@ -279,7 +282,7 @@
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
仅在 Android 与 iOS 的图形客户端中支持。
|
||||
|
||||
匹配 WiFi SSID。
|
||||
|
||||
@@ -287,7 +290,7 @@
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持。
|
||||
仅在 Android 与 iOS 的图形客户端中支持。
|
||||
|
||||
匹配 WiFi BSSID。
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
### Structure
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
@@ -124,7 +128,7 @@ Match source IP CIDR.
|
||||
|
||||
!!! info ""
|
||||
|
||||
`ip_cidr` is an alias for `source_ip_cidr` when `rule_set_ipcidr_match_source` enabled in route/DNS rules.
|
||||
`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.
|
||||
|
||||
Match IP CIDR.
|
||||
|
||||
@@ -168,7 +172,7 @@ Match android package name.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
Match WiFi SSID.
|
||||
|
||||
@@ -176,7 +180,7 @@ Match WiFi SSID.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and Apple platforms.
|
||||
Only supported in graphical clients on Android and iOS.
|
||||
|
||||
Match WiFi BSSID.
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# Rule Set
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
# Source Format
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-alert-decagram: [utls](#utls)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-alert-decagram: [utls](#utls)
|
||||
|
||||
@@ -4,6 +4,35 @@ icon: material/package
|
||||
|
||||
# Package Manager
|
||||
|
||||
## :material-tram: Repository Installation
|
||||
|
||||
=== ":material-debian: Debian / APT"
|
||||
|
||||
```bash
|
||||
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
||||
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
||||
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: Redhat / DNF"
|
||||
|
||||
```bash
|
||||
sudo dnf -y install dnf-plugins-core
|
||||
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
|
||||
sudo dnf install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: CentOS / YUM"
|
||||
|
||||
```bash
|
||||
sudo yum install -y yum-utils
|
||||
sudo yum-config-manager --add-repo https://sing-box.app/sing-box.repo
|
||||
sudo yum install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
## :material-download-box: Manual Installation
|
||||
|
||||
=== ":material-debian: Debian / DEB"
|
||||
|
||||
@@ -4,6 +4,35 @@ icon: material/package
|
||||
|
||||
# 包管理器
|
||||
|
||||
## :material-tram: 仓库安装
|
||||
|
||||
=== ":material-debian: Debian / APT"
|
||||
|
||||
```bash
|
||||
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
|
||||
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
|
||||
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" | \
|
||||
sudo tee /etc/apt/sources.list.d/sagernet.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: Redhat / DNF"
|
||||
|
||||
```bash
|
||||
sudo dnf -y install dnf-plugins-core
|
||||
sudo dnf config-manager --add-repo https://sing-box.app/sing-box.repo
|
||||
sudo dnf install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
=== ":material-redhat: CentOS / YUM"
|
||||
|
||||
```bash
|
||||
sudo yum install -y yum-utils
|
||||
sudo yum-config-manager --add-repo https://sing-box.app/sing-box.repo
|
||||
sudo yum install sing-box # or sing-box-beta
|
||||
```
|
||||
|
||||
## :material-download-box: 手动安装
|
||||
|
||||
=== ":material-debian: Debian / DEB"
|
||||
|
||||
8
docs/installation/tools/sing-box.repo
Normal file
8
docs/installation/tools/sing-box.repo
Normal file
@@ -0,0 +1,8 @@
|
||||
[sing-box]
|
||||
name=sing-box
|
||||
baseurl=https://rpm.sagernet.org/
|
||||
metalink=https://sing-box.app/sing-box.repo
|
||||
enabled=1
|
||||
repo_gpgcheck=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://sing-box.app/gpg.key
|
||||
@@ -290,6 +290,52 @@ flowchart TB
|
||||
|
||||
=== ":material-dns: DNS rules"
|
||||
|
||||
!!! info
|
||||
|
||||
DNS rules are optional if FakeIP is used.
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://8.8.8.8"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"detour": "direct"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"outbound": "any",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Direct",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Global",
|
||||
"server": "google"
|
||||
},
|
||||
{
|
||||
"geosite": "geolocation-cn",
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-dns: DNS rules (1.8.0+)"
|
||||
|
||||
!!! info
|
||||
|
||||
DNS rules are optional if FakeIP is used.
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
@@ -336,136 +382,75 @@ flowchart TB
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-dns: DNS rules (1.9.0+)"
|
||||
|
||||
=== ":material-shield-off: With DNS Leaks"
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://8.8.8.8"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "https://223.5.5.5/dns-query",
|
||||
"detour": "direct"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"outbound": "any",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Direct",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Global",
|
||||
"server": "google"
|
||||
},
|
||||
{
|
||||
"rule_set": "geosite-geolocation-cn",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Default",
|
||||
"server": "google"
|
||||
},
|
||||
{
|
||||
"rule_set": "geoip-cn",
|
||||
"server": "local"
|
||||
}
|
||||
]
|
||||
},
|
||||
"route": {
|
||||
"rule_set": [
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "geosite-geolocation-cn",
|
||||
"format": "binary",
|
||||
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-cn.srs"
|
||||
},
|
||||
{
|
||||
"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-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
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "or",
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns"
|
||||
},
|
||||
{
|
||||
"port": 53
|
||||
}
|
||||
],
|
||||
"outbound": "dns"
|
||||
},
|
||||
{
|
||||
"geoip": "private",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Direct",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"clash_mode": "Global",
|
||||
"outbound": "default"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "or",
|
||||
"rules": [
|
||||
{
|
||||
"port": 853
|
||||
},
|
||||
{
|
||||
"network": "udp",
|
||||
"port": 443
|
||||
},
|
||||
{
|
||||
"protocol": "stun"
|
||||
}
|
||||
],
|
||||
"outbound": "block"
|
||||
},
|
||||
{
|
||||
"geosite": "geolocation-cn",
|
||||
"outbound": "direct"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== ":material-router-network: Route rules (1.8.0+)"
|
||||
|
||||
```json
|
||||
{
|
||||
"outbounds": [
|
||||
|
||||
@@ -2,28 +2,6 @@
|
||||
icon: material/arrange-bring-forward
|
||||
---
|
||||
|
||||
## 1.9.0
|
||||
|
||||
!!! warning "Unstable"
|
||||
|
||||
This version is still under development, and the following migration guide may be changed in the future.
|
||||
|
||||
### `domain_suffix` behavior update
|
||||
|
||||
For historical reasons, sing-box's `domain_suffix` rule matches literal prefixes instead of the same as other projects.
|
||||
|
||||
sing-box 1.9.0 modifies the behavior of `domain_suffix`: If the rule value is prefixed with `.`,
|
||||
the behavior is unchanged, otherwise it matches `(domain|.+\.domain)` instead.
|
||||
|
||||
### `process_path` format update on Windows
|
||||
|
||||
The `process_path` rule of sing-box is inherited from Clash,
|
||||
the original code uses the local system's path format (e.g. `\Device\HarddiskVolume1\folder\program.exe`),
|
||||
but when the device has multiple disks, the HarddiskVolume serial number is not stable.
|
||||
|
||||
sing-box 1.9.0 make QueryFullProcessImageNameW output a Win32 path (such as `C:\folder\program.exe`),
|
||||
which will disrupt the existing `process_path` use cases in Windows.
|
||||
|
||||
## 1.8.0
|
||||
|
||||
### :material-close-box: Migrate cache file from Clash API to independent options
|
||||
|
||||
@@ -2,27 +2,6 @@
|
||||
icon: material/arrange-bring-forward
|
||||
---
|
||||
|
||||
## 1.9.0
|
||||
|
||||
!!! warning "不稳定的"
|
||||
|
||||
该版本仍在开发中,迁移指南可能将在未来更改。
|
||||
|
||||
### `domain_suffix` 行为更新
|
||||
|
||||
由于历史原因,sing-box 的 `domain_suffix` 规则匹配字面前缀,而不与其他项目相同。
|
||||
|
||||
sing-box 1.9.0 修改了 `domain_suffix` 的行为:如果规则值以 `.` 为前缀则行为不变,否则改为匹配 `(domain|.+\.domain)`。
|
||||
|
||||
### 对 Windows 上 `process_path` 格式的更新
|
||||
|
||||
sing-box 的 `process_path` 规则继承自Clash,
|
||||
原始代码使用本地系统的路径格式(例如 `\Device\HarddiskVolume1\folder\program.exe`),
|
||||
但是当设备有多个硬盘时,该 HarddiskVolume 系列号并不稳定。
|
||||
|
||||
sing-box 1.9.0 使 QueryFullProcessImageNameW 输出 Win32 路径(如 `C:\folder\program.exe`),
|
||||
这将会破坏现有的 Windows `process_path` 用例。
|
||||
|
||||
## 1.8.0
|
||||
|
||||
### :material-close-box: 将缓存文件从 Clash API 迁移到独立选项
|
||||
|
||||
@@ -29,7 +29,6 @@ var (
|
||||
string(bucketExpand),
|
||||
string(bucketMode),
|
||||
string(bucketRuleSet),
|
||||
string(bucketRDRC),
|
||||
}
|
||||
|
||||
cacheIDDefault = []byte("default")
|
||||
@@ -38,25 +37,17 @@ var (
|
||||
var _ adapter.CacheFile = (*CacheFile)(nil)
|
||||
|
||||
type CacheFile struct {
|
||||
ctx context.Context
|
||||
path string
|
||||
cacheID []byte
|
||||
storeFakeIP bool
|
||||
storeRDRC bool
|
||||
rdrcTimeout time.Duration
|
||||
ctx context.Context
|
||||
path string
|
||||
cacheID []byte
|
||||
storeFakeIP bool
|
||||
|
||||
DB *bbolt.DB
|
||||
saveMetadataTimer *time.Timer
|
||||
saveFakeIPAccess sync.RWMutex
|
||||
saveAccess sync.RWMutex
|
||||
saveDomain map[netip.Addr]string
|
||||
saveAddress4 map[string]netip.Addr
|
||||
saveAddress6 map[string]netip.Addr
|
||||
saveRDRCAccess sync.RWMutex
|
||||
saveRDRC map[saveRDRCCacheKey]bool
|
||||
}
|
||||
|
||||
type saveRDRCCacheKey struct {
|
||||
TransportName string
|
||||
QuestionName string
|
||||
saveMetadataTimer *time.Timer
|
||||
}
|
||||
|
||||
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||
@@ -70,25 +61,14 @@ func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||
if 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{
|
||||
ctx: ctx,
|
||||
path: filemanager.BasePath(ctx, path),
|
||||
cacheID: cacheIDBytes,
|
||||
storeFakeIP: options.StoreFakeIP,
|
||||
storeRDRC: options.StoreRDRC,
|
||||
rdrcTimeout: rdrcTimeout,
|
||||
saveDomain: make(map[netip.Addr]string),
|
||||
saveAddress4: make(map[string]netip.Addr),
|
||||
saveAddress6: make(map[string]netip.Addr),
|
||||
saveRDRC: make(map[saveRDRCCacheKey]bool),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/sagernet/bbolt"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
@@ -58,12 +59,13 @@ func (c *CacheFile) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPSaveMetadataAsync(metadata *adapter.FakeIPMetadata) {
|
||||
if timer := c.saveMetadataTimer; timer != nil {
|
||||
timer.Stop()
|
||||
if c.saveMetadataTimer == nil {
|
||||
c.saveMetadataTimer = time.AfterFunc(C.FakeIPMetadataSaveInterval, func() {
|
||||
_ = c.FakeIPSaveMetadata(metadata)
|
||||
})
|
||||
} else {
|
||||
c.saveMetadataTimer.Reset(C.FakeIPMetadataSaveInterval)
|
||||
}
|
||||
c.saveMetadataTimer = time.AfterFunc(10*time.Second, func() {
|
||||
_ = c.FakeIPSaveMetadata(metadata)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
|
||||
@@ -89,34 +91,34 @@ func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
|
||||
c.saveFakeIPAccess.Lock()
|
||||
c.saveAccess.Lock()
|
||||
c.saveDomain[address] = domain
|
||||
if address.Is4() {
|
||||
c.saveAddress4[domain] = address
|
||||
} else {
|
||||
c.saveAddress6[domain] = address
|
||||
}
|
||||
c.saveFakeIPAccess.Unlock()
|
||||
c.saveAccess.Unlock()
|
||||
go func() {
|
||||
err := c.FakeIPStore(address, domain)
|
||||
if err != nil {
|
||||
logger.Warn("save FakeIP cache: ", err)
|
||||
logger.Warn("save FakeIP address pair: ", err)
|
||||
}
|
||||
c.saveFakeIPAccess.Lock()
|
||||
c.saveAccess.Lock()
|
||||
delete(c.saveDomain, address)
|
||||
if address.Is4() {
|
||||
delete(c.saveAddress4, domain)
|
||||
} else {
|
||||
delete(c.saveAddress6, domain)
|
||||
}
|
||||
c.saveFakeIPAccess.Unlock()
|
||||
c.saveAccess.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
||||
c.saveFakeIPAccess.RLock()
|
||||
c.saveAccess.RLock()
|
||||
cachedDomain, cached := c.saveDomain[address]
|
||||
c.saveFakeIPAccess.RUnlock()
|
||||
c.saveAccess.RUnlock()
|
||||
if cached {
|
||||
return cachedDomain, true
|
||||
}
|
||||
@@ -137,13 +139,13 @@ func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bo
|
||||
cachedAddress netip.Addr
|
||||
cached bool
|
||||
)
|
||||
c.saveFakeIPAccess.RLock()
|
||||
c.saveAccess.RLock()
|
||||
if !isIPv6 {
|
||||
cachedAddress, cached = c.saveAddress4[domain]
|
||||
} else {
|
||||
cachedAddress, cached = c.saveAddress6[domain]
|
||||
}
|
||||
c.saveFakeIPAccess.RUnlock()
|
||||
c.saveAccess.RUnlock()
|
||||
if cached {
|
||||
return cachedAddress, true
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
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,7 +3,6 @@ package experimental
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -28,26 +27,11 @@ func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.O
|
||||
}
|
||||
|
||||
func CalculateClashModeList(options option.Options) []string {
|
||||
var clashModes []string
|
||||
clashModes = append(clashModes, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...)
|
||||
clashModes = append(clashModes, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...)
|
||||
clashModes = common.FilterNotDefault(common.Uniq(clashModes))
|
||||
predefinedOrder := []string{
|
||||
"Rule", "Global", "Direct",
|
||||
}
|
||||
var newClashModes []string
|
||||
for _, mode := range clashModes {
|
||||
if !common.Contains(predefinedOrder, mode) {
|
||||
newClashModes = append(newClashModes, mode)
|
||||
}
|
||||
}
|
||||
sort.Strings(newClashModes)
|
||||
for _, mode := range predefinedOrder {
|
||||
if common.Contains(clashModes, mode) {
|
||||
newClashModes = append(newClashModes, mode)
|
||||
}
|
||||
}
|
||||
return newClashModes
|
||||
var clashMode []string
|
||||
clashMode = append(clashMode, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...)
|
||||
clashMode = append(clashMode, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...)
|
||||
clashMode = common.FilterNotDefault(common.Uniq(clashMode))
|
||||
return clashMode
|
||||
}
|
||||
|
||||
func extraClashModeFromRule(rules []option.Rule) []string {
|
||||
|
||||
@@ -4,6 +4,7 @@ const (
|
||||
CommandLog int32 = iota
|
||||
CommandStatus
|
||||
CommandServiceReload
|
||||
CommandServiceClose
|
||||
CommandCloseConnections
|
||||
CommandGroup
|
||||
CommandSelectOutbound
|
||||
|
||||
@@ -44,3 +44,41 @@ func (s *CommandServer) handleServiceReload(conn net.Conn) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandClient) ServiceClose() error {
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceClose))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hasError bool
|
||||
err = binary.Read(conn, binary.BigEndian, &hasError)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return E.New(errorMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleServiceClose(conn net.Conn) error {
|
||||
rErr := s.service.Close()
|
||||
s.handler.PostServiceClose()
|
||||
err := binary.Write(conn, binary.BigEndian, rErr != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rErr != nil {
|
||||
return rw.WriteVString(conn, rErr.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -37,6 +37,7 @@ type CommandServer struct {
|
||||
|
||||
type CommandServerHandler interface {
|
||||
ServiceReload() error
|
||||
PostServiceClose()
|
||||
GetSystemProxyStatus() *SystemProxyStatus
|
||||
SetSystemProxyEnabled(isEnabled bool) error
|
||||
}
|
||||
@@ -58,16 +59,19 @@ func (s *CommandServer) SetService(newService *BoxService) {
|
||||
if newService != nil {
|
||||
service.PtrFromContext[urltest.HistoryStorage](newService.ctx).SetHook(s.urlTestUpdate)
|
||||
newService.instance.Router().ClashServer().(*clashapi.Server).SetModeUpdateHook(s.modeUpdate)
|
||||
s.savedLines.Init()
|
||||
select {
|
||||
case s.logReset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
s.service = newService
|
||||
s.notifyURLTestUpdate()
|
||||
}
|
||||
|
||||
func (s *CommandServer) ResetLog() {
|
||||
s.savedLines.Init()
|
||||
select {
|
||||
case s.logReset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) notifyURLTestUpdate() {
|
||||
select {
|
||||
case s.urlTestUpdate <- struct{}{}:
|
||||
@@ -152,6 +156,8 @@ func (s *CommandServer) handleConnection(conn net.Conn) error {
|
||||
return s.handleStatusConn(conn)
|
||||
case CommandServiceReload:
|
||||
return s.handleServiceReload(conn)
|
||||
case CommandServiceClose:
|
||||
return s.handleServiceClose(conn)
|
||||
case CommandCloseConnections:
|
||||
return s.handleCloseConnections(conn)
|
||||
case CommandGroup:
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
|
||||
mDNS "github.com/miekg/dns"
|
||||
@@ -23,11 +25,9 @@ type LocalDNSTransport interface {
|
||||
|
||||
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
||||
if transport == nil {
|
||||
dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
return dns.NewLocalTransport(options), nil
|
||||
})
|
||||
dns.RegisterTransport([]string{"local"}, dns.CreateLocalTransport)
|
||||
} else {
|
||||
dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
dns.RegisterTransport([]string{"local"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return &platformLocalDNSTransport{
|
||||
iif: transport,
|
||||
}, nil
|
||||
|
||||
@@ -3,14 +3,17 @@ package libbox
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
runtimeDebug "runtime/debug"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -72,6 +75,16 @@ func (s *BoxService) Start() error {
|
||||
}
|
||||
|
||||
func (s *BoxService) Close() error {
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
go func() {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-time.After(C.FatalStopTimeout):
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
s.cancel()
|
||||
s.urlTestHistoryStorage.Close()
|
||||
return s.instance.Close()
|
||||
|
||||
@@ -13,33 +13,28 @@ type servicePauseFields struct {
|
||||
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.instance.Router().ResetNetwork()
|
||||
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,7 +7,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/common/humanize"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
_ "github.com/sagernet/sing-box/include"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -28,8 +28,6 @@ type TunOptions interface {
|
||||
IsHTTPProxyEnabled() bool
|
||||
GetHTTPProxyServer() string
|
||||
GetHTTPProxyServerPort() int32
|
||||
GetHTTPProxyBypassDomain() StringIterator
|
||||
GetHTTPProxyMatchDomain() StringIterator
|
||||
}
|
||||
|
||||
type RoutePrefix struct {
|
||||
@@ -158,11 +156,3 @@ func (o *tunOptions) GetHTTPProxyServer() string {
|
||||
func (o *tunOptions) GetHTTPProxyServerPort() int32 {
|
||||
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)
|
||||
}
|
||||
|
||||
43
go.mod
43
go.mod
@@ -11,29 +11,29 @@ require (
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/gofrs/uuid/v5 v5.1.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
|
||||
github.com/libdns/alidns v1.0.3
|
||||
github.com/libdns/cloudflare v0.1.0
|
||||
github.com/libdns/cloudflare v0.1.1
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.2.0
|
||||
github.com/miekg/dns v1.1.58
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/ooni/go-libtor v1.1.8
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||
github.com/sagernet/gomobile v0.1.3
|
||||
github.com/sagernet/gvisor v0.0.0-20240214044702-a3d61928a32f
|
||||
github.com/sagernet/quic-go v0.41.0-beta.2
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
|
||||
github.com/sagernet/quic-go v0.40.1
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.3.3-beta.2
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.12
|
||||
github.com/sagernet/sing v0.3.8
|
||||
github.com/sagernet/sing-dns v0.1.14
|
||||
github.com/sagernet/sing-mux v0.2.0
|
||||
github.com/sagernet/sing-quic v0.1.9-beta.1
|
||||
github.com/sagernet/sing-quic v0.1.12
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.2.4-beta.1
|
||||
github.com/sagernet/sing-tun v0.2.6
|
||||
github.com/sagernet/sing-vmess v0.1.8
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
||||
@@ -41,15 +41,15 @@ require (
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.uber.org/zap v1.27.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/sys v0.17.0
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/sys v0.19.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/grpc v1.62.0
|
||||
google.golang.org/protobuf v1.32.0
|
||||
google.golang.org/grpc v1.63.2
|
||||
google.golang.org/protobuf v1.33.0
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
|
||||
@@ -64,7 +64,6 @@ require (
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
@@ -72,7 +71,7 @@ require (
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
@@ -80,18 +79,18 @@ require (
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||
golang.org/x/mod v0.15.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.18.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
golang.org/x/tools v0.20.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
|
||||
97
go.sum
97
go.sum
@@ -34,14 +34,11 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/gofrs/uuid/v5 v5.1.0 h1:S5rqVKIigghZTCBKPCw0Y+bXkn26K3TB5mvQq2Ix8dk=
|
||||
github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
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/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
@@ -65,17 +62,17 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
||||
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
||||
github.com/libdns/cloudflare v0.1.0 h1:93WkJaGaiXCe353LHEP36kAWCUw0YjFqwhkBkU2/iic=
|
||||
github.com/libdns/cloudflare v0.1.0/go.mod h1:a44IP6J1YH6nvcNl1PverfJviADgXUnsozR3a7vBKN8=
|
||||
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
||||
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
|
||||
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
|
||||
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
||||
@@ -100,33 +97,31 @@ github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQ
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
|
||||
github.com/sagernet/gomobile v0.1.3 h1:ohjIb1Ou2+1558PnZour3od69suSuvkdSVOlO1tC4B8=
|
||||
github.com/sagernet/gomobile v0.1.3/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||
github.com/sagernet/gvisor v0.0.0-20240214044702-a3d61928a32f h1:7hj/CcCkUiC6gfhX4D+QNyodmhfurW2L2Q4qzJ1bPnI=
|
||||
github.com/sagernet/gvisor v0.0.0-20240214044702-a3d61928a32f/go.mod h1:bLmnT/4M4+yKB6F7JtRsbUr+YJ64yXwFIygjyYDFQzQ=
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE=
|
||||
github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.41.0-beta.2 h1:NtFC1Ief+SYJkfRq68D1OEqZQTNh2jYSpyRLhjT+m6U=
|
||||
github.com/sagernet/quic-go v0.41.0-beta.2/go.mod h1:X10Mf9DVHuSEReOLov/XuflD13MVLH3WtppVVFnSItU=
|
||||
github.com/sagernet/quic-go v0.40.1 h1:qLeTIJR0d0JWRmDWo346nLsVN6EWihd1kalJYPEd0TM=
|
||||
github.com/sagernet/quic-go v0.40.1/go.mod h1:CcKTpzTAISxrM4PA5M20/wYuz9Tj6Tx4DwGbNl9UQrU=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.3.3-beta.2 h1:ZQkONX/Vwu0p3qxWMlQDDABwAxdH8jVD3rKUB/o1f1o=
|
||||
github.com/sagernet/sing v0.3.3-beta.2/go.mod h1:qHySJ7u8po9DABtMYEkNBcOumx7ZZJf/fbv2sfTkNHE=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.12 h1:d9rd5YKYW1DleHHflgsLivPbnruKc7SEYXKZbmz48VM=
|
||||
github.com/sagernet/sing-dns v0.2.0-beta.12/go.mod h1:gfs585rEu+ZgsXJJiecEIK5avrF5SYlCAbFfZ1B66hs=
|
||||
github.com/sagernet/sing v0.3.8 h1:gm4JKalPhydMYX2zFOTnnd4TXtM/16WFRqSjMepYQQk=
|
||||
github.com/sagernet/sing v0.3.8/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
|
||||
github.com/sagernet/sing-dns v0.1.14 h1:kxE/Ik3jMXmD3sXsdt9MgrNzLFWt64mghV+MQqzyf40=
|
||||
github.com/sagernet/sing-dns v0.1.14/go.mod h1:AA+vZMNovuPN5i/sPnfF6756Nq94nzb5nXodMWbta5w=
|
||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
||||
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||
github.com/sagernet/sing-quic v0.1.9-beta.1 h1:rCmgLUq2d4EA643EAvjbfUYYVMPCss0GpmS4pJCT2Lw=
|
||||
github.com/sagernet/sing-quic v0.1.9-beta.1/go.mod h1:F4AXCZiwtRtYdLUTjVMO6elTpA/lLJe17sFlHhHmDVw=
|
||||
github.com/sagernet/sing-quic v0.1.12 h1:4KjG7LASZck0svGDfzf3aVNidRRQRC/w2HUMk/PHiNE=
|
||||
github.com/sagernet/sing-quic v0.1.12/go.mod h1:L+VtzvuPbf8VW8F4R7KiygqpXY4lO7t2wwcQuHjh8Ew=
|
||||
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-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.2.4-0.20240228052322-427e1a732caa h1:6d/j3790DkkkoH7vo9RVfv9mDlj8tP9dbrc1Dr0Z/20=
|
||||
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-tun v0.2.6 h1:FKXzh34uuO5RStBgf3Zi+Txan5eS9YTVOQrJRAbJBHk=
|
||||
github.com/sagernet/sing-tun v0.2.6/go.mod h1:MKAAHUzVfj7d9zos4lsz6wjXu86/mJyd/gejiAnWj/w=
|
||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
@@ -139,8 +134,6 @@ github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYASco
|
||||
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -149,8 +142,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
@@ -170,17 +163,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -190,10 +184,10 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.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.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
@@ -201,19 +195,16 @@ 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/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.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
||||
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/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
|
||||
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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -116,6 +116,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
@@ -129,6 +130,7 @@ func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
func (h *Hysteria) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
|
||||
@@ -127,6 +127,7 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
||||
func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
@@ -140,6 +141,7 @@ func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata a
|
||||
func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/common/uot"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/include"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
@@ -108,8 +109,8 @@ func (n *Naive) Start() error {
|
||||
|
||||
if common.Contains(n.network, N.NetworkUDP) {
|
||||
err := n.configureHTTP3Listener()
|
||||
if !C.WithQUIC && len(n.network) > 1 {
|
||||
n.logger.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
if !include.WithQUIC && len(n.network) > 1 {
|
||||
log.Warn(E.Cause(err, "naive http3 disabled"))
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
@@ -111,6 +112,7 @@ func (h *TUIC) newConnection(ctx context.Context, conn net.Conn, metadata adapte
|
||||
func (h *TUIC) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
ctx = log.ContextWithNewID(ctx)
|
||||
metadata = h.createPacketMetadata(conn, metadata)
|
||||
h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||||
userID, _ := auth.UserFromContext[int](ctx)
|
||||
if userName := h.userNameList[userID]; userName != "" {
|
||||
metadata.User = userName
|
||||
|
||||
@@ -153,7 +153,7 @@ func (t *Tun) Start() error {
|
||||
tunInterface tun.Tun
|
||||
err error
|
||||
)
|
||||
monitor := taskmonitor.New(t.logger, C.DefaultStartTimeout)
|
||||
monitor := taskmonitor.New(t.logger, C.StartTimeout)
|
||||
monitor.Start("open tun interface")
|
||||
if t.platformInterface != nil {
|
||||
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions)
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"dhcp"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
dns.RegisterTransport([]string{"dhcp"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,3 +6,5 @@ import (
|
||||
_ "github.com/sagernet/sing-box/transport/v2rayquic"
|
||||
_ "github.com/sagernet/sing-dns/quic"
|
||||
)
|
||||
|
||||
const WithQUIC = true
|
||||
|
||||
@@ -11,12 +11,15 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
const WithQUIC = false
|
||||
|
||||
func init() {
|
||||
dns.RegisterTransport([]string{"quic", "h3"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||
dns.RegisterTransport([]string{"quic", "h3"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
||||
return nil, C.ErrQUICNotIncluded
|
||||
})
|
||||
v2ray.RegisterQUICConstructor(
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// 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)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -19,7 +19,6 @@ type DNSServerOptions struct {
|
||||
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSClientOptions struct {
|
||||
@@ -27,7 +26,6 @@ type DNSClientOptions struct {
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
}
|
||||
|
||||
type DNSFakeIPOptions struct {
|
||||
|
||||
@@ -8,12 +8,10 @@ type ExperimentalOptions struct {
|
||||
}
|
||||
|
||||
type CacheFileOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
CacheID string `json:"cache_id,omitempty"`
|
||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||
StoreRDRC bool `json:"store_rdrc,omitempty"`
|
||||
RDRCTimeout Duration `json:"rdrc_timeout,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
CacheID string `json:"cache_id,omitempty"`
|
||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||
}
|
||||
|
||||
type ClashAPIOptions struct {
|
||||
|
||||
@@ -65,43 +65,38 @@ func (r DNSRule) IsValid() bool {
|
||||
}
|
||||
|
||||
type DefaultDNSRule struct {
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network Listable[string] `json:"network,omitempty"`
|
||||
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol Listable[string] `json:"protocol,omitempty"`
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet Listable[string] `json:"rule_set,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"`
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network Listable[string] `json:"network,omitempty"`
|
||||
AuthUser Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol Listable[string] `json:"protocol,omitempty"`
|
||||
Domain Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
|
||||
SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath Listable[string] `json:"process_path,omitempty"`
|
||||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
}
|
||||
|
||||
func (r DefaultDNSRule) IsValid() bool {
|
||||
@@ -110,18 +105,16 @@ func (r DefaultDNSRule) IsValid() bool {
|
||||
defaultValue.Server = r.Server
|
||||
defaultValue.DisableCache = r.DisableCache
|
||||
defaultValue.RewriteTTL = r.RewriteTTL
|
||||
defaultValue.ClientSubnet = r.ClientSubnet
|
||||
return !reflect.DeepEqual(r, defaultValue)
|
||||
}
|
||||
|
||||
type LogicalDNSRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||
Mode string `json:"mode"`
|
||||
Rules []DNSRule `json:"rules,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
}
|
||||
|
||||
func (r LogicalDNSRule) IsValid() bool {
|
||||
|
||||
@@ -7,6 +7,4 @@ type TunPlatformOptions struct {
|
||||
type HTTPProxyOptions struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn))
|
||||
conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination)
|
||||
if originDestination != destination {
|
||||
conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination)
|
||||
}
|
||||
@@ -156,14 +156,14 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
||||
}
|
||||
|
||||
func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if h.loopBack.CheckConn(metadata.Source.AddrPort()) {
|
||||
if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
|
||||
return E.New("reject loopback connection to ", metadata.Destination)
|
||||
}
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
if h.loopBack.CheckPacketConn(metadata.Source.AddrPort()) {
|
||||
if h.loopBack.CheckPacketConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) {
|
||||
return E.New("reject loopback packet connection to ", metadata.Destination)
|
||||
}
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
|
||||
@@ -10,58 +10,83 @@ import (
|
||||
)
|
||||
|
||||
type loopBackDetector struct {
|
||||
// router adapter.Router
|
||||
connAccess sync.RWMutex
|
||||
packetConnAccess sync.RWMutex
|
||||
connMap map[netip.AddrPort]bool
|
||||
packetConnMap map[netip.AddrPort]bool
|
||||
connMap map[netip.AddrPort]netip.AddrPort
|
||||
packetConnMap map[uint16]uint16
|
||||
}
|
||||
|
||||
func newLoopBackDetector() *loopBackDetector {
|
||||
func newLoopBackDetector( /*router adapter.Router*/ ) *loopBackDetector {
|
||||
return &loopBackDetector{
|
||||
connMap: make(map[netip.AddrPort]bool),
|
||||
packetConnMap: make(map[netip.AddrPort]bool),
|
||||
// router: router,
|
||||
connMap: make(map[netip.AddrPort]netip.AddrPort),
|
||||
packetConnMap: make(map[uint16]uint16),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
|
||||
connAddr := M.AddrPortFromNet(conn.LocalAddr())
|
||||
if !connAddr.IsValid() {
|
||||
source := M.AddrPortFromNet(conn.LocalAddr())
|
||||
if !source.IsValid() {
|
||||
return conn
|
||||
}
|
||||
if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
|
||||
/*if !source.Addr().IsLoopback() {
|
||||
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
|
||||
if err != nil {
|
||||
return conn
|
||||
}
|
||||
}*/
|
||||
if !N.IsPublicAddr(source.Addr()) {
|
||||
return conn
|
||||
}
|
||||
l.packetConnAccess.Lock()
|
||||
l.packetConnMap[connAddr] = true
|
||||
l.packetConnMap[source.Port()] = M.AddrPortFromNet(conn.RemoteAddr()).Port()
|
||||
l.packetConnAccess.Unlock()
|
||||
return &loopBackDetectUDPWrapper{abstractUDPConn: udpConn, detector: l, connAddr: connAddr}
|
||||
return &loopBackDetectUDPWrapper{abstractUDPConn: udpConn, detector: l, connPort: source.Port()}
|
||||
} else {
|
||||
l.connAccess.Lock()
|
||||
l.connMap[connAddr] = true
|
||||
l.connMap[source] = M.AddrPortFromNet(conn.RemoteAddr())
|
||||
l.connAccess.Unlock()
|
||||
return &loopBackDetectWrapper{Conn: conn, detector: l, connAddr: connAddr}
|
||||
return &loopBackDetectWrapper{Conn: conn, detector: l, connAddr: source}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn) N.NetPacketConn {
|
||||
connAddr := M.AddrPortFromNet(conn.LocalAddr())
|
||||
if !connAddr.IsValid() {
|
||||
func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn, destination M.Socksaddr) N.NetPacketConn {
|
||||
source := M.AddrPortFromNet(conn.LocalAddr())
|
||||
if !source.IsValid() {
|
||||
return conn
|
||||
}
|
||||
l.packetConnAccess.Lock()
|
||||
l.packetConnMap[connAddr] = true
|
||||
l.packetConnMap[source.Port()] = destination.AddrPort().Port()
|
||||
l.packetConnAccess.Unlock()
|
||||
return &loopBackDetectPacketWrapper{NetPacketConn: conn, detector: l, connAddr: connAddr}
|
||||
return &loopBackDetectPacketWrapper{NetPacketConn: conn, detector: l, connPort: source.Port()}
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) CheckConn(connAddr netip.AddrPort) bool {
|
||||
func (l *loopBackDetector) CheckConn(source netip.AddrPort, local netip.AddrPort) bool {
|
||||
l.connAccess.RLock()
|
||||
defer l.connAccess.RUnlock()
|
||||
return l.connMap[connAddr]
|
||||
destination, loaded := l.connMap[source]
|
||||
return loaded && destination != local
|
||||
}
|
||||
|
||||
func (l *loopBackDetector) CheckPacketConn(connAddr netip.AddrPort) bool {
|
||||
func (l *loopBackDetector) CheckPacketConn(source netip.AddrPort, local netip.AddrPort) bool {
|
||||
if !source.IsValid() {
|
||||
return false
|
||||
}
|
||||
/*if !source.Addr().IsLoopback() {
|
||||
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}*/
|
||||
if N.IsPublicAddr(source.Addr()) {
|
||||
return false
|
||||
}
|
||||
l.packetConnAccess.RLock()
|
||||
defer l.packetConnAccess.RUnlock()
|
||||
return l.packetConnMap[connAddr]
|
||||
destinationPort, loaded := l.packetConnMap[source.Port()]
|
||||
return loaded && destinationPort != local.Port()
|
||||
}
|
||||
|
||||
type loopBackDetectWrapper struct {
|
||||
@@ -95,14 +120,14 @@ func (w *loopBackDetectWrapper) Upstream() any {
|
||||
type loopBackDetectPacketWrapper struct {
|
||||
N.NetPacketConn
|
||||
detector *loopBackDetector
|
||||
connAddr netip.AddrPort
|
||||
connPort uint16
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (w *loopBackDetectPacketWrapper) Close() error {
|
||||
w.closeOnce.Do(func() {
|
||||
w.detector.packetConnAccess.Lock()
|
||||
delete(w.detector.packetConnMap, w.connAddr)
|
||||
delete(w.detector.packetConnMap, w.connPort)
|
||||
w.detector.packetConnAccess.Unlock()
|
||||
})
|
||||
return w.NetPacketConn.Close()
|
||||
@@ -128,14 +153,14 @@ type abstractUDPConn interface {
|
||||
type loopBackDetectUDPWrapper struct {
|
||||
abstractUDPConn
|
||||
detector *loopBackDetector
|
||||
connAddr netip.AddrPort
|
||||
connPort uint16
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (w *loopBackDetectUDPWrapper) Close() error {
|
||||
w.closeOnce.Do(func() {
|
||||
w.detector.packetConnAccess.Lock()
|
||||
delete(w.detector.packetConnMap, w.connAddr)
|
||||
delete(w.detector.packetConnMap, w.connPort)
|
||||
w.detector.packetConnAccess.Unlock()
|
||||
})
|
||||
return w.abstractUDPConn.Close()
|
||||
|
||||
@@ -111,7 +111,9 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
break
|
||||
@@ -239,11 +241,16 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
|
||||
return err
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer, err := dns.TruncateDNSMessage(&message, response, 1024)
|
||||
response = truncateDNSMessage(response, 512) // TODO: add an option to custom UDP buffer size
|
||||
responseBuffer := buf.NewSize(dns.FixedPacketSize)
|
||||
responseBuffer.Resize(1024, 0)
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
responseBuffer.Release()
|
||||
return err
|
||||
}
|
||||
responseBuffer.Truncate(len(n))
|
||||
err = conn.WritePacket(responseBuffer, destination)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
@@ -257,3 +264,22 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
|
||||
})
|
||||
return group.Run(fastClose)
|
||||
}
|
||||
|
||||
func truncateDNSMessage(response *mDNS.Msg, maxLen int) *mDNS.Msg {
|
||||
responseLen := response.Len()
|
||||
if responseLen <= maxLen {
|
||||
return response
|
||||
}
|
||||
newResponse := *response
|
||||
response = &newResponse
|
||||
for len(response.Answer) > 0 && responseLen > maxLen {
|
||||
response.Answer = response.Answer[:len(response.Answer)-1]
|
||||
response.Truncated = true
|
||||
responseLen = response.Len()
|
||||
}
|
||||
if responseLen > maxLen {
|
||||
response.Ns = nil
|
||||
response.Extra = nil
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -43,7 +42,6 @@ import (
|
||||
serviceNTP "github.com/sagernet/sing/common/ntp"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
"github.com/sagernet/sing/common/winpowrprof"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
)
|
||||
@@ -70,7 +68,6 @@ type Router struct {
|
||||
geositeCache map[string]adapter.Rule
|
||||
needFindProcess bool
|
||||
dnsClient *dns.Client
|
||||
dnsIndependentCache bool
|
||||
defaultDomainStrategy dns.DomainStrategy
|
||||
dnsRules []adapter.DNSRule
|
||||
ruleSets []adapter.RuleSet
|
||||
@@ -88,7 +85,6 @@ type Router struct {
|
||||
networkMonitor tun.NetworkUpdateMonitor
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
packageManager tun.PackageManager
|
||||
powerListener winpowrprof.EventListener
|
||||
processSearcher process.Searcher
|
||||
timeService *ntp.Service
|
||||
pauseManager pause.Manager
|
||||
@@ -124,7 +120,6 @@ func NewRouter(
|
||||
geositeOptions: common.PtrValueOrDefault(options.Geosite),
|
||||
geositeCache: make(map[string]adapter.Rule),
|
||||
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
|
||||
dnsIndependentCache: dnsOptions.IndependentCache,
|
||||
defaultDetour: options.Final,
|
||||
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
|
||||
autoDetectInterface: options.AutoDetectInterface,
|
||||
@@ -141,17 +136,7 @@ func NewRouter(
|
||||
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
|
||||
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
|
||||
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
|
||||
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,
|
||||
Logger: router.dnsLogger,
|
||||
})
|
||||
for i, ruleOptions := range options.Rules {
|
||||
routeRule, err := NewRule(router, router.logger, ruleOptions, true)
|
||||
@@ -237,20 +222,7 @@ func NewRouter(
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
}
|
||||
}
|
||||
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,
|
||||
})
|
||||
transport, err := dns.CreateTransport(tag, ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||
}
|
||||
@@ -290,12 +262,7 @@ func NewRouter(
|
||||
}
|
||||
if defaultTransport == nil {
|
||||
if len(transports) == 0 {
|
||||
transports = append(transports, common.Must1(dns.CreateTransport(dns.TransportOptions{
|
||||
Context: ctx,
|
||||
Name: "local",
|
||||
Address: "local",
|
||||
Dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{})),
|
||||
})))
|
||||
transports = append(transports, dns.NewLocalTransport("local", N.SystemDialer))
|
||||
}
|
||||
defaultTransport = transports[0]
|
||||
}
|
||||
@@ -354,14 +321,6 @@ func NewRouter(
|
||||
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 {
|
||||
timeService, err := ntp.NewService(ctx, router, logFactory.NewLogger("ntp"), ntpOptions)
|
||||
if err != nil {
|
||||
@@ -463,7 +422,7 @@ func (r *Router) Outbounds() []adapter.Outbound {
|
||||
}
|
||||
|
||||
func (r *Router) PreStart() error {
|
||||
monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
|
||||
monitor := taskmonitor.New(r.logger, C.StartTimeout)
|
||||
if r.interfaceMonitor != nil {
|
||||
monitor.Start("initialize interface monitor")
|
||||
err := r.interfaceMonitor.Start()
|
||||
@@ -492,7 +451,7 @@ func (r *Router) PreStart() error {
|
||||
}
|
||||
|
||||
func (r *Router) Start() error {
|
||||
monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
|
||||
monitor := taskmonitor.New(r.logger, C.StartTimeout)
|
||||
if r.needGeoIPDatabase {
|
||||
monitor.Start("initialize geoip database")
|
||||
err := r.prepareGeoIPDatabase()
|
||||
@@ -601,16 +560,6 @@ func (r *Router) Start() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.powerListener != nil {
|
||||
monitor.Start("start power listener")
|
||||
err := r.powerListener.Start()
|
||||
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
|
||||
@@ -629,11 +578,6 @@ func (r *Router) Start() error {
|
||||
return E.Cause(err, "initialize rule[", i, "]")
|
||||
}
|
||||
}
|
||||
|
||||
monitor.Start("initialize DNS client")
|
||||
r.dnsClient.Start()
|
||||
monitor.Finish()
|
||||
|
||||
for i, rule := range r.dnsRules {
|
||||
monitor.Start("initialize DNS rule[", i, "]")
|
||||
err := rule.Start()
|
||||
@@ -662,7 +606,7 @@ func (r *Router) Start() error {
|
||||
}
|
||||
|
||||
func (r *Router) Close() error {
|
||||
monitor := taskmonitor.New(r.logger, C.DefaultStopTimeout)
|
||||
monitor := taskmonitor.New(r.logger, C.StopTimeout)
|
||||
var err error
|
||||
for i, rule := range r.rules {
|
||||
monitor.Start("close rule[", i, "]")
|
||||
@@ -713,13 +657,6 @@ func (r *Router) Close() error {
|
||||
})
|
||||
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 {
|
||||
monitor.Start("close time service")
|
||||
err = E.Append(err, r.timeService.Close(), func(err error) error {
|
||||
@@ -1252,19 +1189,3 @@ func (r *Router) updateWIFIState() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
"context"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/cache"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -37,55 +37,41 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) {
|
||||
return domain, loaded
|
||||
}
|
||||
|
||||
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) {
|
||||
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool) (context.Context, dns.Transport, dns.DomainStrategy) {
|
||||
metadata := adapter.ContextFrom(ctx)
|
||||
if metadata == nil {
|
||||
panic("no context")
|
||||
}
|
||||
if index < len(r.dnsRules) {
|
||||
dnsRules := r.dnsRules
|
||||
if index != -1 {
|
||||
dnsRules = dnsRules[index+1:]
|
||||
}
|
||||
for ruleIndex, rule := range dnsRules {
|
||||
metadata.ResetRuleCache()
|
||||
if rule.Match(metadata) {
|
||||
detour := rule.Outbound()
|
||||
transport, loaded := r.transportMap[detour]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
||||
continue
|
||||
}
|
||||
_, isFakeIP := transport.(adapter.FakeIPTransport)
|
||||
if isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
displayRuleIndex := ruleIndex
|
||||
if index != -1 {
|
||||
displayRuleIndex += index + 1
|
||||
}
|
||||
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
|
||||
}
|
||||
for i, rule := range r.dnsRules {
|
||||
metadata.ResetRuleCache()
|
||||
if rule.Match(metadata) {
|
||||
detour := rule.Outbound()
|
||||
transport, loaded := r.transportMap[detour]
|
||||
if !loaded {
|
||||
r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour)
|
||||
continue
|
||||
}
|
||||
if _, isFakeIP := transport.(adapter.FakeIPTransport); isFakeIP && !allowFakeIP {
|
||||
continue
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "match[", i, "] ", rule.String(), " => ", detour)
|
||||
if rule.DisableCache() {
|
||||
ctx = dns.ContextWithDisableCache(ctx, true)
|
||||
}
|
||||
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
|
||||
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL)
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||
return ctx, transport, domainStrategy
|
||||
} else {
|
||||
return ctx, transport, r.defaultDomainStrategy
|
||||
}
|
||||
}
|
||||
}
|
||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded {
|
||||
return ctx, r.defaultTransport, domainStrategy, nil, -1
|
||||
return ctx, r.defaultTransport, domainStrategy
|
||||
} else {
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy, nil, -1
|
||||
return ctx, r.defaultTransport, r.defaultDomainStrategy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,15 +80,13 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
||||
}
|
||||
var (
|
||||
response *mDNS.Msg
|
||||
cached bool
|
||||
transport dns.Transport
|
||||
err error
|
||||
response *mDNS.Msg
|
||||
cached bool
|
||||
err error
|
||||
)
|
||||
response, cached = r.dnsClient.ExchangeCache(ctx, message)
|
||||
if !cached {
|
||||
var metadata *adapter.InboundContext
|
||||
ctx, metadata = adapter.AppendContext(ctx)
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
if len(message.Question) > 0 {
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
@@ -113,130 +97,50 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
}
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
var (
|
||||
strategy dns.DomainStrategy
|
||||
rule adapter.DNSRule
|
||||
ruleIndex int
|
||||
)
|
||||
ruleIndex = -1
|
||||
for {
|
||||
var (
|
||||
dnsCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
addressLimit bool
|
||||
)
|
||||
|
||||
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex)
|
||||
dnsCtx, cancel = context.WithTimeout(dnsCtx, C.DNSTimeout)
|
||||
if rule != nil && rule.WithAddressLimit() && isAddressQuery(message) {
|
||||
addressLimit = true
|
||||
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, strategy, func(response *mDNS.Msg) bool {
|
||||
metadata.DestinationAddresses, _ = dns.MessageToAddresses(response)
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
})
|
||||
} else {
|
||||
addressLimit = false
|
||||
response, err = r.dnsClient.Exchange(dnsCtx, transport, message, strategy)
|
||||
}
|
||||
cancel()
|
||||
if err != nil {
|
||||
if errors.Is(err, dns.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
|
||||
}
|
||||
ctx, transport, strategy := r.matchDNS(ctx, true)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
response, err = r.dnsClient.Exchange(ctx, transport, message, strategy)
|
||||
if err != nil && len(message.Question) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(message.Question) > 0 && response != nil {
|
||||
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)
|
||||
}
|
||||
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
||||
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))
|
||||
}
|
||||
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
|
||||
return response, err
|
||||
}
|
||||
|
||||
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)
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Domain = domain
|
||||
var (
|
||||
transport dns.Transport
|
||||
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, transport, transportStrategy := r.matchDNS(ctx, false)
|
||||
if strategy == dns.DomainStrategyAsIS {
|
||||
strategy = transportStrategy
|
||||
}
|
||||
if len(responseAddrs) > 0 {
|
||||
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " "))
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
addrs, err := r.dnsClient.Lookup(ctx, transport, domain, strategy)
|
||||
if len(addrs) > 0 {
|
||||
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(addrs), " "))
|
||||
} else if err != nil {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
||||
} else {
|
||||
r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
|
||||
err = dns.RCodeNameError
|
||||
}
|
||||
return responseAddrs, err
|
||||
return addrs, err
|
||||
}
|
||||
|
||||
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
|
||||
@@ -250,13 +154,10 @@ func (r *Router) ClearDNSCache() {
|
||||
}
|
||||
}
|
||||
|
||||
func isAddressQuery(message *mDNS.Msg) bool {
|
||||
for _, question := range message.Question {
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
return true
|
||||
}
|
||||
func LogDNSAnswers(logger log.ContextLogger, ctx context.Context, domain string, answers []mDNS.RR) {
|
||||
for _, answer := range answers {
|
||||
logger.InfoContext(ctx, "exchanged ", domain, " ", mDNS.Type(answer.Header().Rrtype).String(), " ", formatQuestion(answer.String()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func fqdnToDomain(fqdn string) string {
|
||||
|
||||
@@ -59,7 +59,7 @@ func isGeoIPRule(rule option.DefaultRule) bool {
|
||||
}
|
||||
|
||||
func isGeoIPDNSRule(rule option.DefaultDNSRule) bool {
|
||||
return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) || len(rule.GeoIP) > 0 && common.Any(rule.GeoIP, notPrivateNode)
|
||||
return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode)
|
||||
}
|
||||
|
||||
func isGeositeRule(rule option.DefaultRule) bool {
|
||||
@@ -97,7 +97,3 @@ func isWIFIDNSRule(rule option.DefaultDNSRule) bool {
|
||||
func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool {
|
||||
return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
|
||||
}
|
||||
|
||||
func isIPCIDRHeadlessRule(rule option.DefaultHeadlessRule) bool {
|
||||
return len(rule.IPCIDR) > 0 || rule.IPSet != nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ type abstractDefaultRule struct {
|
||||
sourceAddressItems []RuleItem
|
||||
sourcePortItems []RuleItem
|
||||
destinationAddressItems []RuleItem
|
||||
destinationIPCIDRItems []RuleItem
|
||||
destinationPortItems []RuleItem
|
||||
allItems []RuleItem
|
||||
ruleSetItem RuleItem
|
||||
@@ -65,7 +64,6 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
if len(r.sourceAddressItems) > 0 && !metadata.SourceAddressMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.sourceAddressItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.SourceAddressMatch = true
|
||||
@@ -75,7 +73,6 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
if len(r.sourcePortItems) > 0 && !metadata.SourcePortMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.sourcePortItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.SourcePortMatch = true
|
||||
@@ -85,7 +82,6 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
if len(r.destinationAddressItems) > 0 && !metadata.DestinationAddressMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.destinationAddressItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationAddressMatch = true
|
||||
@@ -94,18 +90,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if !metadata.IgnoreDestinationIPCIDRMatch && len(r.destinationIPCIDRItems) > 0 && !metadata.DestinationAddressMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.destinationIPCIDRItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationAddressMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.destinationPortItems) > 0 && !metadata.DestinationPortMatch {
|
||||
metadata.DidMatch = true
|
||||
for _, item := range r.destinationPortItems {
|
||||
if item.Match(metadata) {
|
||||
metadata.DestinationPortMatch = true
|
||||
@@ -115,9 +100,6 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
for _, item := range r.items {
|
||||
if _, isRuleSet := item.(*RuleSetItem); !isRuleSet {
|
||||
metadata.DidMatch = true
|
||||
}
|
||||
if !item.Match(metadata) {
|
||||
return r.invert
|
||||
}
|
||||
@@ -131,7 +113,7 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
return r.invert
|
||||
}
|
||||
|
||||
if ((!metadata.IgnoreDestinationIPCIDRMatch && len(r.destinationIPCIDRItems) > 0) || len(r.destinationAddressItems) > 0) && !metadata.DestinationAddressMatch {
|
||||
if len(r.destinationAddressItems) > 0 && !metadata.DestinationAddressMatch {
|
||||
return r.invert
|
||||
}
|
||||
|
||||
@@ -139,10 +121,6 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
|
||||
return r.invert
|
||||
}
|
||||
|
||||
if !metadata.DidMatch {
|
||||
return false
|
||||
}
|
||||
|
||||
return !r.invert
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
}
|
||||
if len(options.GeoIP) > 0 {
|
||||
item := NewGeoIPItem(router, logger, false, options.GeoIP)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
@@ -130,12 +130,12 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "ipcidr")
|
||||
}
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.IPIsPrivate {
|
||||
item := NewIPIsPrivateItem(false)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
@@ -40,7 +37,6 @@ type DefaultDNSRule struct {
|
||||
abstractDefaultRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Addr
|
||||
}
|
||||
|
||||
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
||||
@@ -51,7 +47,6 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
},
|
||||
disableCache: options.DisableCache,
|
||||
rewriteTTL: options.RewriteTTL,
|
||||
clientSubnet: (*netip.Addr)(options.ClientSubnet),
|
||||
}
|
||||
if len(options.Inbound) > 0 {
|
||||
item := NewInboundRule(options.Inbound)
|
||||
@@ -116,11 +111,6 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.GeoIP) > 0 {
|
||||
item := NewGeoIPItem(router, logger, false, options.GeoIP)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourceIPCIDR) > 0 {
|
||||
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
||||
if err != nil {
|
||||
@@ -129,24 +119,11 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.IPCIDR) > 0 {
|
||||
item, err := NewIPCIDRItem(false, options.IPCIDR)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "ip_cidr")
|
||||
}
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.SourceIPIsPrivate {
|
||||
item := NewIPIsPrivateItem(true)
|
||||
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.IPIsPrivate {
|
||||
item := NewIPIsPrivateItem(false)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
item := NewPortItem(true, options.SourcePort)
|
||||
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
||||
@@ -219,7 +196,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.RuleSet) > 0 {
|
||||
item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource)
|
||||
item := NewRuleSetItem(router, options.RuleSet, false)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
@@ -234,45 +211,12 @@ func (r *DefaultDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) ClientSubnet() *netip.Addr {
|
||||
return r.clientSubnet
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) WithAddressLimit() bool {
|
||||
if len(r.destinationIPCIDRItems) > 0 {
|
||||
return true
|
||||
}
|
||||
for _, rawRule := range r.items {
|
||||
ruleSet, isRuleSet := rawRule.(*RuleSetItem)
|
||||
if !isRuleSet {
|
||||
continue
|
||||
}
|
||||
if ruleSet.ContainsIPCIDRRule() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) Match(metadata *adapter.InboundContext) bool {
|
||||
metadata.IgnoreDestinationIPCIDRMatch = true
|
||||
defer func() {
|
||||
metadata.IgnoreDestinationIPCIDRMatch = false
|
||||
}()
|
||||
return r.abstractDefaultRule.Match(metadata)
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) MatchAddressLimit(metadata *adapter.InboundContext) bool {
|
||||
return r.abstractDefaultRule.Match(metadata)
|
||||
}
|
||||
|
||||
var _ adapter.DNSRule = (*LogicalDNSRule)(nil)
|
||||
|
||||
type LogicalDNSRule struct {
|
||||
abstractLogicalRule
|
||||
disableCache bool
|
||||
rewriteTTL *uint32
|
||||
clientSubnet *netip.Addr
|
||||
}
|
||||
|
||||
func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
|
||||
@@ -310,51 +254,3 @@ func (r *LogicalDNSRule) DisableCache() bool {
|
||||
func (r *LogicalDNSRule) RewriteTTL() *uint32 {
|
||||
return r.rewriteTTL
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) ClientSubnet() *netip.Addr {
|
||||
return r.clientSubnet
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) WithAddressLimit() bool {
|
||||
for _, rawRule := range r.rules {
|
||||
switch rule := rawRule.(type) {
|
||||
case *DefaultDNSRule:
|
||||
if rule.WithAddressLimit() {
|
||||
return true
|
||||
}
|
||||
case *LogicalDNSRule:
|
||||
if rule.WithAddressLimit() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) Match(metadata *adapter.InboundContext) bool {
|
||||
if r.mode == C.LogicalTypeAnd {
|
||||
return common.All(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).Match(metadata)
|
||||
}) != r.invert
|
||||
} else {
|
||||
return common.Any(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).Match(metadata)
|
||||
}) != r.invert
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LogicalDNSRule) MatchAddressLimit(metadata *adapter.InboundContext) bool {
|
||||
if r.mode == C.LogicalTypeAnd {
|
||||
return common.All(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).MatchAddressLimit(metadata)
|
||||
}) != r.invert
|
||||
} else {
|
||||
return common.Any(r.rules, func(it adapter.HeadlessRule) bool {
|
||||
metadata.ResetRuleCache()
|
||||
return it.(adapter.DNSRule).MatchAddressLimit(metadata)
|
||||
}) != r.invert
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,11 +80,11 @@ func NewDefaultHeadlessRule(router adapter.Router, options option.DefaultHeadles
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "ipcidr")
|
||||
}
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
} else if options.IPSet != nil {
|
||||
item := NewRawIPCIDRItem(false, options.IPSet)
|
||||
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
||||
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.SourcePort) > 0 {
|
||||
|
||||
@@ -24,12 +24,14 @@ func (r *IPIsPrivateItem) Match(metadata *adapter.InboundContext) bool {
|
||||
} else {
|
||||
destination = metadata.Destination.Addr
|
||||
}
|
||||
if destination.IsValid() && !N.IsPublicAddr(destination) {
|
||||
return true
|
||||
if destination.IsValid() {
|
||||
return !N.IsPublicAddr(destination)
|
||||
}
|
||||
for _, destinationAddress := range metadata.DestinationAddresses {
|
||||
if !N.IsPublicAddr(destinationAddress) {
|
||||
return true
|
||||
if !r.isSource {
|
||||
for _, destinationAddress := range metadata.DestinationAddresses {
|
||||
if !N.IsPublicAddr(destinationAddress) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
@@ -14,7 +13,7 @@ var _ RuleItem = (*RuleSetItem)(nil)
|
||||
type RuleSetItem struct {
|
||||
router adapter.Router
|
||||
tagList []string
|
||||
setList []adapter.RuleSet
|
||||
setList []adapter.HeadlessRule
|
||||
ipcidrMatchSource bool
|
||||
}
|
||||
|
||||
@@ -47,12 +46,6 @@ func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool {
|
||||
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 {
|
||||
if len(r.tagList) == 1 {
|
||||
return F.ToString("rule_set=", r.tagList[0])
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user