mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
13 Commits
dev-wifi-l
...
v1.13.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
178d426364 | ||
|
|
e60c70d262 | ||
|
|
b09cb7969b | ||
|
|
7058c2e08a | ||
|
|
095750bfaf | ||
|
|
4572a808b6 | ||
|
|
234e4bce67 | ||
|
|
337b7e0b17 | ||
|
|
4f2ec1a42f | ||
|
|
ca7fd0c429 | ||
|
|
9a32fc1121 | ||
|
|
4e6e3d2471 | ||
|
|
f9c973072b |
@@ -1,27 +1,25 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
VERSION="1.25.1"
|
VERSION="1.23.12"
|
||||||
|
|
||||||
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_win7
|
mv go go_legacy
|
||||||
cd go_win7
|
cd go_legacy
|
||||||
|
|
||||||
# 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.25.x
|
# this patch file only works on golang1.23.x
|
||||||
# that means after golang1.26 release it must be changed
|
# that means after golang1.24 release it must be changed
|
||||||
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.25/
|
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/
|
||||||
# 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"
|
||||||
|
|
||||||
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
|
curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1
|
||||||
|
curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.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/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.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/69e2eed6dd0f6d815ebf15797761c13f31213dd6.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
|
|
||||||
72
.github/workflows/build.yml
vendored
72
.github/workflows/build.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -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_win7: true, legacy_name: "windows-7" }
|
- { os: windows, arch: amd64, legacy_go123: true, legacy_name: "windows-7" }
|
||||||
- { os: windows, arch: "386" }
|
- { os: windows, arch: "386" }
|
||||||
- { os: windows, arch: "386", legacy_win7: true, legacy_name: "windows-7" }
|
- { os: windows, arch: "386", legacy_go123: true, legacy_name: "windows-7" }
|
||||||
- { os: windows, arch: arm64 }
|
- { os: windows, arch: arm64 }
|
||||||
|
|
||||||
- { os: darwin, arch: amd64 }
|
- { os: darwin, arch: amd64 }
|
||||||
@@ -110,29 +110,29 @@ jobs:
|
|||||||
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
if: ${{ ! (matrix.legacy_go123 || matrix.legacy_go124) }}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Go 1.24
|
- name: Setup Go 1.24
|
||||||
if: matrix.legacy_go124
|
if: matrix.legacy_go124
|
||||||
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 for Windows 7
|
- name: Cache Go 1.23
|
||||||
if: matrix.legacy_win7
|
if: matrix.legacy_go123
|
||||||
id: cache-go-for-windows7
|
id: cache-legacy-go
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/go_win7
|
~/go/go_legacy
|
||||||
key: go_win7_1251
|
key: go_legacy_12312
|
||||||
- name: Setup Go for Windows 7
|
- name: Setup Go 1.23
|
||||||
if: matrix.legacy_win7 && steps.cache-go-for-windows7.outputs.cache-hit != 'true'
|
if: matrix.legacy_go123 && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||||
run: |-
|
run: |-
|
||||||
.github/setup_go_for_windows7.sh
|
.github/setup_legacy_go.sh
|
||||||
- name: Setup Go for Windows 7
|
- name: Setup Go 1.23
|
||||||
if: matrix.legacy_win7
|
if: matrix.legacy_go123
|
||||||
run: |-
|
run: |-
|
||||||
echo "PATH=$HOME/go/go_win7/bin:$PATH" >> $GITHUB_ENV
|
echo "PATH=$HOME/go/go_legacy/bin:$PATH" >> $GITHUB_ENV
|
||||||
echo "GOROOT=$HOME/go/go_win7" >> $GITHUB_ENV
|
echo "GOROOT=$HOME/go/go_legacy" >> $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,10 +146,27 @@ 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,badlinkname,tfogo_checklinkname0'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build
|
- name: Build
|
||||||
if: matrix.os != 'android'
|
if: matrix.os != 'darwin' && matrix.os != 'android'
|
||||||
|
run: |
|
||||||
|
set -xeuo pipefail
|
||||||
|
mkdir -p dist
|
||||||
|
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||||
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
||||||
|
./cmd/sing-box
|
||||||
|
env:
|
||||||
|
CGO_ENABLED: "0"
|
||||||
|
GOOS: ${{ matrix.os }}
|
||||||
|
GOARCH: ${{ matrix.arch }}
|
||||||
|
GO386: ${{ matrix.go386 }}
|
||||||
|
GOARM: ${{ matrix.goarm }}
|
||||||
|
GOMIPS: ${{ matrix.gomips }}
|
||||||
|
GOMIPS64: ${{ matrix.gomips }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build darwin
|
||||||
|
if: matrix.os == 'darwin'
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
@@ -174,7 +191,7 @@ jobs:
|
|||||||
export CXX="${CC}++"
|
export CXX="${CC}++"
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
GOOS=$BUILD_GOOS GOARCH=$BUILD_GOARCH build go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
@@ -300,7 +317,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -380,7 +397,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
@@ -432,8 +449,7 @@ jobs:
|
|||||||
SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}
|
SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}
|
||||||
build_apple:
|
build_apple:
|
||||||
name: Build Apple clients
|
name: Build Apple clients
|
||||||
runs-on: macos-26
|
runs-on: macos-15
|
||||||
if: false
|
|
||||||
needs:
|
needs:
|
||||||
- calculate_version
|
- calculate_version
|
||||||
strategy:
|
strategy:
|
||||||
@@ -479,7 +495,15 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
|
- name: Setup Xcode stable
|
||||||
|
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||||
|
run: |-
|
||||||
|
sudo xcode-select -s /Applications/Xcode_16.4.app
|
||||||
|
- name: Setup Xcode beta
|
||||||
|
if: matrix.if && github.ref == 'refs/heads/dev-next'
|
||||||
|
run: |-
|
||||||
|
sudo xcode-select -s /Applications/Xcode_16.4.app
|
||||||
- name: Set tag
|
- name: Set tag
|
||||||
if: matrix.if
|
if: matrix.if
|
||||||
run: |-
|
run: |-
|
||||||
|
|||||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -28,11 +28,11 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25
|
go-version: ~1.24.6
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
with:
|
with:
|
||||||
version: v2.4.0
|
version: latest
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
install-mode: binary
|
install-mode: binary
|
||||||
verify: false
|
verify: false
|
||||||
|
|||||||
8
.github/workflows/linux.yml
vendored
8
.github/workflows/linux.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.25.1
|
go-version: ^1.25.0
|
||||||
- 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
|
||||||
@@ -85,14 +85,14 @@ 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,badlinkname,tfogo_checklinkname0'
|
TAGS='with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale'
|
||||||
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
echo "BUILD_TAGS=${TAGS}" >> "${GITHUB_ENV}"
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
go build -v -trimpath -o dist/sing-box -tags "${BUILD_TAGS}" \
|
||||||
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }} -checklinkname=0' \
|
-ldflags '-s -buildid= -X github.com/sagernet/sing-box/constant.Version=${{ needs.calculate_version.outputs.version }}' \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,6 +15,4 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
/config.d/
|
/config.d/
|
||||||
/venv/
|
/venv/
|
||||||
CLAUDE.md
|
|
||||||
AGENTS.md
|
|
||||||
/.claude/
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
version: "2"
|
version: "2"
|
||||||
run:
|
run:
|
||||||
go: "1.25"
|
go: "1.24"
|
||||||
build-tags:
|
build-tags:
|
||||||
- with_gvisor
|
- with_gvisor
|
||||||
- with_quic
|
- with_quic
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ 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,badlinkname,tfogo_checklinkname0" \
|
"with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale" \
|
||||||
-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=" \
|
||||||
./cmd/sing-box
|
./cmd/sing-box
|
||||||
FROM --platform=$TARGETPLATFORM alpine AS dist
|
FROM --platform=$TARGETPLATFORM alpine AS dist
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
|
|||||||
10
Makefile
10
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,badlinkname,tfogo_checklinkname0
|
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale
|
||||||
|
|
||||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||||
@@ -17,10 +17,6 @@ build:
|
|||||||
export GOTOOLCHAIN=local && \
|
export GOTOOLCHAIN=local && \
|
||||||
go build $(MAIN_PARAMS) $(MAIN)
|
go build $(MAIN_PARAMS) $(MAIN)
|
||||||
|
|
||||||
race:
|
|
||||||
export GOTOOLCHAIN=local && \
|
|
||||||
go build -race $(MAIN_PARAMS) $(MAIN)
|
|
||||||
|
|
||||||
ci_build:
|
ci_build:
|
||||||
export GOTOOLCHAIN=local && \
|
export GOTOOLCHAIN=local && \
|
||||||
go build $(PARAMS) $(MAIN) && \
|
go build $(PARAMS) $(MAIN) && \
|
||||||
@@ -38,7 +34,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@v0.8.0
|
go install -v mvdan.cc/gofumpt@latest
|
||||||
go install -v github.com/daixiang0/gci@latest
|
go install -v github.com/daixiang0/gci@latest
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
@@ -49,7 +45,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@v2.4.0
|
go install -v github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||||
|
|
||||||
proto:
|
proto:
|
||||||
@go run ./cmd/internal/protogen
|
@go run ./cmd/internal/protogen
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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"
|
||||||
@@ -56,7 +57,6 @@ type InboundContext struct {
|
|||||||
Domain string
|
Domain string
|
||||||
Client string
|
Client string
|
||||||
SniffContext any
|
SniffContext any
|
||||||
SnifferNames []string
|
|
||||||
SniffError error
|
SniffError error
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
@@ -84,7 +84,7 @@ type InboundContext struct {
|
|||||||
DestinationAddresses []netip.Addr
|
DestinationAddresses []netip.Addr
|
||||||
SourceGeoIPCode string
|
SourceGeoIPCode string
|
||||||
GeoIPCode string
|
GeoIPCode string
|
||||||
ProcessInfo *ConnectionOwner
|
ProcessInfo *process.Info
|
||||||
QueryType uint16
|
QueryType uint16
|
||||||
FakeIP bool
|
FakeIP bool
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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
|
||||||
@@ -25,10 +24,9 @@ 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
|
||||||
UpdateWIFIState()
|
|
||||||
ResetNetwork()
|
ResetNetwork()
|
||||||
|
UpdateWIFIState()
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkOptions struct {
|
type NetworkOptions struct {
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package adapter
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
|
||||||
|
|
||||||
"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-tun"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,16 +20,10 @@ type Outbound interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OutboundWithPreferredRoutes interface {
|
type OutboundWithPreferredRoutes interface {
|
||||||
Outbound
|
|
||||||
PreferredDomain(domain string) bool
|
PreferredDomain(domain string) bool
|
||||||
PreferredAddress(address netip.Addr) bool
|
PreferredAddress(address netip.Addr) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectRouteOutbound interface {
|
|
||||||
Outbound
|
|
||||||
NewDirectRouteConnection(metadata InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutboundRegistry interface {
|
type OutboundRegistry interface {
|
||||||
option.OutboundOptionsRegistry
|
option.OutboundOptionsRegistry
|
||||||
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -6,10 +6,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-tun"
|
|
||||||
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/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
@@ -21,9 +19,10 @@ import (
|
|||||||
type Router interface {
|
type Router interface {
|
||||||
Lifecycle
|
Lifecycle
|
||||||
ConnectionRouter
|
ConnectionRouter
|
||||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
PreMatch(metadata InboundContext) 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()
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
|
|||||||
// Deprecated: removed
|
// Deprecated: removed
|
||||||
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
func UpstreamMetadata(metadata InboundContext) M.Metadata {
|
||||||
return M.Metadata{
|
return M.Metadata{
|
||||||
Source: metadata.Source.Unwrap(),
|
Source: metadata.Source,
|
||||||
Destination: metadata.Destination.Unwrap(),
|
Destination: metadata.Destination,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
box.go
9
box.go
@@ -22,6 +22,7 @@ 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"
|
||||||
@@ -138,7 +139,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[adapter.PlatformInterface](ctx)
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
var defaultLogWriter io.Writer
|
var defaultLogWriter io.Writer
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
defaultLogWriter = io.Discard
|
defaultLogWriter = io.Discard
|
||||||
@@ -183,7 +184,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, dnsOptions)
|
networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize network manager")
|
return nil, E.Cause(err, "initialize network manager")
|
||||||
}
|
}
|
||||||
@@ -526,7 +527,3 @@ 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: e08fbfcfea...875f572f64
Submodule clients/apple updated: 84d8cf1757...c5734677bd
@@ -134,7 +134,6 @@ func publishTestflight(ctx context.Context) error {
|
|||||||
asc.PlatformTVOS,
|
asc.PlatformTVOS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
waitingForProcess := false
|
|
||||||
for _, platform := range platforms {
|
for _, platform := range platforms {
|
||||||
log.Info(string(platform), " list builds")
|
log.Info(string(platform), " list builds")
|
||||||
for {
|
for {
|
||||||
@@ -146,13 +145,12 @@ func publishTestflight(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
build := builds.Data[0]
|
build := builds.Data[0]
|
||||||
if !waitingForProcess && (common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute) {
|
if common.Contains(buildIDs, build.ID) || time.Since(build.Attributes.UploadedDate.Time) > 30*time.Minute {
|
||||||
log.Info(string(platform), " ", tag, " waiting for process")
|
log.Info(string(platform), " ", tag, " waiting for process")
|
||||||
time.Sleep(15 * time.Second)
|
time.Sleep(15 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if *build.Attributes.ProcessingState != "VALID" {
|
if *build.Attributes.ProcessingState != "VALID" {
|
||||||
waitingForProcess = true
|
|
||||||
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
|
log.Info(string(platform), " ", tag, " waiting for process: ", *build.Attributes.ProcessingState)
|
||||||
time.Sleep(15 * time.Second)
|
time.Sleep(15 * time.Second)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ var (
|
|||||||
sharedFlags []string
|
sharedFlags []string
|
||||||
debugFlags []string
|
debugFlags []string
|
||||||
sharedTags []string
|
sharedTags []string
|
||||||
macOSTags []string
|
darwinTags []string
|
||||||
memcTags []string
|
memcTags []string
|
||||||
notMemcTags []string
|
notMemcTags []string
|
||||||
debugTags []string
|
debugTags []string
|
||||||
@@ -59,11 +59,11 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
currentTag = "unknown"
|
currentTag = "unknown"
|
||||||
}
|
}
|
||||||
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+"-s -w -buildid= -checklinkname=0")
|
||||||
|
|
||||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
|
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api", "with_conntrack")
|
||||||
macOSTags = append(macOSTags, "with_dhcp")
|
darwinTags = append(darwinTags, "with_dhcp")
|
||||||
memcTags = append(memcTags, "with_tailscale")
|
memcTags = append(memcTags, "with_tailscale")
|
||||||
notMemcTags = append(notMemcTags, "with_low_memory")
|
notMemcTags = append(notMemcTags, "with_low_memory")
|
||||||
debugTags = append(debugTags, "debug")
|
debugTags = append(debugTags, "debug")
|
||||||
@@ -106,17 +106,17 @@ func buildAndroid() {
|
|||||||
"-libname=box",
|
"-libname=box",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags := append(sharedTags, memcTags...)
|
||||||
|
if debugEnabled {
|
||||||
|
tags = append(tags, debugTags...)
|
||||||
|
}
|
||||||
|
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
args = append(args, sharedFlags...)
|
args = append(args, sharedFlags...)
|
||||||
} else {
|
} else {
|
||||||
args = append(args, debugFlags...)
|
args = append(args, debugFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := append(sharedTags, memcTags...)
|
|
||||||
if debugEnabled {
|
|
||||||
tags = append(tags, debugTags...)
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, "-tags", strings.Join(tags, ","))
|
args = append(args, "-tags", strings.Join(tags, ","))
|
||||||
args = append(args, "./experimental/libbox")
|
args = append(args, "./experimental/libbox")
|
||||||
|
|
||||||
@@ -158,9 +158,7 @@ func buildApple() {
|
|||||||
"-tags-not-macos=with_low_memory",
|
"-tags-not-macos=with_low_memory",
|
||||||
}
|
}
|
||||||
if !withTailscale {
|
if !withTailscale {
|
||||||
args = append(args, "-tags-macos="+strings.Join(append(macOSTags, memcTags...), ","))
|
args = append(args, "-tags-macos="+strings.Join(memcTags, ","))
|
||||||
} else {
|
|
||||||
args = append(args, "-tags-macos="+strings.Join(macOSTags, ","))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
@@ -169,7 +167,7 @@ func buildApple() {
|
|||||||
args = append(args, debugFlags...)
|
args = append(args, debugFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := sharedTags
|
tags := append(sharedTags, darwinTags...)
|
||||||
if withTailscale {
|
if withTailscale {
|
||||||
tags = append(tags, memcTags...)
|
tags = append(tags, memcTags...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.Conn, error) {
|
Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, 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,176 +0,0 @@
|
|||||||
//go:build go1.25 && badlinkname
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"sync/atomic"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RawConn struct {
|
|
||||||
pointer unsafe.Pointer
|
|
||||||
methods *Methods
|
|
||||||
|
|
||||||
IsClient *bool
|
|
||||||
IsHandshakeComplete *atomic.Bool
|
|
||||||
Vers *uint16
|
|
||||||
CipherSuite *uint16
|
|
||||||
|
|
||||||
RawInput *bytes.Buffer
|
|
||||||
Input *bytes.Reader
|
|
||||||
Hand *bytes.Buffer
|
|
||||||
|
|
||||||
CloseNotifySent *bool
|
|
||||||
CloseNotifyErr *error
|
|
||||||
|
|
||||||
In *RawHalfConn
|
|
||||||
Out *RawHalfConn
|
|
||||||
|
|
||||||
BytesSent *int64
|
|
||||||
PacketsSent *int64
|
|
||||||
|
|
||||||
ActiveCall *atomic.Int32
|
|
||||||
Tmp *[16]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRawConn(rawTLSConn tls.Conn) (*RawConn, error) {
|
|
||||||
var (
|
|
||||||
pointer unsafe.Pointer
|
|
||||||
methods *Methods
|
|
||||||
loaded bool
|
|
||||||
)
|
|
||||||
for _, tlsCreator := range methodRegistry {
|
|
||||||
pointer, methods, loaded = tlsCreator(rawTLSConn)
|
|
||||||
if loaded {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !loaded {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := &RawConn{
|
|
||||||
pointer: pointer,
|
|
||||||
methods: methods,
|
|
||||||
}
|
|
||||||
|
|
||||||
rawConn := reflect.Indirect(reflect.ValueOf(rawTLSConn))
|
|
||||||
|
|
||||||
rawIsClient := rawConn.FieldByName("isClient")
|
|
||||||
if !rawIsClient.IsValid() || rawIsClient.Kind() != reflect.Bool {
|
|
||||||
return nil, E.New("invalid Conn.isClient")
|
|
||||||
}
|
|
||||||
conn.IsClient = (*bool)(unsafe.Pointer(rawIsClient.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawIsHandshakeComplete := rawConn.FieldByName("isHandshakeComplete")
|
|
||||||
if !rawIsHandshakeComplete.IsValid() || rawIsHandshakeComplete.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.isHandshakeComplete")
|
|
||||||
}
|
|
||||||
conn.IsHandshakeComplete = (*atomic.Bool)(unsafe.Pointer(rawIsHandshakeComplete.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawVers := rawConn.FieldByName("vers")
|
|
||||||
if !rawVers.IsValid() || rawVers.Kind() != reflect.Uint16 {
|
|
||||||
return nil, E.New("invalid Conn.vers")
|
|
||||||
}
|
|
||||||
conn.Vers = (*uint16)(unsafe.Pointer(rawVers.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCipherSuite := rawConn.FieldByName("cipherSuite")
|
|
||||||
if !rawCipherSuite.IsValid() || rawCipherSuite.Kind() != reflect.Uint16 {
|
|
||||||
return nil, E.New("invalid Conn.cipherSuite")
|
|
||||||
}
|
|
||||||
conn.CipherSuite = (*uint16)(unsafe.Pointer(rawCipherSuite.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawRawInput := rawConn.FieldByName("rawInput")
|
|
||||||
if !rawRawInput.IsValid() || rawRawInput.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.rawInput")
|
|
||||||
}
|
|
||||||
conn.RawInput = (*bytes.Buffer)(unsafe.Pointer(rawRawInput.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawInput := rawConn.FieldByName("input")
|
|
||||||
if !rawInput.IsValid() || rawInput.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.input")
|
|
||||||
}
|
|
||||||
conn.Input = (*bytes.Reader)(unsafe.Pointer(rawInput.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawHand := rawConn.FieldByName("hand")
|
|
||||||
if !rawHand.IsValid() || rawHand.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.hand")
|
|
||||||
}
|
|
||||||
conn.Hand = (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCloseNotifySent := rawConn.FieldByName("closeNotifySent")
|
|
||||||
if !rawCloseNotifySent.IsValid() || rawCloseNotifySent.Kind() != reflect.Bool {
|
|
||||||
return nil, E.New("invalid Conn.closeNotifySent")
|
|
||||||
}
|
|
||||||
conn.CloseNotifySent = (*bool)(unsafe.Pointer(rawCloseNotifySent.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCloseNotifyErr := rawConn.FieldByName("closeNotifyErr")
|
|
||||||
if !rawCloseNotifyErr.IsValid() || rawCloseNotifyErr.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("invalid Conn.closeNotifyErr")
|
|
||||||
}
|
|
||||||
conn.CloseNotifyErr = (*error)(unsafe.Pointer(rawCloseNotifyErr.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawIn := rawConn.FieldByName("in")
|
|
||||||
if !rawIn.IsValid() || rawIn.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.in")
|
|
||||||
}
|
|
||||||
halfIn, err := NewRawHalfConn(rawIn, methods)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid Conn.in")
|
|
||||||
}
|
|
||||||
conn.In = halfIn
|
|
||||||
|
|
||||||
rawOut := rawConn.FieldByName("out")
|
|
||||||
if !rawOut.IsValid() || rawOut.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.out")
|
|
||||||
}
|
|
||||||
halfOut, err := NewRawHalfConn(rawOut, methods)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "invalid Conn.out")
|
|
||||||
}
|
|
||||||
conn.Out = halfOut
|
|
||||||
|
|
||||||
rawBytesSent := rawConn.FieldByName("bytesSent")
|
|
||||||
if !rawBytesSent.IsValid() || rawBytesSent.Kind() != reflect.Int64 {
|
|
||||||
return nil, E.New("invalid Conn.bytesSent")
|
|
||||||
}
|
|
||||||
conn.BytesSent = (*int64)(unsafe.Pointer(rawBytesSent.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawPacketsSent := rawConn.FieldByName("packetsSent")
|
|
||||||
if !rawPacketsSent.IsValid() || rawPacketsSent.Kind() != reflect.Int64 {
|
|
||||||
return nil, E.New("invalid Conn.packetsSent")
|
|
||||||
}
|
|
||||||
conn.PacketsSent = (*int64)(unsafe.Pointer(rawPacketsSent.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawActiveCall := rawConn.FieldByName("activeCall")
|
|
||||||
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("invalid Conn.activeCall")
|
|
||||||
}
|
|
||||||
conn.ActiveCall = (*atomic.Int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawTmp := rawConn.FieldByName("tmp")
|
|
||||||
if !rawTmp.IsValid() || rawTmp.Kind() != reflect.Array || rawTmp.Len() != 16 || rawTmp.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("invalid Conn.tmp")
|
|
||||||
}
|
|
||||||
conn.Tmp = (*[16]byte)(unsafe.Pointer(rawTmp.UnsafeAddr()))
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RawConn) ReadRecord() error {
|
|
||||||
return c.methods.readRecord(c.pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RawConn) HandlePostHandshakeMessage() error {
|
|
||||||
return c.methods.handlePostHandshakeMessage(c.pointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RawConn) WriteRecordLocked(typ uint16, data []byte) (int, error) {
|
|
||||||
return c.methods.writeRecordLocked(c.pointer, typ, data)
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
//go:build go1.25 && badlinkname
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"hash"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RawHalfConn struct {
|
|
||||||
pointer unsafe.Pointer
|
|
||||||
methods *Methods
|
|
||||||
*sync.Mutex
|
|
||||||
Err *error
|
|
||||||
Version *uint16
|
|
||||||
Cipher *any
|
|
||||||
Seq *[8]byte
|
|
||||||
ScratchBuf *[13]byte
|
|
||||||
TrafficSecret *[]byte
|
|
||||||
Mac *hash.Hash
|
|
||||||
RawKey *[]byte
|
|
||||||
RawIV *[]byte
|
|
||||||
RawMac *[]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRawHalfConn(rawHalfConn reflect.Value, methods *Methods) (*RawHalfConn, error) {
|
|
||||||
halfConn := &RawHalfConn{
|
|
||||||
pointer: (unsafe.Pointer)(rawHalfConn.UnsafeAddr()),
|
|
||||||
methods: methods,
|
|
||||||
}
|
|
||||||
|
|
||||||
rawMutex := rawHalfConn.FieldByName("Mutex")
|
|
||||||
if !rawMutex.IsValid() || rawMutex.Kind() != reflect.Struct {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.Mutex")
|
|
||||||
}
|
|
||||||
halfConn.Mutex = (*sync.Mutex)(unsafe.Pointer(rawMutex.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawErr := rawHalfConn.FieldByName("err")
|
|
||||||
if !rawErr.IsValid() || rawErr.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.err")
|
|
||||||
}
|
|
||||||
halfConn.Err = (*error)(unsafe.Pointer(rawErr.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawVersion := rawHalfConn.FieldByName("version")
|
|
||||||
if !rawVersion.IsValid() || rawVersion.Kind() != reflect.Uint16 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.version")
|
|
||||||
}
|
|
||||||
halfConn.Version = (*uint16)(unsafe.Pointer(rawVersion.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawCipher := rawHalfConn.FieldByName("cipher")
|
|
||||||
if !rawCipher.IsValid() || rawCipher.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.cipher")
|
|
||||||
}
|
|
||||||
halfConn.Cipher = (*any)(unsafe.Pointer(rawCipher.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawSeq := rawHalfConn.FieldByName("seq")
|
|
||||||
if !rawSeq.IsValid() || rawSeq.Kind() != reflect.Array || rawSeq.Len() != 8 || rawSeq.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.seq")
|
|
||||||
}
|
|
||||||
halfConn.Seq = (*[8]byte)(unsafe.Pointer(rawSeq.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawScratchBuf := rawHalfConn.FieldByName("scratchBuf")
|
|
||||||
if !rawScratchBuf.IsValid() || rawScratchBuf.Kind() != reflect.Array || rawScratchBuf.Len() != 13 || rawScratchBuf.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.scratchBuf")
|
|
||||||
}
|
|
||||||
halfConn.ScratchBuf = (*[13]byte)(unsafe.Pointer(rawScratchBuf.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawTrafficSecret := rawHalfConn.FieldByName("trafficSecret")
|
|
||||||
if !rawTrafficSecret.IsValid() || rawTrafficSecret.Kind() != reflect.Slice || rawTrafficSecret.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.trafficSecret")
|
|
||||||
}
|
|
||||||
halfConn.TrafficSecret = (*[]byte)(unsafe.Pointer(rawTrafficSecret.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawMac := rawHalfConn.FieldByName("mac")
|
|
||||||
if !rawMac.IsValid() || rawMac.Kind() != reflect.Interface {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.mac")
|
|
||||||
}
|
|
||||||
halfConn.Mac = (*hash.Hash)(unsafe.Pointer(rawMac.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawKey := rawHalfConn.FieldByName("rawKey")
|
|
||||||
if rawKey.IsValid() {
|
|
||||||
if /*!rawKey.IsValid() || */ rawKey.Kind() != reflect.Slice || rawKey.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.rawKey")
|
|
||||||
}
|
|
||||||
halfConn.RawKey = (*[]byte)(unsafe.Pointer(rawKey.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawIV := rawHalfConn.FieldByName("rawIV")
|
|
||||||
if !rawIV.IsValid() || rawIV.Kind() != reflect.Slice || rawIV.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.rawIV")
|
|
||||||
}
|
|
||||||
halfConn.RawIV = (*[]byte)(unsafe.Pointer(rawIV.UnsafeAddr()))
|
|
||||||
|
|
||||||
rawMAC := rawHalfConn.FieldByName("rawMac")
|
|
||||||
if !rawMAC.IsValid() || rawMAC.Kind() != reflect.Slice || rawMAC.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, E.New("badtls: invalid halfConn.rawMac")
|
|
||||||
}
|
|
||||||
halfConn.RawMac = (*[]byte)(unsafe.Pointer(rawMAC.UnsafeAddr()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return halfConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) Decrypt(record []byte) ([]byte, uint8, error) {
|
|
||||||
return hc.methods.decrypt(hc.pointer, record)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) SetErrorLocked(err error) error {
|
|
||||||
return hc.methods.setErrorLocked(hc.pointer, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) SetTrafficSecret(suite unsafe.Pointer, level int, secret []byte) {
|
|
||||||
hc.methods.setTrafficSecret(hc.pointer, suite, level, secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hc *RawHalfConn) ExplicitNonceLen() int {
|
|
||||||
return hc.methods.explicitNonceLen(hc.pointer)
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
//go:build go1.25 && badlinkname
|
//go:build go1.21 && !without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/tls"
|
"github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
@@ -12,21 +21,63 @@ var _ N.ReadWaiter = (*ReadWaitConn)(nil)
|
|||||||
|
|
||||||
type ReadWaitConn struct {
|
type ReadWaitConn struct {
|
||||||
tls.Conn
|
tls.Conn
|
||||||
rawConn *RawConn
|
halfAccess *sync.Mutex
|
||||||
readWaitOptions N.ReadWaitOptions
|
rawInput *bytes.Buffer
|
||||||
|
input *bytes.Reader
|
||||||
|
hand *bytes.Buffer
|
||||||
|
readWaitOptions N.ReadWaitOptions
|
||||||
|
tlsReadRecord func() error
|
||||||
|
tlsHandlePostHandshakeMessage func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
|
||||||
if _, isReadWaitConn := conn.(N.ReadWaiter); isReadWaitConn {
|
var (
|
||||||
return conn, nil
|
loaded bool
|
||||||
|
tlsReadRecord func() error
|
||||||
|
tlsHandlePostHandshakeMessage func() error
|
||||||
|
)
|
||||||
|
for _, tlsCreator := range tlsRegistry {
|
||||||
|
loaded, tlsReadRecord, tlsHandlePostHandshakeMessage = tlsCreator(conn)
|
||||||
|
if loaded {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rawConn, err := NewRawConn(conn)
|
if !loaded {
|
||||||
if err != nil {
|
return nil, os.ErrInvalid
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||||
|
rawHalfConn := rawConn.FieldByName("in")
|
||||||
|
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid half conn")
|
||||||
|
}
|
||||||
|
rawHalfMutex := rawHalfConn.FieldByName("Mutex")
|
||||||
|
if !rawHalfMutex.IsValid() || rawHalfMutex.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid half mutex")
|
||||||
|
}
|
||||||
|
halfAccess := (*sync.Mutex)(unsafe.Pointer(rawHalfMutex.UnsafeAddr()))
|
||||||
|
rawRawInput := rawConn.FieldByName("rawInput")
|
||||||
|
if !rawRawInput.IsValid() || rawRawInput.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid raw input")
|
||||||
|
}
|
||||||
|
rawInput := (*bytes.Buffer)(unsafe.Pointer(rawRawInput.UnsafeAddr()))
|
||||||
|
rawInput0 := rawConn.FieldByName("input")
|
||||||
|
if !rawInput0.IsValid() || rawInput0.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid input")
|
||||||
|
}
|
||||||
|
input := (*bytes.Reader)(unsafe.Pointer(rawInput0.UnsafeAddr()))
|
||||||
|
rawHand := rawConn.FieldByName("hand")
|
||||||
|
if !rawHand.IsValid() || rawHand.Kind() != reflect.Struct {
|
||||||
|
return nil, E.New("badtls: invalid hand")
|
||||||
|
}
|
||||||
|
hand := (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
|
||||||
return &ReadWaitConn{
|
return &ReadWaitConn{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
rawConn: rawConn,
|
halfAccess: halfAccess,
|
||||||
|
rawInput: rawInput,
|
||||||
|
input: input,
|
||||||
|
hand: hand,
|
||||||
|
tlsReadRecord: tlsReadRecord,
|
||||||
|
tlsHandlePostHandshakeMessage: tlsHandlePostHandshakeMessage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,36 +87,36 @@ func (c *ReadWaitConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
||||||
//err = c.HandshakeContext(context.Background())
|
err = c.HandshakeContext(context.Background())
|
||||||
//if err != nil {
|
if err != nil {
|
||||||
// return
|
return
|
||||||
//}
|
}
|
||||||
c.rawConn.In.Lock()
|
c.halfAccess.Lock()
|
||||||
defer c.rawConn.In.Unlock()
|
defer c.halfAccess.Unlock()
|
||||||
for c.rawConn.Input.Len() == 0 {
|
for c.input.Len() == 0 {
|
||||||
err = c.rawConn.ReadRecord()
|
err = c.tlsReadRecord()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for c.rawConn.Hand.Len() > 0 {
|
for c.hand.Len() > 0 {
|
||||||
err = c.rawConn.HandlePostHandshakeMessage()
|
err = c.tlsHandlePostHandshakeMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer = c.readWaitOptions.NewBuffer()
|
buffer = c.readWaitOptions.NewBuffer()
|
||||||
n, err := c.rawConn.Input.Read(buffer.FreeBytes())
|
n, err := c.input.Read(buffer.FreeBytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buffer.Truncate(n)
|
buffer.Truncate(n)
|
||||||
|
|
||||||
if n != 0 && c.rawConn.Input.Len() == 0 && c.rawConn.Input.Len() > 0 &&
|
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
|
||||||
// recordType(c.RawInput.Bytes()[0]) == recordTypeAlert {
|
// recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
|
||||||
c.rawConn.RawInput.Bytes()[0] == 21 {
|
c.rawInput.Bytes()[0] == 21 {
|
||||||
_ = c.rawConn.ReadRecord()
|
_ = c.tlsReadRecord()
|
||||||
// return n, err // will be io.EOF on closeNotify
|
// return n, err // will be io.EOF on closeNotify
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +128,24 @@ func (c *ReadWaitConn) Upstream() any {
|
|||||||
return c.Conn
|
return c.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ReadWaitConn) ReaderReplaceable() bool {
|
var tlsRegistry []func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error)
|
||||||
return true
|
|
||||||
|
func init() {
|
||||||
|
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||||
|
tlsConn, loaded := conn.(*tls.STDConn)
|
||||||
|
if !loaded {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return true, func() error {
|
||||||
|
return stdTLSReadRecord(tlsConn)
|
||||||
|
}, func() error {
|
||||||
|
return stdTLSHandlePostHandshakeMessage(tlsConn)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname stdTLSReadRecord crypto/tls.(*Conn).readRecord
|
||||||
|
func stdTLSReadRecord(c *tls.STDConn) error
|
||||||
|
|
||||||
|
//go:linkname stdTLSHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
||||||
|
func stdTLSHandlePostHandshakeMessage(c *tls.STDConn) error
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !go1.25 || !badlinkname
|
//go:build !go1.21 || without_badtls
|
||||||
|
|
||||||
package badtls
|
package badtls
|
||||||
|
|
||||||
|
|||||||
32
common/badtls/read_wait_utls.go
Normal file
32
common/badtls/read_wait_utls.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//go:build go1.21 && !without_badtls && with_utls
|
||||||
|
|
||||||
|
package badtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
|
||||||
|
"github.com/metacubex/utls"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tlsRegistry = append(tlsRegistry, func(conn net.Conn) (loaded bool, tlsReadRecord func() error, tlsHandlePostHandshakeMessage func() error) {
|
||||||
|
tlsConn, loaded := common.Cast[*tls.UConn](conn)
|
||||||
|
if !loaded {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return true, func() error {
|
||||||
|
return utlsReadRecord(tlsConn.Conn)
|
||||||
|
}, func() error {
|
||||||
|
return utlsHandlePostHandshakeMessage(tlsConn.Conn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname utlsReadRecord github.com/metacubex/utls.(*Conn).readRecord
|
||||||
|
func utlsReadRecord(c *tls.Conn) error
|
||||||
|
|
||||||
|
//go:linkname utlsHandlePostHandshakeMessage github.com/metacubex/utls.(*Conn).handlePostHandshakeMessage
|
||||||
|
func utlsHandlePostHandshakeMessage(c *tls.Conn) error
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
//go:build go1.25 && badlinkname
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Methods struct {
|
|
||||||
readRecord func(c unsafe.Pointer) error
|
|
||||||
handlePostHandshakeMessage func(c unsafe.Pointer) error
|
|
||||||
writeRecordLocked func(c unsafe.Pointer, typ uint16, data []byte) (int, error)
|
|
||||||
|
|
||||||
setErrorLocked func(hc unsafe.Pointer, err error) error
|
|
||||||
decrypt func(hc unsafe.Pointer, record []byte) ([]byte, uint8, error)
|
|
||||||
setTrafficSecret func(hc unsafe.Pointer, suite unsafe.Pointer, level int, secret []byte)
|
|
||||||
explicitNonceLen func(hc unsafe.Pointer) int
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodRegistry []func(conn net.Conn) (unsafe.Pointer, *Methods, bool)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
methodRegistry = append(methodRegistry, func(conn net.Conn) (unsafe.Pointer, *Methods, bool) {
|
|
||||||
tlsConn, loaded := conn.(*tls.Conn)
|
|
||||||
if !loaded {
|
|
||||||
return nil, nil, false
|
|
||||||
}
|
|
||||||
return unsafe.Pointer(tlsConn), &Methods{
|
|
||||||
readRecord: stdTLSReadRecord,
|
|
||||||
handlePostHandshakeMessage: stdTLSHandlePostHandshakeMessage,
|
|
||||||
writeRecordLocked: stdWriteRecordLocked,
|
|
||||||
|
|
||||||
setErrorLocked: stdSetErrorLocked,
|
|
||||||
decrypt: stdDecrypt,
|
|
||||||
setTrafficSecret: stdSetTrafficSecret,
|
|
||||||
explicitNonceLen: stdExplicitNonceLen,
|
|
||||||
}, true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname stdTLSReadRecord crypto/tls.(*Conn).readRecord
|
|
||||||
func stdTLSReadRecord(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname stdTLSHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
|
|
||||||
func stdTLSHandlePostHandshakeMessage(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname stdWriteRecordLocked crypto/tls.(*Conn).writeRecordLocked
|
|
||||||
func stdWriteRecordLocked(c unsafe.Pointer, typ uint16, data []byte) (int, error)
|
|
||||||
|
|
||||||
//go:linkname stdSetErrorLocked crypto/tls.(*halfConn).setErrorLocked
|
|
||||||
func stdSetErrorLocked(hc unsafe.Pointer, err error) error
|
|
||||||
|
|
||||||
//go:linkname stdDecrypt crypto/tls.(*halfConn).decrypt
|
|
||||||
func stdDecrypt(hc unsafe.Pointer, record []byte) ([]byte, uint8, error)
|
|
||||||
|
|
||||||
//go:linkname stdSetTrafficSecret crypto/tls.(*halfConn).setTrafficSecret
|
|
||||||
func stdSetTrafficSecret(hc unsafe.Pointer, suite unsafe.Pointer, level int, secret []byte)
|
|
||||||
|
|
||||||
//go:linkname stdExplicitNonceLen crypto/tls.(*halfConn).explicitNonceLen
|
|
||||||
func stdExplicitNonceLen(hc unsafe.Pointer) int
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
//go:build go1.25 && badlinkname
|
|
||||||
|
|
||||||
package badtls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
|
|
||||||
"github.com/metacubex/utls"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
methodRegistry = append(methodRegistry, func(conn net.Conn) (unsafe.Pointer, *Methods, bool) {
|
|
||||||
var pointer unsafe.Pointer
|
|
||||||
if uConn, loaded := N.CastReader[*tls.Conn](conn); loaded {
|
|
||||||
pointer = unsafe.Pointer(uConn)
|
|
||||||
} else if uConn, loaded := N.CastReader[*tls.UConn](conn); loaded {
|
|
||||||
pointer = unsafe.Pointer(uConn.Conn)
|
|
||||||
} else {
|
|
||||||
return nil, nil, false
|
|
||||||
}
|
|
||||||
return pointer, &Methods{
|
|
||||||
readRecord: utlsReadRecord,
|
|
||||||
handlePostHandshakeMessage: utlsHandlePostHandshakeMessage,
|
|
||||||
writeRecordLocked: utlsWriteRecordLocked,
|
|
||||||
|
|
||||||
setErrorLocked: utlsSetErrorLocked,
|
|
||||||
decrypt: utlsDecrypt,
|
|
||||||
setTrafficSecret: utlsSetTrafficSecret,
|
|
||||||
explicitNonceLen: utlsExplicitNonceLen,
|
|
||||||
}, true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname utlsReadRecord github.com/metacubex/utls.(*Conn).readRecord
|
|
||||||
func utlsReadRecord(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname utlsHandlePostHandshakeMessage github.com/metacubex/utls.(*Conn).handlePostHandshakeMessage
|
|
||||||
func utlsHandlePostHandshakeMessage(c unsafe.Pointer) error
|
|
||||||
|
|
||||||
//go:linkname utlsWriteRecordLocked github.com/metacubex/utls.(*Conn).writeRecordLocked
|
|
||||||
func utlsWriteRecordLocked(hc unsafe.Pointer, typ uint16, data []byte) (int, error)
|
|
||||||
|
|
||||||
//go:linkname utlsSetErrorLocked github.com/metacubex/utls.(*halfConn).setErrorLocked
|
|
||||||
func utlsSetErrorLocked(hc unsafe.Pointer, err error) error
|
|
||||||
|
|
||||||
//go:linkname utlsDecrypt github.com/metacubex/utls.(*halfConn).decrypt
|
|
||||||
func utlsDecrypt(hc unsafe.Pointer, record []byte) ([]byte, uint8, error)
|
|
||||||
|
|
||||||
//go:linkname utlsSetTrafficSecret github.com/metacubex/utls.(*halfConn).setTrafficSecret
|
|
||||||
func utlsSetTrafficSecret(hc unsafe.Pointer, suite unsafe.Pointer, level int, secret []byte)
|
|
||||||
|
|
||||||
//go:linkname utlsExplicitNonceLen github.com/metacubex/utls.(*halfConn).explicitNonceLen
|
|
||||||
func utlsExplicitNonceLen(hc unsafe.Pointer) int
|
|
||||||
@@ -5,8 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
"golang.org/x/mod/semver"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Version struct {
|
type Version struct {
|
||||||
@@ -18,19 +16,7 @@ type Version struct {
|
|||||||
PreReleaseVersion int
|
PreReleaseVersion int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Version) LessThan(anotherVersion Version) bool {
|
func (v Version) After(anotherVersion Version) bool {
|
||||||
return !v.GreaterThanOrEqual(anotherVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Version) LessThanOrEqual(anotherVersion Version) bool {
|
|
||||||
return v == anotherVersion || anotherVersion.GreaterThan(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Version) GreaterThanOrEqual(anotherVersion Version) bool {
|
|
||||||
return v == anotherVersion || v.GreaterThan(anotherVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Version) GreaterThan(anotherVersion Version) bool {
|
|
||||||
if v.Major > anotherVersion.Major {
|
if v.Major > anotherVersion.Major {
|
||||||
return true
|
return true
|
||||||
} else if v.Major < anotherVersion.Major {
|
} else if v.Major < anotherVersion.Major {
|
||||||
@@ -58,29 +44,19 @@ func (v Version) GreaterThan(anotherVersion Version) bool {
|
|||||||
} else if v.PreReleaseVersion < anotherVersion.PreReleaseVersion {
|
} else if v.PreReleaseVersion < anotherVersion.PreReleaseVersion {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
} else if v.PreReleaseIdentifier == "rc" && anotherVersion.PreReleaseIdentifier == "beta" {
|
||||||
preReleaseIdentifier := parsePreReleaseIdentifier(v.PreReleaseIdentifier)
|
|
||||||
anotherPreReleaseIdentifier := parsePreReleaseIdentifier(anotherVersion.PreReleaseIdentifier)
|
|
||||||
if preReleaseIdentifier < anotherPreReleaseIdentifier {
|
|
||||||
return true
|
return true
|
||||||
} else if preReleaseIdentifier > anotherPreReleaseIdentifier {
|
} else if v.PreReleaseIdentifier == "beta" && anotherVersion.PreReleaseIdentifier == "rc" {
|
||||||
|
return false
|
||||||
|
} else if v.PreReleaseIdentifier == "beta" && anotherVersion.PreReleaseIdentifier == "alpha" {
|
||||||
|
return true
|
||||||
|
} else if v.PreReleaseIdentifier == "alpha" && anotherVersion.PreReleaseIdentifier == "beta" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePreReleaseIdentifier(identifier string) int {
|
|
||||||
if strings.HasPrefix(identifier, "rc") {
|
|
||||||
return 1
|
|
||||||
} else if strings.HasPrefix(identifier, "beta") {
|
|
||||||
return 2
|
|
||||||
} else if strings.HasPrefix(identifier, "alpha") {
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Version) VersionString() string {
|
func (v Version) VersionString() string {
|
||||||
return F.ToString(v.Major, ".", v.Minor, ".", v.Patch)
|
return F.ToString(v.Major, ".", v.Minor, ".", v.Patch)
|
||||||
}
|
}
|
||||||
@@ -107,10 +83,6 @@ func (v Version) BadString() string {
|
|||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsValid(versionName string) bool {
|
|
||||||
return semver.IsValid("v" + versionName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Parse(versionName string) (version Version) {
|
func Parse(versionName string) (version Version) {
|
||||||
if strings.HasPrefix(versionName, "v") {
|
if strings.HasPrefix(versionName, "v") {
|
||||||
versionName = versionName[1:]
|
versionName = versionName[1:]
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ func TestCompareVersion(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
require.Equal(t, "1.3.0-beta.1", Parse("v1.3.0-beta1").String())
|
require.Equal(t, "1.3.0-beta.1", Parse("v1.3.0-beta1").String())
|
||||||
require.Equal(t, "1.3-beta1", Parse("v1.3.0-beta.1").BadString())
|
require.Equal(t, "1.3-beta1", Parse("v1.3.0-beta.1").BadString())
|
||||||
require.True(t, Parse("1.3.0").GreaterThan(Parse("1.3-beta1")))
|
require.True(t, Parse("1.3.0").After(Parse("1.3-beta1")))
|
||||||
require.True(t, Parse("1.3.0").GreaterThan(Parse("1.3.0-beta1")))
|
require.True(t, Parse("1.3.0").After(Parse("1.3.0-beta1")))
|
||||||
require.True(t, Parse("1.3.0-beta1").GreaterThan(Parse("1.3.0-alpha1")))
|
require.True(t, Parse("1.3.0-beta1").After(Parse("1.3.0-alpha1")))
|
||||||
require.True(t, Parse("1.3.1").GreaterThan(Parse("1.3.0")))
|
require.True(t, Parse("1.3.1").After(Parse("1.3.0")))
|
||||||
require.True(t, Parse("1.4").GreaterThan(Parse("1.3")))
|
require.True(t, Parse("1.4").After(Parse("1.3")))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"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"
|
||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
var _ adapter.CertificateStore = (*Store)(nil)
|
var _ adapter.CertificateStore = (*Store)(nil)
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
access sync.RWMutex
|
|
||||||
systemPool *x509.CertPool
|
systemPool *x509.CertPool
|
||||||
currentPool *x509.CertPool
|
currentPool *x509.CertPool
|
||||||
certificate string
|
certificate string
|
||||||
@@ -35,7 +34,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[adapter.PlatformInterface](ctx)
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
var systemValid bool
|
var systemValid bool
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
for _, cert := range platformInterface.SystemCertificates() {
|
for _, cert := range platformInterface.SystemCertificates() {
|
||||||
@@ -116,14 +115,10 @@ func (s *Store) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Pool() *x509.CertPool {
|
func (s *Store) Pool() *x509.CertPool {
|
||||||
s.access.RLock()
|
|
||||||
defer s.access.RUnlock()
|
|
||||||
return s.currentPool
|
return s.currentPool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) update() error {
|
func (s *Store) update() error {
|
||||||
s.access.Lock()
|
|
||||||
defer s.access.Unlock()
|
|
||||||
var currentPool *x509.CertPool
|
var currentPool *x509.CertPool
|
||||||
if s.systemPool == nil {
|
if s.systemPool == nil {
|
||||||
currentPool = x509.NewCertPool()
|
currentPool = x509.NewCertPool()
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ 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/atomic"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
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 (
|
||||||
@@ -29,8 +29,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DefaultDialer struct {
|
type DefaultDialer struct {
|
||||||
dialer4 tfo.Dialer
|
dialer4 tcpDialer
|
||||||
dialer6 tfo.Dialer
|
dialer6 tcpDialer
|
||||||
udpDialer4 net.Dialer
|
udpDialer4 net.Dialer
|
||||||
udpDialer6 net.Dialer
|
udpDialer6 net.Dialer
|
||||||
udpListener net.ListenConfig
|
udpListener net.ListenConfig
|
||||||
@@ -43,12 +43,12 @@ type DefaultDialer struct {
|
|||||||
networkType []C.InterfaceType
|
networkType []C.InterfaceType
|
||||||
fallbackNetworkType []C.InterfaceType
|
fallbackNetworkType []C.InterfaceType
|
||||||
networkFallbackDelay time.Duration
|
networkFallbackDelay time.Duration
|
||||||
networkLastFallback common.TypedValue[time.Time]
|
networkLastFallback atomic.TypedValue[time.Time]
|
||||||
}
|
}
|
||||||
|
|
||||||
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[adapter.PlatformInterface](ctx)
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dialer net.Dialer
|
dialer net.Dialer
|
||||||
@@ -89,40 +89,42 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
|
|||||||
|
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
defaultOptions := networkManager.DefaultOptions()
|
defaultOptions := networkManager.DefaultOptions()
|
||||||
if defaultOptions.BindInterface != "" {
|
if !disableDefaultBind {
|
||||||
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
if defaultOptions.BindInterface != "" {
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
|
||||||
} else if networkManager.AutoDetectInterface() && !disableDefaultBind {
|
|
||||||
if platformInterface != nil {
|
|
||||||
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
|
|
||||||
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
|
|
||||||
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
|
|
||||||
if networkStrategy == nil && len(networkType) == 0 && len(fallbackNetworkType) == 0 {
|
|
||||||
networkStrategy = defaultOptions.NetworkStrategy
|
|
||||||
networkType = defaultOptions.NetworkType
|
|
||||||
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
|
||||||
}
|
|
||||||
networkFallbackDelay = time.Duration(options.FallbackDelay)
|
|
||||||
if networkFallbackDelay == 0 && defaultOptions.FallbackDelay != 0 {
|
|
||||||
networkFallbackDelay = defaultOptions.FallbackDelay
|
|
||||||
}
|
|
||||||
if networkStrategy == nil {
|
|
||||||
networkStrategy = common.Ptr(C.NetworkStrategyDefault)
|
|
||||||
defaultNetworkStrategy = true
|
|
||||||
}
|
|
||||||
bindFunc := networkManager.ProtectFunc()
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
|
||||||
} else {
|
|
||||||
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
} else if networkManager.AutoDetectInterface() {
|
||||||
|
if platformInterface != nil {
|
||||||
|
networkStrategy = (*C.NetworkStrategy)(options.NetworkStrategy)
|
||||||
|
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
|
||||||
|
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
|
||||||
|
if networkStrategy == nil && len(networkType) == 0 && len(fallbackNetworkType) == 0 {
|
||||||
|
networkStrategy = defaultOptions.NetworkStrategy
|
||||||
|
networkType = defaultOptions.NetworkType
|
||||||
|
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
||||||
|
}
|
||||||
|
networkFallbackDelay = time.Duration(options.FallbackDelay)
|
||||||
|
if networkFallbackDelay == 0 && defaultOptions.FallbackDelay != 0 {
|
||||||
|
networkFallbackDelay = defaultOptions.FallbackDelay
|
||||||
|
}
|
||||||
|
if networkStrategy == nil {
|
||||||
|
networkStrategy = common.Ptr(C.NetworkStrategyDefault)
|
||||||
|
defaultNetworkStrategy = true
|
||||||
|
}
|
||||||
|
bindFunc := networkManager.ProtectFunc()
|
||||||
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
} else {
|
||||||
|
bindFunc := networkManager.AutoDetectInterfaceFunc()
|
||||||
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||||
|
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||||
|
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
|
||||||
dialer.Control = control.Append(dialer.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
|
||||||
listener.Control = control.Append(listener.Control, setMarkWrapper(networkManager, defaultOptions.RoutingMark, true))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
@@ -178,10 +180,19 @@ 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 {
|
||||||
dialer4.SetMultipathTCP(true)
|
if !go121Available {
|
||||||
|
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,
|
||||||
@@ -261,7 +272,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 = d.dialer4.Dialer
|
dialer = dialerFromTCPDialer(d.dialer4)
|
||||||
} else {
|
} else {
|
||||||
dialer = d.udpDialer4
|
dialer = d.udpDialer4
|
||||||
}
|
}
|
||||||
@@ -307,14 +318,6 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) DialerForICMPDestination(destination netip.Addr) net.Dialer {
|
|
||||||
if !destination.Is6() {
|
|
||||||
return d.dialer6.Dialer
|
|
||||||
} else {
|
|
||||||
return d.dialer4.Dialer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||||
if strategy == nil {
|
if strategy == nil {
|
||||||
strategy = d.networkStrategy
|
strategy = d.networkStrategy
|
||||||
@@ -348,8 +351,18 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
|||||||
return trackPacketConn(packetConn, nil)
|
return trackPacketConn(packetConn, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) WireGuardControl() control.Func {
|
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
||||||
return d.udpListener.Control
|
udpListener := d.udpListener
|
||||||
|
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) {
|
||||||
|
|||||||
19
common/dialer/default_go1.20.go
Normal file
19
common/dialer/default_go1.20.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//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
|
||||||
|
}
|
||||||
11
common/dialer/default_go1.21.go
Normal file
11
common/dialer/default_go1.21.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build go1.21
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
const go121Available = true
|
||||||
|
|
||||||
|
func setMultiPathTCP(dialer *net.Dialer) {
|
||||||
|
dialer.SetMultipathTCP(true)
|
||||||
|
}
|
||||||
22
common/dialer/default_nongo1.20.go
Normal file
22
common/dialer/default_nongo1.20.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//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
|
||||||
|
}
|
||||||
12
common/dialer/default_nongo1.21.go
Normal file
12
common/dialer/default_nongo1.21.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build !go1.21
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const go121Available = false
|
||||||
|
|
||||||
|
func setMultiPathTCP(dialer *net.Dialer) {
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build go1.20
|
||||||
|
|
||||||
package dialer
|
package dialer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,15 +8,15 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/atomic"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
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/database64128/tfo-go/v2"
|
"github.com/metacubex/tfo-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type slowOpenConn struct {
|
type slowOpenConn struct {
|
||||||
@@ -30,7 +32,7 @@ type slowOpenConn struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func DialSlowContext(dialer *tfo.Dialer, ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func DialSlowContext(dialer *tcpDialer, 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:
|
||||||
|
|||||||
20
common/dialer/tfo_stub.go
Normal file
20
common/dialer/tfo_stub.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//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,9 +1,13 @@
|
|||||||
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 {
|
||||||
WireGuardControl() control.Func
|
ListenPacketCompat(network, address string) (net.PacketConn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var WgControlFns []control.Func
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Conn struct {
|
|
||||||
aTLS.Conn
|
|
||||||
ctx context.Context
|
|
||||||
logger logger.ContextLogger
|
|
||||||
conn net.Conn
|
|
||||||
rawConn *badtls.RawConn
|
|
||||||
syscallConn syscall.Conn
|
|
||||||
rawSyscallConn syscall.RawConn
|
|
||||||
readWaitOptions N.ReadWaitOptions
|
|
||||||
kernelTx bool
|
|
||||||
kernelRx bool
|
|
||||||
pendingRxSplice bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConn(ctx context.Context, logger logger.ContextLogger, conn aTLS.Conn, txOffload, rxOffload bool) (aTLS.Conn, error) {
|
|
||||||
err := Load()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
syscallConn, isSyscallConn := N.CastReader[interface {
|
|
||||||
io.Reader
|
|
||||||
syscall.Conn
|
|
||||||
}](conn.NetConn())
|
|
||||||
if !isSyscallConn {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
rawSyscallConn, err := syscallConn.SyscallConn()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawConn, err := badtls.NewRawConn(conn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if *rawConn.Vers != tls.VersionTLS13 {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
for rawConn.RawInput.Len() > 0 {
|
|
||||||
err = rawConn.ReadRecord()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for rawConn.Hand.Len() > 0 {
|
|
||||||
err = rawConn.HandlePostHandshakeMessage()
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "handle post-handshake messages")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kConn := &Conn{
|
|
||||||
Conn: conn,
|
|
||||||
ctx: ctx,
|
|
||||||
logger: logger,
|
|
||||||
conn: conn.NetConn(),
|
|
||||||
rawConn: rawConn,
|
|
||||||
syscallConn: syscallConn,
|
|
||||||
rawSyscallConn: rawSyscallConn,
|
|
||||||
}
|
|
||||||
err = kConn.setupKernel(txOffload, rxOffload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return kConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Upstream() any {
|
|
||||||
return c.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) SyscallConnForRead() syscall.RawConn {
|
|
||||||
if !c.kernelRx {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !*c.rawConn.IsClient {
|
|
||||||
c.logger.WarnContext(c.ctx, "ktls: RX splice is unavailable on the server size, since it will cause an unknown failure")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
c.logger.DebugContext(c.ctx, "ktls: RX splice requested")
|
|
||||||
return c.rawSyscallConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) HandleSyscallReadError(inputErr error) ([]byte, error) {
|
|
||||||
if errors.Is(inputErr, unix.EINVAL) {
|
|
||||||
c.pendingRxSplice = true
|
|
||||||
err := c.readRecord()
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "ktls: handle non-application-data record")
|
|
||||||
}
|
|
||||||
var input bytes.Buffer
|
|
||||||
if c.rawConn.Input.Len() > 0 {
|
|
||||||
_, err = c.rawConn.Input.WriteTo(&input)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return input.Bytes(), nil
|
|
||||||
} else if errors.Is(inputErr, unix.EBADMSG) {
|
|
||||||
return nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertBadRecordMAC))
|
|
||||||
} else {
|
|
||||||
return nil, E.Cause(inputErr, "ktls: unexpected errno")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) SyscallConnForWrite() syscall.RawConn {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
c.logger.DebugContext(c.ctx, "ktls: TX splice requested")
|
|
||||||
return c.rawSyscallConn
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// alert level
|
|
||||||
alertLevelWarning = 1
|
|
||||||
alertLevelError = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
alertCloseNotify = 0
|
|
||||||
alertUnexpectedMessage = 10
|
|
||||||
alertBadRecordMAC = 20
|
|
||||||
alertDecryptionFailed = 21
|
|
||||||
alertRecordOverflow = 22
|
|
||||||
alertDecompressionFailure = 30
|
|
||||||
alertHandshakeFailure = 40
|
|
||||||
alertBadCertificate = 42
|
|
||||||
alertUnsupportedCertificate = 43
|
|
||||||
alertCertificateRevoked = 44
|
|
||||||
alertCertificateExpired = 45
|
|
||||||
alertCertificateUnknown = 46
|
|
||||||
alertIllegalParameter = 47
|
|
||||||
alertUnknownCA = 48
|
|
||||||
alertAccessDenied = 49
|
|
||||||
alertDecodeError = 50
|
|
||||||
alertDecryptError = 51
|
|
||||||
alertExportRestriction = 60
|
|
||||||
alertProtocolVersion = 70
|
|
||||||
alertInsufficientSecurity = 71
|
|
||||||
alertInternalError = 80
|
|
||||||
alertInappropriateFallback = 86
|
|
||||||
alertUserCanceled = 90
|
|
||||||
alertNoRenegotiation = 100
|
|
||||||
alertMissingExtension = 109
|
|
||||||
alertUnsupportedExtension = 110
|
|
||||||
alertCertificateUnobtainable = 111
|
|
||||||
alertUnrecognizedName = 112
|
|
||||||
alertBadCertificateStatusResponse = 113
|
|
||||||
alertBadCertificateHashValue = 114
|
|
||||||
alertUnknownPSKIdentity = 115
|
|
||||||
alertCertificateRequired = 116
|
|
||||||
alertNoApplicationProtocol = 120
|
|
||||||
alertECHRequired = 121
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) sendAlertLocked(err uint8) error {
|
|
||||||
switch err {
|
|
||||||
case alertNoRenegotiation, alertCloseNotify:
|
|
||||||
c.rawConn.Tmp[0] = alertLevelWarning
|
|
||||||
default:
|
|
||||||
c.rawConn.Tmp[0] = alertLevelError
|
|
||||||
}
|
|
||||||
c.rawConn.Tmp[1] = byte(err)
|
|
||||||
|
|
||||||
_, writeErr := c.writeRecordLocked(recordTypeAlert, c.rawConn.Tmp[0:2])
|
|
||||||
if err == alertCloseNotify {
|
|
||||||
// closeNotify is a special case in that it isn't an error.
|
|
||||||
return writeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.rawConn.Out.SetErrorLocked(&net.OpError{Op: "local error", Err: tls.AlertError(err)})
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendAlert sends a TLS alert message.
|
|
||||||
func (c *Conn) sendAlert(err uint8) error {
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
return c.sendAlertLocked(err)
|
|
||||||
}
|
|
||||||
@@ -1,326 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
|
||||||
)
|
|
||||||
|
|
||||||
type kernelCryptoCipherType uint16
|
|
||||||
|
|
||||||
const (
|
|
||||||
TLS_CIPHER_AES_GCM_128 kernelCryptoCipherType = 51
|
|
||||||
TLS_CIPHER_AES_GCM_128_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_AES_GCM_128_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_GCM_128_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_AES_GCM_128_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_AES_GCM_256 kernelCryptoCipherType = 52
|
|
||||||
TLS_CIPHER_AES_GCM_256_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_AES_GCM_256_KEY_SIZE kernelCryptoCipherType = 32
|
|
||||||
TLS_CIPHER_AES_GCM_256_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_AES_GCM_256_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_AES_CCM_128 kernelCryptoCipherType = 53
|
|
||||||
TLS_CIPHER_AES_CCM_128_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_AES_CCM_128_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_CCM_128_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_AES_CCM_128_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305 kernelCryptoCipherType = 54
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE kernelCryptoCipherType = 12
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE kernelCryptoCipherType = 32
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE kernelCryptoCipherType = 0
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
// TLS_CIPHER_SM4_GCM kernelCryptoCipherType = 55
|
|
||||||
// TLS_CIPHER_SM4_GCM_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
// TLS_CIPHER_SM4_GCM_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_GCM_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
// TLS_CIPHER_SM4_GCM_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
// TLS_CIPHER_SM4_CCM kernelCryptoCipherType = 56
|
|
||||||
// TLS_CIPHER_SM4_CCM_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
// TLS_CIPHER_SM4_CCM_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_CCM_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
// TLS_CIPHER_SM4_CCM_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
// TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_ARIA_GCM_128 kernelCryptoCipherType = 57
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_KEY_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
|
|
||||||
TLS_CIPHER_ARIA_GCM_256 kernelCryptoCipherType = 58
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_IV_SIZE kernelCryptoCipherType = 8
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_KEY_SIZE kernelCryptoCipherType = 32
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_SALT_SIZE kernelCryptoCipherType = 4
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_TAG_SIZE kernelCryptoCipherType = 16
|
|
||||||
TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE kernelCryptoCipherType = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
type kernelCrypto interface {
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type kernelCryptoInfo struct {
|
|
||||||
version uint16
|
|
||||||
cipher_type kernelCryptoCipherType
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoAES128GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoAES128GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_AES_GCM_128_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_AES_GCM_128_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_AES_GCM_128_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoAES128GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_AES_GCM_128
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoAES256GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoAES256GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_AES_GCM_256_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_AES_GCM_256_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_AES_GCM_256_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoAES256GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_AES_GCM_256
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoAES128CCM{}
|
|
||||||
|
|
||||||
type kernelCryptoAES128CCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_AES_CCM_128_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_AES_CCM_128_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_AES_CCM_128_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoAES128CCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_AES_CCM_128
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoChacha20Poly1035{}
|
|
||||||
|
|
||||||
type kernelCryptoChacha20Poly1035 struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoChacha20Poly1035) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_CHACHA20_POLY1305
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// var _ kernelCrypto = &kernelCryptoSM4GCM{}
|
|
||||||
|
|
||||||
// type kernelCryptoSM4GCM struct {
|
|
||||||
// kernelCryptoInfo
|
|
||||||
// iv [TLS_CIPHER_SM4_GCM_IV_SIZE]byte
|
|
||||||
// key [TLS_CIPHER_SM4_GCM_KEY_SIZE]byte
|
|
||||||
// salt [TLS_CIPHER_SM4_GCM_SALT_SIZE]byte
|
|
||||||
// rec_seq [TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE]byte
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (crypto *kernelCryptoSM4GCM) String() string {
|
|
||||||
// crypto.cipher_type = TLS_CIPHER_SM4_GCM
|
|
||||||
// return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var _ kernelCrypto = &kernelCryptoSM4CCM{}
|
|
||||||
|
|
||||||
// type kernelCryptoSM4CCM struct {
|
|
||||||
// kernelCryptoInfo
|
|
||||||
// iv [TLS_CIPHER_SM4_CCM_IV_SIZE]byte
|
|
||||||
// key [TLS_CIPHER_SM4_CCM_KEY_SIZE]byte
|
|
||||||
// salt [TLS_CIPHER_SM4_CCM_SALT_SIZE]byte
|
|
||||||
// rec_seq [TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE]byte
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (crypto *kernelCryptoSM4CCM) String() string {
|
|
||||||
// crypto.cipher_type = TLS_CIPHER_SM4_CCM
|
|
||||||
// return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
// }
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoARIA128GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoARIA128GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_ARIA_GCM_128_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_ARIA_GCM_128_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_ARIA_GCM_128_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoARIA128GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_ARIA_GCM_128
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ kernelCrypto = &kernelCryptoARIA256GCM{}
|
|
||||||
|
|
||||||
type kernelCryptoARIA256GCM struct {
|
|
||||||
kernelCryptoInfo
|
|
||||||
iv [TLS_CIPHER_ARIA_GCM_256_IV_SIZE]byte
|
|
||||||
key [TLS_CIPHER_ARIA_GCM_256_KEY_SIZE]byte
|
|
||||||
salt [TLS_CIPHER_ARIA_GCM_256_SALT_SIZE]byte
|
|
||||||
rec_seq [TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (crypto *kernelCryptoARIA256GCM) String() string {
|
|
||||||
crypto.cipher_type = TLS_CIPHER_ARIA_GCM_256
|
|
||||||
return string((*[unsafe.Sizeof(*crypto)]byte)(unsafe.Pointer(crypto))[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func kernelCipher(kernel *Support, hc *badtls.RawHalfConn, cipherSuite uint16, isRX bool) kernelCrypto {
|
|
||||||
if !kernel.TLS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *hc.Version {
|
|
||||||
case tls.VersionTLS12:
|
|
||||||
if isRX && !kernel.TLS_Version13_RX {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case tls.VersionTLS13:
|
|
||||||
if !kernel.TLS_Version13 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if isRX && !kernel.TLS_Version13_RX {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var key, iv []byte
|
|
||||||
if *hc.Version == tls.VersionTLS13 {
|
|
||||||
key, iv = trafficKey(cipherSuiteTLS13ByID(cipherSuite), *hc.TrafficSecret)
|
|
||||||
/*if isRX {
|
|
||||||
key, iv = trafficKey(cipherSuiteTLS13ByID(cipherSuite), keyLog.RemoteTrafficSecret)
|
|
||||||
} else {
|
|
||||||
key, iv = trafficKey(cipherSuiteTLS13ByID(cipherSuite), keyLog.TrafficSecret)
|
|
||||||
}*/
|
|
||||||
} else {
|
|
||||||
// csPtr := cipherSuiteByID(cipherSuite)
|
|
||||||
// keysFromMasterSecret(*hc.Version, csPtr, keyLog.Secret, keyLog.Random)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch cipherSuite {
|
|
||||||
case tls.TLS_AES_128_GCM_SHA256, tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
|
||||||
crypto := new(kernelCryptoAES128GCM)
|
|
||||||
|
|
||||||
crypto.version = *hc.Version
|
|
||||||
copy(crypto.key[:], key)
|
|
||||||
copy(crypto.iv[:], iv[4:])
|
|
||||||
copy(crypto.salt[:], iv[:4])
|
|
||||||
crypto.rec_seq = *hc.Seq
|
|
||||||
|
|
||||||
return crypto
|
|
||||||
case tls.TLS_AES_256_GCM_SHA384, tls.TLS_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
|
|
||||||
if !kernel.TLS_AES_256_GCM {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
crypto := new(kernelCryptoAES256GCM)
|
|
||||||
|
|
||||||
crypto.version = *hc.Version
|
|
||||||
copy(crypto.key[:], key)
|
|
||||||
copy(crypto.iv[:], iv[4:])
|
|
||||||
copy(crypto.salt[:], iv[:4])
|
|
||||||
crypto.rec_seq = *hc.Seq
|
|
||||||
|
|
||||||
return crypto
|
|
||||||
//case tls.TLS_AES_128_CCM_SHA256, tls.TLS_RSA_WITH_AES_128_CCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_SHA256:
|
|
||||||
// if !kernel.TLS_AES_128_CCM {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// crypto := new(kernelCryptoAES128CCM)
|
|
||||||
//
|
|
||||||
// crypto.version = *hc.Version
|
|
||||||
// copy(crypto.key[:], key)
|
|
||||||
// copy(crypto.iv[:], iv[4:])
|
|
||||||
// copy(crypto.salt[:], iv[:4])
|
|
||||||
// crypto.rec_seq = *hc.Seq
|
|
||||||
//
|
|
||||||
// return crypto
|
|
||||||
case tls.TLS_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
|
|
||||||
if !kernel.TLS_CHACHA20_POLY1305 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
crypto := new(kernelCryptoChacha20Poly1035)
|
|
||||||
|
|
||||||
crypto.version = *hc.Version
|
|
||||||
copy(crypto.key[:], key)
|
|
||||||
copy(crypto.iv[:], iv)
|
|
||||||
crypto.rec_seq = *hc.Seq
|
|
||||||
|
|
||||||
return crypto
|
|
||||||
//case tls.TLS_RSA_WITH_ARIA_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256:
|
|
||||||
// if !kernel.TLS_ARIA_GCM {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// crypto := new(kernelCryptoARIA128GCM)
|
|
||||||
//
|
|
||||||
// crypto.version = *hc.Version
|
|
||||||
// copy(crypto.key[:], key)
|
|
||||||
// copy(crypto.iv[:], iv[4:])
|
|
||||||
// copy(crypto.salt[:], iv[:4])
|
|
||||||
// crypto.rec_seq = *hc.Seq
|
|
||||||
//
|
|
||||||
// return crypto
|
|
||||||
//case tls.TLS_RSA_WITH_ARIA_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384:
|
|
||||||
// if !kernel.TLS_ARIA_GCM {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// crypto := new(kernelCryptoARIA256GCM)
|
|
||||||
//
|
|
||||||
// crypto.version = *hc.Version
|
|
||||||
// copy(crypto.key[:], key)
|
|
||||||
// copy(crypto.iv[:], iv[4:])
|
|
||||||
// copy(crypto.salt[:], iv[:4])
|
|
||||||
// crypto.rec_seq = *hc.Seq
|
|
||||||
//
|
|
||||||
// return crypto
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) Close() error {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interlock with Conn.Write above.
|
|
||||||
var x int32
|
|
||||||
for {
|
|
||||||
x = c.rawConn.ActiveCall.Load()
|
|
||||||
if x&1 != 0 {
|
|
||||||
return net.ErrClosed
|
|
||||||
}
|
|
||||||
if c.rawConn.ActiveCall.CompareAndSwap(x, x|1) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if x != 0 {
|
|
||||||
// io.Writer and io.Closer should not be used concurrently.
|
|
||||||
// If Close is called while a Write is currently in-flight,
|
|
||||||
// interpret that as a sign that this Close is really just
|
|
||||||
// being used to break the Write and/or clean up resources and
|
|
||||||
// avoid sending the alertCloseNotify, which may block
|
|
||||||
// waiting on handshakeMutex or the c.out mutex.
|
|
||||||
return c.conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
var alertErr error
|
|
||||||
if c.rawConn.IsHandshakeComplete.Load() {
|
|
||||||
if err := c.closeNotify(); err != nil {
|
|
||||||
alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.conn.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return alertErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) closeNotify() error {
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
|
|
||||||
if !*c.rawConn.CloseNotifySent {
|
|
||||||
// Set a Write Deadline to prevent possibly blocking forever.
|
|
||||||
c.SetWriteDeadline(time.Now().Add(time.Second * 5))
|
|
||||||
*c.rawConn.CloseNotifyErr = c.sendAlertLocked(alertCloseNotify)
|
|
||||||
*c.rawConn.CloseNotifySent = true
|
|
||||||
// Any subsequent writes will fail.
|
|
||||||
c.SetWriteDeadline(time.Now())
|
|
||||||
}
|
|
||||||
return *c.rawConn.CloseNotifyErr
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxPlaintext = 16384 // maximum plaintext payload length
|
|
||||||
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
|
|
||||||
maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3
|
|
||||||
recordHeaderLen = 5 // record header length
|
|
||||||
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
|
|
||||||
maxHandshakeCertificateMsg = 262144 // maximum certificate message size (256 KiB)
|
|
||||||
maxUselessRecords = 16 // maximum number of consecutive non-advancing records
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
recordTypeChangeCipherSpec = 20
|
|
||||||
recordTypeAlert = 21
|
|
||||||
recordTypeHandshake = 22
|
|
||||||
recordTypeApplicationData = 23
|
|
||||||
)
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The marshalingFunction type is an adapter to allow the use of ordinary
|
|
||||||
// functions as cryptobyte.MarshalingValue.
|
|
||||||
type marshalingFunction func(b *cryptobyte.Builder) error
|
|
||||||
|
|
||||||
func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error {
|
|
||||||
return f(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If
|
|
||||||
// the length of the sequence is not the value specified, it produces an error.
|
|
||||||
func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) {
|
|
||||||
b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error {
|
|
||||||
if len(v) != n {
|
|
||||||
return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v))
|
|
||||||
}
|
|
||||||
b.AddBytes(v)
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder.
|
|
||||||
func addUint64(b *cryptobyte.Builder, v uint64) {
|
|
||||||
b.AddUint32(uint32(v >> 32))
|
|
||||||
b.AddUint32(uint32(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint64 decodes a big-endian, 64-bit value into out and advances over it.
|
|
||||||
// It reports whether the read was successful.
|
|
||||||
func readUint64(s *cryptobyte.String, out *uint64) bool {
|
|
||||||
var hi, lo uint32
|
|
||||||
if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
*out = uint64(hi)<<32 | uint64(lo)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a
|
|
||||||
// []byte instead of a cryptobyte.String.
|
|
||||||
func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
|
|
||||||
return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a
|
|
||||||
// []byte instead of a cryptobyte.String.
|
|
||||||
func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
|
|
||||||
return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a
|
|
||||||
// []byte instead of a cryptobyte.String.
|
|
||||||
func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
|
|
||||||
return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out))
|
|
||||||
}
|
|
||||||
|
|
||||||
type keyUpdateMsg struct {
|
|
||||||
updateRequested bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *keyUpdateMsg) marshal() ([]byte, error) {
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddUint8(typeKeyUpdate)
|
|
||||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
if m.updateRequested {
|
|
||||||
b.AddUint8(1)
|
|
||||||
} else {
|
|
||||||
b.AddUint8(0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *keyUpdateMsg) unmarshal(data []byte) bool {
|
|
||||||
s := cryptobyte.String(data)
|
|
||||||
|
|
||||||
var updateRequested uint8
|
|
||||||
if !s.Skip(4) || // message type and uint24 length field
|
|
||||||
!s.ReadUint8(&updateRequested) || !s.Empty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch updateRequested {
|
|
||||||
case 0:
|
|
||||||
m.updateRequested = false
|
|
||||||
case 1:
|
|
||||||
m.updateRequested = true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS handshake message types.
|
|
||||||
const (
|
|
||||||
typeHelloRequest uint8 = 0
|
|
||||||
typeClientHello uint8 = 1
|
|
||||||
typeServerHello uint8 = 2
|
|
||||||
typeNewSessionTicket uint8 = 4
|
|
||||||
typeEndOfEarlyData uint8 = 5
|
|
||||||
typeEncryptedExtensions uint8 = 8
|
|
||||||
typeCertificate uint8 = 11
|
|
||||||
typeServerKeyExchange uint8 = 12
|
|
||||||
typeCertificateRequest uint8 = 13
|
|
||||||
typeServerHelloDone uint8 = 14
|
|
||||||
typeCertificateVerify uint8 = 15
|
|
||||||
typeClientKeyExchange uint8 = 16
|
|
||||||
typeFinished uint8 = 20
|
|
||||||
typeCertificateStatus uint8 = 22
|
|
||||||
typeKeyUpdate uint8 = 24
|
|
||||||
typeCompressedCertificate uint8 = 25
|
|
||||||
typeMessageHash uint8 = 254 // synthetic message
|
|
||||||
)
|
|
||||||
|
|
||||||
// TLS compression types.
|
|
||||||
const (
|
|
||||||
compressionNone uint8 = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// TLS extension numbers
|
|
||||||
const (
|
|
||||||
extensionServerName uint16 = 0
|
|
||||||
extensionStatusRequest uint16 = 5
|
|
||||||
extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7
|
|
||||||
extensionSupportedPoints uint16 = 11
|
|
||||||
extensionSignatureAlgorithms uint16 = 13
|
|
||||||
extensionALPN uint16 = 16
|
|
||||||
extensionSCT uint16 = 18
|
|
||||||
extensionPadding uint16 = 21
|
|
||||||
extensionExtendedMasterSecret uint16 = 23
|
|
||||||
extensionCompressCertificate uint16 = 27 // compress_certificate in TLS 1.3
|
|
||||||
extensionSessionTicket uint16 = 35
|
|
||||||
extensionPreSharedKey uint16 = 41
|
|
||||||
extensionEarlyData uint16 = 42
|
|
||||||
extensionSupportedVersions uint16 = 43
|
|
||||||
extensionCookie uint16 = 44
|
|
||||||
extensionPSKModes uint16 = 45
|
|
||||||
extensionCertificateAuthorities uint16 = 47
|
|
||||||
extensionSignatureAlgorithmsCert uint16 = 50
|
|
||||||
extensionKeyShare uint16 = 51
|
|
||||||
extensionQUICTransportParameters uint16 = 57
|
|
||||||
extensionALPS uint16 = 17513
|
|
||||||
extensionRenegotiationInfo uint16 = 0xff01
|
|
||||||
extensionECHOuterExtensions uint16 = 0xfd00
|
|
||||||
extensionEncryptedClientHello uint16 = 0xfe0d
|
|
||||||
)
|
|
||||||
|
|
||||||
type handshakeMessage interface {
|
|
||||||
marshal() ([]byte, error)
|
|
||||||
unmarshal([]byte) bool
|
|
||||||
}
|
|
||||||
type newSessionTicketMsgTLS13 struct {
|
|
||||||
lifetime uint32
|
|
||||||
ageAdd uint32
|
|
||||||
nonce []byte
|
|
||||||
label []byte
|
|
||||||
maxEarlyData uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *newSessionTicketMsgTLS13) marshal() ([]byte, error) {
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddUint8(typeNewSessionTicket)
|
|
||||||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddUint32(m.lifetime)
|
|
||||||
b.AddUint32(m.ageAdd)
|
|
||||||
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(m.nonce)
|
|
||||||
})
|
|
||||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddBytes(m.label)
|
|
||||||
})
|
|
||||||
|
|
||||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
if m.maxEarlyData > 0 {
|
|
||||||
b.AddUint16(extensionEarlyData)
|
|
||||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
|
||||||
b.AddUint32(m.maxEarlyData)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool {
|
|
||||||
*m = newSessionTicketMsgTLS13{}
|
|
||||||
s := cryptobyte.String(data)
|
|
||||||
|
|
||||||
var extensions cryptobyte.String
|
|
||||||
if !s.Skip(4) || // message type and uint24 length field
|
|
||||||
!s.ReadUint32(&m.lifetime) ||
|
|
||||||
!s.ReadUint32(&m.ageAdd) ||
|
|
||||||
!readUint8LengthPrefixed(&s, &m.nonce) ||
|
|
||||||
!readUint16LengthPrefixed(&s, &m.label) ||
|
|
||||||
!s.ReadUint16LengthPrefixed(&extensions) ||
|
|
||||||
!s.Empty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for !extensions.Empty() {
|
|
||||||
var extension uint16
|
|
||||||
var extData cryptobyte.String
|
|
||||||
if !extensions.ReadUint16(&extension) ||
|
|
||||||
!extensions.ReadUint16LengthPrefixed(&extData) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch extension {
|
|
||||||
case extensionEarlyData:
|
|
||||||
if !extData.ReadUint32(&m.maxEarlyData) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Ignore unknown extensions.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !extData.Empty() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// handlePostHandshakeMessage processes a handshake message arrived after the
|
|
||||||
// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
|
|
||||||
func (c *Conn) handlePostHandshakeMessage() error {
|
|
||||||
if *c.rawConn.Vers != tls.VersionTLS13 {
|
|
||||||
return errors.New("ktls: kernel does not support TLS 1.2 renegotiation")
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := c.readHandshake(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
//c.retryCount++
|
|
||||||
//if c.retryCount > maxUselessRecords {
|
|
||||||
// c.sendAlert(alertUnexpectedMessage)
|
|
||||||
// return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
|
|
||||||
//}
|
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case *newSessionTicketMsgTLS13:
|
|
||||||
// return errors.New("ktls: received new session ticket")
|
|
||||||
return nil
|
|
||||||
case *keyUpdateMsg:
|
|
||||||
return c.handleKeyUpdate(msg)
|
|
||||||
}
|
|
||||||
// The QUIC layer is supposed to treat an unexpected post-handshake CertificateRequest
|
|
||||||
// as a QUIC-level PROTOCOL_VIOLATION error (RFC 9001, Section 4.4). Returning an
|
|
||||||
// unexpected_message alert here doesn't provide it with enough information to distinguish
|
|
||||||
// this condition from other unexpected messages. This is probably fine.
|
|
||||||
c.sendAlert(alertUnexpectedMessage)
|
|
||||||
return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
|
|
||||||
//if c.quic != nil {
|
|
||||||
// c.sendAlert(alertUnexpectedMessage)
|
|
||||||
// return c.in.setErrorLocked(errors.New("tls: received unexpected key update message"))
|
|
||||||
//}
|
|
||||||
|
|
||||||
cipherSuite := cipherSuiteTLS13ByID(*c.rawConn.CipherSuite)
|
|
||||||
if cipherSuite == nil {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertInternalError))
|
|
||||||
}
|
|
||||||
|
|
||||||
newSecret := nextTrafficSecret(cipherSuite, *c.rawConn.In.TrafficSecret)
|
|
||||||
c.rawConn.In.SetTrafficSecret(cipherSuite, 0 /*tls.QUICEncryptionLevelInitial*/, newSecret)
|
|
||||||
|
|
||||||
err := c.resetupRX()
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlert(alertInternalError)
|
|
||||||
return c.rawConn.In.SetErrorLocked(fmt.Errorf("ktls: resetupRX failed: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyUpdate.updateRequested {
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
|
|
||||||
resetup, err := c.resetupTX()
|
|
||||||
if err != nil {
|
|
||||||
c.sendAlertLocked(alertInternalError)
|
|
||||||
return c.rawConn.Out.SetErrorLocked(fmt.Errorf("ktls: resetupTX failed: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &keyUpdateMsg{}
|
|
||||||
msgBytes, err := msg.marshal()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = c.writeRecordLocked(recordTypeHandshake, msgBytes)
|
|
||||||
if err != nil {
|
|
||||||
// Surface the error at the next write.
|
|
||||||
c.rawConn.Out.SetErrorLocked(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newSecret := nextTrafficSecret(cipherSuite, *c.rawConn.Out.TrafficSecret)
|
|
||||||
c.rawConn.Out.SetTrafficSecret(cipherSuite, 0 /*QUICEncryptionLevelInitial*/, newSecret)
|
|
||||||
|
|
||||||
err = resetup()
|
|
||||||
if err != nil {
|
|
||||||
return c.rawConn.Out.SetErrorLocked(fmt.Errorf("ktls: resetupTX failed: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readHandshakeBytes(n int) error {
|
|
||||||
//if c.quic != nil {
|
|
||||||
// return c.quicReadHandshakeBytes(n)
|
|
||||||
//}
|
|
||||||
for c.rawConn.Hand.Len() < n {
|
|
||||||
if err := c.readRecord(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readHandshake(transcript io.Writer) (any, error) {
|
|
||||||
if err := c.readHandshakeBytes(4); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data := c.rawConn.Hand.Bytes()
|
|
||||||
|
|
||||||
maxHandshakeSize := maxHandshake
|
|
||||||
// hasVers indicates we're past the first message, forcing someone trying to
|
|
||||||
// make us just allocate a large buffer to at least do the initial part of
|
|
||||||
// the handshake first.
|
|
||||||
//if c.haveVers && data[0] == typeCertificate {
|
|
||||||
// Since certificate messages are likely to be the only messages that
|
|
||||||
// can be larger than maxHandshake, we use a special limit for just
|
|
||||||
// those messages.
|
|
||||||
//maxHandshakeSize = maxHandshakeCertificateMsg
|
|
||||||
//}
|
|
||||||
|
|
||||||
n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
|
||||||
if n > maxHandshakeSize {
|
|
||||||
c.sendAlertLocked(alertInternalError)
|
|
||||||
return nil, c.rawConn.In.SetErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshakeSize))
|
|
||||||
}
|
|
||||||
if err := c.readHandshakeBytes(4 + n); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data = c.rawConn.Hand.Next(4 + n)
|
|
||||||
return c.unmarshalHandshakeMessage(data, transcript)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) unmarshalHandshakeMessage(data []byte, transcript io.Writer) (any, error) {
|
|
||||||
var m handshakeMessage
|
|
||||||
switch data[0] {
|
|
||||||
case typeNewSessionTicket:
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
m = new(newSessionTicketMsgTLS13)
|
|
||||||
} else {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
case typeKeyUpdate:
|
|
||||||
m = new(keyUpdateMsg)
|
|
||||||
default:
|
|
||||||
return nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The handshake message unmarshalers
|
|
||||||
// expect to be able to keep references to data,
|
|
||||||
// so pass in a fresh copy that won't be overwritten.
|
|
||||||
data = append([]byte(nil), data...)
|
|
||||||
|
|
||||||
if !m.unmarshal(data) {
|
|
||||||
return nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertDecodeError))
|
|
||||||
}
|
|
||||||
|
|
||||||
if transcript != nil {
|
|
||||||
transcript.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/badversion"
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/shell"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// mod from https://gitlab.com/go-extension/tls
|
|
||||||
|
|
||||||
const (
|
|
||||||
TLS_TX = 1
|
|
||||||
TLS_RX = 2
|
|
||||||
TLS_TX_ZEROCOPY_RO = 3 // TX zerocopy (only sendfile now)
|
|
||||||
TLS_RX_EXPECT_NO_PAD = 4 // Attempt opportunistic zero-copy, TLS 1.3 only
|
|
||||||
|
|
||||||
TLS_SET_RECORD_TYPE = 1
|
|
||||||
TLS_GET_RECORD_TYPE = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type Support struct {
|
|
||||||
TLS, TLS_RX bool
|
|
||||||
TLS_Version13, TLS_Version13_RX bool
|
|
||||||
|
|
||||||
TLS_TX_ZEROCOPY bool
|
|
||||||
TLS_RX_NOPADDING bool
|
|
||||||
|
|
||||||
TLS_AES_256_GCM bool
|
|
||||||
TLS_AES_128_CCM bool
|
|
||||||
TLS_CHACHA20_POLY1305 bool
|
|
||||||
TLS_SM4 bool
|
|
||||||
TLS_ARIA_GCM bool
|
|
||||||
|
|
||||||
TLS_Version13_KeyUpdate bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var KernelSupport = sync.OnceValues(func() (*Support, error) {
|
|
||||||
var uname unix.Utsname
|
|
||||||
err := unix.Uname(&uname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
kernelVersion := badversion.Parse(strings.Trim(string(uname.Release[:]), "\x00"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var support Support
|
|
||||||
switch {
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 6, Minor: 14}):
|
|
||||||
support.TLS_Version13_KeyUpdate = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 6, Minor: 1}):
|
|
||||||
support.TLS_ARIA_GCM = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 6}):
|
|
||||||
support.TLS_Version13_RX = true
|
|
||||||
support.TLS_RX_NOPADDING = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 19}):
|
|
||||||
support.TLS_TX_ZEROCOPY = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 16}):
|
|
||||||
support.TLS_SM4 = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 11}):
|
|
||||||
support.TLS_CHACHA20_POLY1305 = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 2}):
|
|
||||||
support.TLS_AES_128_CCM = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 5, Minor: 1}):
|
|
||||||
support.TLS_AES_256_GCM = true
|
|
||||||
support.TLS_Version13 = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 4, Minor: 17}):
|
|
||||||
support.TLS_RX = true
|
|
||||||
fallthrough
|
|
||||||
case kernelVersion.GreaterThanOrEqual(badversion.Version{Major: 4, Minor: 13}):
|
|
||||||
support.TLS = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if support.TLS && support.TLS_Version13 {
|
|
||||||
_, err := os.Stat("/sys/module/tls")
|
|
||||||
if err != nil {
|
|
||||||
if os.Getuid() == 0 {
|
|
||||||
output, err := shell.Exec("modprobe", "tls").Read()
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Extend(E.Cause(err, "modprobe tls"), output)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, E.New("ktls: kernel TLS module not loaded")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &support, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
func Load() error {
|
|
||||||
support, err := KernelSupport()
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "ktls: check availability")
|
|
||||||
}
|
|
||||||
if !support.TLS || !support.TLS_Version13 {
|
|
||||||
return E.New("ktls: kernel does not support TLS 1.3")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) setupKernel(txOffload, rxOffload bool) error {
|
|
||||||
if !txOffload && !rxOffload {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
support, err := KernelSupport()
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "check availability")
|
|
||||||
}
|
|
||||||
if !support.TLS || !support.TLS_Version13 {
|
|
||||||
return E.New("kernel does not support TLS 1.3")
|
|
||||||
}
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TCP, unix.TCP_ULP, "tls")
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return os.NewSyscallError("setsockopt", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if txOffload {
|
|
||||||
txCrypto := kernelCipher(support, c.rawConn.Out, *c.rawConn.CipherSuite, false)
|
|
||||||
if txCrypto == nil {
|
|
||||||
return E.New("unsupported cipher suite")
|
|
||||||
}
|
|
||||||
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_TX, txCrypto.String())
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if support.TLS_TX_ZEROCOPY {
|
|
||||||
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptInt(int(fd), unix.SOL_TLS, TLS_TX_ZEROCOPY_RO, 1)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.kernelTx = true
|
|
||||||
c.logger.DebugContext(c.ctx, "ktls: kernel TLS TX enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if rxOffload {
|
|
||||||
rxCrypto := kernelCipher(support, c.rawConn.In, *c.rawConn.CipherSuite, true)
|
|
||||||
if rxCrypto == nil {
|
|
||||||
return E.New("unsupported cipher suite")
|
|
||||||
}
|
|
||||||
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_RX, rxCrypto.String())
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if *c.rawConn.Vers >= tls.VersionTLS13 && support.TLS_RX_NOPADDING {
|
|
||||||
err = control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptInt(int(fd), unix.SOL_TLS, TLS_RX_EXPECT_NO_PAD, 1)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.kernelRx = true
|
|
||||||
c.logger.DebugContext(c.ctx, "ktls: kernel TLS RX enabled")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) resetupTX() (func() error, error) {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
support, err := KernelSupport()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !support.TLS_Version13_KeyUpdate {
|
|
||||||
return nil, errors.New("ktls: kernel does not support rekey")
|
|
||||||
}
|
|
||||||
txCrypto := kernelCipher(support, c.rawConn.Out, *c.rawConn.CipherSuite, false)
|
|
||||||
if txCrypto == nil {
|
|
||||||
return nil, errors.New("ktls: set kernelCipher on unsupported tls session")
|
|
||||||
}
|
|
||||||
return func() error {
|
|
||||||
return control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_TX, txCrypto.String())
|
|
||||||
})
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) resetupRX() error {
|
|
||||||
if !c.kernelRx {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
support, err := KernelSupport()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !support.TLS_Version13_KeyUpdate {
|
|
||||||
return errors.New("ktls: kernel does not support rekey")
|
|
||||||
}
|
|
||||||
rxCrypto := kernelCipher(support, c.rawConn.In, *c.rawConn.CipherSuite, true)
|
|
||||||
if rxCrypto == nil {
|
|
||||||
return errors.New("ktls: set kernelCipher on unsupported tls session")
|
|
||||||
}
|
|
||||||
return control.Raw(c.rawSyscallConn, func(fd uintptr) error {
|
|
||||||
return syscall.SetsockoptString(int(fd), unix.SOL_TLS, TLS_RX, rxCrypto.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readKernelRecord() (uint8, []byte, error) {
|
|
||||||
if c.rawConn.RawInput.Len() < maxPlaintext {
|
|
||||||
c.rawConn.RawInput.Grow(maxPlaintext - c.rawConn.RawInput.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
data := c.rawConn.RawInput.Bytes()[:maxPlaintext]
|
|
||||||
|
|
||||||
// cmsg for record type
|
|
||||||
buffer := make([]byte, unix.CmsgSpace(1))
|
|
||||||
cmsg := (*unix.Cmsghdr)(unsafe.Pointer(&buffer[0]))
|
|
||||||
cmsg.SetLen(unix.CmsgLen(1))
|
|
||||||
|
|
||||||
var iov unix.Iovec
|
|
||||||
iov.Base = &data[0]
|
|
||||||
iov.SetLen(len(data))
|
|
||||||
|
|
||||||
var msg unix.Msghdr
|
|
||||||
msg.Control = &buffer[0]
|
|
||||||
msg.Controllen = cmsg.Len
|
|
||||||
msg.Iov = &iov
|
|
||||||
msg.Iovlen = 1
|
|
||||||
|
|
||||||
var n int
|
|
||||||
var err error
|
|
||||||
er := c.rawSyscallConn.Read(func(fd uintptr) bool {
|
|
||||||
n, err = recvmsg(int(fd), &msg, 0)
|
|
||||||
return err != unix.EAGAIN || c.pendingRxSplice
|
|
||||||
})
|
|
||||||
if er != nil {
|
|
||||||
return 0, nil, er
|
|
||||||
}
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
case syscall.EINVAL, syscall.EAGAIN:
|
|
||||||
return 0, nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertProtocolVersion))
|
|
||||||
case syscall.EMSGSIZE:
|
|
||||||
return 0, nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertRecordOverflow))
|
|
||||||
case syscall.EBADMSG:
|
|
||||||
return 0, nil, c.rawConn.In.SetErrorLocked(c.sendAlert(alertDecryptError))
|
|
||||||
default:
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if n <= 0 {
|
|
||||||
return 0, nil, c.rawConn.In.SetErrorLocked(io.EOF)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmsg.Level == unix.SOL_TLS && cmsg.Type == TLS_GET_RECORD_TYPE {
|
|
||||||
typ := buffer[unix.CmsgLen(0)]
|
|
||||||
return typ, data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return recordTypeApplicationData, data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) writeKernelRecord(typ uint16, data []byte) (int, error) {
|
|
||||||
if typ == recordTypeApplicationData {
|
|
||||||
return c.conn.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmsg for record type
|
|
||||||
buffer := make([]byte, unix.CmsgSpace(1))
|
|
||||||
cmsg := (*unix.Cmsghdr)(unsafe.Pointer(&buffer[0]))
|
|
||||||
cmsg.SetLen(unix.CmsgLen(1))
|
|
||||||
buffer[unix.CmsgLen(0)] = byte(typ)
|
|
||||||
cmsg.Level = unix.SOL_TLS
|
|
||||||
cmsg.Type = TLS_SET_RECORD_TYPE
|
|
||||||
|
|
||||||
var iov unix.Iovec
|
|
||||||
iov.Base = &data[0]
|
|
||||||
iov.SetLen(len(data))
|
|
||||||
|
|
||||||
var msg unix.Msghdr
|
|
||||||
msg.Control = &buffer[0]
|
|
||||||
msg.Controllen = cmsg.Len
|
|
||||||
msg.Iov = &iov
|
|
||||||
msg.Iovlen = 1
|
|
||||||
|
|
||||||
var n int
|
|
||||||
var err error
|
|
||||||
ew := c.rawSyscallConn.Write(func(fd uintptr) bool {
|
|
||||||
n, err = sendmsg(int(fd), &msg, 0)
|
|
||||||
return err != unix.EAGAIN
|
|
||||||
})
|
|
||||||
if ew != nil {
|
|
||||||
return 0, ew
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname recvmsg golang.org/x/sys/unix.recvmsg
|
|
||||||
func recvmsg(fd int, msg *unix.Msghdr, flags int) (n int, err error)
|
|
||||||
|
|
||||||
//go:linkname sendmsg golang.org/x/sys/unix.sendmsg
|
|
||||||
func sendmsg(fd int, msg *unix.Msghdr, flags int) (n int, err error)
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
//go:linkname cipherSuiteByID github.com/metacubex/utls.cipherSuiteByID
|
|
||||||
func cipherSuiteByID(id uint16) unsafe.Pointer
|
|
||||||
|
|
||||||
//go:linkname keysFromMasterSecret github.com/metacubex/utls.keysFromMasterSecret
|
|
||||||
func keysFromMasterSecret(version uint16, suite unsafe.Pointer, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte)
|
|
||||||
|
|
||||||
//go:linkname cipherSuiteTLS13ByID github.com/metacubex/utls.cipherSuiteTLS13ByID
|
|
||||||
func cipherSuiteTLS13ByID(id uint16) unsafe.Pointer
|
|
||||||
|
|
||||||
//go:linkname nextTrafficSecret github.com/metacubex/utls.(*cipherSuiteTLS13).nextTrafficSecret
|
|
||||||
func nextTrafficSecret(cs unsafe.Pointer, trafficSecret []byte) []byte
|
|
||||||
|
|
||||||
//go:linkname trafficKey github.com/metacubex/utls.(*cipherSuiteTLS13).trafficKey
|
|
||||||
func trafficKey(cs unsafe.Pointer, trafficSecret []byte) (key, iv []byte)
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) Read(b []byte) (int, error) {
|
|
||||||
if !c.kernelRx {
|
|
||||||
return c.Conn.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b) == 0 {
|
|
||||||
// Put this after Handshake, in case people were calling
|
|
||||||
// Read(nil) for the side effect of the Handshake.
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c.rawConn.In.Lock()
|
|
||||||
defer c.rawConn.In.Unlock()
|
|
||||||
|
|
||||||
for c.rawConn.Input.Len() == 0 {
|
|
||||||
if err := c.readRecord(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
for c.rawConn.Hand.Len() > 0 {
|
|
||||||
if err := c.handlePostHandshakeMessage(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, _ := c.rawConn.Input.Read(b)
|
|
||||||
|
|
||||||
// If a close-notify alert is waiting, read it so that we can return (n,
|
|
||||||
// EOF) instead of (n, nil), to signal to the HTTP response reading
|
|
||||||
// goroutine that the connection is now closed. This eliminates a race
|
|
||||||
// where the HTTP response reading goroutine would otherwise not observe
|
|
||||||
// the EOF until its next read, by which time a client goroutine might
|
|
||||||
// have already tried to reuse the HTTP connection for a new request.
|
|
||||||
// See https://golang.org/cl/76400046 and https://golang.org/issue/3514
|
|
||||||
if n != 0 && c.rawConn.Input.Len() == 0 && c.rawConn.RawInput.Len() > 0 &&
|
|
||||||
c.rawConn.RawInput.Bytes()[0] == recordTypeAlert {
|
|
||||||
if err := c.readRecord(); err != nil {
|
|
||||||
return n, err // will be io.EOF on closeNotify
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) readRecord() error {
|
|
||||||
if *c.rawConn.In.Err != nil {
|
|
||||||
return *c.rawConn.In.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
typ, data, err := c.readRawRecord()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) > maxPlaintext {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertRecordOverflow))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application Data messages are always protected.
|
|
||||||
if c.rawConn.In.Cipher == nil && typ == recordTypeApplicationData {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
//if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
|
|
||||||
// This is a state-advancing message: reset the retry count.
|
|
||||||
// c.retryCount = 0
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Handshake messages MUST NOT be interleaved with other record types in TLS 1.3.
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 && typ != recordTypeHandshake && c.rawConn.Hand.Len() > 0 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typ {
|
|
||||||
default:
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
case recordTypeAlert:
|
|
||||||
//if c.quic != nil {
|
|
||||||
// return c.rawConn.In.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
//}
|
|
||||||
if len(data) != 2 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
if data[1] == alertCloseNotify {
|
|
||||||
return c.rawConn.In.SetErrorLocked(io.EOF)
|
|
||||||
}
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
// TLS 1.3 removed warning-level alerts except for alertUserCanceled
|
|
||||||
// (RFC 8446, § 6.1). Since at least one major implementation
|
|
||||||
// (https://bugs.openjdk.org/browse/JDK-8323517) misuses this alert,
|
|
||||||
// many TLS stacks now ignore it outright when seen in a TLS 1.3
|
|
||||||
// handshake (e.g. BoringSSL, NSS, Rustls).
|
|
||||||
if data[1] == alertUserCanceled {
|
|
||||||
// Like TLS 1.2 alertLevelWarning alerts, we drop the record and retry.
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
return c.rawConn.In.SetErrorLocked(&net.OpError{Op: "remote error", Err: tls.AlertError(data[1])})
|
|
||||||
}
|
|
||||||
switch data[0] {
|
|
||||||
case alertLevelWarning:
|
|
||||||
// Drop the record on the floor and retry.
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
case alertLevelError:
|
|
||||||
return c.rawConn.In.SetErrorLocked(&net.OpError{Op: "remote error", Err: tls.AlertError(data[1])})
|
|
||||||
default:
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
|
|
||||||
case recordTypeChangeCipherSpec:
|
|
||||||
if len(data) != 1 || data[0] != 1 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertDecodeError))
|
|
||||||
}
|
|
||||||
// Handshake messages are not allowed to fragment across the CCS.
|
|
||||||
if c.rawConn.Hand.Len() > 0 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
// In TLS 1.3, change_cipher_spec records are ignored until the
|
|
||||||
// Finished. See RFC 8446, Appendix D.4. Note that according to Section
|
|
||||||
// 5, a server can send a ChangeCipherSpec before its ServerHello, when
|
|
||||||
// c.vers is still unset. That's not useful though and suspicious if the
|
|
||||||
// server then selects a lower protocol version, so don't allow that.
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
// if !expectChangeCipherSpec {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
//}
|
|
||||||
//if err := c.rawConn.In.changeCipherSpec(); err != nil {
|
|
||||||
// return c.rawConn.In.setErrorLocked(c.sendAlert(err.(alert)))
|
|
||||||
//}
|
|
||||||
|
|
||||||
case recordTypeApplicationData:
|
|
||||||
// Some OpenSSL servers send empty records in order to randomize the
|
|
||||||
// CBC RawIV. Ignore a limited number of empty records.
|
|
||||||
if len(data) == 0 {
|
|
||||||
return c.retryReadRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
// Note that data is owned by c.rawInput, following the Next call above,
|
|
||||||
// to avoid copying the plaintext. This is safe because c.rawInput is
|
|
||||||
// not read from or written to until c.input is drained.
|
|
||||||
c.rawConn.Input.Reset(data)
|
|
||||||
case recordTypeHandshake:
|
|
||||||
if len(data) == 0 {
|
|
||||||
return c.rawConn.In.SetErrorLocked(c.sendAlert(alertUnexpectedMessage))
|
|
||||||
}
|
|
||||||
c.rawConn.Hand.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:staticcheck
|
|
||||||
func (c *Conn) readRawRecord() (typ uint8, data []byte, err error) {
|
|
||||||
// Read from kernel.
|
|
||||||
if c.kernelRx {
|
|
||||||
return c.readKernelRecord()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read header, payload.
|
|
||||||
if err = c.readFromUntil(c.conn, recordHeaderLen); err != nil {
|
|
||||||
// RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
|
|
||||||
// is an error, but popular web sites seem to do this, so we accept it
|
|
||||||
// if and only if at the record boundary.
|
|
||||||
if err == io.ErrUnexpectedEOF && c.rawConn.RawInput.Len() == 0 {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
if e, ok := err.(net.Error); !ok || !e.Temporary() {
|
|
||||||
c.rawConn.In.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hdr := c.rawConn.RawInput.Bytes()[:recordHeaderLen]
|
|
||||||
typ = hdr[0]
|
|
||||||
|
|
||||||
vers := uint16(hdr[1])<<8 | uint16(hdr[2])
|
|
||||||
expectedVers := *c.rawConn.Vers
|
|
||||||
if expectedVers == tls.VersionTLS13 {
|
|
||||||
// All TLS 1.3 records are expected to have 0x0303 (1.2) after
|
|
||||||
// the initial hello (RFC 8446 Section 5.1).
|
|
||||||
expectedVers = tls.VersionTLS12
|
|
||||||
}
|
|
||||||
n := int(hdr[3])<<8 | int(hdr[4])
|
|
||||||
if /*c.haveVers && */ vers != expectedVers {
|
|
||||||
c.sendAlert(alertProtocolVersion)
|
|
||||||
msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, expectedVers)
|
|
||||||
err = c.rawConn.In.SetErrorLocked(c.newRecordHeaderError(nil, msg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//if !c.haveVers {
|
|
||||||
// // First message, be extra suspicious: this might not be a TLS
|
|
||||||
// // client. Bail out before reading a full 'body', if possible.
|
|
||||||
// // The current max version is 3.3 so if the version is >= 16.0,
|
|
||||||
// // it's probably not real.
|
|
||||||
// if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 {
|
|
||||||
// err = c.rawConn.In.SetErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake"))
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext {
|
|
||||||
c.sendAlert(alertRecordOverflow)
|
|
||||||
msg := fmt.Sprintf("oversized record received with length %d", n)
|
|
||||||
err = c.rawConn.In.SetErrorLocked(c.newRecordHeaderError(nil, msg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = c.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
|
|
||||||
if e, ok := err.(net.Error); !ok || !e.Temporary() {
|
|
||||||
c.rawConn.In.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process message.
|
|
||||||
record := c.rawConn.RawInput.Next(recordHeaderLen + n)
|
|
||||||
data, typ, err = c.rawConn.In.Decrypt(record)
|
|
||||||
if err != nil {
|
|
||||||
err = c.rawConn.In.SetErrorLocked(c.sendAlert(uint8(err.(tls.AlertError))))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// retryReadRecord recurs into readRecordOrCCS to drop a non-advancing record, like
|
|
||||||
// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
|
|
||||||
func (c *Conn) retryReadRecord( /*expectChangeCipherSpec bool*/ ) error {
|
|
||||||
//c.retryCount++
|
|
||||||
//if c.retryCount > maxUselessRecords {
|
|
||||||
// c.sendAlert(alertUnexpectedMessage)
|
|
||||||
// return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
|
|
||||||
//}
|
|
||||||
return c.readRecord( /*expectChangeCipherSpec*/ )
|
|
||||||
}
|
|
||||||
|
|
||||||
// atLeastReader reads from R, stopping with EOF once at least N bytes have been
|
|
||||||
// read. It is different from an io.LimitedReader in that it doesn't cut short
|
|
||||||
// the last Read call, and in that it considers an early EOF an error.
|
|
||||||
type atLeastReader struct {
|
|
||||||
R io.Reader
|
|
||||||
N int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *atLeastReader) Read(p []byte) (int, error) {
|
|
||||||
if r.N <= 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
n, err := r.R.Read(p)
|
|
||||||
r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809
|
|
||||||
if r.N > 0 && err == io.EOF {
|
|
||||||
return n, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
if r.N <= 0 && err == nil {
|
|
||||||
return n, io.EOF
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// readFromUntil reads from r into c.rawConn.RawInput until c.rawConn.RawInput contains
|
|
||||||
// at least n bytes or else returns an error.
|
|
||||||
func (c *Conn) readFromUntil(r io.Reader, n int) error {
|
|
||||||
if c.rawConn.RawInput.Len() >= n {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
needs := n - c.rawConn.RawInput.Len()
|
|
||||||
// There might be extra input waiting on the wire. Make a best effort
|
|
||||||
// attempt to fetch it so that it can be used in (*Conn).Read to
|
|
||||||
// "predict" closeNotify alerts.
|
|
||||||
c.rawConn.RawInput.Grow(needs + bytes.MinRead)
|
|
||||||
_, err := c.rawConn.RawInput.ReadFrom(&atLeastReader{r, int64(needs)})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err tls.RecordHeaderError) {
|
|
||||||
err.Msg = msg
|
|
||||||
err.Conn = conn
|
|
||||||
copy(err.RecordHeader[:], c.rawConn.RawInput.Bytes())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {
|
|
||||||
c.readWaitOptions = options
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
|
|
||||||
c.rawConn.In.Lock()
|
|
||||||
defer c.rawConn.In.Unlock()
|
|
||||||
for c.rawConn.Input.Len() == 0 {
|
|
||||||
err = c.readRecord()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer = c.readWaitOptions.NewBuffer()
|
|
||||||
n, err := c.rawConn.Input.Read(buffer.FreeBytes())
|
|
||||||
if err != nil {
|
|
||||||
buffer.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer.Truncate(n)
|
|
||||||
if n != 0 && c.rawConn.Input.Len() == 0 && c.rawConn.Input.Len() > 0 &&
|
|
||||||
c.rawConn.RawInput.Bytes()[0] == recordTypeAlert {
|
|
||||||
_ = c.rawConn.ReadRecord()
|
|
||||||
}
|
|
||||||
c.readWaitOptions.PostReturn(buffer)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//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 +0,0 @@
|
|||||||
//go:build !linux
|
|
||||||
|
|
||||||
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 is only supported on Linux")
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//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")
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux && go1.25 && badlinkname
|
|
||||||
|
|
||||||
package ktls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) Write(b []byte) (int, error) {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return c.Conn.Write(b)
|
|
||||||
}
|
|
||||||
// interlock with Close below
|
|
||||||
for {
|
|
||||||
x := c.rawConn.ActiveCall.Load()
|
|
||||||
if x&1 != 0 {
|
|
||||||
return 0, net.ErrClosed
|
|
||||||
}
|
|
||||||
if c.rawConn.ActiveCall.CompareAndSwap(x, x+2) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer c.rawConn.ActiveCall.Add(-2)
|
|
||||||
|
|
||||||
//if err := c.Conn.HandshakeContext(context.Background()); err != nil {
|
|
||||||
// return 0, err
|
|
||||||
//}
|
|
||||||
|
|
||||||
c.rawConn.Out.Lock()
|
|
||||||
defer c.rawConn.Out.Unlock()
|
|
||||||
|
|
||||||
if err := *c.rawConn.Out.Err; err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.rawConn.IsHandshakeComplete.Load() {
|
|
||||||
return 0, tls.AlertError(alertInternalError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *c.rawConn.CloseNotifySent {
|
|
||||||
// return 0, errShutdown
|
|
||||||
return 0, errors.New("tls: protocol is shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS 1.0 is susceptible to a chosen-plaintext
|
|
||||||
// attack when using block mode ciphers due to predictable IVs.
|
|
||||||
// This can be prevented by splitting each Application Data
|
|
||||||
// record into two records, effectively randomizing the RawIV.
|
|
||||||
//
|
|
||||||
// https://www.openssl.org/~bodo/tls-cbc.txt
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
|
|
||||||
// https://www.imperialviolet.org/2012/01/15/beastfollowup.html
|
|
||||||
|
|
||||||
var m int
|
|
||||||
if len(b) > 1 && *c.rawConn.Vers == tls.VersionTLS10 {
|
|
||||||
if _, ok := (*c.rawConn.Out.Cipher).(cipher.BlockMode); ok {
|
|
||||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
|
|
||||||
if err != nil {
|
|
||||||
return n, c.rawConn.Out.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
m, b = 1, b[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b)
|
|
||||||
return n + m, c.rawConn.Out.SetErrorLocked(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) writeRecordLocked(typ uint16, data []byte) (n int, err error) {
|
|
||||||
if !c.kernelTx {
|
|
||||||
return c.rawConn.WriteRecordLocked(typ, data)
|
|
||||||
}
|
|
||||||
/*for len(data) > 0 {
|
|
||||||
m := len(data)
|
|
||||||
if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
|
|
||||||
m = maxPayload
|
|
||||||
}
|
|
||||||
_, err = c.writeKernelRecord(typ, data[:m])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n += m
|
|
||||||
data = data[m:]
|
|
||||||
}*/
|
|
||||||
return c.writeKernelRecord(typ, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// tcpMSSEstimate is a conservative estimate of the TCP maximum segment
|
|
||||||
// size (MSS). A constant is used, rather than querying the kernel for
|
|
||||||
// the actual MSS, to avoid complexity. The value here is the IPv6
|
|
||||||
// minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
|
|
||||||
// bytes) and a TCP header with timestamps (32 bytes).
|
|
||||||
tcpMSSEstimate = 1208
|
|
||||||
|
|
||||||
// recordSizeBoostThreshold is the number of bytes of application data
|
|
||||||
// sent after which the TLS record size will be increased to the
|
|
||||||
// maximum.
|
|
||||||
recordSizeBoostThreshold = 128 * 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Conn) maxPayloadSizeForWrite(typ uint16) int {
|
|
||||||
if /*c.config.DynamicRecordSizingDisabled ||*/ typ != recordTypeApplicationData {
|
|
||||||
return maxPlaintext
|
|
||||||
}
|
|
||||||
|
|
||||||
if *c.rawConn.PacketsSent >= recordSizeBoostThreshold {
|
|
||||||
return maxPlaintext
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract TLS overheads to get the maximum payload size.
|
|
||||||
payloadBytes := tcpMSSEstimate - recordHeaderLen - c.rawConn.Out.ExplicitNonceLen()
|
|
||||||
if rawCipher := *c.rawConn.Out.Cipher; rawCipher != nil {
|
|
||||||
switch ciph := rawCipher.(type) {
|
|
||||||
case cipher.Stream:
|
|
||||||
payloadBytes -= (*c.rawConn.Out.Mac).Size()
|
|
||||||
case cipher.AEAD:
|
|
||||||
payloadBytes -= ciph.Overhead()
|
|
||||||
/*case cbcMode:
|
|
||||||
blockSize := ciph.BlockSize()
|
|
||||||
// The payload must fit in a multiple of blockSize, with
|
|
||||||
// room for at least one padding byte.
|
|
||||||
payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
|
|
||||||
// The RawMac is appended before padding so affects the
|
|
||||||
// payload size directly.
|
|
||||||
payloadBytes -= c.out.mac.Size()*/
|
|
||||||
default:
|
|
||||||
panic("unknown cipher type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
|
||||||
payloadBytes-- // encrypted ContentType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow packet growth in arithmetic progression up to max.
|
|
||||||
pkt := *c.rawConn.PacketsSent
|
|
||||||
*c.rawConn.PacketsSent++
|
|
||||||
if pkt > 1000 {
|
|
||||||
return maxPlaintext // avoid overflow in multiply below
|
|
||||||
}
|
|
||||||
|
|
||||||
n := payloadBytes * int(pkt+1)
|
|
||||||
if n > maxPlaintext {
|
|
||||||
n = maxPlaintext
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
11
common/listener/listener_go121.go
Normal file
11
common/listener/listener_go121.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build go1.21
|
||||||
|
|
||||||
|
package listener
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
const go121Available = true
|
||||||
|
|
||||||
|
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||||
|
listenConfig.SetMultipathTCP(true)
|
||||||
|
}
|
||||||
16
common/listener/listener_go123.go
Normal file
16
common/listener/listener_go123.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//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,
|
||||||
|
}
|
||||||
|
}
|
||||||
10
common/listener/listener_nongo121.go
Normal file
10
common/listener/listener_nongo121.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
//go:build !go1.21
|
||||||
|
|
||||||
|
package listener
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
const go121Available = false
|
||||||
|
|
||||||
|
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||||
|
}
|
||||||
15
common/listener/listener_nongo123.go
Normal file
15
common/listener/listener_nongo123.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//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))
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package listener
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -17,7 +16,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/database64128/tfo-go/v2"
|
"github.com/metacubex/tfo-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l *Listener) ListenTCP() (net.Listener, error) {
|
func (l *Listener) ListenTCP() (net.Listener, error) {
|
||||||
@@ -46,19 +45,18 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
|
|||||||
if keepInterval == 0 {
|
if keepInterval == 0 {
|
||||||
keepInterval = C.TCPKeepAliveInterval
|
keepInterval = C.TCPKeepAliveInterval
|
||||||
}
|
}
|
||||||
listenConfig.KeepAliveConfig = net.KeepAliveConfig{
|
setKeepAliveConfig(&listenConfig, keepIdle, keepInterval)
|
||||||
Enable: true,
|
|
||||||
Idle: keepIdle,
|
|
||||||
Interval: keepInterval,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if l.listenOptions.TCPMultiPath {
|
if l.listenOptions.TCPMultiPath {
|
||||||
listenConfig.SetMultipathTCP(true)
|
if !go121Available {
|
||||||
|
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 {
|
||||||
return control.Raw(conn, func(fd uintptr) error {
|
return control.Raw(conn, func(fd uintptr) error {
|
||||||
return redir.TProxy(fd, !strings.HasSuffix(network, "4"), false)
|
return redir.TProxy(fd, !M.ParseSocksaddr(address).IsIPv4(), false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@@ -42,7 +41,7 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
|
|||||||
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 {
|
||||||
return control.Raw(conn, func(fd uintptr) error {
|
return control.Raw(conn, func(fd uintptr) error {
|
||||||
return redir.TProxy(fd, !strings.HasSuffix(network, "4"), true)
|
return redir.TProxy(fd, !M.ParseSocksaddr(address).IsIPv4(), true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ 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"
|
||||||
@@ -13,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Searcher interface {
|
type Searcher interface {
|
||||||
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error)
|
FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNotFound = E.New("process not found")
|
var ErrNotFound = E.New("process not found")
|
||||||
@@ -23,7 +22,15 @@ type Config struct {
|
|||||||
PackageManager tun.PackageManager
|
PackageManager tun.PackageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
type Info struct {
|
||||||
|
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
|
||||||
@@ -31,7 +38,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.UserName = osUser.Username
|
info.User = osUser.Username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info, nil
|
return info, nil
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,22 +17,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) (*adapter.ConnectionOwner, error) {
|
func (s *androidSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, 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 &adapter.ConnectionOwner{
|
return &Info{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
AndroidPackageName: sharedPackage,
|
PackageName: sharedPackage,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
if packageName, loaded := s.packageManager.PackageByID(uid % 100000); loaded {
|
||||||
return &adapter.ConnectionOwner{
|
return &Info{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
AndroidPackageName: packageName,
|
PackageName: packageName,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return &adapter.ConnectionOwner{UserId: int32(uid)}, nil
|
return &Info{UserId: int32(uid)}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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"
|
||||||
@@ -24,12 +23,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) (*adapter.ConnectionOwner, error) {
|
func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, 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 &adapter.ConnectionOwner{ProcessPath: processName, UserId: -1}, nil
|
return &Info{ProcessPath: processName, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var structSize = func() int {
|
var structSize = func() int {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,7 +19,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) (*adapter.ConnectionOwner, error) {
|
func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, 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
|
||||||
@@ -29,7 +28,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 &adapter.ConnectionOwner{
|
return &Info{
|
||||||
UserId: int32(uid),
|
UserId: int32(uid),
|
||||||
ProcessPath: processPath,
|
ProcessPath: processPath,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ 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"
|
||||||
|
|
||||||
@@ -28,16 +27,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) (*adapter.ConnectionOwner, error) {
|
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, 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 &adapter.ConnectionOwner{ProcessID: pid, UserId: -1}, err
|
return &Info{ProcessID: pid, UserId: -1}, err
|
||||||
}
|
}
|
||||||
return &adapter.ConnectionOwner{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
return &Info{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProcessPath(pid uint32) (string, error) {
|
func getProcessPath(pid uint32) (string, error) {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package settings
|
|
||||||
|
|
||||||
import "github.com/sagernet/sing-box/adapter"
|
|
||||||
|
|
||||||
type WIFIMonitor interface {
|
|
||||||
ReadWIFIState() adapter.WIFIState
|
|
||||||
Start() error
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//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
|
|
||||||
}
|
|
||||||
@@ -56,7 +56,7 @@ func TestSniffUQUICChrome115(t *testing.T) {
|
|||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
require.Equal(t, metadata.Client, C.ClientQUICGo)
|
||||||
require.Equal(t, metadata.Domain, "www.google.com")
|
require.Equal(t, metadata.Domain, "www.google.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
|||||||
case ruleItemNetworkIsConstrained:
|
case ruleItemNetworkIsConstrained:
|
||||||
rule.NetworkIsConstrained = true
|
rule.NetworkIsConstrained = true
|
||||||
case ruleItemNetworkInterfaceAddress:
|
case ruleItemNetworkInterfaceAddress:
|
||||||
rule.NetworkInterfaceAddress = new(badjson.TypedMap[option.InterfaceType, badoption.Listable[*badoption.Prefixable]])
|
rule.NetworkInterfaceAddress = new(badjson.TypedMap[option.InterfaceType, badoption.Listable[badoption.Prefixable]])
|
||||||
var size uint64
|
var size uint64
|
||||||
size, err = binary.ReadUvarint(reader)
|
size, err = binary.ReadUvarint(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -247,7 +247,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var value []*badoption.Prefixable
|
var value []badoption.Prefixable
|
||||||
var prefixCount uint64
|
var prefixCount uint64
|
||||||
prefixCount, err = binary.ReadUvarint(reader)
|
prefixCount, err = binary.ReadUvarint(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -259,12 +259,12 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
value = append(value, common.Ptr(badoption.Prefixable(prefix)))
|
value = append(value, badoption.Prefixable(prefix))
|
||||||
}
|
}
|
||||||
rule.NetworkInterfaceAddress.Put(option.InterfaceType(key), value)
|
rule.NetworkInterfaceAddress.Put(option.InterfaceType(key), value)
|
||||||
}
|
}
|
||||||
case ruleItemDefaultInterfaceAddress:
|
case ruleItemDefaultInterfaceAddress:
|
||||||
var value []*badoption.Prefixable
|
var value []badoption.Prefixable
|
||||||
var prefixCount uint64
|
var prefixCount uint64
|
||||||
prefixCount, err = binary.ReadUvarint(reader)
|
prefixCount, err = binary.ReadUvarint(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -276,7 +276,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
value = append(value, common.Ptr(badoption.Prefixable(prefix)))
|
value = append(value, badoption.Prefixable(prefix))
|
||||||
}
|
}
|
||||||
rule.DefaultInterfaceAddress = value
|
rule.DefaultInterfaceAddress = value
|
||||||
case ruleItemFinal:
|
case ruleItemFinal:
|
||||||
@@ -437,10 +437,6 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = varbin.WriteUvarint(writer, uint64(len(entry.Value)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, rawPrefix := range entry.Value {
|
for _, rawPrefix := range entry.Value {
|
||||||
err = writePrefix(writer, rawPrefix.Build(netip.Prefix{}))
|
err = writePrefix(writer, rawPrefix.Build(netip.Prefix{}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -10,63 +10,32 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/badtls"
|
"github.com/sagernet/sing-box/common/badtls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"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"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDialerFromOptions(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
func NewDialerFromOptions(ctx context.Context, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return dialer, nil
|
return dialer, nil
|
||||||
}
|
}
|
||||||
config, err := NewClientWithOptions(ClientOptions{
|
config, err := NewClient(ctx, serverAddress, options)
|
||||||
Context: ctx,
|
|
||||||
Logger: logger,
|
|
||||||
ServerAddress: serverAddress,
|
|
||||||
Options: options,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewDialer(dialer, config), nil
|
return NewDialer(dialer, config), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
return NewClientWithOptions(ClientOptions{
|
if !options.Enabled {
|
||||||
Context: ctx,
|
|
||||||
Logger: logger,
|
|
||||||
ServerAddress: serverAddress,
|
|
||||||
Options: options,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientOptions struct {
|
|
||||||
Context context.Context
|
|
||||||
Logger logger.ContextLogger
|
|
||||||
ServerAddress string
|
|
||||||
Options option.OutboundTLSOptions
|
|
||||||
KTLSCompatible bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientWithOptions(options ClientOptions) (Config, error) {
|
|
||||||
if !options.Options.Enabled {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if !options.KTLSCompatible {
|
if options.Reality != nil && options.Reality.Enabled {
|
||||||
if options.Options.KernelTx {
|
return NewRealityClient(ctx, serverAddress, options)
|
||||||
options.Logger.Warn("enabling kTLS TX in current scenarios will definitely reduce performance, please checkout https://sing-box.sagernet.org/configuration/shared/tls/#kernel_tx")
|
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||||
}
|
return NewUTLSClient(ctx, serverAddress, options)
|
||||||
}
|
}
|
||||||
if options.Options.KernelRx {
|
return NewSTDClient(ctx, serverAddress, options)
|
||||||
options.Logger.Warn("enabling kTLS RX will definitely reduce performance, please checkout https://sing-box.sagernet.org/configuration/shared/tls/#kernel_rx")
|
|
||||||
}
|
|
||||||
if options.Options.Reality != nil && options.Options.Reality.Enabled {
|
|
||||||
return NewRealityClient(options.Context, options.Logger, options.ServerAddress, options.Options)
|
|
||||||
} else if options.Options.UTLS != nil && options.Options.UTLS.Enabled {
|
|
||||||
return NewUTLSClient(options.Context, options.Logger, options.ServerAddress, options.Options)
|
|
||||||
}
|
|
||||||
return NewSTDClient(options.Context, options.Logger, options.ServerAddress, options.Options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
||||||
@@ -119,19 +88,21 @@ func (d *defaultDialer) dialContext(ctx context.Context, destination M.Socksaddr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tlsConn, err := aTLS.ClientHandshake(ctx, conn, d.config)
|
tlsConn, err := ClientHandshake(ctx, conn, d.config)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
conn.Close()
|
return tlsConn, nil
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
if echRetry {
|
||||||
var echErr *tls.ECHRejectionError
|
var echErr *tls.ECHRejectionError
|
||||||
if echRetry && errors.As(err, &echErr) && len(echErr.RetryConfigList) > 0 {
|
if 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 nil, err
|
return d.dialContext(ctx, destination, false)
|
||||||
}
|
}
|
||||||
return tlsConn, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *defaultDialer) Upstream() any {
|
func (d *defaultDialer) Upstream() any {
|
||||||
|
|||||||
@@ -69,7 +69,11 @@ func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions,
|
|||||||
} else {
|
} else {
|
||||||
return E.New("missing ECH keys")
|
return E.New("missing ECH keys")
|
||||||
}
|
}
|
||||||
echKeys, err := parseECHKeys(echKey)
|
block, rest := pem.Decode(echKey)
|
||||||
|
if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
|
||||||
|
return E.New("invalid ECH keys pem")
|
||||||
|
}
|
||||||
|
echKeys, err := UnmarshalECHKeys(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "parse ECH keys")
|
return E.Cause(err, "parse ECH keys")
|
||||||
}
|
}
|
||||||
@@ -81,29 +85,21 @@ func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) setECHServerConfig(echKey []byte) error {
|
func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error {
|
||||||
echKeys, err := parseECHKeys(echKey)
|
echKey, err := os.ReadFile(echKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return E.Cause(err, "reload ECH keys from ", echKeyPath)
|
||||||
}
|
}
|
||||||
c.access.Lock()
|
|
||||||
config := c.config.Clone()
|
|
||||||
config.EncryptedClientHelloKeys = echKeys
|
|
||||||
c.config = config
|
|
||||||
c.access.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseECHKeys(echKey []byte) ([]tls.EncryptedClientHelloKey, error) {
|
|
||||||
block, _ := pem.Decode(echKey)
|
block, _ := pem.Decode(echKey)
|
||||||
if block == nil || block.Type != "ECH KEYS" {
|
if block == nil || block.Type != "ECH KEYS" {
|
||||||
return nil, E.New("invalid ECH keys pem")
|
return E.New("invalid ECH keys pem")
|
||||||
}
|
}
|
||||||
echKeys, err := UnmarshalECHKeys(block.Bytes)
|
echKeys, err := UnmarshalECHKeys(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse ECH keys")
|
return E.Cause(err, "parse ECH keys")
|
||||||
}
|
}
|
||||||
return echKeys, nil
|
tlsConfig.EncryptedClientHelloKeys = echKeys
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ECHClientConfig struct {
|
type ECHClientConfig struct {
|
||||||
|
|||||||
23
common/tls/ech_stub.go
Normal file
23
common/tls/ech_stub.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//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 reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error {
|
||||||
|
return E.New("ECH requires go1.24, please recompile your binary.")
|
||||||
|
}
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/ktls"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
type KTLSClientConfig struct {
|
|
||||||
Config
|
|
||||||
logger logger.ContextLogger
|
|
||||||
kernelTx, kernelRx bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *KTLSClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
|
||||||
tlsConn, err := aTLS.ClientHandshake(ctx, conn, w.Config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
kConn, err := ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
|
|
||||||
if err != nil {
|
|
||||||
tlsConn.Close()
|
|
||||||
return nil, E.Cause(err, "initialize kernel TLS")
|
|
||||||
}
|
|
||||||
return kConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *KTLSClientConfig) Clone() Config {
|
|
||||||
return &KTLSClientConfig{
|
|
||||||
w.Config.Clone(),
|
|
||||||
w.logger,
|
|
||||||
w.kernelTx,
|
|
||||||
w.kernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type KTlSServerConfig struct {
|
|
||||||
ServerConfig
|
|
||||||
logger logger.ContextLogger
|
|
||||||
kernelTx, kernelRx bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *KTlSServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
|
||||||
tlsConn, err := aTLS.ServerHandshake(ctx, conn, w.ServerConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
kConn, err := ktls.NewConn(ctx, w.logger, tlsConn, w.kernelTx, w.kernelRx)
|
|
||||||
if err != nil {
|
|
||||||
tlsConn.Close()
|
|
||||||
return nil, E.Cause(err, "initialize kernel TLS")
|
|
||||||
}
|
|
||||||
return kConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *KTlSServerConfig) Clone() Config {
|
|
||||||
return &KTlSServerConfig{
|
|
||||||
w.ServerConfig.Clone().(ServerConfig),
|
|
||||||
w.logger,
|
|
||||||
w.kernelTx,
|
|
||||||
w.kernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,12 +28,10 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"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/debug"
|
"github.com/sagernet/sing/common/debug"
|
||||||
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/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
|
|
||||||
@@ -51,12 +49,12 @@ type RealityClientConfig struct {
|
|||||||
shortID [8]byte
|
shortID [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRealityClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewRealityClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {
|
||||||
if options.UTLS == nil || !options.UTLS.Enabled {
|
if options.UTLS == nil || !options.UTLS.Enabled {
|
||||||
return nil, E.New("uTLS is required by reality client")
|
return nil, E.New("uTLS is required by reality client")
|
||||||
}
|
}
|
||||||
|
|
||||||
uClient, err := NewUTLSClient(ctx, logger, serverAddress, options)
|
uClient, err := NewUTLSClient(ctx, serverAddress, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -76,20 +74,7 @@ func NewRealityClient(ctx context.Context, logger logger.ContextLogger, serverAd
|
|||||||
if decodedLen > 8 {
|
if decodedLen > 8 {
|
||||||
return nil, E.New("invalid short_id")
|
return nil, E.New("invalid short_id")
|
||||||
}
|
}
|
||||||
|
return &RealityClientConfig{ctx, uClient.(*UTLSClientConfig), publicKey, shortID}, nil
|
||||||
var config Config = &RealityClientConfig{ctx, uClient.(*UTLSClientConfig), publicKey, shortID}
|
|
||||||
if options.KernelRx || options.KernelTx {
|
|
||||||
if !C.IsLinux {
|
|
||||||
return nil, E.New("kTLS is only supported on Linux")
|
|
||||||
}
|
|
||||||
config = &KTLSClientConfig{
|
|
||||||
Config: config,
|
|
||||||
logger: logger,
|
|
||||||
kernelTx: options.KernelTx,
|
|
||||||
kernelRx: options.KernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *RealityClientConfig) ServerName() string {
|
func (e *RealityClientConfig) ServerName() string {
|
||||||
@@ -108,7 +93,7 @@ func (e *RealityClientConfig) SetNextProtos(nextProto []string) {
|
|||||||
e.uClient.SetNextProtos(nextProto)
|
e.uClient.SetNextProtos(nextProto)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *RealityClientConfig) STDConfig() (*STDConfig, error) {
|
func (e *RealityClientConfig) Config() (*STDConfig, error) {
|
||||||
return nil, E.New("unsupported usage for reality")
|
return nil, E.New("unsupported usage for reality")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,11 +307,3 @@ func (c *realityClientConnWrapper) Upstream() any {
|
|||||||
func (c *realityClientConnWrapper) CloseWrite() error {
|
func (c *realityClientConnWrapper) CloseWrite() error {
|
||||||
return c.Close()
|
return c.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *realityClientConnWrapper) ReaderReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *realityClientConnWrapper) WriterReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
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"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -29,7 +28,7 @@ type RealityServerConfig struct {
|
|||||||
config *utls.RealityConfig
|
config *utls.RealityConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRealityServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewRealityServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (*RealityServerConfig, error) {
|
||||||
var tlsConfig utls.RealityConfig
|
var tlsConfig utls.RealityConfig
|
||||||
|
|
||||||
if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
||||||
@@ -68,10 +67,7 @@ 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.CurvePreferences) > 0 {
|
if len(options.Certificate) > 0 || options.CertificatePath != "" {
|
||||||
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 != "" {
|
||||||
@@ -123,22 +119,7 @@ func NewRealityServer(ctx context.Context, logger log.ContextLogger, options opt
|
|||||||
return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
return &RealityServerConfig{&tlsConfig}, nil
|
||||||
return nil, E.New("Reality is conflict with ECH")
|
|
||||||
}
|
|
||||||
var config ServerConfig = &RealityServerConfig{&tlsConfig}
|
|
||||||
if options.KernelTx || options.KernelRx {
|
|
||||||
if !C.IsLinux {
|
|
||||||
return nil, E.New("kTLS is only supported on Linux")
|
|
||||||
}
|
|
||||||
config = &KTlSServerConfig{
|
|
||||||
ServerConfig: config,
|
|
||||||
logger: logger,
|
|
||||||
kernelTx: options.KernelTx,
|
|
||||||
kernelRx: options.KernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) ServerName() string {
|
func (c *RealityServerConfig) ServerName() string {
|
||||||
@@ -157,7 +138,7 @@ func (c *RealityServerConfig) SetNextProtos(nextProto []string) {
|
|||||||
c.config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) STDConfig() (*tls.Config, error) {
|
func (c *RealityServerConfig) Config() (*tls.Config, error) {
|
||||||
return nil, E.New("unsupported usage for reality")
|
return nil, E.New("unsupported usage for reality")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,11 +206,3 @@ func (c *realityConnWrapper) Upstream() any {
|
|||||||
func (c *realityConnWrapper) CloseWrite() error {
|
func (c *realityConnWrapper) CloseWrite() error {
|
||||||
return c.Close()
|
return c.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *realityConnWrapper) ReaderReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *realityConnWrapper) WriterReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,37 +12,14 @@ import (
|
|||||||
aTLS "github.com/sagernet/sing/common/tls"
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerOptions struct {
|
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
Context context.Context
|
if !options.Enabled {
|
||||||
Logger log.ContextLogger
|
|
||||||
Options option.InboundTLSOptions
|
|
||||||
KTLSCompatible bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
|
|
||||||
return NewServerWithOptions(ServerOptions{
|
|
||||||
Context: ctx,
|
|
||||||
Logger: logger,
|
|
||||||
Options: options,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServerWithOptions(options ServerOptions) (ServerConfig, error) {
|
|
||||||
if !options.Options.Enabled {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if !options.KTLSCompatible {
|
if options.Reality != nil && options.Reality.Enabled {
|
||||||
if options.Options.KernelTx {
|
return NewRealityServer(ctx, logger, options)
|
||||||
options.Logger.Warn("enabling kTLS TX in current scenarios will definitely reduce performance, please checkout https://sing-box.sagernet.org/configuration/shared/tls/#kernel_tx")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if options.Options.KernelRx {
|
return NewSTDServer(ctx, logger, options)
|
||||||
options.Logger.Warn("enabling kTLS RX will definitely reduce performance, please checkout https://sing-box.sagernet.org/configuration/shared/tls/#kernel_rx")
|
|
||||||
}
|
|
||||||
if options.Options.Reality != nil && options.Options.Reality.Enabled {
|
|
||||||
return NewRealityServer(options.Context, options.Logger, options.Options)
|
|
||||||
}
|
|
||||||
return NewSTDServer(options.Context, options.Logger, options.Options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
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"
|
||||||
@@ -14,10 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/tlsfragment"
|
"github.com/sagernet/sing-box/common/tlsfragment"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"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/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,7 +40,7 @@ func (c *STDClientConfig) SetNextProtos(nextProto []string) {
|
|||||||
c.config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDClientConfig) STDConfig() (*STDConfig, error) {
|
func (c *STDClientConfig) Config() (*STDConfig, error) {
|
||||||
return c.config, nil
|
return c.config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,13 +52,7 @@ func (c *STDClientConfig) Client(conn net.Conn) (Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDClientConfig) Clone() Config {
|
func (c *STDClientConfig) Clone() Config {
|
||||||
return &STDClientConfig{
|
return &STDClientConfig{c.ctx, c.config.Clone(), c.fragment, c.fragmentFallbackDelay, c.recordFragment}
|
||||||
ctx: c.ctx,
|
|
||||||
config: c.config.Clone(),
|
|
||||||
fragment: c.fragment,
|
|
||||||
fragmentFallbackDelay: c.fragmentFallbackDelay,
|
|
||||||
recordFragment: c.recordFragment,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDClientConfig) ECHConfigList() []byte {
|
func (c *STDClientConfig) ECHConfigList() []byte {
|
||||||
@@ -74,7 +63,7 @@ func (c *STDClientConfig) SetECHConfigList(EncryptedClientHelloConfigList []byte
|
|||||||
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
|
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
var serverName string
|
var serverName string
|
||||||
if options.ServerName != "" {
|
if options.ServerName != "" {
|
||||||
serverName = options.ServerName
|
serverName = options.ServerName
|
||||||
@@ -111,15 +100,6 @@ 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
|
||||||
}
|
}
|
||||||
@@ -149,9 +129,6 @@ 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"))
|
||||||
@@ -169,43 +146,10 @@ func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddres
|
|||||||
}
|
}
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
stdConfig := &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
var err error
|
return parseECHClientConfig(ctx, stdConfig, options)
|
||||||
config, err = parseECHClientConfig(ctx, config.(ECHCapableConfig), options)
|
} else {
|
||||||
if err != nil {
|
return stdConfig, nil
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if options.KernelRx || options.KernelTx {
|
|
||||||
if !C.IsLinux {
|
|
||||||
return nil, E.New("kTLS is only supported on Linux")
|
|
||||||
}
|
|
||||||
config = &KTLSClientConfig{
|
|
||||||
Config: config,
|
|
||||||
logger: logger,
|
|
||||||
kernelTx: options.KernelTx,
|
|
||||||
kernelRx: options.KernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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,16 +3,13 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
@@ -23,36 +20,26 @@ import (
|
|||||||
var errInsecureUnused = E.New("tls: insecure unused")
|
var errInsecureUnused = E.New("tls: insecure unused")
|
||||||
|
|
||||||
type STDServerConfig struct {
|
type STDServerConfig struct {
|
||||||
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 {
|
||||||
c.access.RLock()
|
|
||||||
defer c.access.RUnlock()
|
|
||||||
return c.config.ServerName
|
return c.config.ServerName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) SetServerName(serverName string) {
|
func (c *STDServerConfig) SetServerName(serverName string) {
|
||||||
c.access.Lock()
|
c.config.ServerName = serverName
|
||||||
defer c.access.Unlock()
|
|
||||||
config := c.config.Clone()
|
|
||||||
config.ServerName = serverName
|
|
||||||
c.config = config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) NextProtos() []string {
|
func (c *STDServerConfig) NextProtos() []string {
|
||||||
c.access.RLock()
|
|
||||||
defer c.access.RUnlock()
|
|
||||||
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||||
return c.config.NextProtos[1:]
|
return c.config.NextProtos[1:]
|
||||||
} else {
|
} else {
|
||||||
@@ -61,18 +48,14 @@ func (c *STDServerConfig) NextProtos() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
func (c *STDServerConfig) SetNextProtos(nextProto []string) {
|
||||||
c.access.Lock()
|
|
||||||
defer c.access.Unlock()
|
|
||||||
config := c.config.Clone()
|
|
||||||
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
if c.acmeService != nil && len(c.config.NextProtos) > 1 && c.config.NextProtos[0] == ACMETLS1Protocol {
|
||||||
config.NextProtos = append(c.config.NextProtos[:1], nextProto...)
|
c.config.NextProtos = append(c.config.NextProtos[:1], nextProto...)
|
||||||
} else {
|
} else {
|
||||||
config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
c.config = config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *STDServerConfig) STDConfig() (*STDConfig, error) {
|
func (c *STDServerConfig) Config() (*STDConfig, error) {
|
||||||
return c.config, nil
|
return c.config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +77,9 @@ func (c *STDServerConfig) Start() error {
|
|||||||
if c.acmeService != nil {
|
if c.acmeService != nil {
|
||||||
return c.acmeService.Start()
|
return c.acmeService.Start()
|
||||||
} else {
|
} else {
|
||||||
|
if c.certificatePath == "" && c.keyPath == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
err := c.startWatcher()
|
err := c.startWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Warn("create fsnotify watcher: ", err)
|
c.logger.Warn("create fsnotify watcher: ", err)
|
||||||
@@ -113,12 +99,6 @@ 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 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
||||||
Path: watchPath,
|
Path: watchPath,
|
||||||
Callback: func(path string) {
|
Callback: func(path string) {
|
||||||
@@ -158,42 +138,10 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "reload key pair")
|
return E.Cause(err, "reload key pair")
|
||||||
}
|
}
|
||||||
c.access.Lock()
|
c.config.Certificates = []tls.Certificate{keyPair}
|
||||||
config := c.config.Clone()
|
|
||||||
config.Certificates = []tls.Certificate{keyPair}
|
|
||||||
c.config = config
|
|
||||||
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)
|
err := reloadECHKeys(c.echKeyPath, c.config)
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "reload ECH keys from ", c.echKeyPath)
|
|
||||||
}
|
|
||||||
err = c.setECHServerConfig(echKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -212,7 +160,7 @@ func (c *STDServerConfig) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
if !options.Enabled {
|
if !options.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -264,14 +212,8 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, curveID := range options.CurvePreferences {
|
var certificate []byte
|
||||||
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curveID))
|
var key []byte
|
||||||
}
|
|
||||||
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"))
|
||||||
@@ -313,43 +255,6 @@ 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)
|
||||||
@@ -357,33 +262,14 @@ func NewSTDServer(ctx context.Context, logger log.ContextLogger, options option.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serverConfig := &STDServerConfig{
|
return &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,
|
||||||
clientCertificatePath: options.ClientCertificatePath,
|
keyPath: options.KeyPath,
|
||||||
keyPath: options.KeyPath,
|
echKeyPath: echKeyPath,
|
||||||
echKeyPath: echKeyPath,
|
}, nil
|
||||||
}
|
|
||||||
serverConfig.config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
|
||||||
serverConfig.access.Lock()
|
|
||||||
defer serverConfig.access.Unlock()
|
|
||||||
return serverConfig.config, nil
|
|
||||||
}
|
|
||||||
var config ServerConfig = serverConfig
|
|
||||||
if options.KernelTx || options.KernelRx {
|
|
||||||
if !C.IsLinux {
|
|
||||||
return nil, E.New("kTLS is only supported on Linux")
|
|
||||||
}
|
|
||||||
config = &KTlSServerConfig{
|
|
||||||
ServerConfig: config,
|
|
||||||
logger: logger,
|
|
||||||
kernelTx: options.KernelTx,
|
|
||||||
kernelRx: options.KernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,8 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/tlsfragment"
|
"github.com/sagernet/sing-box/common/tlsfragment"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
|
|
||||||
utls "github.com/metacubex/utls"
|
utls "github.com/metacubex/utls"
|
||||||
@@ -53,7 +50,7 @@ func (c *UTLSClientConfig) SetNextProtos(nextProto []string) {
|
|||||||
c.config.NextProtos = nextProto
|
c.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UTLSClientConfig) STDConfig() (*STDConfig, error) {
|
func (c *UTLSClientConfig) Config() (*STDConfig, error) {
|
||||||
return nil, E.New("unsupported usage for uTLS")
|
return nil, E.New("unsupported usage for uTLS")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,14 +106,6 @@ func (c *utlsConnWrapper) Upstream() any {
|
|||||||
return c.UConn
|
return c.UConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *utlsConnWrapper) ReaderReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *utlsConnWrapper) WriterReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type utlsALPNWrapper struct {
|
type utlsALPNWrapper struct {
|
||||||
utlsConnWrapper
|
utlsConnWrapper
|
||||||
nextProtocols []string
|
nextProtocols []string
|
||||||
@@ -142,7 +131,7 @@ func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error {
|
|||||||
return c.UConn.HandshakeContext(ctx)
|
return c.UConn.HandshakeContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
var serverName string
|
var serverName string
|
||||||
if options.ServerName != "" {
|
if options.ServerName != "" {
|
||||||
serverName = options.ServerName
|
serverName = options.ServerName
|
||||||
@@ -167,15 +156,6 @@ 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
|
||||||
}
|
}
|
||||||
@@ -226,28 +206,15 @@ func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddre
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var config Config = &UTLSClientConfig{ctx, &tlsConfig, id, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
uConfig := &UTLSClientConfig{ctx, &tlsConfig, id, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
|
||||||
if options.ECH != nil && options.ECH.Enabled {
|
if options.ECH != nil && options.ECH.Enabled {
|
||||||
if options.Reality != nil && options.Reality.Enabled {
|
if options.Reality != nil && options.Reality.Enabled {
|
||||||
return nil, E.New("Reality is conflict with ECH")
|
return nil, E.New("Reality is conflict with ECH")
|
||||||
}
|
}
|
||||||
config, err = parseECHClientConfig(ctx, config.(ECHCapableConfig), options)
|
return parseECHClientConfig(ctx, uConfig, options)
|
||||||
if err != nil {
|
} else {
|
||||||
return nil, err
|
return uConfig, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (options.KernelRx || options.KernelTx) && !common.PtrValueOrDefault(options.Reality).Enabled {
|
|
||||||
if !C.IsLinux {
|
|
||||||
return nil, E.New("kTLS is only supported on Linux")
|
|
||||||
}
|
|
||||||
config = &KTLSClientConfig{
|
|
||||||
Config: config,
|
|
||||||
logger: logger,
|
|
||||||
kernelTx: options.KernelTx,
|
|
||||||
kernelRx: options.KernelRx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewUTLSClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewUTLSClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
|
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRealityClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
func NewRealityClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||||
return nil, E.New(`uTLS, which is required by reality is not included in this build, rebuild with -tags with_utls`)
|
return nil, E.New(`uTLS, which is required by reality is not included in this build, rebuild with -tags with_utls`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,9 +109,6 @@ func (c *Conn) Write(b []byte) (n int, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if i != len(splitIndexes) {
|
|
||||||
time.Sleep(c.fallbackDelay)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error {
|
func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fallbackDelay time.Duration) error {
|
||||||
_, err := conn.Write(payload)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
time.Sleep(fallbackDelay)
|
time.Sleep(fallbackDelay)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,6 @@ func writeAndWaitAck(ctx context.Context, conn *net.TCPConn, payload []byte, fal
|
|||||||
err := winiphlpapi.WriteAndWaitAck(ctx, conn, payload)
|
err := winiphlpapi.WriteAndWaitAck(ctx, conn, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
|
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
|
||||||
if _, err := conn.Write(payload); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
time.Sleep(fallbackDelay)
|
time.Sleep(fallbackDelay)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"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/common"
|
||||||
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/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
@@ -46,15 +47,15 @@ func (s *HistoryStorage) LoadURLTestHistory(tag string) *adapter.URLTestHistory
|
|||||||
func (s *HistoryStorage) DeleteURLTestHistory(tag string) {
|
func (s *HistoryStorage) DeleteURLTestHistory(tag string) {
|
||||||
s.access.Lock()
|
s.access.Lock()
|
||||||
delete(s.delayHistory, tag)
|
delete(s.delayHistory, tag)
|
||||||
s.notifyUpdated()
|
|
||||||
s.access.Unlock()
|
s.access.Unlock()
|
||||||
|
s.notifyUpdated()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HistoryStorage) StoreURLTestHistory(tag string, history *adapter.URLTestHistory) {
|
func (s *HistoryStorage) StoreURLTestHistory(tag string, history *adapter.URLTestHistory) {
|
||||||
s.access.Lock()
|
s.access.Lock()
|
||||||
s.delayHistory[tag] = history
|
s.delayHistory[tag] = history
|
||||||
s.notifyUpdated()
|
|
||||||
s.access.Unlock()
|
s.access.Unlock()
|
||||||
|
s.notifyUpdated()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HistoryStorage) notifyUpdated() {
|
func (s *HistoryStorage) notifyUpdated() {
|
||||||
@@ -68,8 +69,6 @@ func (s *HistoryStorage) notifyUpdated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *HistoryStorage) Close() error {
|
func (s *HistoryStorage) Close() error {
|
||||||
s.access.Lock()
|
|
||||||
defer s.access.Unlock()
|
|
||||||
s.updateHook = nil
|
s.updateHook = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -99,7 +98,7 @@ func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer instance.Close()
|
defer instance.Close()
|
||||||
if N.NeedHandshakeForWrite(instance) {
|
if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](instance); isEarlyConn && earlyConn.NeedHandshake() {
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(http.MethodHead, link, nil)
|
req, err := http.NewRequest(http.MethodHead, link, nil)
|
||||||
|
|||||||
@@ -40,5 +40,4 @@ const (
|
|||||||
const (
|
const (
|
||||||
RuleActionRejectMethodDefault = "default"
|
RuleActionRejectMethodDefault = "default"
|
||||||
RuleActionRejectMethodDrop = "drop"
|
RuleActionRejectMethodDrop = "drop"
|
||||||
RuleActionRejectMethodReply = "reply"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,702 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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]
|
|
||||||
}
|
|
||||||
@@ -1,774 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,204 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,919 +0,0 @@
|
|||||||
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",
|
|
||||||
}
|
|
||||||
149
dns/client.go
149
dns/client.go
@@ -2,14 +2,12 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/compatible"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -19,7 +17,7 @@ import (
|
|||||||
"github.com/sagernet/sing/contrab/freelru"
|
"github.com/sagernet/sing/contrab/freelru"
|
||||||
"github.com/sagernet/sing/contrab/maphash"
|
"github.com/sagernet/sing/contrab/maphash"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
dns "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -32,18 +30,16 @@ var (
|
|||||||
var _ adapter.DNSClient = (*Client)(nil)
|
var _ adapter.DNSClient = (*Client)(nil)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
disableCache bool
|
disableCache bool
|
||||||
disableExpire bool
|
disableExpire bool
|
||||||
independentCache bool
|
independentCache bool
|
||||||
clientSubnet netip.Prefix
|
clientSubnet netip.Prefix
|
||||||
rdrc adapter.RDRCStore
|
rdrc adapter.RDRCStore
|
||||||
initRDRCFunc func() adapter.RDRCStore
|
initRDRCFunc func() adapter.RDRCStore
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
cache freelru.Cache[dns.Question, *dns.Msg]
|
cache freelru.Cache[dns.Question, *dns.Msg]
|
||||||
cacheLock compatible.Map[dns.Question, chan struct{}]
|
transportCache freelru.Cache[transportCacheKey, *dns.Msg]
|
||||||
transportCache freelru.Cache[transportCacheKey, *dns.Msg]
|
|
||||||
transportCacheLock compatible.Map[dns.Question, chan struct{}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientOptions struct {
|
type ClientOptions struct {
|
||||||
@@ -100,15 +96,17 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
if c.logger != nil {
|
if c.logger != nil {
|
||||||
c.logger.WarnContext(ctx, "bad question size: ", len(message.Question))
|
c.logger.WarnContext(ctx, "bad question size: ", len(message.Question))
|
||||||
}
|
}
|
||||||
return FixedResponseStatus(message, dns.RcodeFormatError), nil
|
responseMessage := dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Id: message.Id,
|
||||||
|
Response: true,
|
||||||
|
Rcode: dns.RcodeFormatError,
|
||||||
|
},
|
||||||
|
Question: message.Question,
|
||||||
|
}
|
||||||
|
return &responseMessage, nil
|
||||||
}
|
}
|
||||||
question := message.Question[0]
|
question := message.Question[0]
|
||||||
if question.Qtype == dns.TypeA && options.Strategy == C.DomainStrategyIPv6Only || question.Qtype == dns.TypeAAAA && options.Strategy == C.DomainStrategyIPv4Only {
|
|
||||||
if c.logger != nil {
|
|
||||||
c.logger.DebugContext(ctx, "strategy rejected")
|
|
||||||
}
|
|
||||||
return FixedResponseStatus(message, dns.RcodeSuccess), nil
|
|
||||||
}
|
|
||||||
clientSubnet := options.ClientSubnet
|
clientSubnet := options.ClientSubnet
|
||||||
if !clientSubnet.IsValid() {
|
if !clientSubnet.IsValid() {
|
||||||
clientSubnet = c.clientSubnet
|
clientSubnet = c.clientSubnet
|
||||||
@@ -116,38 +114,12 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
if clientSubnet.IsValid() {
|
if clientSubnet.IsValid() {
|
||||||
message = SetClientSubnet(message, clientSubnet)
|
message = SetClientSubnet(message, clientSubnet)
|
||||||
}
|
}
|
||||||
|
|
||||||
isSimpleRequest := len(message.Question) == 1 &&
|
isSimpleRequest := len(message.Question) == 1 &&
|
||||||
len(message.Ns) == 0 &&
|
len(message.Ns) == 0 &&
|
||||||
(len(message.Extra) == 0 || len(message.Extra) == 1 &&
|
len(message.Extra) == 0 &&
|
||||||
message.Extra[0].Header().Rrtype == dns.TypeOPT &&
|
|
||||||
message.Extra[0].Header().Class > 0 &&
|
|
||||||
message.Extra[0].Header().Ttl == 0 &&
|
|
||||||
len(message.Extra[0].(*dns.OPT).Option) == 0) &&
|
|
||||||
!options.ClientSubnet.IsValid()
|
!options.ClientSubnet.IsValid()
|
||||||
disableCache := !isSimpleRequest || c.disableCache || options.DisableCache
|
disableCache := !isSimpleRequest || c.disableCache || options.DisableCache
|
||||||
if !disableCache {
|
if !disableCache {
|
||||||
if c.cache != nil {
|
|
||||||
cond, loaded := c.cacheLock.LoadOrStore(question, make(chan struct{}))
|
|
||||||
if loaded {
|
|
||||||
<-cond
|
|
||||||
} else {
|
|
||||||
defer func() {
|
|
||||||
c.cacheLock.Delete(question)
|
|
||||||
close(cond)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
} else if c.transportCache != nil {
|
|
||||||
cond, loaded := c.transportCacheLock.LoadOrStore(question, make(chan struct{}))
|
|
||||||
if loaded {
|
|
||||||
<-cond
|
|
||||||
} else {
|
|
||||||
defer func() {
|
|
||||||
c.transportCacheLock.Delete(question)
|
|
||||||
close(cond)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response, ttl := c.loadResponse(question, transport)
|
response, ttl := c.loadResponse(question, transport)
|
||||||
if response != nil {
|
if response != nil {
|
||||||
logCachedResponse(c.logger, ctx, response, ttl)
|
logCachedResponse(c.logger, ctx, response, ttl)
|
||||||
@@ -155,14 +127,27 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if question.Qtype == dns.TypeA && options.Strategy == C.DomainStrategyIPv6Only || question.Qtype == dns.TypeAAAA && options.Strategy == C.DomainStrategyIPv4Only {
|
||||||
|
responseMessage := dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Id: message.Id,
|
||||||
|
Response: true,
|
||||||
|
Rcode: dns.RcodeSuccess,
|
||||||
|
},
|
||||||
|
Question: []dns.Question{question},
|
||||||
|
}
|
||||||
|
if c.logger != nil {
|
||||||
|
c.logger.DebugContext(ctx, "strategy rejected")
|
||||||
|
}
|
||||||
|
return &responseMessage, nil
|
||||||
|
}
|
||||||
messageId := message.Id
|
messageId := message.Id
|
||||||
contextTransport, clientSubnetLoaded := transportTagFromContext(ctx)
|
contextTransport, clientSubnetLoaded := transportTagFromContext(ctx)
|
||||||
if clientSubnetLoaded && transport.Tag() == contextTransport {
|
if clientSubnetLoaded && transport.Tag() == contextTransport {
|
||||||
return nil, E.New("DNS query loopback in transport[", contextTransport, "]")
|
return nil, E.New("DNS query loopback in transport[", contextTransport, "]")
|
||||||
}
|
}
|
||||||
ctx = contextWithTransportTag(ctx, transport.Tag())
|
ctx = contextWithTransportTag(ctx, transport.Tag())
|
||||||
if !disableCache && responseChecker != nil && c.rdrc != nil {
|
if responseChecker != nil && c.rdrc != nil {
|
||||||
rejected := c.rdrc.LoadRDRC(transport.Tag(), question.Name, question.Qtype)
|
rejected := c.rdrc.LoadRDRC(transport.Tag(), question.Name, question.Qtype)
|
||||||
if rejected {
|
if rejected {
|
||||||
return nil, ErrResponseRejectedCached
|
return nil, ErrResponseRejectedCached
|
||||||
@@ -172,12 +157,7 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
response, err := transport.Exchange(ctx, message)
|
response, err := transport.Exchange(ctx, message)
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var rcodeError RcodeError
|
return nil, err
|
||||||
if errors.As(err, &rcodeError) {
|
|
||||||
response = FixedResponseStatus(message, int(rcodeError))
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
|
/*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA {
|
||||||
validResponse := response
|
validResponse := response
|
||||||
@@ -214,17 +194,15 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
response.Answer = append(response.Answer, validResponse.Answer...)
|
response.Answer = append(response.Answer, validResponse.Answer...)
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
disableCache = disableCache || response.Rcode != dns.RcodeSuccess || len(response.Answer) == 0
|
|
||||||
if responseChecker != nil {
|
if responseChecker != nil {
|
||||||
var rejected bool
|
var rejected bool
|
||||||
// TODO: add accept_any rule and support to check response instead of addresses
|
if !(response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError) {
|
||||||
if response.Rcode != dns.RcodeSuccess || len(response.Answer) == 0 {
|
|
||||||
rejected = true
|
rejected = true
|
||||||
} else {
|
} else {
|
||||||
rejected = !responseChecker(MessageToAddresses(response))
|
rejected = !responseChecker(MessageToAddresses(response))
|
||||||
}
|
}
|
||||||
if rejected {
|
if rejected {
|
||||||
if !disableCache && c.rdrc != nil {
|
if c.rdrc != nil {
|
||||||
c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger)
|
c.rdrc.SaveRDRCAsync(transport.Tag(), question.Name, question.Qtype, c.logger)
|
||||||
}
|
}
|
||||||
logRejectedResponse(c.logger, ctx, response)
|
logRejectedResponse(c.logger, ctx, response)
|
||||||
@@ -281,7 +259,7 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
logExchangedResponse(c.logger, ctx, response, timeToLive)
|
logExchangedResponse(c.logger, ctx, response, timeToLive)
|
||||||
return response, nil
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Lookup(ctx context.Context, transport adapter.DNSTransport, domain string, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error) {
|
func (c *Client) Lookup(ctx context.Context, transport adapter.DNSTransport, domain string, options adapter.DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error) {
|
||||||
@@ -327,7 +305,8 @@ func (c *Client) Lookup(ctx context.Context, transport adapter.DNSTransport, dom
|
|||||||
func (c *Client) ClearCache() {
|
func (c *Client) ClearCache() {
|
||||||
if c.cache != nil {
|
if c.cache != nil {
|
||||||
c.cache.Purge()
|
c.cache.Purge()
|
||||||
} else if c.transportCache != nil {
|
}
|
||||||
|
if c.transportCache != nil {
|
||||||
c.transportCache.Purge()
|
c.transportCache.Purge()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,36 +320,36 @@ func (c *Client) LookupCache(domain string, strategy C.DomainStrategy) ([]netip.
|
|||||||
}
|
}
|
||||||
dnsName := dns.Fqdn(domain)
|
dnsName := dns.Fqdn(domain)
|
||||||
if strategy == C.DomainStrategyIPv4Only {
|
if strategy == C.DomainStrategyIPv4Only {
|
||||||
addresses, err := c.questionCache(dns.Question{
|
response, err := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeA,
|
Qtype: dns.TypeA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != ErrNotCached {
|
if err != ErrNotCached {
|
||||||
return addresses, true
|
return response, true
|
||||||
}
|
}
|
||||||
} else if strategy == C.DomainStrategyIPv6Only {
|
} else if strategy == C.DomainStrategyIPv6Only {
|
||||||
addresses, err := c.questionCache(dns.Question{
|
response, err := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeAAAA,
|
Qtype: dns.TypeAAAA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if err != ErrNotCached {
|
if err != ErrNotCached {
|
||||||
return addresses, true
|
return response, true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response4, _ := c.loadResponse(dns.Question{
|
response4, _ := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeA,
|
Qtype: dns.TypeA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
response6, _ := c.loadResponse(dns.Question{
|
response6, _ := c.questionCache(dns.Question{
|
||||||
Name: dnsName,
|
Name: dnsName,
|
||||||
Qtype: dns.TypeAAAA,
|
Qtype: dns.TypeAAAA,
|
||||||
Qclass: dns.ClassINET,
|
Qclass: dns.ClassINET,
|
||||||
}, nil)
|
}, nil)
|
||||||
if response4 != nil || response6 != nil {
|
if len(response4) > 0 || len(response6) > 0 {
|
||||||
return sortAddresses(MessageToAddresses(response4), MessageToAddresses(response6), strategy), true
|
return sortAddresses(response4, response6, strategy), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
@@ -411,15 +390,15 @@ func (c *Client) storeCache(transport adapter.DNSTransport, question dns.Questio
|
|||||||
transportTag: transport.Tag(),
|
transportTag: transport.Tag(),
|
||||||
}, message)
|
}, message)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !c.independentCache {
|
||||||
|
c.cache.AddWithLifetime(question, message, time.Second*time.Duration(timeToLive))
|
||||||
} else {
|
} else {
|
||||||
if !c.independentCache {
|
c.transportCache.AddWithLifetime(transportCacheKey{
|
||||||
c.cache.AddWithLifetime(question, message, time.Second*time.Duration(timeToLive))
|
Question: question,
|
||||||
} else {
|
transportTag: transport.Tag(),
|
||||||
c.transportCache.AddWithLifetime(transportCacheKey{
|
}, message, time.Second*time.Duration(timeToLive))
|
||||||
Question: question,
|
|
||||||
transportTag: transport.Tag(),
|
|
||||||
}, message, time.Second*time.Duration(timeToLive))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,9 +517,6 @@ func (c *Client) loadResponse(question dns.Question, transport adapter.DNSTransp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MessageToAddresses(response *dns.Msg) []netip.Addr {
|
func MessageToAddresses(response *dns.Msg) []netip.Addr {
|
||||||
if response == nil || response.Rcode != dns.RcodeSuccess {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
addresses := make([]netip.Addr, 0, len(response.Answer))
|
addresses := make([]netip.Addr, 0, len(response.Answer))
|
||||||
for _, rawAnswer := range response.Answer {
|
for _, rawAnswer := range response.Answer {
|
||||||
switch answer := rawAnswer.(type) {
|
switch answer := rawAnswer.(type) {
|
||||||
@@ -585,12 +561,9 @@ func transportTagFromContext(ctx context.Context) (string, bool) {
|
|||||||
func FixedResponseStatus(message *dns.Msg, rcode int) *dns.Msg {
|
func FixedResponseStatus(message *dns.Msg, rcode int) *dns.Msg {
|
||||||
return &dns.Msg{
|
return &dns.Msg{
|
||||||
MsgHdr: dns.MsgHdr{
|
MsgHdr: dns.MsgHdr{
|
||||||
Id: message.Id,
|
Id: message.Id,
|
||||||
Response: true,
|
Rcode: rcode,
|
||||||
Authoritative: true,
|
Response: true,
|
||||||
RecursionDesired: true,
|
|
||||||
RecursionAvailable: true,
|
|
||||||
Rcode: rcode,
|
|
||||||
},
|
},
|
||||||
Question: message.Question,
|
Question: message.Question,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ func TruncateDNSMessage(request *dns.Msg, response *dns.Msg, headroom int) (*buf
|
|||||||
}
|
}
|
||||||
responseLen := response.Len()
|
responseLen := response.Len()
|
||||||
if responseLen > maxLen {
|
if responseLen > maxLen {
|
||||||
response = response.Copy()
|
copyResponse := *response
|
||||||
|
response = ©Response
|
||||||
response.Truncate(maxLen)
|
response.Truncate(maxLen)
|
||||||
}
|
}
|
||||||
buffer := buf.NewSize(headroom*2 + 1 + responseLen)
|
buffer := buf.NewSize(headroom*2 + 1 + responseLen)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user