mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
51 Commits
v1.13.0-al
...
dev-wifi-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
766972ce52 | ||
|
|
f1e48a1cad | ||
|
|
ba496ae300 | ||
|
|
4488148322 | ||
|
|
f086454f81 | ||
|
|
f9b6a068ee | ||
|
|
0ad2a441d9 | ||
|
|
c714b59c87 | ||
|
|
bd6b125707 | ||
|
|
d1109cee90 | ||
|
|
b48002b4db | ||
|
|
67cedfd927 | ||
|
|
853b576d12 | ||
|
|
05cd7f6192 | ||
|
|
e4cf55a86d | ||
|
|
2bf605fad4 | ||
|
|
b913899d43 | ||
|
|
e1a22c0cc2 | ||
|
|
d890c8e8d7 | ||
|
|
1b4ffff67c | ||
|
|
6d012c04cd | ||
|
|
c1309b63c9 | ||
|
|
6674b252bf | ||
|
|
8c7b2e4ac3 | ||
|
|
f400c927c4 | ||
|
|
587d330b58 | ||
|
|
719a28920a | ||
|
|
2e0d344a3d | ||
|
|
387084b7c7 | ||
|
|
e15c02ee33 | ||
|
|
2648b80c6b | ||
|
|
391f7a73d2 | ||
|
|
c1565e2bb0 | ||
|
|
605d8b41f9 | ||
|
|
03d8243d41 | ||
|
|
30f1beb071 | ||
|
|
3841d9cb5a | ||
|
|
dbb7345ec9 | ||
|
|
88f000412b | ||
|
|
0f767d5ce1 | ||
|
|
328a6de797 | ||
|
|
886be6414d | ||
|
|
9362d3cab3 | ||
|
|
ced2e39dbf | ||
|
|
2159d8877b | ||
|
|
cb7dba3eff | ||
|
|
d9d7f7880d | ||
|
|
a031aaf2c0 | ||
|
|
4bca951773 | ||
|
|
140735dbde | ||
|
|
714a68bba1 |
@@ -1,25 +1,27 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="1.23.12"
|
VERSION="1.25.1"
|
||||||
|
|
||||||
mkdir -p $HOME/go
|
mkdir -p $HOME/go
|
||||||
cd $HOME/go
|
cd $HOME/go
|
||||||
wget "https://dl.google.com/go/go${VERSION}.linux-amd64.tar.gz"
|
wget "https://dl.google.com/go/go${VERSION}.linux-amd64.tar.gz"
|
||||||
tar -xzf "go${VERSION}.linux-amd64.tar.gz"
|
tar -xzf "go${VERSION}.linux-amd64.tar.gz"
|
||||||
mv go go_legacy
|
mv go go_win7
|
||||||
cd go_legacy
|
cd go_win7
|
||||||
|
|
||||||
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
|
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
|
||||||
# this patch file only works on golang1.23.x
|
# this patch file only works on golang1.25.x
|
||||||
# that means after golang1.24 release it must be changed
|
# that means after golang1.26 release it must be changed
|
||||||
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/
|
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.25/
|
||||||
# revert:
|
# revert:
|
||||||
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
|
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
|
||||||
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
|
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
|
||||||
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
|
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
|
||||||
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
|
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
|
||||||
|
|
||||||
curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1
|
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
|
||||||
curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1
|
|
||||||
curl https://github.com/MetaCubeX/go/commit/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1
|
curl https://github.com/MetaCubeX/go/commit/8cb5472d94c34b88733a81091bd328e70ee565a4.diff | patch --verbose -p 1
|
||||||
curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1
|
curl https://github.com/MetaCubeX/go/commit/6788c4c6f9fafb56729bad6b660f7ee2272d699f.diff | patch --verbose -p 1
|
||||||
|
curl https://github.com/MetaCubeX/go/commit/a5b2168bb836ed9d6601c626f95e56c07923f906.diff | patch --verbose -p 1
|
||||||
|
curl https://github.com/MetaCubeX/go/commit/f56f1e23507e646c85243a71bde7b9629b2f970c.diff | patch --verbose -p 1
|
||||||
30
.github/workflows/build.yml
vendored
30
.github/workflows/build.yml
vendored
@@ -88,9 +88,9 @@ jobs:
|
|||||||
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
- { os: linux, arch: loong64, debian: loongarch64, rpm: loongarch64, openwrt: "loongarch64_generic" }
|
||||||
|
|
||||||
- { os: windows, arch: amd64 }
|
- { os: windows, arch: amd64 }
|
||||||
- { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" }
|
- { os: windows, arch: amd64, legacy_win7: true, legacy_name: "windows-7" }
|
||||||
- { os: windows, arch: "386" }
|
- { os: windows, arch: "386" }
|
||||||
- { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" }
|
- { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
|
||||||
- { os: windows, arch: arm64 }
|
- { os: windows, arch: arm64 }
|
||||||
|
|
||||||
- { os: darwin, arch: amd64 }
|
- { os: darwin, arch: amd64 }
|
||||||
@@ -116,23 +116,23 @@ jobs:
|
|||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ~1.24.6
|
go-version: ~1.24.6
|
||||||
- name: Cache Go 1.23
|
- name: Cache Go for Windows 7
|
||||||
if: matrix.legacy_go123
|
if: matrix.legacy_win7
|
||||||
id: cache-legacy-go
|
id: cache-go-for-windows7
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/go_legacy
|
~/go/go_win7
|
||||||
key: go_legacy_12312
|
key: go_win7_1251
|
||||||
- name: Setup Go 1.23
|
- name: Setup Go for Windows 7
|
||||||
if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
|
||||||
run: |-
|
run: |-
|
||||||
.github/setup_legacy_go.sh
|
.github/setup_go_for_windows7.sh
|
||||||
- name: Setup Go 1.23
|
- name: Setup Go for Windows 7
|
||||||
if: matrix.legacy_go123
|
if: matrix.legacy_win7
|
||||||
run: |-
|
run: |-
|
||||||
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
|
echo "PATH=$HOME/go/go_win7/bin:$PATH" >> $GITHUB_ENV
|
||||||
echo "GOROOT=$HOME/go/go_legacy" >> $GITHUB_ENV
|
echo "GOROOT=$HOME/go/go_win7" >> $GITHUB_ENV
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
if: matrix.os == 'android'
|
if: matrix.os == 'android'
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -146,7 +146,7 @@ jobs:
|
|||||||
- name: Set build tags
|
- name: Set build tags
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0'
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build
|
- name: Build
|
||||||
if: matrix.os != 'android'
|
if: matrix.os != 'android'
|
||||||
|
|||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
|||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: v2.4.0
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
install-mode: binary
|
install-mode: binary
|
||||||
verify: false
|
verify: false
|
||||||
|
|||||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -85,7 +85,7 @@ jobs:
|
|||||||
- name: Set build tags
|
- name: Set build tags
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0'
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,4 +15,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
/config.d/
|
/config.d/
|
||||||
/venv/
|
/venv/
|
||||||
|
CLAUDE.md
|
||||||
|
AGENTS.md
|
||||||
|
/.claude/
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN set -ex \
|
|||||||
&& export COMMIT=$(git rev-parse --short HEAD) \
|
&& export COMMIT=$(git rev-parse --short HEAD) \
|
||||||
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
&& export VERSION=$(go run ./cmd/internal/read_tag) \
|
||||||
&& go build -v -trimpath -tags \
|
&& go build -v -trimpath -tags \
|
||||||
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale" \
|
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0" \
|
||||||
-o /go/bin/sing-box \
|
-o /go/bin/sing-box \
|
||||||
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
-ldflags "-X \"github.com/sagernet/sing-box/constant.Version=$VERSION\" -s -w -buildid= -checklinkname=0" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -1,6 +1,6 @@
|
|||||||
NAME = sing-box
|
NAME = sing-box
|
||||||
COMMIT = $(shell git rev-parse --short HEAD)
|
COMMIT = $(shell git rev-parse --short HEAD)
|
||||||
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale
|
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,badlinkname,tfogo_checklinkname0
|
||||||
|
|
||||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||||
@@ -38,7 +38,7 @@ fmt:
|
|||||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||||
|
|
||||||
fmt_install:
|
fmt_install:
|
||||||
go install -v mvdan.cc/gofumpt@latest
|
go install -v mvdan.cc/gofumpt@v0.8.0
|
||||||
go install -v github.com/daixiang0/gci@latest
|
go install -v github.com/daixiang0/gci@latest
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@@ -49,7 +49,7 @@ lint:
|
|||||||
GOOS=freebsd golangci-lint run ./...
|
GOOS=freebsd golangci-lint run ./...
|
||||||
|
|
||||||
lint_install:
|
lint_install:
|
||||||
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
@go run ./cmd/internal/protogen
|
@go run ./cmd/internal/protogen
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/process"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
@@ -85,7 +84,7 @@ type InboundContext struct {
|
|||||||
DestinationAddresses []netip.Addr
|
DestinationAddresses []netip.Addr
|
||||||
SourceGeoIPCode string
|
SourceGeoIPCode string
|
||||||
GeoIPCode string
|
GeoIPCode string
|
||||||
ProcessInfo *process.Info
|
ProcessInfo *ConnectionOwner
|
||||||
QueryType uint16
|
QueryType uint16
|
||||||
FakeIP bool
|
FakeIP bool
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
type NetworkManager interface {
|
type NetworkManager interface {
|
||||||
Lifecycle
|
Lifecycle
|
||||||
|
Initialize(ruleSets []RuleSet)
|
||||||
InterfaceFinder() control.InterfaceFinder
|
InterfaceFinder() control.InterfaceFinder
|
||||||
UpdateInterfaces() error
|
UpdateInterfaces() error
|
||||||
DefaultNetworkInterface() *NetworkInterface
|
DefaultNetworkInterface() *NetworkInterface
|
||||||
@@ -24,9 +25,10 @@ type NetworkManager interface {
|
|||||||
NetworkMonitor() tun.NetworkUpdateMonitor
|
NetworkMonitor() tun.NetworkUpdateMonitor
|
||||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||||
PackageManager() tun.PackageManager
|
PackageManager() tun.PackageManager
|
||||||
|
NeedWIFIState() bool
|
||||||
WIFIState() WIFIState
|
WIFIState() WIFIState
|
||||||
ResetNetwork()
|
|
||||||
UpdateWIFIState()
|
UpdateWIFIState()
|
||||||
|
ResetNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkOptions struct {
|
type NetworkOptions struct {
|
||||||
|
|||||||
70
adapter/platform.go
Normal file
70
adapter/platform.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlatformInterface interface {
|
||||||
|
Initialize(networkManager NetworkManager) error
|
||||||
|
|
||||||
|
UsePlatformAutoDetectInterfaceControl() bool
|
||||||
|
AutoDetectInterfaceControl(fd int) error
|
||||||
|
|
||||||
|
UsePlatformInterface() bool
|
||||||
|
OpenInterface(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||||
|
|
||||||
|
UsePlatformDefaultInterfaceMonitor() bool
|
||||||
|
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
||||||
|
|
||||||
|
UsePlatformNetworkInterfaces() bool
|
||||||
|
NetworkInterfaces() ([]NetworkInterface, error)
|
||||||
|
|
||||||
|
UnderNetworkExtension() bool
|
||||||
|
NetworkExtensionIncludeAllNetworks() bool
|
||||||
|
|
||||||
|
ClearDNSCache()
|
||||||
|
RequestPermissionForWIFIState() error
|
||||||
|
ReadWIFIState() WIFIState
|
||||||
|
SystemCertificates() []string
|
||||||
|
|
||||||
|
UsePlatformConnectionOwnerFinder() bool
|
||||||
|
FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error)
|
||||||
|
|
||||||
|
UsePlatformWIFIMonitor() bool
|
||||||
|
|
||||||
|
UsePlatformNotification() bool
|
||||||
|
SendNotification(notification *Notification) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type FindConnectionOwnerRequest struct {
|
||||||
|
IpProtocol int32
|
||||||
|
SourceAddress string
|
||||||
|
SourcePort int32
|
||||||
|
DestinationAddress string
|
||||||
|
DestinationPort int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionOwner struct {
|
||||||
|
ProcessID uint32
|
||||||
|
UserId int32
|
||||||
|
UserName string
|
||||||
|
ProcessPath string
|
||||||
|
AndroidPackageName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Notification struct {
|
||||||
|
Identifier string
|
||||||
|
TypeName string
|
||||||
|
TypeID int32
|
||||||
|
Title string
|
||||||
|
Subtitle string
|
||||||
|
Body string
|
||||||
|
OpenURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemProxyStatus struct {
|
||||||
|
Available bool
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ type Router interface {
|
|||||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
||||||
ConnectionRouterEx
|
ConnectionRouterEx
|
||||||
RuleSet(tag string) (RuleSet, bool)
|
RuleSet(tag string) (RuleSet, bool)
|
||||||
NeedWIFIState() bool
|
|
||||||
Rules() []Rule
|
Rules() []Rule
|
||||||
AppendTracker(tracker ConnectionTracker)
|
AppendTracker(tracker ConnectionTracker)
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
|
|||||||
9
box.go
9
box.go
@@ -22,7 +22,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/dns/transport/local"
|
"github.com/sagernet/sing-box/dns/transport/local"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
"github.com/sagernet/sing-box/experimental/cachefile"
|
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/protocol/direct"
|
"github.com/sagernet/sing-box/protocol/direct"
|
||||||
@@ -139,7 +138,7 @@ func New(options Options) (*Box, error) {
|
|||||||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||||
needV2RayAPI = true
|
needV2RayAPI = true
|
||||||
}
|
}
|
||||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||||
var defaultLogWriter io.Writer
|
var defaultLogWriter io.Writer
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
defaultLogWriter = io.Discard
|
defaultLogWriter = io.Discard
|
||||||
@@ -184,7 +183,7 @@ func New(options Options) (*Box, error) {
|
|||||||
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
|
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
|
||||||
dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions)
|
dnsRouter := dns.NewRouter(ctx, logFactory, dnsOptions)
|
||||||
service.MustRegister[adapter.DNSRouter](ctx, dnsRouter)
|
service.MustRegister[adapter.DNSRouter](ctx, dnsRouter)
|
||||||
networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions)
|
networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions, dnsOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize network manager")
|
return nil, E.Cause(err, "initialize network manager")
|
||||||
}
|
}
|
||||||
@@ -527,3 +526,7 @@ func (s *Box) Inbound() adapter.InboundManager {
|
|||||||
func (s *Box) Outbound() adapter.OutboundManager {
|
func (s *Box) Outbound() adapter.OutboundManager {
|
||||||
return s.outbound
|
return s.outbound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Box) LogFactory() log.Factory {
|
||||||
|
return s.logFactory
|
||||||
|
}
|
||||||
|
|||||||
Submodule clients/android updated: cd8ac376f6...e08fbfcfea
Submodule clients/apple updated: 96bee16bf0...84d8cf1757
@@ -62,7 +62,7 @@ func init() {
|
|||||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid= -checklinkname=0")
|
||||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -checklinkname=0")
|
||||||
|
|
||||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack")
|
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
||||||
macOSTags = append(macOSTags, "with_dhcp")
|
macOSTags = append(macOSTags, "with_dhcp")
|
||||||
memcTags = append(memcTags, "with_tailscale")
|
memcTags = append(memcTags, "with_tailscale")
|
||||||
notMemcTags = append(notMemcTags, "with_low_memory")
|
notMemcTags = append(notMemcTags, "with_low_memory")
|
||||||
@@ -107,10 +107,8 @@ func buildAndroid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
// sharedFlags[3] = sharedFlags[3] + " -checklinkname=0"
|
|
||||||
args = append(args, sharedFlags...)
|
args = append(args, sharedFlags...)
|
||||||
} else {
|
} else {
|
||||||
// debugFlags[1] = debugFlags[1] + " -checklinkname=0"
|
|
||||||
args = append(args, debugFlags...)
|
args = append(args, debugFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func initializeHTTP3Client(instance *box.Box) error {
|
|||||||
}
|
}
|
||||||
http3Client = &http.Client{
|
http3Client = &http.Client{
|
||||||
Transport: &http3.Transport{
|
Transport: &http3.Transport{
|
||||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {
|
||||||
destination := M.ParseSocksaddr(addr)
|
destination := M.ParseSocksaddr(addr)
|
||||||
udpConn, dErr := dialer.DialContext(ctx, N.NetworkUDP, destination)
|
udpConn, dErr := dialer.DialContext(ctx, N.NetworkUDP, destination)
|
||||||
if dErr != nil {
|
if dErr != nil {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
//go:build go1.25 && badlinkname
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
//go:build go1.25 && badlinkname
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
//go:build go1.25 && badlinkname
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !go1.25 || without_badtls
|
//go:build !go1.25 || !badlinkname
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
//go:build go1.25 && badlinkname
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build go1.25 && !without_badtls
|
//go:build go1.25 && badlinkname
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/sagernet/fswatch"
|
"github.com/sagernet/fswatch"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
@@ -36,7 +35,7 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
|||||||
switch options.Store {
|
switch options.Store {
|
||||||
case C.CertificateStoreSystem, "":
|
case C.CertificateStoreSystem, "":
|
||||||
systemPool = x509.NewCertPool()
|
systemPool = x509.NewCertPool()
|
||||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||||
var systemValid bool
|
var systemValid bool
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
for _, cert := range platformInterface.SystemCertificates() {
|
for _, cert := range platformInterface.SystemCertificates() {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/conntrack"
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
"github.com/sagernet/sing-box/common/listener"
|
"github.com/sagernet/sing-box/common/listener"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
@@ -20,6 +19,8 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
|
"github.com/database64128/tfo-go/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -28,8 +29,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DefaultDialer struct {
|
type DefaultDialer struct {
|
||||||
dialer4 tcpDialer
|
dialer4 tfo.Dialer
|
||||||
dialer6 tcpDialer
|
dialer6 tfo.Dialer
|
||||||
udpDialer4 net.Dialer
|
udpDialer4 net.Dialer
|
||||||
udpDialer6 net.Dialer
|
udpDialer6 net.Dialer
|
||||||
udpListener net.ListenConfig
|
udpListener net.ListenConfig
|
||||||
@@ -47,7 +48,7 @@ type DefaultDialer struct {
|
|||||||
|
|
||||||
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDialer, error) {
|
||||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||||
platformInterface := service.FromContext[platform.Interface](ctx)
|
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dialer net.Dialer
|
dialer net.Dialer
|
||||||
@@ -177,19 +178,10 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||||
}
|
}
|
||||||
if options.TCPMultiPath {
|
if options.TCPMultiPath {
|
||||||
if !go121Available {
|
dialer4.SetMultipathTCP(true)
|
||||||
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
|
||||||
}
|
|
||||||
setMultiPathTCP(&dialer4)
|
|
||||||
}
|
|
||||||
tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tcpDialer6, err := newTCPDialer(dialer6, options.TCPFastOpen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
tcpDialer4 := tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen}
|
||||||
|
tcpDialer6 := tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen}
|
||||||
return &DefaultDialer{
|
return &DefaultDialer{
|
||||||
dialer4: tcpDialer4,
|
dialer4: tcpDialer4,
|
||||||
dialer6: tcpDialer6,
|
dialer6: tcpDialer6,
|
||||||
@@ -269,7 +261,7 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
|||||||
}
|
}
|
||||||
var dialer net.Dialer
|
var dialer net.Dialer
|
||||||
if N.NetworkName(network) == N.NetworkTCP {
|
if N.NetworkName(network) == N.NetworkTCP {
|
||||||
dialer = dialerFromTCPDialer(d.dialer4)
|
dialer = d.dialer4.Dialer
|
||||||
} else {
|
} else {
|
||||||
dialer = d.udpDialer4
|
dialer = d.udpDialer4
|
||||||
}
|
}
|
||||||
@@ -317,9 +309,9 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
|||||||
|
|
||||||
func (d *DefaultDialer) DialerForICMPDestination(destination netip.Addr) net.Dialer {
|
func (d *DefaultDialer) DialerForICMPDestination(destination netip.Addr) net.Dialer {
|
||||||
if !destination.Is6() {
|
if !destination.Is6() {
|
||||||
return dialerFromTCPDialer(d.dialer6)
|
return d.dialer6.Dialer
|
||||||
} else {
|
} else {
|
||||||
return dialerFromTCPDialer(d.dialer4)
|
return d.dialer4.Dialer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,18 +348,8 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
|||||||
return trackPacketConn(packetConn, nil)
|
return trackPacketConn(packetConn, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
func (d *DefaultDialer) WireGuardControl() control.Func {
|
||||||
udpListener := d.udpListener
|
return d.udpListener.Control
|
||||||
udpListener.Control = control.Append(udpListener.Control, func(network, address string, conn syscall.RawConn) error {
|
|
||||||
for _, wgControlFn := range WgControlFns {
|
|
||||||
err := wgControlFn(network, address, conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return udpListener.ListenPacket(context.Background(), network, address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
//go:build go1.20
|
|
||||||
|
|
||||||
package dialer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/metacubex/tfo-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tcpDialer = tfo.Dialer
|
|
||||||
|
|
||||||
func newTCPDialer(dialer net.Dialer, tfoEnabled bool) (tcpDialer, error) {
|
|
||||||
return tfo.Dialer{Dialer: dialer, DisableTFO: !tfoEnabled}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dialerFromTCPDialer(dialer tcpDialer) net.Dialer {
|
|
||||||
return dialer.Dialer
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
//go:build go1.21
|
|
||||||
|
|
||||||
package dialer
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
const go121Available = true
|
|
||||||
|
|
||||||
func setMultiPathTCP(dialer *net.Dialer) {
|
|
||||||
dialer.SetMultipathTCP(true)
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//go:build !go1.20
|
|
||||||
|
|
||||||
package dialer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tcpDialer = net.Dialer
|
|
||||||
|
|
||||||
func newTCPDialer(dialer net.Dialer, tfoEnabled bool) (tcpDialer, error) {
|
|
||||||
if tfoEnabled {
|
|
||||||
return dialer, E.New("TCP Fast Open requires go1.20, please recompile your binary.")
|
|
||||||
}
|
|
||||||
return dialer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dialerFromTCPDialer(dialer tcpDialer) net.Dialer {
|
|
||||||
return dialer
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//go:build !go1.21
|
|
||||||
|
|
||||||
package dialer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
const go121Available = false
|
|
||||||
|
|
||||||
func setMultiPathTCP(dialer *net.Dialer) {
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build go1.20
|
|
||||||
|
|
||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -16,7 +14,7 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/metacubex/tfo-go"
|
"github.com/database64128/tfo-go/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type slowOpenConn struct {
|
type slowOpenConn struct {
|
||||||
@@ -32,7 +30,7 @@ type slowOpenConn struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func DialSlowContext(dialer *tfo.Dialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
if dialer.DisableTFO || N.NetworkName(network) != N.NetworkTCP {
|
if dialer.DisableTFO || N.NetworkName(network) != N.NetworkTCP {
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP, N.NetworkUDP:
|
case N.NetworkTCP, N.NetworkUDP:
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
//go:build !go1.20
|
|
||||||
|
|
||||||
package dialer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
func DialSlowContext(dialer *tcpDialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
switch N.NetworkName(network) {
|
|
||||||
case N.NetworkTCP, N.NetworkUDP:
|
|
||||||
return dialer.DialContext(ctx, network, destination.String())
|
|
||||||
default:
|
|
||||||
return dialer.DialContext(ctx, network, destination.AddrString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,9 @@
|
|||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WireGuardListener interface {
|
type WireGuardListener interface {
|
||||||
ListenPacketCompat(network, address string) (net.PacketConn, error)
|
WireGuardControl() control.Func
|
||||||
}
|
}
|
||||||
|
|
||||||
var WgControlFns []control.Func
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
15
common/ktls/ktls_stub_nolinkname.go
Normal file
15
common/ktls/ktls_stub_nolinkname.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build linux && go1.25 && !badlinkname
|
||||||
|
|
||||||
|
package ktls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
||||||
|
return nil, E.New("kTLS requires build flags `badlinkname` and `-ldflags=-checklinkname=0`, please recompile your binary")
|
||||||
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
//go:build !linux || !go1.25 || without_badtls
|
//go:build !linux
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
||||||
return nil, os.ErrInvalid
|
return nil, E.New("kTLS is only supported on Linux")
|
||||||
}
|
}
|
||||||
15
common/ktls/ktls_stub_oldgo.go
Normal file
15
common/ktls/ktls_stub_oldgo.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build linux && !go1.25
|
||||||
|
|
||||||
|
package ktls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
||||||
|
return nil, E.New("kTLS requires Go 1.25 or later, please recompile your binary")
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && go1.25 && !without_badtls
|
//go:build linux && go1.25 && badlinkname
|
||||||
|
|
||||||
package ktls
|
package ktls
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
//go:build go1.21
|
|
||||||
|
|
||||||
package listener
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
const go121Available = true
|
|
||||||
|
|
||||||
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
|
||||||
listenConfig.SetMultipathTCP(true)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//go:build go1.23
|
|
||||||
|
|
||||||
package listener
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
|
||||||
listener.KeepAliveConfig = net.KeepAliveConfig{
|
|
||||||
Enable: true,
|
|
||||||
Idle: idle,
|
|
||||||
Interval: interval,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
//go:build !go1.21
|
|
||||||
|
|
||||||
package listener
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
const go121Available = false
|
|
||||||
|
|
||||||
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//go:build !go1.23
|
|
||||||
|
|
||||||
package listener
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setKeepAliveConfig(listener *net.ListenConfig, idle time.Duration, interval time.Duration) {
|
|
||||||
listener.KeepAlive = idle
|
|
||||||
listener.Control = control.Append(listener.Control, control.SetKeepAlivePeriod(idle, interval))
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
"github.com/metacubex/tfo-go"
|
"github.com/database64128/tfo-go/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||||
@@ -46,13 +46,14 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
|||||||
if keepInterval == 0 {
|
if keepInterval == 0 {
|
||||||
keepInterval = C.TCPKeepAliveInterval
|
keepInterval = C.TCPKeepAliveInterval
|
||||||
}
|
}
|
||||||
setKeepAliveConfig(&listenConfig, keepIdle, keepInterval)
|
listenConfig.KeepAliveConfig = net.KeepAliveConfig{
|
||||||
|
Enable: true,
|
||||||
|
Idle: keepIdle,
|
||||||
|
Interval: keepInterval,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if l.listenOptions.TCPMultiPath {
|
if l.listenOptions.TCPMultiPath {
|
||||||
if !go121Available {
|
listenConfig.SetMultipathTCP(true)
|
||||||
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
|
||||||
}
|
|
||||||
setMultiPathTCP(&listenConfig)
|
|
||||||
}
|
}
|
||||||
if l.tproxy {
|
if l.tproxy {
|
||||||
listenConfig.Control = control.Append(listenConfig.Control, func(network, address string, conn syscall.RawConn) error {
|
listenConfig.Control = control.Append(listenConfig.Control, func(network, address string, conn syscall.RawConn) error {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -12,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Searcher interface {
|
type Searcher interface {
|
||||||
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error)
|
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNotFound = E.New("process not found")
|
var ErrNotFound = E.New("process not found")
|
||||||
@@ -22,15 +23,7 @@ type Config struct {
|
|||||||
PackageManager tun.PackageManager
|
PackageManager tun.PackageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
type Info struct {
|
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
ProcessID uint32
|
|
||||||
ProcessPath string
|
|
||||||
PackageName string
|
|
||||||
User string
|
|
||||||
UserId int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
|
||||||
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -38,7 +31,7 @@ func FindProcessInfo(searcher Searcher, ctx context.Context, network string, sou
|
|||||||
if info.UserId != -1 {
|
if info.UserId != -1 {
|
||||||
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
||||||
if osUser != nil {
|
if osUser != nil {
|
||||||
info.User = osUser.Username
|
info.UserName = osUser.Username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info, nil
|
return info, nil
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,22 +18,22 @@ func NewSearcher(config Config) (Searcher, error) {
|
|||||||
return &androidSearcher{config.PackageManager}, nil
|
return &androidSearcher{config.PackageManager}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
_, uid, err := resolveSocketByNetlink(network, source, destination)
|
_, uid, err := resolveSocketByNetlink(network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded {
|
if sharedPackage, loaded := s.packageManager.SharedPackageByID(uid % 100000); loaded {
|
||||||
return &Info{
|
return &adapter.ConnectionOwner{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
PackageName: sharedPackage,
|
AndroidPackageName: sharedPackage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
||||||
return &Info{
|
return &adapter.ConnectionOwner{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
PackageName: packageName,
|
AndroidPackageName: packageName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return &Info{UserId: int32(uid)}, nil
|
return &adapter.ConnectionOwner{UserId: int32(uid)}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@@ -23,12 +24,12 @@ func NewSearcher(_ Config) (Searcher, error) {
|
|||||||
return &darwinSearcher{}, nil
|
return &darwinSearcher{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
processName, err := findProcessName(network, source.Addr(), int(source.Port()))
|
processName, err := findProcessName(network, source.Addr(), int(source.Port()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Info{ProcessPath: processName, UserId: -1}, nil
|
return &adapter.ConnectionOwner{ProcessPath: processName, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var structSize = func() int {
|
var structSize = func() int {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ func NewSearcher(config Config) (Searcher, error) {
|
|||||||
return &linuxSearcher{config.Logger}, nil
|
return &linuxSearcher{config.Logger}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
inode, uid, err := resolveSocketByNetlink(network, source, destination)
|
inode, uid, err := resolveSocketByNetlink(network, source, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -28,7 +29,7 @@ func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, sou
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.DebugContext(ctx, "find process path: ", err)
|
s.logger.DebugContext(ctx, "find process path: ", err)
|
||||||
}
|
}
|
||||||
return &Info{
|
return &adapter.ConnectionOwner{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
ProcessPath: processPath,
|
ProcessPath: processPath,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/winiphlpapi"
|
"github.com/sagernet/sing/common/winiphlpapi"
|
||||||
|
|
||||||
@@ -27,16 +28,16 @@ func initWin32API() error {
|
|||||||
return winiphlpapi.LoadExtendedTable()
|
return winiphlpapi.LoadExtendedTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
pid, err := winiphlpapi.FindPid(network, source)
|
pid, err := winiphlpapi.FindPid(network, source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
path, err := getProcessPath(pid)
|
path, err := getProcessPath(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &Info{ProcessID: pid, UserId: -1}, err
|
return &adapter.ConnectionOwner{ProcessID: pid, UserId: -1}, err
|
||||||
}
|
}
|
||||||
return &Info{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
return &adapter.ConnectionOwner{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProcessPath(pid uint32) (string, error) {
|
func getProcessPath(pid uint32) (string, error) {
|
||||||
|
|||||||
9
common/settings/wifi.go
Normal file
9
common/settings/wifi.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import "github.com/sagernet/sing-box/adapter"
|
||||||
|
|
||||||
|
type WIFIMonitor interface {
|
||||||
|
ReadWIFIState() adapter.WIFIState
|
||||||
|
Start() error
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
46
common/settings/wifi_linux.go
Normal file
46
common/settings/wifi_linux.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinuxWIFIMonitor struct {
|
||||||
|
monitor WIFIMonitor
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||||
|
monitors := []func(func(adapter.WIFIState)) (WIFIMonitor, error){
|
||||||
|
newNetworkManagerMonitor,
|
||||||
|
newIWDMonitor,
|
||||||
|
newWpaSupplicantMonitor,
|
||||||
|
newConnManMonitor,
|
||||||
|
}
|
||||||
|
var errors []error
|
||||||
|
for _, factory := range monitors {
|
||||||
|
monitor, err := factory(callback)
|
||||||
|
if err == nil {
|
||||||
|
return &LinuxWIFIMonitor{monitor: monitor}, nil
|
||||||
|
}
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
return nil, E.Cause(E.Errors(errors...), "no supported WIFI manager found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LinuxWIFIMonitor) ReadWIFIState() adapter.WIFIState {
|
||||||
|
return m.monitor.ReadWIFIState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LinuxWIFIMonitor) Start() error {
|
||||||
|
if m.monitor != nil {
|
||||||
|
return m.monitor.Start()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LinuxWIFIMonitor) Close() error {
|
||||||
|
if m.monitor != nil {
|
||||||
|
return m.monitor.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
160
common/settings/wifi_linux_connman.go
Normal file
160
common/settings/wifi_linux_connman.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type connmanMonitor struct {
|
||||||
|
conn *dbus.Conn
|
||||||
|
callback func(adapter.WIFIState)
|
||||||
|
cancel context.CancelFunc
|
||||||
|
signalChan chan *dbus.Signal
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnManMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||||
|
conn, err := dbus.ConnectSystemBus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmObj := conn.Object("net.connman", "/")
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
call := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0)
|
||||||
|
if call.Err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, call.Err
|
||||||
|
}
|
||||||
|
return &connmanMonitor{conn: conn, callback: callback}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *connmanMonitor) ReadWIFIState() adapter.WIFIState {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmObj := m.conn.Object("net.connman", "/")
|
||||||
|
var services []interface{}
|
||||||
|
err := cmObj.CallWithContext(ctx, "net.connman.Manager.GetServices", 0).Store(&services)
|
||||||
|
if err != nil {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range services {
|
||||||
|
servicePair, ok := service.([]interface{})
|
||||||
|
if !ok || len(servicePair) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceProps, ok := servicePair[1].(map[string]dbus.Variant)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
typeVariant, hasType := serviceProps["Type"]
|
||||||
|
if !hasType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
serviceType, ok := typeVariant.Value().(string)
|
||||||
|
if !ok || serviceType != "wifi" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
stateVariant, hasState := serviceProps["State"]
|
||||||
|
if !hasState {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
state, ok := stateVariant.Value().(string)
|
||||||
|
if !ok || (state != "online" && state != "ready") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nameVariant, hasName := serviceProps["Name"]
|
||||||
|
if !hasName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ssid, ok := nameVariant.Value().(string)
|
||||||
|
if !ok || ssid == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bssidVariant, hasBSSID := serviceProps["BSSID"]
|
||||||
|
if !hasBSSID {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
bssid, ok := bssidVariant.Value().(string)
|
||||||
|
if !ok {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter.WIFIState{
|
||||||
|
SSID: ssid,
|
||||||
|
BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *connmanMonitor) Start() error {
|
||||||
|
if m.callback == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
m.cancel = cancel
|
||||||
|
|
||||||
|
m.signalChan = make(chan *dbus.Signal, 10)
|
||||||
|
m.conn.Signal(m.signalChan)
|
||||||
|
|
||||||
|
err := m.conn.AddMatchSignal(
|
||||||
|
dbus.WithMatchInterface("net.connman.Service"),
|
||||||
|
dbus.WithMatchSender("net.connman"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
go m.monitorSignals(ctx, m.signalChan, state)
|
||||||
|
m.callback(state)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *connmanMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case signal, ok := <-signalChan:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if signal.Name == "PropertyChanged" {
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
if state != lastState {
|
||||||
|
lastState = state
|
||||||
|
m.callback(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *connmanMonitor) Close() error {
|
||||||
|
if m.cancel != nil {
|
||||||
|
m.cancel()
|
||||||
|
}
|
||||||
|
if m.signalChan != nil {
|
||||||
|
m.conn.RemoveSignal(m.signalChan)
|
||||||
|
close(m.signalChan)
|
||||||
|
}
|
||||||
|
if m.conn != nil {
|
||||||
|
return m.conn.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
184
common/settings/wifi_linux_iwd.go
Normal file
184
common/settings/wifi_linux_iwd.go
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type iwdMonitor struct {
|
||||||
|
conn *dbus.Conn
|
||||||
|
callback func(adapter.WIFIState)
|
||||||
|
cancel context.CancelFunc
|
||||||
|
signalChan chan *dbus.Signal
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIWDMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||||
|
conn, err := dbus.ConnectSystemBus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
iwdObj := conn.Object("net.connman.iwd", "/")
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
call := iwdObj.CallWithContext(ctx, "org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0)
|
||||||
|
if call.Err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, call.Err
|
||||||
|
}
|
||||||
|
return &iwdMonitor{conn: conn, callback: callback}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *iwdMonitor) ReadWIFIState() adapter.WIFIState {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
iwdObj := m.conn.Object("net.connman.iwd", "/")
|
||||||
|
var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
|
||||||
|
err := iwdObj.CallWithContext(ctx, "org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0).Store(&objects)
|
||||||
|
if err != nil {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, interfaces := range objects {
|
||||||
|
stationProps, hasStation := interfaces["net.connman.iwd.Station"]
|
||||||
|
if !hasStation {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
stateVariant, hasState := stationProps["State"]
|
||||||
|
if !hasState {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
state, ok := stateVariant.Value().(string)
|
||||||
|
if !ok || state != "connected" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedNetworkVariant, hasNetwork := stationProps["ConnectedNetwork"]
|
||||||
|
if !hasNetwork {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
networkPath, ok := connectedNetworkVariant.Value().(dbus.ObjectPath)
|
||||||
|
if !ok || networkPath == "/" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
networkInterfaces, hasNetworkPath := objects[networkPath]
|
||||||
|
if !hasNetworkPath {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
networkProps, hasNetworkInterface := networkInterfaces["net.connman.iwd.Network"]
|
||||||
|
if !hasNetworkInterface {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nameVariant, hasName := networkProps["Name"]
|
||||||
|
if !hasName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ssid, ok := nameVariant.Value().(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedBSSVariant, hasBSS := stationProps["ConnectedAccessPoint"]
|
||||||
|
if !hasBSS {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
bssPath, ok := connectedBSSVariant.Value().(dbus.ObjectPath)
|
||||||
|
if !ok || bssPath == "/" {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
|
||||||
|
bssInterfaces, hasBSSPath := objects[bssPath]
|
||||||
|
if !hasBSSPath {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
|
||||||
|
bssProps, hasBSSInterface := bssInterfaces["net.connman.iwd.BasicServiceSet"]
|
||||||
|
if !hasBSSInterface {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
|
||||||
|
addressVariant, hasAddress := bssProps["Address"]
|
||||||
|
if !hasAddress {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
bssid, ok := addressVariant.Value().(string)
|
||||||
|
if !ok {
|
||||||
|
return adapter.WIFIState{SSID: ssid}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter.WIFIState{
|
||||||
|
SSID: ssid,
|
||||||
|
BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *iwdMonitor) Start() error {
|
||||||
|
if m.callback == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
m.cancel = cancel
|
||||||
|
|
||||||
|
m.signalChan = make(chan *dbus.Signal, 10)
|
||||||
|
m.conn.Signal(m.signalChan)
|
||||||
|
|
||||||
|
err := m.conn.AddMatchSignal(
|
||||||
|
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
||||||
|
dbus.WithMatchSender("net.connman.iwd"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
go m.monitorSignals(ctx, m.signalChan, state)
|
||||||
|
m.callback(state)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *iwdMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case signal, ok := <-signalChan:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
if state != lastState {
|
||||||
|
lastState = state
|
||||||
|
m.callback(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *iwdMonitor) Close() error {
|
||||||
|
if m.cancel != nil {
|
||||||
|
m.cancel()
|
||||||
|
}
|
||||||
|
if m.signalChan != nil {
|
||||||
|
m.conn.RemoveSignal(m.signalChan)
|
||||||
|
close(m.signalChan)
|
||||||
|
}
|
||||||
|
if m.conn != nil {
|
||||||
|
return m.conn.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
157
common/settings/wifi_linux_nm.go
Normal file
157
common/settings/wifi_linux_nm.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type networkManagerMonitor struct {
|
||||||
|
conn *dbus.Conn
|
||||||
|
callback func(adapter.WIFIState)
|
||||||
|
cancel context.CancelFunc
|
||||||
|
signalChan chan *dbus.Signal
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNetworkManagerMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||||
|
conn, err := dbus.ConnectSystemBus()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nmObj := conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
var state uint32
|
||||||
|
err = nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "State").Store(&state)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &networkManagerMonitor{conn: conn, callback: callback}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *networkManagerMonitor) ReadWIFIState() adapter.WIFIState {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
nmObj := m.conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
|
||||||
|
|
||||||
|
var primaryConnectionPath dbus.ObjectPath
|
||||||
|
err := nmObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "PrimaryConnection").Store(&primaryConnectionPath)
|
||||||
|
if err != nil || primaryConnectionPath == "/" {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
connObj := m.conn.Object("org.freedesktop.NetworkManager", primaryConnectionPath)
|
||||||
|
|
||||||
|
var devicePaths []dbus.ObjectPath
|
||||||
|
err = connObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Connection.Active", "Devices").Store(&devicePaths)
|
||||||
|
if err != nil || len(devicePaths) == 0 {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, devicePath := range devicePaths {
|
||||||
|
deviceObj := m.conn.Object("org.freedesktop.NetworkManager", devicePath)
|
||||||
|
|
||||||
|
var deviceType uint32
|
||||||
|
err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device", "DeviceType").Store(&deviceType)
|
||||||
|
if err != nil || deviceType != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessPointPath dbus.ObjectPath
|
||||||
|
err = deviceObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Device.Wireless", "ActiveAccessPoint").Store(&accessPointPath)
|
||||||
|
if err != nil || accessPointPath == "/" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
apObj := m.conn.Object("org.freedesktop.NetworkManager", accessPointPath)
|
||||||
|
|
||||||
|
var ssidBytes []byte
|
||||||
|
err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "Ssid").Store(&ssidBytes)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var hwAddress string
|
||||||
|
err = apObj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.AccessPoint", "HwAddress").Store(&hwAddress)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ssid := strings.TrimSpace(string(ssidBytes))
|
||||||
|
if ssid == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter.WIFIState{
|
||||||
|
SSID: ssid,
|
||||||
|
BSSID: strings.ToUpper(strings.ReplaceAll(hwAddress, ":", "")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *networkManagerMonitor) Start() error {
|
||||||
|
if m.callback == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
m.cancel = cancel
|
||||||
|
|
||||||
|
m.signalChan = make(chan *dbus.Signal, 10)
|
||||||
|
m.conn.Signal(m.signalChan)
|
||||||
|
|
||||||
|
err := m.conn.AddMatchSignal(
|
||||||
|
dbus.WithMatchSender("org.freedesktop.NetworkManager"),
|
||||||
|
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
go m.monitorSignals(ctx, m.signalChan, state)
|
||||||
|
m.callback(state)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *networkManagerMonitor) monitorSignals(ctx context.Context, signalChan chan *dbus.Signal, lastState adapter.WIFIState) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case signal, ok := <-signalChan:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if signal.Name == "org.freedesktop.DBus.Properties.PropertiesChanged" {
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
if state != lastState {
|
||||||
|
lastState = state
|
||||||
|
m.callback(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *networkManagerMonitor) Close() error {
|
||||||
|
if m.cancel != nil {
|
||||||
|
m.cancel()
|
||||||
|
}
|
||||||
|
if m.signalChan != nil {
|
||||||
|
m.conn.RemoveSignal(m.signalChan)
|
||||||
|
close(m.signalChan)
|
||||||
|
}
|
||||||
|
if m.conn != nil {
|
||||||
|
return m.conn.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
179
common/settings/wifi_linux_wpa.go
Normal file
179
common/settings/wifi_linux_wpa.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wpaSupplicantMonitor struct {
|
||||||
|
socketPath string
|
||||||
|
callback func(adapter.WIFIState)
|
||||||
|
cancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWpaSupplicantMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||||
|
socketDirs := []string{"/var/run/wpa_supplicant", "/run/wpa_supplicant"}
|
||||||
|
for _, socketDir := range socketDirs {
|
||||||
|
entries, err := os.ReadDir(socketDir)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() || entry.Name() == "." || entry.Name() == ".." {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
socketPath := filepath.Join(socketDir, entry.Name())
|
||||||
|
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
|
||||||
|
remoteAddr := &net.UnixAddr{Name: socketPath, Net: "unixgram"}
|
||||||
|
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
return &wpaSupplicantMonitor{socketPath: socketPath, callback: callback}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wpaSupplicantMonitor) ReadWIFIState() adapter.WIFIState {
|
||||||
|
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-%d", os.Getpid()), Net: "unixgram"}
|
||||||
|
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
||||||
|
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
conn.SetDeadline(time.Now().Add(3 * time.Second))
|
||||||
|
|
||||||
|
status, err := m.sendCommand(conn, "STATUS")
|
||||||
|
if err != nil {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ssid, bssid string
|
||||||
|
var connected bool
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(status))
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "wpa_state=") {
|
||||||
|
state := strings.TrimPrefix(line, "wpa_state=")
|
||||||
|
connected = state == "COMPLETED"
|
||||||
|
} else if strings.HasPrefix(line, "ssid=") {
|
||||||
|
ssid = strings.TrimPrefix(line, "ssid=")
|
||||||
|
} else if strings.HasPrefix(line, "bssid=") {
|
||||||
|
bssid = strings.TrimPrefix(line, "bssid=")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !connected || ssid == "" {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter.WIFIState{
|
||||||
|
SSID: ssid,
|
||||||
|
BSSID: strings.ToUpper(strings.ReplaceAll(bssid, ":", "")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wpaSupplicantMonitor) sendCommand(conn *net.UnixConn, command string) (string, error) {
|
||||||
|
_, err := conn.Write([]byte(command + "\n"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := string(buf[:n])
|
||||||
|
if strings.HasPrefix(response, "FAIL") {
|
||||||
|
return "", os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(response), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wpaSupplicantMonitor) Start() error {
|
||||||
|
if m.callback == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
m.cancel = cancel
|
||||||
|
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
go m.monitorEvents(ctx, state)
|
||||||
|
m.callback(state)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wpaSupplicantMonitor) monitorEvents(ctx context.Context, lastState adapter.WIFIState) {
|
||||||
|
var consecutiveErrors int
|
||||||
|
|
||||||
|
localAddr := &net.UnixAddr{Name: fmt.Sprintf("@sing-box-wpa-mon-%d", os.Getpid()), Net: "unixgram"}
|
||||||
|
remoteAddr := &net.UnixAddr{Name: m.socketPath, Net: "unixgram"}
|
||||||
|
conn, err := net.DialUnix("unixgram", localAddr, remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
_, err = conn.Write([]byte("ATTACH\n"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 4096)
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil || !strings.HasPrefix(string(buf[:n]), "OK") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.SetReadDeadline(time.Now().Add(30 * time.Second))
|
||||||
|
n, err := conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
consecutiveErrors++
|
||||||
|
if consecutiveErrors > 10 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
consecutiveErrors = 0
|
||||||
|
|
||||||
|
msg := string(buf[:n])
|
||||||
|
if strings.Contains(msg, "CTRL-EVENT-CONNECTED") || strings.Contains(msg, "CTRL-EVENT-DISCONNECTED") {
|
||||||
|
state := m.ReadWIFIState()
|
||||||
|
if state != lastState {
|
||||||
|
lastState = state
|
||||||
|
m.callback(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wpaSupplicantMonitor) Close() error {
|
||||||
|
if m.cancel != nil {
|
||||||
|
m.cancel()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
27
common/settings/wifi_stub.go
Normal file
27
common/settings/wifi_stub.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package settings
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stubWIFIMonitor struct{}
|
||||||
|
|
||||||
|
func NewWIFIMonitor(callback func(adapter.WIFIState)) (WIFIMonitor, error) {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubWIFIMonitor) ReadWIFIState() adapter.WIFIState {
|
||||||
|
return adapter.WIFIState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubWIFIMonitor) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *stubWIFIMonitor) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -119,21 +119,19 @@ func (d *defaultDialer) dialContext(ctx context.Context, destination M.Socksaddr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tlsConn, err := ClientHandshake(ctx, conn, d.config)
|
tlsConn, err := aTLS.ClientHandshake(ctx, conn, d.config)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
return tlsConn, nil
|
conn.Close()
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
if echRetry {
|
|
||||||
var echErr *tls.ECHRejectionError
|
var echErr *tls.ECHRejectionError
|
||||||
if errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 {
|
if echRetry && errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 {
|
||||||
if echConfig, isECH := d.config.(ECHCapableConfig); isECH {
|
if echConfig, isECH := d.config.(ECHCapableConfig); isECH {
|
||||||
echConfig.SetECHConfigList(echErr.RetryConfigList)
|
echConfig.SetECHConfigList(echErr.RetryConfigList)
|
||||||
|
return d.dialContext(ctx, destination, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return d.dialContext(ctx, destination, false)
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, err
|
return tlsConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *defaultDialer) Upstream() any {
|
func (d *defaultDialer) Upstream() any {
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
//go:build !go1.24
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseECHClientConfig(ctx context.Context, clientConfig ECHCapableConfig, options option.OutboundTLSOptions) (Config, error) {
|
|
||||||
return nil, E.New("ECH requires go1.24, please recompile your binary.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions, tlsConfig *tls.Config, echKeyPath *string) error {
|
|
||||||
return E.New("ECH requires go1.24, please recompile your binary.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *STDServerConfig) setECHServerConfig(echKey []byte) error {
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
@@ -68,7 +68,10 @@ func NewRealityServer(ctx context.Context, logger log.ContextLogger, options opt
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
if len(options.CurvePreferences) > 0 {
|
||||||
|
return nil, E.New("curve preferences is unavailable in reality")
|
||||||
|
}
|
||||||
|
if len(options.Certificate) > 0 || options.CertificatePath != "" || len(options.ClientCertificatePublicKeySHA256) > 0 {
|
||||||
return nil, E.New("certificate is unavailable in reality")
|
return nil, E.New("certificate is unavailable in reality")
|
||||||
}
|
}
|
||||||
if len(options.Key) > 0 || options.KeyPath != "" {
|
if len(options.Key) > 0 || options.KeyPath != "" {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -108,6 +111,15 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(options.CertificatePublicKeySHA256) > 0 {
|
||||||
|
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
||||||
|
return nil, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
|
||||||
|
}
|
||||||
|
tlsConfig.InsecureSkipVerify = true
|
||||||
|
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
return verifyPublicKeySHA256(options.CertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(options.ALPN) > 0 {
|
if len(options.ALPN) > 0 {
|
||||||
tlsConfig.NextProtos = options.ALPN
|
tlsConfig.NextProtos = options.ALPN
|
||||||
}
|
}
|
||||||
@@ -137,6 +149,9 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, curve := range options.CurvePreferences {
|
||||||
|
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curve))
|
||||||
|
}
|
||||||
var certificate []byte
|
var certificate []byte
|
||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
@@ -175,3 +190,22 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func verifyPublicKeySHA256(knownHashValues [][]byte, rawCerts [][]byte, timeFunc func() time.Time) error {
|
||||||
|
leafCertificate, err := x509.ParseCertificate(rawCerts[0])
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "failed to parse leaf certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyBytes, err := x509.MarshalPKIXPublicKey(leafCertificate.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "failed to marshal public key")
|
||||||
|
}
|
||||||
|
hashValue := sha256.Sum256(pubKeyBytes)
|
||||||
|
for _, value := range knownHashValues {
|
||||||
|
if bytes.Equal(value, hashValue[:]) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return E.New("unrecognized remote public key: ", base64.StdEncoding.EncodeToString(hashValue[:]))
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -22,16 +23,17 @@ import (
|
|||||||
var errInsecureUnused = E.New("tls: insecure unused")
|
var errInsecureUnused = E.New("tls: insecure unused")
|
||||||
|
|
||||||
type STDServerConfig struct {
|
type STDServerConfig struct {
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
config *tls.Config
|
config *tls.Config
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
acmeService adapter.SimpleLifecycle
|
acmeService adapter.SimpleLifecycle
|
||||||
certificate []byte
|
certificate []byte
|
||||||
key []byte
|
key []byte
|
||||||
certificatePath string
|
certificatePath string
|
||||||
keyPath string
|
keyPath string
|
||||||
echKeyPath string
|
clientCertificatePath []string
|
||||||
watcher *fswatch.Watcher
|
echKeyPath string
|
||||||
|
watcher *fswatch.Watcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) ServerName() string {
|
func (c *STDServerConfig) ServerName() string {
|
||||||
@@ -111,6 +113,9 @@ func (c *STDServerConfig) startWatcher() error {
|
|||||||
if c.echKeyPath != "" {
|
if c.echKeyPath != "" {
|
||||||
watchPath = append(watchPath, c.echKeyPath)
|
watchPath = append(watchPath, c.echKeyPath)
|
||||||
}
|
}
|
||||||
|
if len(c.clientCertificatePath) > 0 {
|
||||||
|
watchPath = append(watchPath, c.clientCertificatePath...)
|
||||||
|
}
|
||||||
if len(watchPath) == 0 {
|
if len(watchPath) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -159,6 +164,30 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
c.config = config
|
c.config = config
|
||||||
c.access.Unlock()
|
c.access.Unlock()
|
||||||
c.logger.Info("reloaded TLS certificate")
|
c.logger.Info("reloaded TLS certificate")
|
||||||
|
} else if common.Contains(c.clientCertificatePath, path) {
|
||||||
|
clientCertificateCA := x509.NewCertPool()
|
||||||
|
var reloaded bool
|
||||||
|
for _, certPath := range c.clientCertificatePath {
|
||||||
|
content, err := os.ReadFile(certPath)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error(E.Cause(err, "reload certificate from ", c.clientCertificatePath))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
||||||
|
c.logger.Error(E.New("invalid client certificate file: ", certPath))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
reloaded = true
|
||||||
|
}
|
||||||
|
if !reloaded {
|
||||||
|
return E.New("client certificates is empty")
|
||||||
|
}
|
||||||
|
c.access.Lock()
|
||||||
|
config := c.config.Clone()
|
||||||
|
config.ClientCAs = clientCertificateCA
|
||||||
|
c.config = config
|
||||||
|
c.access.Unlock()
|
||||||
|
c.logger.Info("reloaded client certificates")
|
||||||
} else if path == c.echKeyPath {
|
} else if path == c.echKeyPath {
|
||||||
echKey, err := os.ReadFile(c.echKeyPath)
|
echKey, err := os.ReadFile(c.echKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -235,8 +264,14 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var certificate []byte
|
for _, curveID := range options.CurvePreferences {
|
||||||
var key []byte
|
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curveID))
|
||||||
|
}
|
||||||
|
tlsConfig.ClientAuth = tls.ClientAuthType(options.ClientAuthentication)
|
||||||
|
var (
|
||||||
|
certificate []byte
|
||||||
|
key []byte
|
||||||
|
)
|
||||||
if acmeService == nil {
|
if acmeService == nil {
|
||||||
if len(options.Certificate) > 0 {
|
if len(options.Certificate) > 0 {
|
||||||
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
certificate = []byte(strings.Join(options.Certificate, "\n"))
|
||||||
@@ -278,6 +313,43 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
tlsConfig.Certificates = []tls.Certificate{keyPair}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(options.ClientCertificate) > 0 || len(options.ClientCertificatePath) > 0 {
|
||||||
|
if tlsConfig.ClientAuth == tls.NoClientCert {
|
||||||
|
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {
|
||||||
|
if len(options.ClientCertificate) > 0 {
|
||||||
|
clientCertificateCA := x509.NewCertPool()
|
||||||
|
if !clientCertificateCA.AppendCertsFromPEM([]byte(strings.Join(options.ClientCertificate, "\n"))) {
|
||||||
|
return nil, E.New("invalid client certificate strings")
|
||||||
|
}
|
||||||
|
tlsConfig.ClientCAs = clientCertificateCA
|
||||||
|
} else if len(options.ClientCertificatePath) > 0 {
|
||||||
|
clientCertificateCA := x509.NewCertPool()
|
||||||
|
for _, path := range options.ClientCertificatePath {
|
||||||
|
content, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "read client certificate from ", path)
|
||||||
|
}
|
||||||
|
if !clientCertificateCA.AppendCertsFromPEM(content) {
|
||||||
|
return nil, E.New("invalid client certificate file: ", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tlsConfig.ClientCAs = clientCertificateCA
|
||||||
|
} else if len(options.ClientCertificatePublicKeySHA256) > 0 {
|
||||||
|
if tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {
|
||||||
|
tlsConfig.ClientAuth = tls.RequireAnyClientCert
|
||||||
|
} else if tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven {
|
||||||
|
tlsConfig.ClientAuth = tls.RequestClientCert
|
||||||
|
}
|
||||||
|
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
return verifyPublicKeySHA256(options.ClientCertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, E.New("missing client_certificate, client_certificate_path or client_certificate_public_key_sha256 for client authentication")
|
||||||
|
}
|
||||||
|
}
|
||||||
var echKeyPath string
|
var echKeyPath string
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath)
|
err = parseECHServerConfig(ctx, options, tlsConfig, &echKeyPath)
|
||||||
@@ -286,14 +358,15 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
serverConfig := &STDServerConfig{
|
serverConfig := &STDServerConfig{
|
||||||
config: tlsConfig,
|
config: tlsConfig,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
acmeService: acmeService,
|
acmeService: acmeService,
|
||||||
certificate: certificate,
|
certificate: certificate,
|
||||||
key: key,
|
key: key,
|
||||||
certificatePath: options.CertificatePath,
|
certificatePath: options.CertificatePath,
|
||||||
keyPath: options.KeyPath,
|
clientCertificatePath: options.ClientCertificatePath,
|
||||||
echKeyPath: echKeyPath,
|
keyPath: options.KeyPath,
|
||||||
|
echKeyPath: echKeyPath,
|
||||||
}
|
}
|
||||||
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||||
serverConfig.access.Lock()
|
serverConfig.access.Lock()
|
||||||
|
|||||||
@@ -167,6 +167,15 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
}
|
}
|
||||||
tlsConfig.InsecureServerNameToVerify = serverName
|
tlsConfig.InsecureServerNameToVerify = serverName
|
||||||
}
|
}
|
||||||
|
if len(options.CertificatePublicKeySHA256) > 0 {
|
||||||
|
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
||||||
|
return nil, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
|
||||||
|
}
|
||||||
|
tlsConfig.InsecureSkipVerify = true
|
||||||
|
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
return verifyPublicKeySHA256(options.CertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(options.ALPN) > 0 {
|
if len(options.ALPN) > 0 {
|
||||||
tlsConfig.NextProtos = options.ALPN
|
tlsConfig.NextProtos = options.ALPN
|
||||||
}
|
}
|
||||||
|
|||||||
29
daemon/deprecated.go
Normal file
29
daemon/deprecated.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ deprecated.Manager = (*deprecatedManager)(nil)
|
||||||
|
|
||||||
|
type deprecatedManager struct {
|
||||||
|
access sync.Mutex
|
||||||
|
notes []deprecated.Note
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *deprecatedManager) ReportDeprecated(feature deprecated.Note) {
|
||||||
|
m.access.Lock()
|
||||||
|
defer m.access.Unlock()
|
||||||
|
m.notes = common.Uniq(append(m.notes, feature))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *deprecatedManager) Get() []deprecated.Note {
|
||||||
|
m.access.Lock()
|
||||||
|
defer m.access.Unlock()
|
||||||
|
notes := m.notes
|
||||||
|
m.notes = nil
|
||||||
|
return notes
|
||||||
|
}
|
||||||
702
daemon/helper.pb.go
Normal file
702
daemon/helper.pb.go
Normal file
@@ -0,0 +1,702 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubscribeHelperRequestRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
AcceptGetWIFIStateRequests bool `protobuf:"varint,1,opt,name=acceptGetWIFIStateRequests,proto3" json:"acceptGetWIFIStateRequests,omitempty"`
|
||||||
|
AcceptFindConnectionOwnerRequests bool `protobuf:"varint,2,opt,name=acceptFindConnectionOwnerRequests,proto3" json:"acceptFindConnectionOwnerRequests,omitempty"`
|
||||||
|
AcceptSendNotificationRequests bool `protobuf:"varint,3,opt,name=acceptSendNotificationRequests,proto3" json:"acceptSendNotificationRequests,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) Reset() {
|
||||||
|
*x = SubscribeHelperRequestRequest{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SubscribeHelperRequestRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use SubscribeHelperRequestRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SubscribeHelperRequestRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) GetAcceptGetWIFIStateRequests() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.AcceptGetWIFIStateRequests
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) GetAcceptFindConnectionOwnerRequests() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.AcceptFindConnectionOwnerRequests
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SubscribeHelperRequestRequest) GetAcceptSendNotificationRequests() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.AcceptSendNotificationRequests
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
// Types that are valid to be assigned to Request:
|
||||||
|
//
|
||||||
|
// *HelperRequest_GetWIFIState
|
||||||
|
// *HelperRequest_FindConnectionOwner
|
||||||
|
// *HelperRequest_SendNotification
|
||||||
|
Request isHelperRequest_Request `protobuf_oneof:"request"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) Reset() {
|
||||||
|
*x = HelperRequest{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HelperRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[1]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HelperRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HelperRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetRequest() isHelperRequest_Request {
|
||||||
|
if x != nil {
|
||||||
|
return x.Request
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetGetWIFIState() *emptypb.Empty {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Request.(*HelperRequest_GetWIFIState); ok {
|
||||||
|
return x.GetWIFIState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetFindConnectionOwner() *FindConnectionOwnerRequest {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Request.(*HelperRequest_FindConnectionOwner); ok {
|
||||||
|
return x.FindConnectionOwner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperRequest) GetSendNotification() *Notification {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Request.(*HelperRequest_SendNotification); ok {
|
||||||
|
return x.SendNotification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type isHelperRequest_Request interface {
|
||||||
|
isHelperRequest_Request()
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest_GetWIFIState struct {
|
||||||
|
GetWIFIState *emptypb.Empty `protobuf:"bytes,2,opt,name=getWIFIState,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest_FindConnectionOwner struct {
|
||||||
|
FindConnectionOwner *FindConnectionOwnerRequest `protobuf:"bytes,3,opt,name=findConnectionOwner,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRequest_SendNotification struct {
|
||||||
|
SendNotification *Notification `protobuf:"bytes,4,opt,name=sendNotification,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperRequest_GetWIFIState) isHelperRequest_Request() {}
|
||||||
|
|
||||||
|
func (*HelperRequest_FindConnectionOwner) isHelperRequest_Request() {}
|
||||||
|
|
||||||
|
func (*HelperRequest_SendNotification) isHelperRequest_Request() {}
|
||||||
|
|
||||||
|
type FindConnectionOwnerRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
IpProtocol int32 `protobuf:"varint,1,opt,name=ipProtocol,proto3" json:"ipProtocol,omitempty"`
|
||||||
|
SourceAddress string `protobuf:"bytes,2,opt,name=sourceAddress,proto3" json:"sourceAddress,omitempty"`
|
||||||
|
SourcePort int32 `protobuf:"varint,3,opt,name=sourcePort,proto3" json:"sourcePort,omitempty"`
|
||||||
|
DestinationAddress string `protobuf:"bytes,4,opt,name=destinationAddress,proto3" json:"destinationAddress,omitempty"`
|
||||||
|
DestinationPort int32 `protobuf:"varint,5,opt,name=destinationPort,proto3" json:"destinationPort,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) Reset() {
|
||||||
|
*x = FindConnectionOwnerRequest{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FindConnectionOwnerRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[2]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use FindConnectionOwnerRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*FindConnectionOwnerRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetIpProtocol() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.IpProtocol
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetSourceAddress() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.SourceAddress
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetSourcePort() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.SourcePort
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetDestinationAddress() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DestinationAddress
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *FindConnectionOwnerRequest) GetDestinationPort() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.DestinationPort
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Notification struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
|
||||||
|
TypeName string `protobuf:"bytes,2,opt,name=typeName,proto3" json:"typeName,omitempty"`
|
||||||
|
TypeId int32 `protobuf:"varint,3,opt,name=typeId,proto3" json:"typeId,omitempty"`
|
||||||
|
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
|
||||||
|
Subtitle string `protobuf:"bytes,5,opt,name=subtitle,proto3" json:"subtitle,omitempty"`
|
||||||
|
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
|
||||||
|
OpenURL string `protobuf:"bytes,7,opt,name=openURL,proto3" json:"openURL,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) Reset() {
|
||||||
|
*x = Notification{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Notification) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Notification) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[3]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Notification.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Notification) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetIdentifier() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Identifier
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetTypeName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.TypeName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetTypeId() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.TypeId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetTitle() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Title
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetSubtitle() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Subtitle
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetBody() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Body
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Notification) GetOpenURL() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.OpenURL
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
// Types that are valid to be assigned to Response:
|
||||||
|
//
|
||||||
|
// *HelperResponse_WifiState
|
||||||
|
// *HelperResponse_Error
|
||||||
|
// *HelperResponse_ConnectionOwner
|
||||||
|
Response isHelperResponse_Response `protobuf_oneof:"response"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) Reset() {
|
||||||
|
*x = HelperResponse{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HelperResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[4]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HelperResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HelperResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetResponse() isHelperResponse_Response {
|
||||||
|
if x != nil {
|
||||||
|
return x.Response
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetWifiState() *WIFIState {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Response.(*HelperResponse_WifiState); ok {
|
||||||
|
return x.WifiState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetError() string {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Response.(*HelperResponse_Error); ok {
|
||||||
|
return x.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HelperResponse) GetConnectionOwner() *ConnectionOwner {
|
||||||
|
if x != nil {
|
||||||
|
if x, ok := x.Response.(*HelperResponse_ConnectionOwner); ok {
|
||||||
|
return x.ConnectionOwner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type isHelperResponse_Response interface {
|
||||||
|
isHelperResponse_Response()
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse_WifiState struct {
|
||||||
|
WifiState *WIFIState `protobuf:"bytes,2,opt,name=wifiState,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse_Error struct {
|
||||||
|
Error string `protobuf:"bytes,3,opt,name=error,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperResponse_ConnectionOwner struct {
|
||||||
|
ConnectionOwner *ConnectionOwner `protobuf:"bytes,4,opt,name=connectionOwner,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HelperResponse_WifiState) isHelperResponse_Response() {}
|
||||||
|
|
||||||
|
func (*HelperResponse_Error) isHelperResponse_Response() {}
|
||||||
|
|
||||||
|
func (*HelperResponse_ConnectionOwner) isHelperResponse_Response() {}
|
||||||
|
|
||||||
|
type ConnectionOwner struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
UserId int32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"`
|
||||||
|
UserName string `protobuf:"bytes,2,opt,name=userName,proto3" json:"userName,omitempty"`
|
||||||
|
ProcessPath string `protobuf:"bytes,3,opt,name=processPath,proto3" json:"processPath,omitempty"`
|
||||||
|
AndroidPackageName string `protobuf:"bytes,4,opt,name=androidPackageName,proto3" json:"androidPackageName,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) Reset() {
|
||||||
|
*x = ConnectionOwner{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ConnectionOwner) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[5]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConnectionOwner.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ConnectionOwner) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetUserId() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserId
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetUserName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetProcessPath() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ProcessPath
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ConnectionOwner) GetAndroidPackageName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AndroidPackageName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type WIFIState struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Ssid string `protobuf:"bytes,1,opt,name=ssid,proto3" json:"ssid,omitempty"`
|
||||||
|
Bssid string `protobuf:"bytes,2,opt,name=bssid,proto3" json:"bssid,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) Reset() {
|
||||||
|
*x = WIFIState{}
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*WIFIState) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *WIFIState) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_daemon_helper_proto_msgTypes[6]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use WIFIState.ProtoReflect.Descriptor instead.
|
||||||
|
func (*WIFIState) Descriptor() ([]byte, []int) {
|
||||||
|
return file_daemon_helper_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) GetSsid() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Ssid
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WIFIState) GetBssid() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Bssid
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_daemon_helper_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_daemon_helper_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"\x13daemon/helper.proto\x12\x06daemon\x1a\x1bgoogle/protobuf/empty.proto\"\xf5\x01\n" +
|
||||||
|
"\x1dSubscribeHelperRequestRequest\x12>\n" +
|
||||||
|
"\x1aacceptGetWIFIStateRequests\x18\x01 \x01(\bR\x1aacceptGetWIFIStateRequests\x12L\n" +
|
||||||
|
"!acceptFindConnectionOwnerRequests\x18\x02 \x01(\bR!acceptFindConnectionOwnerRequests\x12F\n" +
|
||||||
|
"\x1eacceptSendNotificationRequests\x18\x03 \x01(\bR\x1eacceptSendNotificationRequests\"\x84\x02\n" +
|
||||||
|
"\rHelperRequest\x12\x0e\n" +
|
||||||
|
"\x02id\x18\x01 \x01(\x03R\x02id\x12<\n" +
|
||||||
|
"\fgetWIFIState\x18\x02 \x01(\v2\x16.google.protobuf.EmptyH\x00R\fgetWIFIState\x12V\n" +
|
||||||
|
"\x13findConnectionOwner\x18\x03 \x01(\v2\".daemon.FindConnectionOwnerRequestH\x00R\x13findConnectionOwner\x12B\n" +
|
||||||
|
"\x10sendNotification\x18\x04 \x01(\v2\x14.daemon.NotificationH\x00R\x10sendNotificationB\t\n" +
|
||||||
|
"\arequest\"\xdc\x01\n" +
|
||||||
|
"\x1aFindConnectionOwnerRequest\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"ipProtocol\x18\x01 \x01(\x05R\n" +
|
||||||
|
"ipProtocol\x12$\n" +
|
||||||
|
"\rsourceAddress\x18\x02 \x01(\tR\rsourceAddress\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"sourcePort\x18\x03 \x01(\x05R\n" +
|
||||||
|
"sourcePort\x12.\n" +
|
||||||
|
"\x12destinationAddress\x18\x04 \x01(\tR\x12destinationAddress\x12(\n" +
|
||||||
|
"\x0fdestinationPort\x18\x05 \x01(\x05R\x0fdestinationPort\"\xc2\x01\n" +
|
||||||
|
"\fNotification\x12\x1e\n" +
|
||||||
|
"\n" +
|
||||||
|
"identifier\x18\x01 \x01(\tR\n" +
|
||||||
|
"identifier\x12\x1a\n" +
|
||||||
|
"\btypeName\x18\x02 \x01(\tR\btypeName\x12\x16\n" +
|
||||||
|
"\x06typeId\x18\x03 \x01(\x05R\x06typeId\x12\x14\n" +
|
||||||
|
"\x05title\x18\x04 \x01(\tR\x05title\x12\x1a\n" +
|
||||||
|
"\bsubtitle\x18\x05 \x01(\tR\bsubtitle\x12\x12\n" +
|
||||||
|
"\x04body\x18\x06 \x01(\tR\x04body\x12\x18\n" +
|
||||||
|
"\aopenURL\x18\a \x01(\tR\aopenURL\"\xbc\x01\n" +
|
||||||
|
"\x0eHelperResponse\x12\x0e\n" +
|
||||||
|
"\x02id\x18\x01 \x01(\x03R\x02id\x121\n" +
|
||||||
|
"\twifiState\x18\x02 \x01(\v2\x11.daemon.WIFIStateH\x00R\twifiState\x12\x16\n" +
|
||||||
|
"\x05error\x18\x03 \x01(\tH\x00R\x05error\x12C\n" +
|
||||||
|
"\x0fconnectionOwner\x18\x04 \x01(\v2\x17.daemon.ConnectionOwnerH\x00R\x0fconnectionOwnerB\n" +
|
||||||
|
"\n" +
|
||||||
|
"\bresponse\"\x97\x01\n" +
|
||||||
|
"\x0fConnectionOwner\x12\x16\n" +
|
||||||
|
"\x06userId\x18\x01 \x01(\x05R\x06userId\x12\x1a\n" +
|
||||||
|
"\buserName\x18\x02 \x01(\tR\buserName\x12 \n" +
|
||||||
|
"\vprocessPath\x18\x03 \x01(\tR\vprocessPath\x12.\n" +
|
||||||
|
"\x12androidPackageName\x18\x04 \x01(\tR\x12androidPackageName\"5\n" +
|
||||||
|
"\tWIFIState\x12\x12\n" +
|
||||||
|
"\x04ssid\x18\x01 \x01(\tR\x04ssid\x12\x14\n" +
|
||||||
|
"\x05bssid\x18\x02 \x01(\tR\x05bssidB%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_daemon_helper_proto_rawDescOnce sync.Once
|
||||||
|
file_daemon_helper_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_daemon_helper_proto_rawDescGZIP() []byte {
|
||||||
|
file_daemon_helper_proto_rawDescOnce.Do(func() {
|
||||||
|
file_daemon_helper_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_daemon_helper_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_daemon_helper_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
|
file_daemon_helper_proto_goTypes = []any{
|
||||||
|
(*SubscribeHelperRequestRequest)(nil), // 0: daemon.SubscribeHelperRequestRequest
|
||||||
|
(*HelperRequest)(nil), // 1: daemon.HelperRequest
|
||||||
|
(*FindConnectionOwnerRequest)(nil), // 2: daemon.FindConnectionOwnerRequest
|
||||||
|
(*Notification)(nil), // 3: daemon.Notification
|
||||||
|
(*HelperResponse)(nil), // 4: daemon.HelperResponse
|
||||||
|
(*ConnectionOwner)(nil), // 5: daemon.ConnectionOwner
|
||||||
|
(*WIFIState)(nil), // 6: daemon.WIFIState
|
||||||
|
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var file_daemon_helper_proto_depIdxs = []int32{
|
||||||
|
7, // 0: daemon.HelperRequest.getWIFIState:type_name -> google.protobuf.Empty
|
||||||
|
2, // 1: daemon.HelperRequest.findConnectionOwner:type_name -> daemon.FindConnectionOwnerRequest
|
||||||
|
3, // 2: daemon.HelperRequest.sendNotification:type_name -> daemon.Notification
|
||||||
|
6, // 3: daemon.HelperResponse.wifiState:type_name -> daemon.WIFIState
|
||||||
|
5, // 4: daemon.HelperResponse.connectionOwner:type_name -> daemon.ConnectionOwner
|
||||||
|
5, // [5:5] is the sub-list for method output_type
|
||||||
|
5, // [5:5] is the sub-list for method input_type
|
||||||
|
5, // [5:5] is the sub-list for extension type_name
|
||||||
|
5, // [5:5] is the sub-list for extension extendee
|
||||||
|
0, // [0:5] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_daemon_helper_proto_init() }
|
||||||
|
func file_daemon_helper_proto_init() {
|
||||||
|
if File_daemon_helper_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file_daemon_helper_proto_msgTypes[1].OneofWrappers = []any{
|
||||||
|
(*HelperRequest_GetWIFIState)(nil),
|
||||||
|
(*HelperRequest_FindConnectionOwner)(nil),
|
||||||
|
(*HelperRequest_SendNotification)(nil),
|
||||||
|
}
|
||||||
|
file_daemon_helper_proto_msgTypes[4].OneofWrappers = []any{
|
||||||
|
(*HelperResponse_WifiState)(nil),
|
||||||
|
(*HelperResponse_Error)(nil),
|
||||||
|
(*HelperResponse_ConnectionOwner)(nil),
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_helper_proto_rawDesc), len(file_daemon_helper_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 7,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_daemon_helper_proto_goTypes,
|
||||||
|
DependencyIndexes: file_daemon_helper_proto_depIdxs,
|
||||||
|
MessageInfos: file_daemon_helper_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_daemon_helper_proto = out.File
|
||||||
|
file_daemon_helper_proto_goTypes = nil
|
||||||
|
file_daemon_helper_proto_depIdxs = nil
|
||||||
|
}
|
||||||
61
daemon/helper.proto
Normal file
61
daemon/helper.proto
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package daemon;
|
||||||
|
option go_package = "github.com/sagernet/sing-box/daemon";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
|
||||||
|
message SubscribeHelperRequestRequest {
|
||||||
|
bool acceptGetWIFIStateRequests = 1;
|
||||||
|
bool acceptFindConnectionOwnerRequests = 2;
|
||||||
|
bool acceptSendNotificationRequests = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelperRequest {
|
||||||
|
int64 id = 1;
|
||||||
|
oneof request {
|
||||||
|
google.protobuf.Empty getWIFIState = 2;
|
||||||
|
FindConnectionOwnerRequest findConnectionOwner = 3;
|
||||||
|
Notification sendNotification = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message FindConnectionOwnerRequest {
|
||||||
|
int32 ipProtocol = 1;
|
||||||
|
string sourceAddress = 2;
|
||||||
|
int32 sourcePort = 3;
|
||||||
|
string destinationAddress = 4;
|
||||||
|
int32 destinationPort = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Notification {
|
||||||
|
string identifier = 1;
|
||||||
|
string typeName = 2;
|
||||||
|
int32 typeId = 3;
|
||||||
|
string title = 4;
|
||||||
|
string subtitle = 5;
|
||||||
|
string body = 6;
|
||||||
|
string openURL = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HelperResponse {
|
||||||
|
int64 id = 1;
|
||||||
|
oneof response {
|
||||||
|
WIFIState wifiState = 2;
|
||||||
|
string error = 3;
|
||||||
|
ConnectionOwner connectionOwner = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConnectionOwner {
|
||||||
|
int32 userId = 1;
|
||||||
|
string userName = 2;
|
||||||
|
string processPath = 3;
|
||||||
|
string androidPackageName = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WIFIState {
|
||||||
|
string ssid = 1;
|
||||||
|
string bssid = 2;
|
||||||
|
}
|
||||||
|
|
||||||
147
daemon/instance.go
Normal file
147
daemon/instance.go
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box"
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/dns"
|
||||||
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
|
"github.com/sagernet/sing-box/include"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
"github.com/sagernet/sing/service/pause"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Instance struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
instance *box.Box
|
||||||
|
clashServer adapter.ClashServer
|
||||||
|
cacheFile adapter.CacheFile
|
||||||
|
pauseManager pause.Manager
|
||||||
|
urlTestHistoryStorage *urltest.HistoryStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) baseContext() context.Context {
|
||||||
|
dnsRegistry := include.DNSTransportRegistry()
|
||||||
|
if s.platform != nil && s.platform.UsePlatformLocalDNSTransport() {
|
||||||
|
dns.RegisterTransport[option.LocalDNSServerOptions](dnsRegistry, C.DNSTypeLocal, s.platform.LocalDNSTransport())
|
||||||
|
}
|
||||||
|
ctx := box.Context(s.ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
|
||||||
|
ctx = filemanager.WithDefault(ctx, s.workingDirectory, s.tempDirectory, s.userID, s.groupID)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CheckConfig(configContent string) error {
|
||||||
|
ctx := s.baseContext()
|
||||||
|
options, err := parseConfig(ctx, configContent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
instance, err := box.New(box.Options{
|
||||||
|
Context: ctx,
|
||||||
|
Options: options,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
instance.Close()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) FormatConfig(configContent string) (string, error) {
|
||||||
|
options, err := parseConfig(s.baseContext(), configContent)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
encoder := json.NewEncoder(&buffer)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
err = encoder.Encode(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type OverrideOptions struct {
|
||||||
|
AutoRedirect bool
|
||||||
|
IncludePackage []string
|
||||||
|
ExcludePackage []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) newInstance(profileContent string, overrideOptions *OverrideOptions) (*Instance, error) {
|
||||||
|
ctx := s.baseContext()
|
||||||
|
service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
|
||||||
|
ctx, cancel := context.WithCancel(include.Context(ctx))
|
||||||
|
options, err := parseConfig(ctx, profileContent)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if overrideOptions != nil {
|
||||||
|
for _, inbound := range options.Inbounds {
|
||||||
|
if tunInboundOptions, isTUN := inbound.Options.(*option.TunInboundOptions); isTUN {
|
||||||
|
tunInboundOptions.AutoRedirect = overrideOptions.AutoRedirect
|
||||||
|
tunInboundOptions.IncludePackage = append(tunInboundOptions.IncludePackage, overrideOptions.IncludePackage...)
|
||||||
|
tunInboundOptions.ExcludePackage = append(tunInboundOptions.ExcludePackage, overrideOptions.ExcludePackage...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
urlTestHistoryStorage := urltest.NewHistoryStorage()
|
||||||
|
ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
|
||||||
|
i := &Instance{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
urlTestHistoryStorage: urlTestHistoryStorage,
|
||||||
|
}
|
||||||
|
boxInstance, err := box.New(box.Options{
|
||||||
|
Context: ctx,
|
||||||
|
Options: options,
|
||||||
|
PlatformLogWriter: s,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i.instance = boxInstance
|
||||||
|
i.clashServer = service.FromContext[adapter.ClashServer](ctx)
|
||||||
|
i.pauseManager = service.FromContext[pause.Manager](ctx)
|
||||||
|
i.cacheFile = service.FromContext[adapter.CacheFile](ctx)
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) Start() error {
|
||||||
|
return i.instance.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) Close() error {
|
||||||
|
i.cancel()
|
||||||
|
i.urlTestHistoryStorage.Close()
|
||||||
|
return i.instance.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) Box() *box.Box {
|
||||||
|
return i.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) PauseManager() pause.Manager {
|
||||||
|
return i.pauseManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
|
||||||
|
options, err := json.UnmarshalExtendedContext[option.Options](ctx, []byte(configContent))
|
||||||
|
if err != nil {
|
||||||
|
return option.Options{}, E.Cause(err, "decode config")
|
||||||
|
}
|
||||||
|
return options, nil
|
||||||
|
}
|
||||||
22
daemon/platform.go
Normal file
22
daemon/platform.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/dns"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlatformHandler interface {
|
||||||
|
ServiceStop() error
|
||||||
|
ServiceReload() error
|
||||||
|
SystemProxyStatus() (*SystemProxyStatus, error)
|
||||||
|
SetSystemProxyEnabled(enabled bool) error
|
||||||
|
WriteDebugMessage(message string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlatformInterface interface {
|
||||||
|
adapter.PlatformInterface
|
||||||
|
|
||||||
|
UsePlatformLocalDNSTransport() bool
|
||||||
|
LocalDNSTransport() dns.TransportConstructorFunc[option.LocalDNSServerOptions]
|
||||||
|
}
|
||||||
774
daemon/started_service.go
Normal file
774
daemon/started_service.go
Normal file
@@ -0,0 +1,774 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
|
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||||
|
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||||
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/protocol/group"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/batch"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/memory"
|
||||||
|
"github.com/sagernet/sing/common/observable"
|
||||||
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
|
"github.com/gofrs/uuid/v5"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ StartedServiceServer = (*StartedService)(nil)
|
||||||
|
|
||||||
|
type StartedService struct {
|
||||||
|
ctx context.Context
|
||||||
|
platform PlatformInterface
|
||||||
|
platformHandler PlatformHandler
|
||||||
|
debug bool
|
||||||
|
logMaxLines int
|
||||||
|
workingDirectory string
|
||||||
|
tempDirectory string
|
||||||
|
userID int
|
||||||
|
groupID int
|
||||||
|
systemProxyEnabled bool
|
||||||
|
serviceAccess sync.RWMutex
|
||||||
|
serviceStatus *ServiceStatus
|
||||||
|
serviceStatusSubscriber *observable.Subscriber[*ServiceStatus]
|
||||||
|
serviceStatusObserver *observable.Observer[*ServiceStatus]
|
||||||
|
logAccess sync.RWMutex
|
||||||
|
logLines list.List[*log.Entry]
|
||||||
|
logSubscriber *observable.Subscriber[*log.Entry]
|
||||||
|
logObserver *observable.Observer[*log.Entry]
|
||||||
|
instance *Instance
|
||||||
|
urlTestSubscriber *observable.Subscriber[struct{}]
|
||||||
|
urlTestObserver *observable.Observer[struct{}]
|
||||||
|
urlTestHistoryStorage *urltest.HistoryStorage
|
||||||
|
clashModeSubscriber *observable.Subscriber[struct{}]
|
||||||
|
clashModeObserver *observable.Observer[struct{}]
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceOptions struct {
|
||||||
|
Context context.Context
|
||||||
|
Platform PlatformInterface
|
||||||
|
PlatformHandler PlatformHandler
|
||||||
|
Debug bool
|
||||||
|
LogMaxLines int
|
||||||
|
WorkingDirectory string
|
||||||
|
TempDirectory string
|
||||||
|
UserID int
|
||||||
|
GroupID int
|
||||||
|
SystemProxyEnabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStartedService(options ServiceOptions) *StartedService {
|
||||||
|
s := &StartedService{
|
||||||
|
ctx: options.Context,
|
||||||
|
platform: options.Platform,
|
||||||
|
platformHandler: options.PlatformHandler,
|
||||||
|
debug: options.Debug,
|
||||||
|
logMaxLines: options.LogMaxLines,
|
||||||
|
workingDirectory: options.WorkingDirectory,
|
||||||
|
tempDirectory: options.TempDirectory,
|
||||||
|
userID: options.UserID,
|
||||||
|
groupID: options.GroupID,
|
||||||
|
systemProxyEnabled: options.SystemProxyEnabled,
|
||||||
|
serviceStatus: &ServiceStatus{Status: ServiceStatus_IDLE},
|
||||||
|
serviceStatusSubscriber: observable.NewSubscriber[*ServiceStatus](4),
|
||||||
|
logSubscriber: observable.NewSubscriber[*log.Entry](128),
|
||||||
|
urlTestSubscriber: observable.NewSubscriber[struct{}](1),
|
||||||
|
urlTestHistoryStorage: urltest.NewHistoryStorage(),
|
||||||
|
clashModeSubscriber: observable.NewSubscriber[struct{}](1),
|
||||||
|
}
|
||||||
|
s.serviceStatusObserver = observable.NewObserver(s.serviceStatusSubscriber, 2)
|
||||||
|
s.logObserver = observable.NewObserver(s.logSubscriber, 64)
|
||||||
|
s.urlTestObserver = observable.NewObserver(s.urlTestSubscriber, 1)
|
||||||
|
s.clashModeObserver = observable.NewObserver(s.clashModeSubscriber, 1)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) resetLogs() {
|
||||||
|
s.logAccess.Lock()
|
||||||
|
s.logLines = list.List[*log.Entry]{}
|
||||||
|
s.logAccess.Unlock()
|
||||||
|
s.logSubscriber.Emit(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) updateStatus(newStatus ServiceStatus_Type) {
|
||||||
|
statusObject := &ServiceStatus{Status: newStatus}
|
||||||
|
s.serviceStatusSubscriber.Emit(statusObject)
|
||||||
|
s.serviceStatus = statusObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) updateStatusError(err error) error {
|
||||||
|
statusObject := &ServiceStatus{Status: ServiceStatus_FATAL, ErrorMessage: err.Error()}
|
||||||
|
s.serviceStatusSubscriber.Emit(statusObject)
|
||||||
|
s.serviceStatus = statusObject
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) StartOrReloadService(profileContent string, options *OverrideOptions) error {
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_IDLE, ServiceStatus_STARTED, ServiceStatus_STARTING:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
s.updateStatus(ServiceStatus_STARTING)
|
||||||
|
s.resetLogs()
|
||||||
|
instance, err := s.newInstance(profileContent, options)
|
||||||
|
if err != nil {
|
||||||
|
return s.updateStatusError(err)
|
||||||
|
}
|
||||||
|
s.instance = instance
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
err = instance.Start()
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTING {
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return s.updateStatusError(err)
|
||||||
|
}
|
||||||
|
s.updateStatus(ServiceStatus_STARTED)
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
runtime.GC()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CloseService() error {
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
s.updateStatus(ServiceStatus_STOPPING)
|
||||||
|
if s.instance != nil {
|
||||||
|
err := s.instance.Close()
|
||||||
|
if err != nil {
|
||||||
|
return s.updateStatusError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.instance = nil
|
||||||
|
s.updateStatus(ServiceStatus_IDLE)
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
runtime.GC()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetError(err error) {
|
||||||
|
s.serviceAccess.Lock()
|
||||||
|
s.updateStatusError(err)
|
||||||
|
s.serviceAccess.Unlock()
|
||||||
|
s.WriteMessage(log.LevelError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) StopService(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
err := s.platformHandler.ServiceStop()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) ReloadService(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
err := s.platformHandler.ServiceReload()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeServiceStatus(empty *emptypb.Empty, server grpc.ServerStreamingServer[ServiceStatus]) error {
|
||||||
|
subscription, done, err := s.serviceStatusObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.serviceStatusObserver.UnSubscribe(subscription)
|
||||||
|
err = server.Send(s.serviceStatus)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case newStatus := <-subscription:
|
||||||
|
err = server.Send(newStatus)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeLog(empty *emptypb.Empty, server grpc.ServerStreamingServer[Log]) error {
|
||||||
|
var savedLines []*log.Entry
|
||||||
|
s.logAccess.Lock()
|
||||||
|
savedLines = make([]*log.Entry, 0, s.logLines.Len())
|
||||||
|
for element := s.logLines.Front(); element != nil; element = element.Next() {
|
||||||
|
savedLines = append(savedLines, element.Value)
|
||||||
|
}
|
||||||
|
s.logAccess.Unlock()
|
||||||
|
subscription, done, err := s.logObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.logObserver.UnSubscribe(subscription)
|
||||||
|
err = server.Send(&Log{
|
||||||
|
Messages: common.Map(savedLines, func(it *log.Entry) *Log_Message {
|
||||||
|
return &Log_Message{
|
||||||
|
Level: LogLevel(it.Level),
|
||||||
|
Message: it.Message,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Reset_: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case message := <-subscription:
|
||||||
|
if message == nil {
|
||||||
|
err = server.Send(&Log{Reset_: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
messages := []*Log_Message{{
|
||||||
|
Level: LogLevel(message.Level),
|
||||||
|
Message: message.Message,
|
||||||
|
}}
|
||||||
|
fetch:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message = <-subscription:
|
||||||
|
messages = append(messages, &Log_Message{
|
||||||
|
Level: LogLevel(message.Level),
|
||||||
|
Message: message.Message,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
break fetch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = server.Send(&Log{Messages: messages})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetDefaultLogLevel(ctx context.Context, empty *emptypb.Empty) (*DefaultLogLevel, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
logLevel := s.instance.instance.LogFactory().Level()
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return &DefaultLogLevel{Level: LogLevel(logLevel)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeStatus(request *SubscribeStatusRequest, server grpc.ServerStreamingServer[Status]) error {
|
||||||
|
interval := time.Duration(request.Interval)
|
||||||
|
if interval <= 0 {
|
||||||
|
interval = time.Second // Default to 1 second
|
||||||
|
}
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
status := s.readStatus()
|
||||||
|
uploadTotal := status.UplinkTotal
|
||||||
|
downloadTotal := status.DownlinkTotal
|
||||||
|
for {
|
||||||
|
err := server.Send(status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-ticker.C:
|
||||||
|
}
|
||||||
|
status = s.readStatus()
|
||||||
|
upload := status.UplinkTotal - uploadTotal
|
||||||
|
download := status.DownlinkTotal - downloadTotal
|
||||||
|
uploadTotal = status.UplinkTotal
|
||||||
|
downloadTotal = status.DownlinkTotal
|
||||||
|
status.Uplink = upload
|
||||||
|
status.Downlink = download
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) readStatus() *Status {
|
||||||
|
var status Status
|
||||||
|
status.Memory = memory.Inuse()
|
||||||
|
status.Goroutines = int32(runtime.NumGoroutine())
|
||||||
|
status.ConnectionsOut = int32(conntrack.Count())
|
||||||
|
nowService := s.instance
|
||||||
|
if nowService != nil {
|
||||||
|
if clashServer := nowService.clashServer; clashServer != nil {
|
||||||
|
status.TrafficAvailable = true
|
||||||
|
trafficManager := clashServer.(*clashapi.Server).TrafficManager()
|
||||||
|
status.UplinkTotal, status.DownlinkTotal = trafficManager.Total()
|
||||||
|
status.ConnectionsIn = int32(trafficManager.ConnectionsLen())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeGroups(empty *emptypb.Empty, server grpc.ServerStreamingServer[Groups]) error {
|
||||||
|
subscription, done, err := s.urlTestObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.urlTestObserver.UnSubscribe(subscription)
|
||||||
|
for {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
groups := s.readGroups()
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
err = server.Send(groups)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-subscription:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) readGroups() *Groups {
|
||||||
|
historyStorage := s.instance.urlTestHistoryStorage
|
||||||
|
boxService := s.instance
|
||||||
|
outbounds := boxService.instance.Outbound().Outbounds()
|
||||||
|
var iGroups []adapter.OutboundGroup
|
||||||
|
for _, it := range outbounds {
|
||||||
|
if group, isGroup := it.(adapter.OutboundGroup); isGroup {
|
||||||
|
iGroups = append(iGroups, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var gs Groups
|
||||||
|
for _, iGroup := range iGroups {
|
||||||
|
var g Group
|
||||||
|
g.Tag = iGroup.Tag()
|
||||||
|
g.Type = iGroup.Type()
|
||||||
|
_, g.Selectable = iGroup.(*group.Selector)
|
||||||
|
g.Selected = iGroup.Now()
|
||||||
|
if boxService.cacheFile != nil {
|
||||||
|
if isExpand, loaded := boxService.cacheFile.LoadGroupExpand(g.Tag); loaded {
|
||||||
|
g.IsExpand = isExpand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, itemTag := range iGroup.All() {
|
||||||
|
itemOutbound, isLoaded := boxService.instance.Outbound().Outbound(itemTag)
|
||||||
|
if !isLoaded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var item GroupItem
|
||||||
|
item.Tag = itemTag
|
||||||
|
item.Type = itemOutbound.Type()
|
||||||
|
if history := historyStorage.LoadURLTestHistory(adapter.OutboundTag(itemOutbound)); history != nil {
|
||||||
|
item.UrlTestTime = history.Time.Unix()
|
||||||
|
item.UrlTestDelay = int32(history.Delay)
|
||||||
|
}
|
||||||
|
g.Items = append(g.Items, &item)
|
||||||
|
}
|
||||||
|
if len(g.Items) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
gs.Group = append(gs.Group, &g)
|
||||||
|
}
|
||||||
|
return &gs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetClashModeStatus(ctx context.Context, empty *emptypb.Empty) (*ClashModeStatus, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
clashServer := s.instance.clashServer
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
if clashServer == nil {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
return &ClashModeStatus{
|
||||||
|
ModeList: clashServer.ModeList(),
|
||||||
|
CurrentMode: clashServer.Mode(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeClashMode(empty *emptypb.Empty, server grpc.ServerStreamingServer[ClashMode]) error {
|
||||||
|
subscription, done, err := s.clashModeObserver.Subscribe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.clashModeObserver.UnSubscribe(subscription)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-subscription:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
message := &ClashMode{Mode: s.instance.clashServer.Mode()}
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
err = server.Send(message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetClashMode(ctx context.Context, request *ClashMode) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
clashServer := s.instance.clashServer
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
clashServer.(*clashapi.Server).SetMode(request.Mode)
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) URLTest(ctx context.Context, request *URLTestRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
groupTag := request.OutboundTag
|
||||||
|
abstractOutboundGroup, isLoaded := boxService.instance.Outbound().Outbound(groupTag)
|
||||||
|
if !isLoaded {
|
||||||
|
return nil, E.New("outbound group not found: ", groupTag)
|
||||||
|
}
|
||||||
|
outboundGroup, isOutboundGroup := abstractOutboundGroup.(adapter.OutboundGroup)
|
||||||
|
if !isOutboundGroup {
|
||||||
|
return nil, E.New("outbound is not a group: ", groupTag)
|
||||||
|
}
|
||||||
|
urlTest, isURLTest := abstractOutboundGroup.(*group.URLTest)
|
||||||
|
if isURLTest {
|
||||||
|
go urlTest.CheckOutbounds()
|
||||||
|
} else {
|
||||||
|
var historyStorage adapter.URLTestHistoryStorage
|
||||||
|
if s.instance.clashServer != nil {
|
||||||
|
historyStorage = s.instance.clashServer.HistoryStorage()
|
||||||
|
} else {
|
||||||
|
return nil, E.New("Clash API is required for URLTest on non-URLTest group")
|
||||||
|
}
|
||||||
|
|
||||||
|
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||||
|
itOutbound, _ := boxService.instance.Outbound().Outbound(it)
|
||||||
|
return itOutbound
|
||||||
|
}), func(it adapter.Outbound) bool {
|
||||||
|
if it == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, isGroup := it.(adapter.OutboundGroup)
|
||||||
|
if isGroup {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
b, _ := batch.New(boxService.ctx, batch.WithConcurrencyNum[any](10))
|
||||||
|
for _, detour := range outbounds {
|
||||||
|
outboundToTest := detour
|
||||||
|
outboundTag := outboundToTest.Tag()
|
||||||
|
b.Go(outboundTag, func() (any, error) {
|
||||||
|
t, err := urltest.URLTest(boxService.ctx, "", outboundToTest)
|
||||||
|
if err != nil {
|
||||||
|
historyStorage.DeleteURLTestHistory(outboundTag)
|
||||||
|
} else {
|
||||||
|
historyStorage.StoreURLTestHistory(outboundTag, &adapter.URLTestHistory{
|
||||||
|
Time: time.Now(),
|
||||||
|
Delay: t,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SelectOutbound(ctx context.Context, request *SelectOutboundRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
outboundGroup, isLoaded := boxService.Outbound().Outbound(request.GroupTag)
|
||||||
|
if !isLoaded {
|
||||||
|
return nil, E.New("selector not found: ", request.GroupTag)
|
||||||
|
}
|
||||||
|
selector, isSelector := outboundGroup.(*group.Selector)
|
||||||
|
if !isSelector {
|
||||||
|
return nil, E.New("outbound is not a selector: ", request.GroupTag)
|
||||||
|
}
|
||||||
|
if !selector.SelectOutbound(request.OutboundTag) {
|
||||||
|
return nil, E.New("outbound not found in selector: ", request.OutboundTag)
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetGroupExpand(ctx context.Context, request *SetGroupExpandRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
if boxService.cacheFile != nil {
|
||||||
|
err := boxService.cacheFile.StoreGroupExpand(request.GroupTag, request.IsExpand)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetSystemProxyStatus(ctx context.Context, empty *emptypb.Empty) (*SystemProxyStatus, error) {
|
||||||
|
return s.platformHandler.SystemProxyStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SetSystemProxyEnabled(ctx context.Context, request *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
||||||
|
err := s.platformHandler.SetSystemProxyEnabled(request.Enabled)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeConnections(request *SubscribeConnectionsRequest, server grpc.ServerStreamingServer[Connections]) error {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
ticker := time.NewTicker(time.Duration(request.Interval))
|
||||||
|
defer ticker.Stop()
|
||||||
|
trafficManager := boxService.clashServer.(*clashapi.Server).TrafficManager()
|
||||||
|
var (
|
||||||
|
connections = make(map[uuid.UUID]*Connection)
|
||||||
|
outConnections []*Connection
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
outConnections = outConnections[:0]
|
||||||
|
for _, connection := range trafficManager.Connections() {
|
||||||
|
outConnections = append(outConnections, newConnection(connections, connection, false))
|
||||||
|
}
|
||||||
|
for _, connection := range trafficManager.ClosedConnections() {
|
||||||
|
outConnections = append(outConnections, newConnection(connections, connection, true))
|
||||||
|
}
|
||||||
|
err := server.Send(&Connections{Connections: outConnections})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return s.ctx.Err()
|
||||||
|
case <-server.Context().Done():
|
||||||
|
return server.Context().Err()
|
||||||
|
case <-ticker.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnection(connections map[uuid.UUID]*Connection, metadata trafficontrol.TrackerMetadata, isClosed bool) *Connection {
|
||||||
|
if oldConnection, loaded := connections[metadata.ID]; loaded {
|
||||||
|
if isClosed {
|
||||||
|
if oldConnection.ClosedAt == 0 {
|
||||||
|
oldConnection.Uplink = 0
|
||||||
|
oldConnection.Downlink = 0
|
||||||
|
oldConnection.ClosedAt = metadata.ClosedAt.UnixMilli()
|
||||||
|
}
|
||||||
|
return oldConnection
|
||||||
|
}
|
||||||
|
lastUplink := oldConnection.UplinkTotal
|
||||||
|
lastDownlink := oldConnection.DownlinkTotal
|
||||||
|
uplinkTotal := metadata.Upload.Load()
|
||||||
|
downlinkTotal := metadata.Download.Load()
|
||||||
|
oldConnection.Uplink = uplinkTotal - lastUplink
|
||||||
|
oldConnection.Downlink = downlinkTotal - lastDownlink
|
||||||
|
oldConnection.UplinkTotal = uplinkTotal
|
||||||
|
oldConnection.DownlinkTotal = downlinkTotal
|
||||||
|
return oldConnection
|
||||||
|
}
|
||||||
|
var rule string
|
||||||
|
if metadata.Rule != nil {
|
||||||
|
rule = metadata.Rule.String()
|
||||||
|
}
|
||||||
|
uplinkTotal := metadata.Upload.Load()
|
||||||
|
downlinkTotal := metadata.Download.Load()
|
||||||
|
uplink := uplinkTotal
|
||||||
|
downlink := downlinkTotal
|
||||||
|
var closedAt int64
|
||||||
|
if !metadata.ClosedAt.IsZero() {
|
||||||
|
closedAt = metadata.ClosedAt.UnixMilli()
|
||||||
|
uplink = 0
|
||||||
|
downlink = 0
|
||||||
|
}
|
||||||
|
connection := &Connection{
|
||||||
|
Id: metadata.ID.String(),
|
||||||
|
Inbound: metadata.Metadata.Inbound,
|
||||||
|
InboundType: metadata.Metadata.InboundType,
|
||||||
|
IpVersion: int32(metadata.Metadata.IPVersion),
|
||||||
|
Network: metadata.Metadata.Network,
|
||||||
|
Source: metadata.Metadata.Source.String(),
|
||||||
|
Destination: metadata.Metadata.Destination.String(),
|
||||||
|
Domain: metadata.Metadata.Domain,
|
||||||
|
Protocol: metadata.Metadata.Protocol,
|
||||||
|
User: metadata.Metadata.User,
|
||||||
|
FromOutbound: metadata.Metadata.Outbound,
|
||||||
|
CreatedAt: metadata.CreatedAt.UnixMilli(),
|
||||||
|
ClosedAt: closedAt,
|
||||||
|
Uplink: uplink,
|
||||||
|
Downlink: downlink,
|
||||||
|
UplinkTotal: uplinkTotal,
|
||||||
|
DownlinkTotal: downlinkTotal,
|
||||||
|
Rule: rule,
|
||||||
|
Outbound: metadata.Outbound,
|
||||||
|
OutboundType: metadata.OutboundType,
|
||||||
|
ChainList: metadata.Chain,
|
||||||
|
}
|
||||||
|
connections[metadata.ID] = connection
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CloseConnection(ctx context.Context, request *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
switch s.serviceStatus.Status {
|
||||||
|
case ServiceStatus_STARTING, ServiceStatus_STARTED:
|
||||||
|
default:
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
targetConn := boxService.clashServer.(*clashapi.Server).TrafficManager().Connection(uuid.FromStringOrNil(request.Id))
|
||||||
|
if targetConn != nil {
|
||||||
|
targetConn.Close()
|
||||||
|
}
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) CloseAllConnections(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
conntrack.Close()
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) GetDeprecatedWarnings(ctx context.Context, empty *emptypb.Empty) (*DeprecatedWarnings, error) {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
if s.serviceStatus.Status != ServiceStatus_STARTED {
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
boxService := s.instance
|
||||||
|
s.serviceAccess.RUnlock()
|
||||||
|
notes := service.FromContext[deprecated.Manager](boxService.ctx).(*deprecatedManager).Get()
|
||||||
|
return &DeprecatedWarnings{
|
||||||
|
Warnings: common.Map(notes, func(it deprecated.Note) *DeprecatedWarning {
|
||||||
|
return &DeprecatedWarning{
|
||||||
|
Message: it.Message(),
|
||||||
|
Impending: it.Impending(),
|
||||||
|
MigrationLink: it.MigrationLink,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SubscribeHelperEvents(empty *emptypb.Empty, server grpc.ServerStreamingServer[HelperRequest]) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) SendHelperResponse(ctx context.Context, response *HelperResponse) (*emptypb.Empty, error) {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) WriteMessage(level log.Level, message string) {
|
||||||
|
item := &log.Entry{Level: level, Message: message}
|
||||||
|
s.logSubscriber.Emit(item)
|
||||||
|
s.logAccess.Lock()
|
||||||
|
s.logLines.PushBack(item)
|
||||||
|
if s.logLines.Len() > s.logMaxLines {
|
||||||
|
s.logLines.Remove(s.logLines.Front())
|
||||||
|
}
|
||||||
|
s.logAccess.Unlock()
|
||||||
|
if s.debug {
|
||||||
|
s.platformHandler.WriteDebugMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StartedService) Instance() *Instance {
|
||||||
|
s.serviceAccess.RLock()
|
||||||
|
defer s.serviceAccess.RUnlock()
|
||||||
|
return s.instance
|
||||||
|
}
|
||||||
1906
daemon/started_service.pb.go
Normal file
1906
daemon/started_service.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
204
daemon/started_service.proto
Normal file
204
daemon/started_service.proto
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package daemon;
|
||||||
|
option go_package = "github.com/sagernet/sing-box/daemon";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "daemon/helper.proto";
|
||||||
|
|
||||||
|
service StartedService {
|
||||||
|
rpc StopService(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
rpc ReloadService(google.protobuf.Empty) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
|
rpc SubscribeServiceStatus(google.protobuf.Empty) returns(stream ServiceStatus) {}
|
||||||
|
rpc SubscribeLog(google.protobuf.Empty) returns(stream Log) {}
|
||||||
|
rpc GetDefaultLogLevel(google.protobuf.Empty) returns(DefaultLogLevel) {}
|
||||||
|
rpc SubscribeStatus(SubscribeStatusRequest) returns(stream Status) {}
|
||||||
|
rpc SubscribeGroups(google.protobuf.Empty) returns(stream Groups) {}
|
||||||
|
|
||||||
|
rpc GetClashModeStatus(google.protobuf.Empty) returns(ClashModeStatus) {}
|
||||||
|
rpc SubscribeClashMode(google.protobuf.Empty) returns(stream ClashMode) {}
|
||||||
|
rpc SetClashMode(ClashMode) returns(google.protobuf.Empty) {}
|
||||||
|
|
||||||
|
rpc URLTest(URLTestRequest) returns(google.protobuf.Empty) {}
|
||||||
|
rpc SelectOutbound(SelectOutboundRequest) returns (google.protobuf.Empty) {}
|
||||||
|
rpc SetGroupExpand(SetGroupExpandRequest) returns (google.protobuf.Empty) {}
|
||||||
|
|
||||||
|
rpc GetSystemProxyStatus(google.protobuf.Empty) returns(SystemProxyStatus) {}
|
||||||
|
rpc SetSystemProxyEnabled(SetSystemProxyEnabledRequest) returns(google.protobuf.Empty) {}
|
||||||
|
|
||||||
|
rpc SubscribeConnections(SubscribeConnectionsRequest) returns(stream Connections) {}
|
||||||
|
rpc CloseConnection(CloseConnectionRequest) returns(google.protobuf.Empty) {}
|
||||||
|
rpc CloseAllConnections(google.protobuf.Empty) returns(google.protobuf.Empty) {}
|
||||||
|
rpc GetDeprecatedWarnings(google.protobuf.Empty) returns(DeprecatedWarnings) {}
|
||||||
|
|
||||||
|
rpc SubscribeHelperEvents(google.protobuf.Empty) returns(stream HelperRequest) {}
|
||||||
|
rpc SendHelperResponse(HelperResponse) returns(google.protobuf.Empty) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServiceStatus {
|
||||||
|
enum Type {
|
||||||
|
IDLE = 0;
|
||||||
|
STARTING = 1;
|
||||||
|
STARTED = 2;
|
||||||
|
STOPPING = 3;
|
||||||
|
FATAL = 4;
|
||||||
|
}
|
||||||
|
Type status = 1;
|
||||||
|
string errorMessage = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReloadServiceRequest {
|
||||||
|
string newProfileContent = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeStatusRequest {
|
||||||
|
int64 interval = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LogLevel {
|
||||||
|
PANIC = 0;
|
||||||
|
FATAL = 1;
|
||||||
|
ERROR = 2;
|
||||||
|
WARN = 3;
|
||||||
|
INFO = 4;
|
||||||
|
DEBUG = 5;
|
||||||
|
TRACE = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Log {
|
||||||
|
repeated Message messages = 1;
|
||||||
|
bool reset = 2;
|
||||||
|
message Message {
|
||||||
|
LogLevel level = 1;
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message DefaultLogLevel {
|
||||||
|
LogLevel level = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Status {
|
||||||
|
uint64 memory = 1;
|
||||||
|
int32 goroutines = 2;
|
||||||
|
int32 connectionsIn = 3;
|
||||||
|
int32 connectionsOut = 4;
|
||||||
|
bool trafficAvailable = 5;
|
||||||
|
int64 uplink = 6;
|
||||||
|
int64 downlink = 7;
|
||||||
|
int64 uplinkTotal = 8;
|
||||||
|
int64 downlinkTotal = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Groups {
|
||||||
|
repeated Group group = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Group {
|
||||||
|
string tag = 1;
|
||||||
|
string type = 2;
|
||||||
|
bool selectable = 3;
|
||||||
|
string selected = 4;
|
||||||
|
bool isExpand = 5;
|
||||||
|
repeated GroupItem items = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GroupItem {
|
||||||
|
string tag = 1;
|
||||||
|
string type = 2;
|
||||||
|
int64 urlTestTime = 3;
|
||||||
|
int32 urlTestDelay = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message URLTestRequest {
|
||||||
|
string outboundTag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SelectOutboundRequest {
|
||||||
|
string groupTag = 1;
|
||||||
|
string outboundTag = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetGroupExpandRequest {
|
||||||
|
string groupTag = 1;
|
||||||
|
bool isExpand = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClashMode {
|
||||||
|
string mode = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClashModeStatus {
|
||||||
|
repeated string modeList = 1;
|
||||||
|
string currentMode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SystemProxyStatus {
|
||||||
|
bool available = 1;
|
||||||
|
bool enabled = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetSystemProxyEnabledRequest {
|
||||||
|
bool enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeConnectionsRequest {
|
||||||
|
int64 interval = 1;
|
||||||
|
ConnectionFilter filter = 2;
|
||||||
|
ConnectionSortBy sortBy = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectionFilter {
|
||||||
|
ALL = 0;
|
||||||
|
ACTIVE = 1;
|
||||||
|
CLOSED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectionSortBy {
|
||||||
|
DATE = 0;
|
||||||
|
TRAFFIC = 1;
|
||||||
|
TOTAL_TRAFFIC = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Connections {
|
||||||
|
repeated Connection connections = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Connection {
|
||||||
|
string id = 1;
|
||||||
|
string inbound = 2;
|
||||||
|
string inboundType = 3;
|
||||||
|
int32 ipVersion = 4;
|
||||||
|
string network = 5;
|
||||||
|
string source = 6;
|
||||||
|
string destination = 7;
|
||||||
|
string domain = 8;
|
||||||
|
string protocol = 9;
|
||||||
|
string user = 10;
|
||||||
|
string fromOutbound = 11;
|
||||||
|
int64 createdAt = 12;
|
||||||
|
int64 closedAt = 13;
|
||||||
|
int64 uplink = 14;
|
||||||
|
int64 downlink = 15;
|
||||||
|
int64 uplinkTotal = 16;
|
||||||
|
int64 downlinkTotal = 17;
|
||||||
|
string rule = 18;
|
||||||
|
string outbound = 19;
|
||||||
|
string outboundType = 20;
|
||||||
|
repeated string chainList = 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CloseConnectionRequest {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeprecatedWarnings {
|
||||||
|
repeated DeprecatedWarning warnings = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeprecatedWarning {
|
||||||
|
string message = 1;
|
||||||
|
bool impending = 2;
|
||||||
|
string migrationLink = 3;
|
||||||
|
}
|
||||||
919
daemon/started_service_grpc.pb.go
Normal file
919
daemon/started_service_grpc.pb.go
Normal file
@@ -0,0 +1,919 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
StartedService_StopService_FullMethodName = "/daemon.StartedService/StopService"
|
||||||
|
StartedService_ReloadService_FullMethodName = "/daemon.StartedService/ReloadService"
|
||||||
|
StartedService_SubscribeServiceStatus_FullMethodName = "/daemon.StartedService/SubscribeServiceStatus"
|
||||||
|
StartedService_SubscribeLog_FullMethodName = "/daemon.StartedService/SubscribeLog"
|
||||||
|
StartedService_GetDefaultLogLevel_FullMethodName = "/daemon.StartedService/GetDefaultLogLevel"
|
||||||
|
StartedService_SubscribeStatus_FullMethodName = "/daemon.StartedService/SubscribeStatus"
|
||||||
|
StartedService_SubscribeGroups_FullMethodName = "/daemon.StartedService/SubscribeGroups"
|
||||||
|
StartedService_GetClashModeStatus_FullMethodName = "/daemon.StartedService/GetClashModeStatus"
|
||||||
|
StartedService_SubscribeClashMode_FullMethodName = "/daemon.StartedService/SubscribeClashMode"
|
||||||
|
StartedService_SetClashMode_FullMethodName = "/daemon.StartedService/SetClashMode"
|
||||||
|
StartedService_URLTest_FullMethodName = "/daemon.StartedService/URLTest"
|
||||||
|
StartedService_SelectOutbound_FullMethodName = "/daemon.StartedService/SelectOutbound"
|
||||||
|
StartedService_SetGroupExpand_FullMethodName = "/daemon.StartedService/SetGroupExpand"
|
||||||
|
StartedService_GetSystemProxyStatus_FullMethodName = "/daemon.StartedService/GetSystemProxyStatus"
|
||||||
|
StartedService_SetSystemProxyEnabled_FullMethodName = "/daemon.StartedService/SetSystemProxyEnabled"
|
||||||
|
StartedService_SubscribeConnections_FullMethodName = "/daemon.StartedService/SubscribeConnections"
|
||||||
|
StartedService_CloseConnection_FullMethodName = "/daemon.StartedService/CloseConnection"
|
||||||
|
StartedService_CloseAllConnections_FullMethodName = "/daemon.StartedService/CloseAllConnections"
|
||||||
|
StartedService_GetDeprecatedWarnings_FullMethodName = "/daemon.StartedService/GetDeprecatedWarnings"
|
||||||
|
StartedService_SubscribeHelperEvents_FullMethodName = "/daemon.StartedService/SubscribeHelperEvents"
|
||||||
|
StartedService_SendHelperResponse_FullMethodName = "/daemon.StartedService/SendHelperResponse"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartedServiceClient is the client API for StartedService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type StartedServiceClient interface {
|
||||||
|
StopService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
ReloadService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SubscribeServiceStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceStatus], error)
|
||||||
|
SubscribeLog(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Log], error)
|
||||||
|
GetDefaultLogLevel(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DefaultLogLevel, error)
|
||||||
|
SubscribeStatus(ctx context.Context, in *SubscribeStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Status], error)
|
||||||
|
SubscribeGroups(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Groups], error)
|
||||||
|
GetClashModeStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ClashModeStatus, error)
|
||||||
|
SubscribeClashMode(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ClashMode], error)
|
||||||
|
SetClashMode(ctx context.Context, in *ClashMode, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
URLTest(ctx context.Context, in *URLTestRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SelectOutbound(ctx context.Context, in *SelectOutboundRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SetGroupExpand(ctx context.Context, in *SetGroupExpandRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
GetSystemProxyStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemProxyStatus, error)
|
||||||
|
SetSystemProxyEnabled(ctx context.Context, in *SetSystemProxyEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Connections], error)
|
||||||
|
CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
CloseAllConnections(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
GetDeprecatedWarnings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DeprecatedWarnings, error)
|
||||||
|
SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error)
|
||||||
|
SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type startedServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStartedServiceClient(cc grpc.ClientConnInterface) StartedServiceClient {
|
||||||
|
return &startedServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) StopService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_StopService_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) ReloadService(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_ReloadService_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeServiceStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ServiceStatus], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[0], StartedService_SubscribeServiceStatus_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, ServiceStatus]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeServiceStatusClient = grpc.ServerStreamingClient[ServiceStatus]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeLog(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Log], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[1], StartedService_SubscribeLog_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, Log]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeLogClient = grpc.ServerStreamingClient[Log]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetDefaultLogLevel(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DefaultLogLevel, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(DefaultLogLevel)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetDefaultLogLevel_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeStatus(ctx context.Context, in *SubscribeStatusRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Status], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[2], StartedService_SubscribeStatus_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[SubscribeStatusRequest, Status]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeStatusClient = grpc.ServerStreamingClient[Status]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeGroups(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Groups], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[3], StartedService_SubscribeGroups_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, Groups]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeGroupsClient = grpc.ServerStreamingClient[Groups]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetClashModeStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ClashModeStatus, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(ClashModeStatus)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetClashModeStatus_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeClashMode(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ClashMode], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[4], StartedService_SubscribeClashMode_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, ClashMode]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeClashModeClient = grpc.ServerStreamingClient[ClashMode]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SetClashMode(ctx context.Context, in *ClashMode, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SetClashMode_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) URLTest(ctx context.Context, in *URLTestRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_URLTest_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SelectOutbound(ctx context.Context, in *SelectOutboundRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SelectOutbound_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SetGroupExpand(ctx context.Context, in *SetGroupExpandRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SetGroupExpand_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetSystemProxyStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemProxyStatus, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(SystemProxyStatus)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetSystemProxyStatus_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SetSystemProxyEnabled(ctx context.Context, in *SetSystemProxyEnabledRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SetSystemProxyEnabled_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeConnections(ctx context.Context, in *SubscribeConnectionsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Connections], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[5], StartedService_SubscribeConnections_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[SubscribeConnectionsRequest, Connections]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeConnectionsClient = grpc.ServerStreamingClient[Connections]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) CloseConnection(ctx context.Context, in *CloseConnectionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_CloseConnection_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) CloseAllConnections(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_CloseAllConnections_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) GetDeprecatedWarnings(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DeprecatedWarnings, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(DeprecatedWarnings)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_GetDeprecatedWarnings_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SubscribeHelperEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HelperRequest], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[6], StartedService_SubscribeHelperEvents_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[emptypb.Empty, HelperRequest]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeHelperEventsClient = grpc.ServerStreamingClient[HelperRequest]
|
||||||
|
|
||||||
|
func (c *startedServiceClient) SendHelperResponse(ctx context.Context, in *HelperResponse, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StartedService_SendHelperResponse_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartedServiceServer is the server API for StartedService service.
|
||||||
|
// All implementations must embed UnimplementedStartedServiceServer
|
||||||
|
// for forward compatibility.
|
||||||
|
type StartedServiceServer interface {
|
||||||
|
StopService(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
|
ReloadService(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
|
SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error
|
||||||
|
SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error
|
||||||
|
GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error)
|
||||||
|
SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error
|
||||||
|
SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error
|
||||||
|
GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error)
|
||||||
|
SubscribeClashMode(*emptypb.Empty, grpc.ServerStreamingServer[ClashMode]) error
|
||||||
|
SetClashMode(context.Context, *ClashMode) (*emptypb.Empty, error)
|
||||||
|
URLTest(context.Context, *URLTestRequest) (*emptypb.Empty, error)
|
||||||
|
SelectOutbound(context.Context, *SelectOutboundRequest) (*emptypb.Empty, error)
|
||||||
|
SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error)
|
||||||
|
GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error)
|
||||||
|
SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error)
|
||||||
|
SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error
|
||||||
|
CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error)
|
||||||
|
CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
|
||||||
|
GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error)
|
||||||
|
SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error
|
||||||
|
SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error)
|
||||||
|
mustEmbedUnimplementedStartedServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedStartedServiceServer must be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedStartedServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) StopService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method StopService not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) ReloadService(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ReloadService not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeServiceStatus(*emptypb.Empty, grpc.ServerStreamingServer[ServiceStatus]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeServiceStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeLog(*emptypb.Empty, grpc.ServerStreamingServer[Log]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeLog not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetDefaultLogLevel(context.Context, *emptypb.Empty) (*DefaultLogLevel, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetDefaultLogLevel not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeStatus(*SubscribeStatusRequest, grpc.ServerStreamingServer[Status]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeGroups(*emptypb.Empty, grpc.ServerStreamingServer[Groups]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeGroups not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetClashModeStatus(context.Context, *emptypb.Empty) (*ClashModeStatus, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetClashModeStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeClashMode(*emptypb.Empty, grpc.ServerStreamingServer[ClashMode]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeClashMode not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SetClashMode(context.Context, *ClashMode) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SetClashMode not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) URLTest(context.Context, *URLTestRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method URLTest not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SelectOutbound(context.Context, *SelectOutboundRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SelectOutbound not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SetGroupExpand(context.Context, *SetGroupExpandRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SetGroupExpand not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetSystemProxyStatus(context.Context, *emptypb.Empty) (*SystemProxyStatus, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetSystemProxyStatus not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SetSystemProxyEnabled(context.Context, *SetSystemProxyEnabledRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SetSystemProxyEnabled not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeConnections(*SubscribeConnectionsRequest, grpc.ServerStreamingServer[Connections]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeConnections not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) CloseConnection(context.Context, *CloseConnectionRequest) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CloseConnection not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) CloseAllConnections(context.Context, *emptypb.Empty) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CloseAllConnections not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) GetDeprecatedWarnings(context.Context, *emptypb.Empty) (*DeprecatedWarnings, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetDeprecatedWarnings not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SubscribeHelperEvents(*emptypb.Empty, grpc.ServerStreamingServer[HelperRequest]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SubscribeHelperEvents not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedStartedServiceServer) SendHelperResponse(context.Context, *HelperResponse) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method SendHelperResponse not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedStartedServiceServer) mustEmbedUnimplementedStartedServiceServer() {}
|
||||||
|
func (UnimplementedStartedServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeStartedServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to StartedServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeStartedServiceServer interface {
|
||||||
|
mustEmbedUnimplementedStartedServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterStartedServiceServer(s grpc.ServiceRegistrar, srv StartedServiceServer) {
|
||||||
|
// If the following call pancis, it indicates UnimplementedStartedServiceServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&StartedService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_StopService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).StopService(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_StopService_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).StopService(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_ReloadService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).ReloadService(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_ReloadService_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).ReloadService(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeServiceStatus_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeServiceStatus(m, &grpc.GenericServerStream[emptypb.Empty, ServiceStatus]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeServiceStatusServer = grpc.ServerStreamingServer[ServiceStatus]
|
||||||
|
|
||||||
|
func _StartedService_SubscribeLog_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeLog(m, &grpc.GenericServerStream[emptypb.Empty, Log]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeLogServer = grpc.ServerStreamingServer[Log]
|
||||||
|
|
||||||
|
func _StartedService_GetDefaultLogLevel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetDefaultLogLevel(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetDefaultLogLevel_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetDefaultLogLevel(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeStatus_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(SubscribeStatusRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeStatus(m, &grpc.GenericServerStream[SubscribeStatusRequest, Status]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeStatusServer = grpc.ServerStreamingServer[Status]
|
||||||
|
|
||||||
|
func _StartedService_SubscribeGroups_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeGroups(m, &grpc.GenericServerStream[emptypb.Empty, Groups]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeGroupsServer = grpc.ServerStreamingServer[Groups]
|
||||||
|
|
||||||
|
func _StartedService_GetClashModeStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetClashModeStatus(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetClashModeStatus_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetClashModeStatus(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeClashMode_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeClashMode(m, &grpc.GenericServerStream[emptypb.Empty, ClashMode]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeClashModeServer = grpc.ServerStreamingServer[ClashMode]
|
||||||
|
|
||||||
|
func _StartedService_SetClashMode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ClashMode)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SetClashMode(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SetClashMode_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SetClashMode(ctx, req.(*ClashMode))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_URLTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(URLTestRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).URLTest(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_URLTest_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).URLTest(ctx, req.(*URLTestRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SelectOutbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SelectOutboundRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SelectOutbound(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SelectOutbound_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SelectOutbound(ctx, req.(*SelectOutboundRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SetGroupExpand_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SetGroupExpandRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SetGroupExpand(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SetGroupExpand_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SetGroupExpand(ctx, req.(*SetGroupExpandRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_GetSystemProxyStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetSystemProxyStatus(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetSystemProxyStatus_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetSystemProxyStatus(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SetSystemProxyEnabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SetSystemProxyEnabledRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SetSystemProxyEnabled(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SetSystemProxyEnabled_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SetSystemProxyEnabled(ctx, req.(*SetSystemProxyEnabledRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeConnections_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(SubscribeConnectionsRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeConnections(m, &grpc.GenericServerStream[SubscribeConnectionsRequest, Connections]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeConnectionsServer = grpc.ServerStreamingServer[Connections]
|
||||||
|
|
||||||
|
func _StartedService_CloseConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CloseConnectionRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).CloseConnection(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_CloseConnection_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).CloseConnection(ctx, req.(*CloseConnectionRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_CloseAllConnections_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).CloseAllConnections(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_CloseAllConnections_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).CloseAllConnections(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_GetDeprecatedWarnings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).GetDeprecatedWarnings(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_GetDeprecatedWarnings_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).GetDeprecatedWarnings(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StartedService_SubscribeHelperEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(emptypb.Empty)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(StartedServiceServer).SubscribeHelperEvents(m, &grpc.GenericServerStream[emptypb.Empty, HelperRequest]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type StartedService_SubscribeHelperEventsServer = grpc.ServerStreamingServer[HelperRequest]
|
||||||
|
|
||||||
|
func _StartedService_SendHelperResponse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(HelperResponse)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StartedServiceServer).SendHelperResponse(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StartedService_SendHelperResponse_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StartedServiceServer).SendHelperResponse(ctx, req.(*HelperResponse))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartedService_ServiceDesc is the grpc.ServiceDesc for StartedService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var StartedService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "daemon.StartedService",
|
||||||
|
HandlerType: (*StartedServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "StopService",
|
||||||
|
Handler: _StartedService_StopService_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ReloadService",
|
||||||
|
Handler: _StartedService_ReloadService_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetDefaultLogLevel",
|
||||||
|
Handler: _StartedService_GetDefaultLogLevel_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetClashModeStatus",
|
||||||
|
Handler: _StartedService_GetClashModeStatus_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SetClashMode",
|
||||||
|
Handler: _StartedService_SetClashMode_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "URLTest",
|
||||||
|
Handler: _StartedService_URLTest_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SelectOutbound",
|
||||||
|
Handler: _StartedService_SelectOutbound_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SetGroupExpand",
|
||||||
|
Handler: _StartedService_SetGroupExpand_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetSystemProxyStatus",
|
||||||
|
Handler: _StartedService_GetSystemProxyStatus_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SetSystemProxyEnabled",
|
||||||
|
Handler: _StartedService_SetSystemProxyEnabled_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CloseConnection",
|
||||||
|
Handler: _StartedService_CloseConnection_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CloseAllConnections",
|
||||||
|
Handler: _StartedService_CloseAllConnections_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetDeprecatedWarnings",
|
||||||
|
Handler: _StartedService_GetDeprecatedWarnings_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SendHelperResponse",
|
||||||
|
Handler: _StartedService_SendHelperResponse_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeServiceStatus",
|
||||||
|
Handler: _StartedService_SubscribeServiceStatus_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeLog",
|
||||||
|
Handler: _StartedService_SubscribeLog_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeStatus",
|
||||||
|
Handler: _StartedService_SubscribeStatus_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeGroups",
|
||||||
|
Handler: _StartedService_SubscribeGroups_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeClashMode",
|
||||||
|
Handler: _StartedService_SubscribeClashMode_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeConnections",
|
||||||
|
Handler: _StartedService_SubscribeConnections_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StreamName: "SubscribeHelperEvents",
|
||||||
|
Handler: _StartedService_SubscribeHelperEvents_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "daemon/started_service.proto",
|
||||||
|
}
|
||||||
@@ -15,8 +15,7 @@ func TruncateDNSMessage(request *dns.Msg, response *dns.Msg, headroom int) (*buf
|
|||||||
}
|
}
|
||||||
responseLen := response.Len()
|
responseLen := response.Len()
|
||||||
if responseLen > maxLen {
|
if responseLen > maxLen {
|
||||||
copyResponse := *response
|
response = response.Copy()
|
||||||
response = ©Response
|
|
||||||
response.Truncate(maxLen)
|
response.Truncate(maxLen)
|
||||||
}
|
}
|
||||||
buffer := buf.NewSize(headroom*2 + 1 + responseLen)
|
buffer := buf.NewSize(headroom*2 + 1 + responseLen)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
R "github.com/sagernet/sing-box/route/rule"
|
R "github.com/sagernet/sing-box/route/rule"
|
||||||
@@ -38,7 +37,7 @@ type Router struct {
|
|||||||
rules []adapter.DNSRule
|
rules []adapter.DNSRule
|
||||||
defaultDomainStrategy C.DomainStrategy
|
defaultDomainStrategy C.DomainStrategy
|
||||||
dnsReverseMapping freelru.Cache[netip.Addr, string]
|
dnsReverseMapping freelru.Cache[netip.Addr, string]
|
||||||
platformInterface platform.Interface
|
platformInterface adapter.PlatformInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOptions) *Router {
|
func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOptions) *Router {
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
|
||||||
sHTTP "github.com/sagernet/sing/protocol/http"
|
sHTTP "github.com/sagernet/sing/protocol/http"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
@@ -47,7 +46,7 @@ type HTTPSTransport struct {
|
|||||||
destination *url.URL
|
destination *url.URL
|
||||||
headers http.Header
|
headers http.Header
|
||||||
transportAccess sync.Mutex
|
transportAccess sync.Mutex
|
||||||
transport *http.Transport
|
transport *HTTPSTransportWrapper
|
||||||
transportResetAt time.Time
|
transportResetAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,11 +61,8 @@ func NewHTTPS(ctx context.Context, logger log.ContextLogger, tag string, options
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if common.Error(tlsConfig.STDConfig()) == nil && !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) {
|
if len(tlsConfig.NextProtos()) == 0 {
|
||||||
tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), http2.NextProtoTLS))
|
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS, "http/1.1"})
|
||||||
}
|
|
||||||
if !common.Contains(tlsConfig.NextProtos(), "http/1.1") {
|
|
||||||
tlsConfig.SetNextProtos(append(tlsConfig.NextProtos(), "http/1.1"))
|
|
||||||
}
|
}
|
||||||
headers := options.Headers.Build()
|
headers := options.Headers.Build()
|
||||||
host := headers.Get("Host")
|
host := headers.Get("Host")
|
||||||
@@ -124,37 +120,13 @@ func NewHTTPSRaw(
|
|||||||
serverAddr M.Socksaddr,
|
serverAddr M.Socksaddr,
|
||||||
tlsConfig tls.Config,
|
tlsConfig tls.Config,
|
||||||
) *HTTPSTransport {
|
) *HTTPSTransport {
|
||||||
var transport *http.Transport
|
|
||||||
if tlsConfig != nil {
|
|
||||||
transport = &http.Transport{
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
tcpConn, hErr := dialer.DialContext(ctx, network, serverAddr)
|
|
||||||
if hErr != nil {
|
|
||||||
return nil, hErr
|
|
||||||
}
|
|
||||||
tlsConn, hErr := aTLS.ClientHandshake(ctx, tcpConn, tlsConfig)
|
|
||||||
if hErr != nil {
|
|
||||||
tcpConn.Close()
|
|
||||||
return nil, hErr
|
|
||||||
}
|
|
||||||
return tlsConn, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
transport = &http.Transport{
|
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
return dialer.DialContext(ctx, network, serverAddr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &HTTPSTransport{
|
return &HTTPSTransport{
|
||||||
TransportAdapter: adapter,
|
TransportAdapter: adapter,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
destination: destination,
|
destination: destination,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
transport: transport,
|
transport: NewHTTPSTransportWrapper(tls.NewDialer(dialer, tlsConfig), serverAddr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
79
dns/transport/https_transport.go
Normal file
79
dns/transport/https_transport.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errFallback = E.New("fallback to HTTP/1.1")
|
||||||
|
|
||||||
|
type HTTPSTransportWrapper struct {
|
||||||
|
http2Transport *http2.Transport
|
||||||
|
httpTransport *http.Transport
|
||||||
|
fallback *atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTTPSTransportWrapper(dialer tls.Dialer, serverAddr M.Socksaddr) *HTTPSTransportWrapper {
|
||||||
|
var fallback atomic.Bool
|
||||||
|
return &HTTPSTransportWrapper{
|
||||||
|
http2Transport: &http2.Transport{
|
||||||
|
DialTLSContext: func(ctx context.Context, _, _ string, _ *tls.STDConfig) (net.Conn, error) {
|
||||||
|
tlsConn, err := dialer.DialTLSContext(ctx, serverAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
state := tlsConn.ConnectionState()
|
||||||
|
if state.NegotiatedProtocol == http2.NextProtoTLS {
|
||||||
|
return tlsConn, nil
|
||||||
|
}
|
||||||
|
tlsConn.Close()
|
||||||
|
fallback.Store(true)
|
||||||
|
return nil, errFallback
|
||||||
|
},
|
||||||
|
},
|
||||||
|
httpTransport: &http.Transport{
|
||||||
|
DialTLSContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return dialer.DialTLSContext(ctx, serverAddr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fallback: &fallback,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTPSTransportWrapper) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||||
|
if h.fallback.Load() {
|
||||||
|
return h.httpTransport.RoundTrip(request)
|
||||||
|
} else {
|
||||||
|
response, err := h.http2Transport.RoundTrip(request)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, errFallback) {
|
||||||
|
return h.httpTransport.RoundTrip(request)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTPSTransportWrapper) CloseIdleConnections() {
|
||||||
|
h.http2Transport.CloseIdleConnections()
|
||||||
|
h.httpTransport.CloseIdleConnections()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTPSTransportWrapper) Clone() *HTTPSTransportWrapper {
|
||||||
|
return &HTTPSTransportWrapper{
|
||||||
|
httpTransport: h.httpTransport,
|
||||||
|
http2Transport: &http2.Transport{
|
||||||
|
DialTLSContext: h.http2Transport.DialTLSContext,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,7 +102,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
|||||||
destination: &destinationURL,
|
destination: &destinationURL,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
transport: &http3.Transport{
|
transport: &http3.Transport{
|
||||||
Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (quic.EarlyConnection, error) {
|
Dial: func(ctx context.Context, addr string, tlsCfg *tls.STDConfig, cfg *quic.Config) (*quic.Conn, error) {
|
||||||
conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, serverAddr)
|
conn, dialErr := transportDialer.DialContext(ctx, N.NetworkUDP, serverAddr)
|
||||||
if dialErr != nil {
|
if dialErr != nil {
|
||||||
return nil, dialErr
|
return nil, dialErr
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ type Transport struct {
|
|||||||
serverAddr M.Socksaddr
|
serverAddr M.Socksaddr
|
||||||
tlsConfig tls.Config
|
tlsConfig tls.Config
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
connection quic.EarlyConnection
|
connection *quic.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteTLSDNSServerOptions) (adapter.DNSTransport, error) {
|
func NewQUIC(ctx context.Context, logger log.ContextLogger, tag string, options option.RemoteTLSDNSServerOptions) (adapter.DNSTransport, error) {
|
||||||
@@ -88,7 +88,7 @@ func (t *Transport) Close() error {
|
|||||||
|
|
||||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||||
var (
|
var (
|
||||||
conn quic.Connection
|
conn *quic.Conn
|
||||||
err error
|
err error
|
||||||
response *mDNS.Msg
|
response *mDNS.Msg
|
||||||
)
|
)
|
||||||
@@ -110,7 +110,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) openConnection() (quic.EarlyConnection, error) {
|
func (t *Transport) openConnection() (*quic.Conn, error) {
|
||||||
connection := t.connection
|
connection := t.connection
|
||||||
if connection != nil && !common.Done(connection.Context()) {
|
if connection != nil && !common.Done(connection.Context()) {
|
||||||
return connection, nil
|
return connection, nil
|
||||||
@@ -139,7 +139,7 @@ func (t *Transport) openConnection() (quic.EarlyConnection, error) {
|
|||||||
return earlyConnection, nil
|
return earlyConnection, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) exchange(ctx context.Context, message *mDNS.Msg, conn quic.Connection) (*mDNS.Msg, error) {
|
func (t *Transport) exchange(ctx context.Context, message *mDNS.Msg, conn *quic.Conn) (*mDNS.Msg, error) {
|
||||||
stream, err := conn.OpenStreamSync(ctx)
|
stream, err := conn.OpenStreamSync(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -2,10 +2,28 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 1.13.0-alpha.14
|
#### 1.13.0-alpha.20
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.12.9
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.13.0-alpha.16
|
||||||
|
|
||||||
|
* Add curve preferences, pinned public key SHA256 and mTLS for TLS options **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
See [TLS](/configuration/shared/tls/).
|
||||||
|
|
||||||
|
#### 1.13.0-alpha.15
|
||||||
|
|
||||||
|
* Update quic-go to v0.54.0
|
||||||
|
* Update gVisor to v20250811
|
||||||
|
* Update Tailscale to v1.86.5
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.12.8
|
#### 1.12.8
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|||||||
54
docs/configuration/certificate/index.zh.md
Normal file
54
docs/configuration/certificate/index.zh.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# 证书
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"store": "",
|
||||||
|
"certificate": [],
|
||||||
|
"certificate_path": [],
|
||||||
|
"certificate_directory_path": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### store
|
||||||
|
|
||||||
|
默认的 X509 受信任 CA 证书列表。
|
||||||
|
|
||||||
|
| 类型 | 描述 |
|
||||||
|
|--------------------|--------------------------------------------------------------------------------------------|
|
||||||
|
| `system`(默认) | 系统受信任的 CA 证书 |
|
||||||
|
| `mozilla` | [Mozilla 包含列表](https://wiki.mozilla.org/CA/Included_Certificates)(已移除中国 CA 证书) |
|
||||||
|
| `none` | 空列表 |
|
||||||
|
|
||||||
|
#### certificate
|
||||||
|
|
||||||
|
要信任的证书行数组,PEM 格式。
|
||||||
|
|
||||||
|
#### certificate_path
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
文件修改时将自动重新加载。
|
||||||
|
|
||||||
|
要信任的证书路径,PEM 格式。
|
||||||
|
|
||||||
|
#### certificate_directory_path
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
文件修改时将自动重新加载。
|
||||||
|
|
||||||
|
搜索要信任的证书的目录路径,PEM 格式。
|
||||||
38
docs/configuration/dns/server/dhcp.zh.md
Normal file
38
docs/configuration/dns/server/dhcp.zh.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# DHCP
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "dhcp",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"interface": "",
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### interface
|
||||||
|
|
||||||
|
要监听的网络接口名称。
|
||||||
|
|
||||||
|
默认使用默认接口。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
35
docs/configuration/dns/server/fakeip.zh.md
Normal file
35
docs/configuration/dns/server/fakeip.zh.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# Fake IP
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "fakeip",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"inet4_range": "198.18.0.0/15",
|
||||||
|
"inet6_range": "fc00::/18"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### inet4_range
|
||||||
|
|
||||||
|
FakeIP 的 IPv4 地址范围。
|
||||||
|
|
||||||
|
#### inet6_range
|
||||||
|
|
||||||
|
FakeIP 的 IPv6 地址范围。
|
||||||
96
docs/configuration/dns/server/hosts.zh.md
Normal file
96
docs/configuration/dns/server/hosts.zh.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# Hosts
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "hosts",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"path": [],
|
||||||
|
"predefined": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### path
|
||||||
|
|
||||||
|
hosts 文件路径列表。
|
||||||
|
|
||||||
|
默认使用 `/etc/hosts`。
|
||||||
|
|
||||||
|
在 Windows 上默认使用 `C:\Windows\System32\Drivers\etc\hosts`。
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// "path": "/etc/hosts"
|
||||||
|
|
||||||
|
"path": [
|
||||||
|
"/etc/hosts",
|
||||||
|
"$HOME/.hosts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### predefined
|
||||||
|
|
||||||
|
预定义的 hosts。
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"predefined": {
|
||||||
|
"www.google.com": "127.0.0.1",
|
||||||
|
"localhost": [
|
||||||
|
"127.0.0.1",
|
||||||
|
"::1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
=== "如果可用则使用 hosts"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
...
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "hosts",
|
||||||
|
"tag": "hosts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"ip_accept_any": true,
|
||||||
|
"server": "hosts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
71
docs/configuration/dns/server/http3.zh.md
Normal file
71
docs/configuration/dns/server/http3.zh.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# DNS over HTTP3 (DoH3)
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "h3",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"server": "",
|
||||||
|
"server_port": 443,
|
||||||
|
|
||||||
|
"path": "",
|
||||||
|
"headers": {},
|
||||||
|
|
||||||
|
"tls": {},
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "与旧版 H3 服务器的区别"
|
||||||
|
|
||||||
|
* 旧服务器默认使用默认出站,除非指定了绕行;新服务器像出站一样使用拨号器,相当于默认使用空的直连出站。
|
||||||
|
* 旧服务器使用 `address_resolver` 和 `address_strategy` 来解析服务器中的域名;新服务器改用 [拨号字段](/zh/configuration/shared/dial/) 中的 `domain_resolver` 和 `domain_strategy`。
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
DNS 服务器的地址。
|
||||||
|
|
||||||
|
如果使用域名,还必须设置 `domain_resolver` 来解析 IP 地址。
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
DNS 服务器的端口。
|
||||||
|
|
||||||
|
默认使用 `443`。
|
||||||
|
|
||||||
|
#### path
|
||||||
|
|
||||||
|
DNS 服务器的路径。
|
||||||
|
|
||||||
|
默认使用 `/dns-query`。
|
||||||
|
|
||||||
|
#### headers
|
||||||
|
|
||||||
|
发送到 DNS 服务器的额外标头。
|
||||||
|
|
||||||
|
#### tls
|
||||||
|
|
||||||
|
TLS 配置,参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
71
docs/configuration/dns/server/https.zh.md
Normal file
71
docs/configuration/dns/server/https.zh.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# DNS over HTTPS (DoH)
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "https",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"server": "",
|
||||||
|
"server_port": 443,
|
||||||
|
|
||||||
|
"path": "",
|
||||||
|
"headers": {},
|
||||||
|
|
||||||
|
"tls": {},
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "与旧版 HTTPS 服务器的区别"
|
||||||
|
|
||||||
|
* 旧服务器默认使用默认出站,除非指定了绕行;新服务器像出站一样使用拨号器,相当于默认使用空的直连出站。
|
||||||
|
* 旧服务器使用 `address_resolver` 和 `address_strategy` 来解析服务器中的域名;新服务器改用 [拨号字段](/zh/configuration/shared/dial/) 中的 `domain_resolver` 和 `domain_strategy`。
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
DNS 服务器的地址。
|
||||||
|
|
||||||
|
如果使用域名,还必须设置 `domain_resolver` 来解析 IP 地址。
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
DNS 服务器的端口。
|
||||||
|
|
||||||
|
默认使用 `443`。
|
||||||
|
|
||||||
|
#### path
|
||||||
|
|
||||||
|
DNS 服务器的路径。
|
||||||
|
|
||||||
|
默认使用 `/dns-query`。
|
||||||
|
|
||||||
|
#### headers
|
||||||
|
|
||||||
|
发送到 DNS 服务器的额外标头。
|
||||||
|
|
||||||
|
#### tls
|
||||||
|
|
||||||
|
TLS 配置,参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
61
docs/configuration/dns/server/local.zh.md
Normal file
61
docs/configuration/dns/server/local.zh.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.13.0 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [prefer_go](#prefer_go)
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# Local
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "local",
|
||||||
|
"tag": "",
|
||||||
|
"prefer_go": false,
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "与旧版本地服务器的区别"
|
||||||
|
|
||||||
|
* 旧的传统本地服务器只处理 IP 请求;新的服务器处理所有类型的请求,并支持 IP 请求的并发处理。
|
||||||
|
* 旧的本地服务器默认使用默认出站,除非指定了绕行;新服务器像出站一样使用拨号器,相当于默认使用空的直连出站。
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### prefer_go
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.13.0 起"
|
||||||
|
|
||||||
|
启用后,`local` DNS 服务器将尽可能通过拨号自身来解析 DNS。
|
||||||
|
|
||||||
|
具体来说,它禁用了在 sing-box 1.13.0 中作为功能添加的以下行为:
|
||||||
|
|
||||||
|
1. 在 Apple 平台上:尝试在 NetworkExtension 中使用 `getaddrinfo` 解析 A/AAAA 请求。
|
||||||
|
2. 在 Linux 上:当可用时通过 `systemd-resolvd` 的 DBus 接口进行解析。
|
||||||
|
|
||||||
|
作为唯一的例外,它无法禁用以下行为:
|
||||||
|
|
||||||
|
1. 在 Android 图形客户端中,
|
||||||
|
`local` 将始终通过平台接口解析 DNS,
|
||||||
|
因为没有其他方法来获取上游 DNS 服务器;
|
||||||
|
在运行 Android 10 以下版本的设备上,此接口只能解析 A/AAAA 请求。
|
||||||
|
|
||||||
|
2. 在 macOS 上,`local` 会在 Network Extension 中首先尝试 DHCP,由于 DHCP 遵循拨号字段,
|
||||||
|
它不会被 `prefer_go` 禁用。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
58
docs/configuration/dns/server/quic.zh.md
Normal file
58
docs/configuration/dns/server/quic.zh.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# DNS over QUIC (DoQ)
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "quic",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"server": "",
|
||||||
|
"server_port": 853,
|
||||||
|
|
||||||
|
"tls": {},
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "与旧版 QUIC 服务器的区别"
|
||||||
|
|
||||||
|
* 旧服务器默认使用默认出站,除非指定了绕行;新服务器像出站一样使用拨号器,相当于默认使用空的直连出站。
|
||||||
|
* 旧服务器使用 `address_resolver` 和 `address_strategy` 来解析服务器中的域名;新服务器改用 [拨号字段](/zh/configuration/shared/dial/) 中的 `domain_resolver` 和 `domain_strategy`。
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
DNS 服务器的地址。
|
||||||
|
|
||||||
|
如果使用域名,还必须设置 `domain_resolver` 来解析 IP 地址。
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
DNS 服务器的端口。
|
||||||
|
|
||||||
|
默认使用 `853`。
|
||||||
|
|
||||||
|
#### tls
|
||||||
|
|
||||||
|
TLS 配置,参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
83
docs/configuration/dns/server/resolved.zh.md
Normal file
83
docs/configuration/dns/server/resolved.zh.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# Resolved
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "resolved",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"service": "resolved",
|
||||||
|
"accept_default_resolvers": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### service
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
[Resolved 服务](/zh/configuration/service/resolved) 的标签。
|
||||||
|
|
||||||
|
#### accept_default_resolvers
|
||||||
|
|
||||||
|
指示是否除了匹配域名外,还应接受默认 DNS 解析器以进行回退查询。
|
||||||
|
|
||||||
|
具体来说,默认 DNS 解析器是设置了 `SetLinkDefaultRoute` 或 `SetLinkDomains ~.` 的 DNS 服务器。
|
||||||
|
|
||||||
|
如果未启用,对于不匹配搜索域或匹配域的请求,将返回 `NXDOMAIN`。
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
=== "仅分割 DNS"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "local",
|
||||||
|
"tag": "local"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "resolved",
|
||||||
|
"tag": "resolved",
|
||||||
|
"service": "resolved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"ip_accept_any": true,
|
||||||
|
"server": "resolved"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "用作全局 DNS"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "resolved",
|
||||||
|
"service": "resolved",
|
||||||
|
"accept_default_resolvers": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
83
docs/configuration/dns/server/tailscale.zh.md
Normal file
83
docs/configuration/dns/server/tailscale.zh.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# Tailscale
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "tailscale",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"endpoint": "ts-ep",
|
||||||
|
"accept_default_resolvers": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### endpoint
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
[Tailscale 端点](/zh/configuration/endpoint/tailscale) 的标签。
|
||||||
|
|
||||||
|
#### accept_default_resolvers
|
||||||
|
|
||||||
|
指示是否除了 MagicDNS 外,还应接受默认 DNS 解析器以进行回退查询。
|
||||||
|
|
||||||
|
如果未启用,对于非 Tailscale 域名查询将返回 `NXDOMAIN`。
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
=== "仅 MagicDNS"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "local",
|
||||||
|
"tag": "local"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tailscale",
|
||||||
|
"tag": "ts",
|
||||||
|
"endpoint": "ts-ep"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"ip_accept_any": true,
|
||||||
|
"server": "ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "用作全局 DNS"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "tailscale",
|
||||||
|
"endpoint": "ts-ep",
|
||||||
|
"accept_default_resolvers": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
52
docs/configuration/dns/server/tcp.zh.md
Normal file
52
docs/configuration/dns/server/tcp.zh.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# TCP
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "tcp",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"server": "",
|
||||||
|
"server_port": 53,
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "与旧版 TCP 服务器的区别"
|
||||||
|
|
||||||
|
* 旧服务器默认使用默认出站,除非指定了绕行;新服务器像出站一样使用拨号器,相当于默认使用空的直连出站。
|
||||||
|
* 旧服务器使用 `address_resolver` 和 `address_strategy` 来解析服务器中的域名;新服务器改用 [拨号字段](/zh/configuration/shared/dial/) 中的 `domain_resolver` 和 `domain_strategy`。
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
DNS 服务器的地址。
|
||||||
|
|
||||||
|
如果使用域名,还必须设置 `domain_resolver` 来解析 IP 地址。
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
DNS 服务器的端口。
|
||||||
|
|
||||||
|
默认使用 `53`。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
58
docs/configuration/dns/server/tls.zh.md
Normal file
58
docs/configuration/dns/server/tls.zh.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# DNS over TLS (DoT)
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "tls",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"server": "",
|
||||||
|
"server_port": 853,
|
||||||
|
|
||||||
|
"tls": {},
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "与旧版 TLS 服务器的区别"
|
||||||
|
|
||||||
|
* 旧服务器默认使用默认出站,除非指定了绕行;新服务器像出站一样使用拨号器,相当于默认使用空的直连出站。
|
||||||
|
* 旧服务器使用 `address_resolver` 和 `address_strategy` 来解析服务器中的域名;新服务器改用 [拨号字段](/zh/configuration/shared/dial/) 中的 `domain_resolver` 和 `domain_strategy`。
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
DNS 服务器的地址。
|
||||||
|
|
||||||
|
如果使用域名,还必须设置 `domain_resolver` 来解析 IP 地址。
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
DNS 服务器的端口。
|
||||||
|
|
||||||
|
默认使用 `853`。
|
||||||
|
|
||||||
|
#### tls
|
||||||
|
|
||||||
|
TLS 配置,参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
52
docs/configuration/dns/server/udp.zh.md
Normal file
52
docs/configuration/dns/server/udp.zh.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
# UDP
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"type": "udp",
|
||||||
|
"tag": "",
|
||||||
|
|
||||||
|
"server": "",
|
||||||
|
"server_port": 53,
|
||||||
|
|
||||||
|
// 拨号字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "与旧版 UDP 服务器的区别"
|
||||||
|
|
||||||
|
* 旧服务器默认使用默认出站,除非指定了绕行;新服务器像出站一样使用拨号器,相当于默认使用空的直连出站。
|
||||||
|
* 旧服务器使用 `address_resolver` 和 `address_strategy` 来解析服务器中的域名;新服务器改用 [拨号字段](/zh/configuration/shared/dial/) 中的 `domain_resolver` 和 `domain_strategy`。
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### server
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
DNS 服务器的地址。
|
||||||
|
|
||||||
|
如果使用域名,还必须设置 `domain_resolver` 来解析 IP 地址。
|
||||||
|
|
||||||
|
#### server_port
|
||||||
|
|
||||||
|
DNS 服务器的端口。
|
||||||
|
|
||||||
|
默认使用 `53`。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
103
docs/configuration/endpoint/tailscale.zh.md
Normal file
103
docs/configuration/endpoint/tailscale.zh.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.12.0 起"
|
||||||
|
|
||||||
|
### 结构
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "tailscale",
|
||||||
|
"tag": "ts-ep",
|
||||||
|
"state_directory": "",
|
||||||
|
"auth_key": "",
|
||||||
|
"control_url": "",
|
||||||
|
"ephemeral": false,
|
||||||
|
"hostname": "",
|
||||||
|
"accept_routes": false,
|
||||||
|
"exit_node": "",
|
||||||
|
"exit_node_allow_lan_access": false,
|
||||||
|
"advertise_routes": [],
|
||||||
|
"advertise_exit_node": false,
|
||||||
|
"udp_timeout": "5m",
|
||||||
|
|
||||||
|
... // 拨号字段
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字段
|
||||||
|
|
||||||
|
#### state_directory
|
||||||
|
|
||||||
|
存储 Tailscale 状态的目录。
|
||||||
|
|
||||||
|
默认使用 `tailscale`。
|
||||||
|
|
||||||
|
示例:`$HOME/.tailscale`
|
||||||
|
|
||||||
|
#### auth_key
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
认证密钥不是必需的。默认情况下,sing-box 将记录登录 URL(或在图形客户端上弹出通知)。
|
||||||
|
|
||||||
|
用于创建节点的认证密钥。如果节点已经创建(从之前存储的状态),则不使用此字段。
|
||||||
|
|
||||||
|
#### control_url
|
||||||
|
|
||||||
|
协调服务器 URL。
|
||||||
|
|
||||||
|
默认使用 `https://controlplane.tailscale.com`。
|
||||||
|
|
||||||
|
#### ephemeral
|
||||||
|
|
||||||
|
指示实例是否应注册为临时节点 (https://tailscale.com/s/ephemeral-nodes)。
|
||||||
|
|
||||||
|
#### hostname
|
||||||
|
|
||||||
|
节点的主机名。
|
||||||
|
|
||||||
|
默认使用系统主机名。
|
||||||
|
|
||||||
|
示例:`localhost`
|
||||||
|
|
||||||
|
#### accept_routes
|
||||||
|
|
||||||
|
指示节点是否应接受其他节点通告的路由。
|
||||||
|
|
||||||
|
#### exit_node
|
||||||
|
|
||||||
|
要使用的出口节点名称或 IP 地址。
|
||||||
|
|
||||||
|
#### exit_node_allow_lan_access
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
当出口节点没有相应的通告路由时,即使设置了 `exit_node_allow_lan_access`,私有流量也无法路由到出口节点。
|
||||||
|
|
||||||
|
指示本地可访问的子网应该直接路由还是通过出口节点路由。
|
||||||
|
|
||||||
|
#### advertise_routes
|
||||||
|
|
||||||
|
通告到 Tailscale 网络的 CIDR 前缀,作为可通过当前节点访问的路由。
|
||||||
|
|
||||||
|
示例:`["192.168.1.1/24"]`
|
||||||
|
|
||||||
|
#### advertise_exit_node
|
||||||
|
|
||||||
|
指示节点是否应将自己通告为出口节点。
|
||||||
|
|
||||||
|
#### udp_timeout
|
||||||
|
|
||||||
|
UDP NAT 过期时间。
|
||||||
|
|
||||||
|
默认使用 `5m`。
|
||||||
|
|
||||||
|
### 拨号字段
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
Tailscale 端点中的拨号字段仅控制它如何连接到控制平面,与实际连接无关。
|
||||||
|
|
||||||
|
参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。
|
||||||
@@ -43,13 +43,11 @@ Trojan 用户。
|
|||||||
|
|
||||||
#### tls
|
#### tls
|
||||||
|
|
||||||
==如果启用 HTTP3 则必填==
|
TLS 配置,参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||||
|
|
||||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
|
||||||
|
|
||||||
#### fallback
|
#### fallback
|
||||||
|
|
||||||
!!! quote ""
|
!!! failure ""
|
||||||
|
|
||||||
没有证据表明 GFW 基于 HTTP 响应检测并阻止 Trojan 服务器,并且在服务器上打开标准 http/s 端口是一个更大的特征。
|
没有证据表明 GFW 基于 HTTP 响应检测并阻止 Trojan 服务器,并且在服务器上打开标准 http/s 端口是一个更大的特征。
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user