mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-13 20:28:32 +10:00
Compare commits
28 Commits
v1.12.0-al
...
dev-mitm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
003423745e | ||
|
|
fb3007fa80 | ||
|
|
5361d2acec | ||
|
|
1fe983a81b | ||
|
|
b01fe5d364 | ||
|
|
74920b44ac | ||
|
|
5e28a80e63 | ||
|
|
b55bfca7de | ||
|
|
a0dc394c8f | ||
|
|
87f3423d7e | ||
|
|
244243f206 | ||
|
|
89855ff548 | ||
|
|
8a388e9c90 | ||
|
|
6bf39156ec | ||
|
|
ab021bee74 | ||
|
|
6fd95e157a | ||
|
|
6c0e2bf526 | ||
|
|
4f61fc20e0 | ||
|
|
438405bbf1 | ||
|
|
b0a2ed9f7e | ||
|
|
f4d5823bb2 | ||
|
|
e8b43a97f7 | ||
|
|
00460f15f6 | ||
|
|
ca33246c9e | ||
|
|
e04a4181f4 | ||
|
|
ff4f455f25 | ||
|
|
9a7c0d9136 | ||
|
|
57de88b9c9 |
22
.github/setup_legacy_go.sh
vendored
22
.github/setup_legacy_go.sh
vendored
@@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
VERSION="1.23.6"
|
|
||||||
wget "https://dl.google.com/go/go${VERSION}.linux-amd64.tar.gz"
|
|
||||||
tar -xzf "go${VERSION}.linux-amd64.tar.gz"
|
|
||||||
mv go $HOME/go/go_legacy
|
|
||||||
cd $HOME/go/go_legacy
|
|
||||||
|
|
||||||
# modify from https://github.com/restic/restic/issues/4636#issuecomment-1896455557
|
|
||||||
# this patch file only works on golang1.23.x
|
|
||||||
# that means after golang1.24 release it must be changed
|
|
||||||
# see: https://github.com/MetaCubeX/go/commits/release-branch.go1.23/
|
|
||||||
# revert:
|
|
||||||
# 693def151adff1af707d82d28f55dba81ceb08e1: "crypto/rand,runtime: switch RtlGenRandom for ProcessPrng"
|
|
||||||
# 7c1157f9544922e96945196b47b95664b1e39108: "net: remove sysSocket fallback for Windows 7"
|
|
||||||
# 48042aa09c2f878c4faa576948b07fe625c4707a: "syscall: remove Windows 7 console handle workaround"
|
|
||||||
# a17d959debdb04cd550016a3501dd09d50cd62e7: "runtime: always use LoadLibraryEx to load system libraries"
|
|
||||||
|
|
||||||
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/6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76.diff | patch --verbose -p 1
|
|
||||||
curl https://github.com/MetaCubeX/go/commit/69e2eed6dd0f6d815ebf15797761c13f31213dd6.diff | patch --verbose -p 1
|
|
||||||
35
.github/workflows/build.yml
vendored
35
.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.24
|
go-version: ^1.23
|
||||||
- name: Check input version
|
- name: Check input version
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
run: |-
|
run: |-
|
||||||
@@ -112,6 +112,7 @@ jobs:
|
|||||||
- name: darwin_amd64
|
- name: darwin_amd64
|
||||||
goos: darwin
|
goos: darwin
|
||||||
goarch: amd64
|
goarch: amd64
|
||||||
|
require_legacy_go: true
|
||||||
- name: android_arm64
|
- name: android_arm64
|
||||||
goos: android
|
goos: android
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
@@ -133,23 +134,26 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Cache legacy Go
|
- name: Cache legacy Go
|
||||||
if: matrix.require_legacy_go
|
if: matrix.require_legacy_go
|
||||||
id: cache-legacy-go
|
id: cache-legacy-go
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/go/go_legacy
|
~/go/go1.20.14
|
||||||
key: go_legacy_1236
|
key: go120
|
||||||
- name: Setup legacy Go
|
- name: Setup legacy Go
|
||||||
if: matrix.require_legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
if: matrix.require_legacy_go && steps.cache-legacy-go.outputs.cache-hit != 'true'
|
||||||
run: bash .github/setup_legacy_go.sh
|
run: |-
|
||||||
|
wget https://dl.google.com/go/go1.20.14.linux-amd64.tar.gz
|
||||||
|
tar -xzf go1.20.14.linux-amd64.tar.gz
|
||||||
|
mv go $HOME/go/go1.20.14
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
if: matrix.goos == 'android'
|
if: matrix.goos == 'android'
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r28
|
ndk-version: r28-beta2
|
||||||
local-cache: true
|
local-cache: true
|
||||||
- name: Setup Goreleaser
|
- name: Setup Goreleaser
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@v6
|
||||||
@@ -215,12 +219,12 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r28
|
ndk-version: r28-beta3
|
||||||
- name: Setup OpenJDK
|
- name: Setup OpenJDK
|
||||||
run: |-
|
run: |-
|
||||||
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
||||||
@@ -252,16 +256,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: ~/.gradle
|
path: ~/.gradle
|
||||||
key: gradle-${{ hashFiles('**/*.gradle') }}
|
key: gradle-${{ hashFiles('**/*.gradle') }}
|
||||||
- name: Update version
|
|
||||||
if: github.event_name == 'workflow_dispatch'
|
|
||||||
run: |-
|
|
||||||
go run -v ./cmd/internal/update_android_version --ci
|
|
||||||
- name: Update nightly version
|
|
||||||
if: github.event_name != 'workflow_dispatch'
|
|
||||||
run: |-
|
|
||||||
go run -v ./cmd/internal/update_android_version --ci --nightly
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |-
|
run: |-
|
||||||
|
go run -v ./cmd/internal/update_android_version --ci
|
||||||
mkdir clients/android/app/libs
|
mkdir clients/android/app/libs
|
||||||
cp libbox.aar clients/android/app/libs
|
cp libbox.aar clients/android/app/libs
|
||||||
cd clients/android
|
cd clients/android
|
||||||
@@ -297,12 +294,12 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Setup Android NDK
|
- name: Setup Android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r28
|
ndk-version: r28-beta3
|
||||||
- name: Setup OpenJDK
|
- name: Setup OpenJDK
|
||||||
run: |-
|
run: |-
|
||||||
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
sudo apt update && sudo apt install -y openjdk-17-jdk-headless
|
||||||
@@ -395,7 +392,7 @@ jobs:
|
|||||||
if: matrix.if
|
if: matrix.if
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Setup Xcode stable
|
- name: Setup Xcode stable
|
||||||
if: matrix.if && github.ref == 'refs/heads/main-next'
|
if: matrix.if && github.ref == 'refs/heads/main-next'
|
||||||
run: |-
|
run: |-
|
||||||
|
|||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: ^1.24
|
go-version: ^1.23
|
||||||
- name: Extract signing key
|
- name: Extract signing key
|
||||||
run: |-
|
run: |-
|
||||||
mkdir -p $HOME/.gnupg
|
mkdir -p $HOME/.gnupg
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ linters-settings:
|
|||||||
- -SA1003
|
- -SA1003
|
||||||
|
|
||||||
run:
|
run:
|
||||||
go: "1.24"
|
go: "1.23"
|
||||||
build-tags:
|
build-tags:
|
||||||
- with_gvisor
|
- with_gvisor
|
||||||
- with_quic
|
- with_quic
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ builds:
|
|||||||
- with_reality_server
|
- with_reality_server
|
||||||
- with_acme
|
- with_acme
|
||||||
- with_clash_api
|
- with_clash_api
|
||||||
- with_tailscale
|
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
targets:
|
targets:
|
||||||
|
|||||||
@@ -21,10 +21,8 @@ builds:
|
|||||||
- with_reality_server
|
- with_reality_server
|
||||||
- with_acme
|
- with_acme
|
||||||
- with_clash_api
|
- with_clash_api
|
||||||
- with_tailscale
|
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
- GOTOOLCHAIN=local
|
|
||||||
targets:
|
targets:
|
||||||
- linux_386
|
- linux_386
|
||||||
- linux_amd64_v1
|
- linux_amd64_v1
|
||||||
@@ -51,19 +49,18 @@ builds:
|
|||||||
- with_reality_server
|
- with_reality_server
|
||||||
- with_acme
|
- with_acme
|
||||||
- with_clash_api
|
- with_clash_api
|
||||||
- with_tailscale
|
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
- GOROOT={{ .Env.GOPATH }}/go_legacy
|
- GOROOT={{ .Env.GOPATH }}/go1.20.14
|
||||||
tool: "{{ .Env.GOPATH }}/go_legacy/bin/go"
|
tool: "{{ .Env.GOPATH }}/go1.20.14/bin/go"
|
||||||
targets:
|
targets:
|
||||||
- windows_amd64_v1
|
- windows_amd64_v1
|
||||||
- windows_386
|
- windows_386
|
||||||
|
- darwin_amd64_v1
|
||||||
- id: android
|
- id: android
|
||||||
<<: *template
|
<<: *template
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=1
|
- CGO_ENABLED=1
|
||||||
- GOTOOLCHAIN=local
|
|
||||||
overrides:
|
overrides:
|
||||||
- goos: android
|
- goos: android
|
||||||
goarch: arm
|
goarch: arm
|
||||||
@@ -125,8 +122,8 @@ nfpms:
|
|||||||
- deb
|
- deb
|
||||||
- rpm
|
- rpm
|
||||||
- archlinux
|
- archlinux
|
||||||
# - apk
|
# - apk
|
||||||
# - ipk
|
# - ipk
|
||||||
priority: extra
|
priority: extra
|
||||||
contents:
|
contents:
|
||||||
- src: release/config/config.json
|
- src: release/config/config.json
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
|
FROM --platform=$BUILDPLATFORM golang:1.23-alpine AS builder
|
||||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||||
COPY . /go/src/github.com/sagernet/sing-box
|
COPY . /go/src/github.com/sagernet/sing-box
|
||||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||||
|
|||||||
13
Makefile
13
Makefile
@@ -2,8 +2,7 @@ NAME = sing-box
|
|||||||
COMMIT = $(shell git rev-parse --short HEAD)
|
COMMIT = $(shell git rev-parse --short HEAD)
|
||||||
TAGS_GO120 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls
|
TAGS_GO120 = with_gvisor,with_dhcp,with_wireguard,with_reality_server,with_clash_api,with_quic,with_utls
|
||||||
TAGS_GO121 = with_ech
|
TAGS_GO121 = with_ech
|
||||||
TAGS_GO123 = with_tailscale
|
TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121)
|
||||||
TAGS ?= $(TAGS_GO118),$(TAGS_GO120),$(TAGS_GO121),$(TAGS_GO123)
|
|
||||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server
|
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server
|
||||||
|
|
||||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||||
@@ -18,17 +17,14 @@ PREFIX ?= $(shell go env GOPATH)
|
|||||||
.PHONY: test release docs build
|
.PHONY: test release docs build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
export GOTOOLCHAIN=local && \
|
|
||||||
go build $(MAIN_PARAMS) $(MAIN)
|
go build $(MAIN_PARAMS) $(MAIN)
|
||||||
|
|
||||||
ci_build_go120:
|
ci_build_go120:
|
||||||
export GOTOOLCHAIN=local && \
|
go build $(PARAMS) $(MAIN)
|
||||||
go build $(PARAMS) $(MAIN) && \
|
|
||||||
go build $(PARAMS) -tags "$(TAGS_GO120)" $(MAIN)
|
go build $(PARAMS) -tags "$(TAGS_GO120)" $(MAIN)
|
||||||
|
|
||||||
ci_build:
|
ci_build:
|
||||||
export GOTOOLCHAIN=local && \
|
go build $(PARAMS) $(MAIN)
|
||||||
go build $(PARAMS) $(MAIN) && \
|
|
||||||
go build $(MAIN_PARAMS) $(MAIN)
|
go build $(MAIN_PARAMS) $(MAIN)
|
||||||
|
|
||||||
generate_completions:
|
generate_completions:
|
||||||
@@ -65,6 +61,9 @@ proto_install:
|
|||||||
go install -v google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
go install -v google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||||
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||||
|
|
||||||
|
update_public_suffix:
|
||||||
|
go generate common/tlsfragment/public_suffix.go
|
||||||
|
|
||||||
update_certificates:
|
update_certificates:
|
||||||
go run ./cmd/internal/update_certificates
|
go run ./cmd/internal/update_certificates
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ type CacheFile interface {
|
|||||||
StoreGroupExpand(group string, expand bool) error
|
StoreGroupExpand(group string, expand bool) error
|
||||||
LoadRuleSet(tag string) *SavedBinary
|
LoadRuleSet(tag string) *SavedBinary
|
||||||
SaveRuleSet(tag string, set *SavedBinary) error
|
SaveRuleSet(tag string, set *SavedBinary) error
|
||||||
|
LoadScript(tag string) *SavedBinary
|
||||||
|
SaveScript(tag string, script *SavedBinary) error
|
||||||
|
SurgePersistentStoreRead(key string) string
|
||||||
|
SurgePersistentStoreWrite(key string, value string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type SavedBinary struct {
|
type SavedBinary struct {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package adapter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -57,6 +59,8 @@ type InboundContext struct {
|
|||||||
Domain string
|
Domain string
|
||||||
Client string
|
Client string
|
||||||
SniffContext any
|
SniffContext any
|
||||||
|
HTTPRequest *http.Request
|
||||||
|
ClientHello *tls.ClientHelloInfo
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
|
|
||||||
@@ -73,6 +77,7 @@ type InboundContext struct {
|
|||||||
UDPTimeout time.Duration
|
UDPTimeout time.Duration
|
||||||
TLSFragment bool
|
TLSFragment bool
|
||||||
TLSFragmentFallbackDelay time.Duration
|
TLSFragmentFallbackDelay time.Duration
|
||||||
|
MITM *option.MITMRouteOptions
|
||||||
|
|
||||||
NetworkStrategy *C.NetworkStrategy
|
NetworkStrategy *C.NetworkStrategy
|
||||||
NetworkType []C.InterfaceType
|
NetworkType []C.InterfaceType
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import E "github.com/sagernet/sing/common/exceptions"
|
import (
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
type StartStage uint8
|
type StartStage uint8
|
||||||
|
|
||||||
@@ -45,6 +47,9 @@ type LifecycleService interface {
|
|||||||
|
|
||||||
func Start(stage StartStage, services ...Lifecycle) error {
|
func Start(stage StartStage, services ...Lifecycle) error {
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
|
if service == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
err := service.Start(stage)
|
err := service.Start(stage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
15
adapter/mitm.go
Normal file
15
adapter/mitm.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MITMEngine interface {
|
||||||
|
Lifecycle
|
||||||
|
ExportCertificate() *x509.Certificate
|
||||||
|
NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||||
|
}
|
||||||
54
adapter/script.go
Normal file
54
adapter/script.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScriptManager interface {
|
||||||
|
Lifecycle
|
||||||
|
Scripts() []Script
|
||||||
|
Script(name string) (Script, bool)
|
||||||
|
SurgeCache() *SurgeInMemoryCache
|
||||||
|
}
|
||||||
|
|
||||||
|
type SurgeInMemoryCache struct {
|
||||||
|
sync.RWMutex
|
||||||
|
Data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Script interface {
|
||||||
|
Type() string
|
||||||
|
Tag() string
|
||||||
|
StartContext(ctx context.Context, startContext *HTTPStartContext) error
|
||||||
|
PostStart() error
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type SurgeScript interface {
|
||||||
|
Script
|
||||||
|
ExecuteGeneric(ctx context.Context, scriptType string, timeout time.Duration, arguments []string) error
|
||||||
|
ExecuteHTTPRequest(ctx context.Context, timeout time.Duration, request *http.Request, body []byte, binaryBody bool, arguments []string) (*HTTPRequestScriptResult, error)
|
||||||
|
ExecuteHTTPResponse(ctx context.Context, timeout time.Duration, request *http.Request, response *http.Response, body []byte, binaryBody bool, arguments []string) (*HTTPResponseScriptResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPRequestScriptResult struct {
|
||||||
|
URL string
|
||||||
|
Headers http.Header
|
||||||
|
Body []byte
|
||||||
|
Response *HTTPRequestScriptResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPRequestScriptResponse struct {
|
||||||
|
Status int
|
||||||
|
Headers http.Header
|
||||||
|
Body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPResponseScriptResult struct {
|
||||||
|
Status int
|
||||||
|
Headers http.Header
|
||||||
|
Body []byte
|
||||||
|
}
|
||||||
35
box.go
35
box.go
@@ -23,9 +23,11 @@ import (
|
|||||||
"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/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing-box/mitm"
|
||||||
"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"
|
||||||
"github.com/sagernet/sing-box/route"
|
"github.com/sagernet/sing-box/route"
|
||||||
|
"github.com/sagernet/sing-box/script"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
@@ -48,6 +50,8 @@ type Box struct {
|
|||||||
dnsRouter *dns.Router
|
dnsRouter *dns.Router
|
||||||
connection *route.ConnectionManager
|
connection *route.ConnectionManager
|
||||||
router *route.Router
|
router *route.Router
|
||||||
|
script *script.Manager
|
||||||
|
mitm adapter.MITMEngine //*mitm.Engine
|
||||||
services []adapter.LifecycleService
|
services []adapter.LifecycleService
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
@@ -173,7 +177,7 @@ func New(options Options) (*Box, error) {
|
|||||||
return nil, E.Cause(err, "initialize network manager")
|
return nil, E.Cause(err, "initialize network manager")
|
||||||
}
|
}
|
||||||
service.MustRegister[adapter.NetworkManager](ctx, networkManager)
|
service.MustRegister[adapter.NetworkManager](ctx, networkManager)
|
||||||
connectionManager := route.NewConnectionManager(logFactory.NewLogger("connection"))
|
connectionManager := route.NewConnectionManager(ctx, logFactory.NewLogger("connection"))
|
||||||
service.MustRegister[adapter.ConnectionManager](ctx, connectionManager)
|
service.MustRegister[adapter.ConnectionManager](ctx, connectionManager)
|
||||||
router := route.NewRouter(ctx, logFactory, routeOptions, dnsOptions)
|
router := route.NewRouter(ctx, logFactory, routeOptions, dnsOptions)
|
||||||
service.MustRegister[adapter.Router](ctx, router)
|
service.MustRegister[adapter.Router](ctx, router)
|
||||||
@@ -181,8 +185,8 @@ func New(options Options) (*Box, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize router")
|
return nil, E.Cause(err, "initialize router")
|
||||||
}
|
}
|
||||||
ntpOptions := common.PtrValueOrDefault(options.NTP)
|
|
||||||
var timeService *tls.TimeServiceWrapper
|
var timeService *tls.TimeServiceWrapper
|
||||||
|
ntpOptions := common.PtrValueOrDefault(options.NTP)
|
||||||
if ntpOptions.Enabled {
|
if ntpOptions.Enabled {
|
||||||
timeService = new(tls.TimeServiceWrapper)
|
timeService = new(tls.TimeServiceWrapper)
|
||||||
service.MustRegister[ntp.TimeService](ctx, timeService)
|
service.MustRegister[ntp.TimeService](ctx, timeService)
|
||||||
@@ -289,6 +293,11 @@ func New(options Options) (*Box, error) {
|
|||||||
"local",
|
"local",
|
||||||
option.LocalDNSServerOptions{},
|
option.LocalDNSServerOptions{},
|
||||||
)))
|
)))
|
||||||
|
scriptManager, err := script.NewManager(ctx, logFactory, options.Scripts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "initialize script manager")
|
||||||
|
}
|
||||||
|
service.MustRegister[adapter.ScriptManager](ctx, scriptManager)
|
||||||
if platformInterface != nil {
|
if platformInterface != nil {
|
||||||
err = platformInterface.Initialize(networkManager)
|
err = platformInterface.Initialize(networkManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -338,6 +347,16 @@ func New(options Options) (*Box, error) {
|
|||||||
timeService.TimeService = ntpService
|
timeService.TimeService = ntpService
|
||||||
services = append(services, adapter.NewLifecycleService(ntpService, "ntp service"))
|
services = append(services, adapter.NewLifecycleService(ntpService, "ntp service"))
|
||||||
}
|
}
|
||||||
|
mitmOptions := common.PtrValueOrDefault(options.MITM)
|
||||||
|
var mitmEngine adapter.MITMEngine
|
||||||
|
if mitmOptions.Enabled {
|
||||||
|
engine, err := mitm.NewEngine(ctx, logFactory.NewLogger("mitm"), mitmOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "create MITM engine")
|
||||||
|
}
|
||||||
|
service.MustRegister[adapter.MITMEngine](ctx, engine)
|
||||||
|
mitmEngine = engine
|
||||||
|
}
|
||||||
return &Box{
|
return &Box{
|
||||||
network: networkManager,
|
network: networkManager,
|
||||||
endpoint: endpointManager,
|
endpoint: endpointManager,
|
||||||
@@ -347,6 +366,8 @@ func New(options Options) (*Box, error) {
|
|||||||
dnsRouter: dnsRouter,
|
dnsRouter: dnsRouter,
|
||||||
connection: connectionManager,
|
connection: connectionManager,
|
||||||
router: router,
|
router: router,
|
||||||
|
script: scriptManager,
|
||||||
|
mitm: mitmEngine,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
logFactory: logFactory,
|
logFactory: logFactory,
|
||||||
logger: logFactory.Logger(),
|
logger: logFactory.Logger(),
|
||||||
@@ -405,11 +426,11 @@ func (s *Box) preStart() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
err = adapter.Start(adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.script, s.mitm, s.outbound, s.inbound, s.endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router)
|
err = adapter.Start(adapter.StartStateStart, s.outbound, s.dnsTransport, s.dnsRouter, s.network, s.connection, s.router, s.script, s.mitm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -433,7 +454,7 @@ func (s *Box) start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.inbound, s.endpoint)
|
err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.script, s.mitm, s.inbound, s.endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -441,7 +462,7 @@ func (s *Box) start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = adapter.Start(adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint)
|
err = adapter.Start(adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.script, s.mitm, s.outbound, s.inbound, s.endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -460,7 +481,7 @@ func (s *Box) Close() error {
|
|||||||
close(s.done)
|
close(s.done)
|
||||||
}
|
}
|
||||||
err := common.Close(
|
err := common.Close(
|
||||||
s.inbound, s.outbound, s.endpoint, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
|
s.inbound, s.outbound, s.endpoint, s.mitm, s.script, s.router, s.connection, s.dnsRouter, s.dnsTransport, s.network,
|
||||||
)
|
)
|
||||||
for _, lifecycleService := range s.services {
|
for _, lifecycleService := range s.services {
|
||||||
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
||||||
|
|||||||
Submodule clients/android updated: 3a2fc9c880...599d8cecac
Submodule clients/apple updated: 3d5d7343fb...e43e3a3f64
@@ -58,7 +58,7 @@ func init() {
|
|||||||
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
sharedFlags = append(sharedFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
||||||
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
debugFlags = append(debugFlags, "-ldflags", "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
||||||
|
|
||||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api", "with_tailscale")
|
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api")
|
||||||
iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack")
|
iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack")
|
||||||
debugTags = append(debugTags, "debug")
|
debugTags = append(debugTags, "debug")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func FindSDK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findNDK() bool {
|
func findNDK() bool {
|
||||||
const fixedVersion = "28.0.13004108"
|
const fixedVersion = "28.0.12916984"
|
||||||
const versionFile = "source.properties"
|
const versionFile = "source.properties"
|
||||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
||||||
androidNDKPath = fixedPath
|
androidNDKPath = fixedPath
|
||||||
|
|||||||
@@ -13,14 +13,10 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var flagRunInCI bool
|
||||||
flagRunInCI bool
|
|
||||||
flagRunNightly bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
flag.BoolVar(&flagRunInCI, "ci", false, "Run in CI")
|
||||||
flag.BoolVar(&flagRunNightly, "nightly", false, "Run nightly")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -50,23 +46,21 @@ func main() {
|
|||||||
switch propPair[0] {
|
switch propPair[0] {
|
||||||
case "VERSION_NAME":
|
case "VERSION_NAME":
|
||||||
if propPair[1] != newVersion {
|
if propPair[1] != newVersion {
|
||||||
log.Info("updated version from ", propPair[1], " to ", newVersion)
|
|
||||||
versionUpdated = true
|
versionUpdated = true
|
||||||
propPair[1] = newVersion
|
propPair[1] = newVersion
|
||||||
|
log.Info("updated version to ", newVersion)
|
||||||
}
|
}
|
||||||
case "GO_VERSION":
|
case "GO_VERSION":
|
||||||
if propPair[1] != runtime.Version() {
|
if propPair[1] != runtime.Version() {
|
||||||
log.Info("updated Go version from ", propPair[1], " to ", runtime.Version())
|
|
||||||
goVersionUpdated = true
|
goVersionUpdated = true
|
||||||
propPair[1] = runtime.Version()
|
propPair[1] = runtime.Version()
|
||||||
|
log.Info("updated Go version to ", runtime.Version())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !(versionUpdated || goVersionUpdated) {
|
if !(versionUpdated || goVersionUpdated) {
|
||||||
log.Info("version not changed")
|
log.Info("version not changed")
|
||||||
return
|
return
|
||||||
} else if flagRunInCI && !flagRunNightly {
|
|
||||||
log.Fatal("version changed, commit changes first.")
|
|
||||||
}
|
}
|
||||||
for _, propPair := range propsList {
|
for _, propPair := range propsList {
|
||||||
switch propPair[0] {
|
switch propPair[0] {
|
||||||
|
|||||||
@@ -60,10 +60,7 @@ func init() {
|
|||||||
generated.WriteString(record[nameIndex])
|
generated.WriteString(record[nameIndex])
|
||||||
generated.WriteString("\n")
|
generated.WriteString("\n")
|
||||||
generated.WriteString(" mozillaIncluded.AppendCertsFromPEM([]byte(`")
|
generated.WriteString(" mozillaIncluded.AppendCertsFromPEM([]byte(`")
|
||||||
cert := record[certIndex]
|
generated.WriteString(record[certIndex])
|
||||||
// Remove single quotes
|
|
||||||
cert = cert[1 : len(cert)-1]
|
|
||||||
generated.WriteString(cert)
|
|
||||||
generated.WriteString("`))\n")
|
generated.WriteString("`))\n")
|
||||||
}
|
}
|
||||||
generated.WriteString("}\n")
|
generated.WriteString("}\n")
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,14 +33,16 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
|||||||
var systemPool *x509.CertPool
|
var systemPool *x509.CertPool
|
||||||
switch options.Store {
|
switch options.Store {
|
||||||
case C.CertificateStoreSystem, "":
|
case C.CertificateStoreSystem, "":
|
||||||
systemPool = x509.NewCertPool()
|
platformInterface := service.FromContext[platform.Interface](ctx)
|
||||||
var systemValid bool
|
systemCertificates := platformInterface.SystemCertificates()
|
||||||
for _, cert := range service.FromContext[platform.Interface](ctx).SystemCertificates() {
|
if len(systemCertificates) > 0 {
|
||||||
if systemPool.AppendCertsFromPEM([]byte(cert)) {
|
systemPool = x509.NewCertPool()
|
||||||
systemValid = true
|
for _, cert := range systemCertificates {
|
||||||
|
if !systemPool.AppendCertsFromPEM([]byte(cert)) {
|
||||||
|
return nil, E.New("invalid system certificate PEM: ", cert)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
if !systemValid {
|
|
||||||
certPool, err := x509.SystemCertPool()
|
certPool, err := x509.SystemCertPool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ type Options struct {
|
|||||||
RemoteIsDomain bool
|
RemoteIsDomain bool
|
||||||
DirectResolver bool
|
DirectResolver bool
|
||||||
ResolverOnDetour bool
|
ResolverOnDetour bool
|
||||||
NewDialer bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: merge with NewWithOptions
|
// TODO: merge with NewWithOptions
|
||||||
@@ -101,8 +100,6 @@ func NewWithOptions(options Options) (N.Dialer, error) {
|
|||||||
}
|
}
|
||||||
dnsQueryOptions.Transport = transport
|
dnsQueryOptions.Transport = transport
|
||||||
resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay)
|
resolveFallbackDelay = time.Duration(dialOptions.FallbackDelay)
|
||||||
} else if options.NewDialer {
|
|
||||||
return nil, E.New("missing domain resolver for domain server address")
|
|
||||||
} else {
|
} else {
|
||||||
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
|
deprecated.Report(options.Context, deprecated.OptionMissingDomainResolver)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ func HTTPHost(_ context.Context, metadata *adapter.InboundContext, reader io.Rea
|
|||||||
}
|
}
|
||||||
metadata.Protocol = C.ProtocolHTTP
|
metadata.Protocol = C.ProtocolHTTP
|
||||||
metadata.Domain = M.ParseSocksaddr(request.Host).AddrString()
|
metadata.Domain = M.ParseSocksaddr(request.Host).AddrString()
|
||||||
|
metadata.HTTPRequest = request
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,26 +12,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSniffQUICChromeNew(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
pkt, err := hex.DecodeString("ca0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea4489ad89c322f75f9a383c90d126a0b21104cb519c2bb32e6a134e86896452e942b26c519b8c7ac9e4c99fae5e1f65cf08fb98443b30e4567932e8fb0789820d8f33037b59ac8113530258c9467dfb52489396dae01f099d28b234efa107fa411f2a1ffa2abe74988e03d662d4296024e95ce0fe1671724937157f77b84990478a2d4060676cf0827b4e8c600654111750414dafa0cccb332f3020c2922a015f445df5edc9c7d2d1ceea9fddcc9ff821c9183aa39a70da20fcc057579e1051c1c899148d6cf9d08b4919822082d040d1ce03ca4f216be6cb7ef03db6df0993ef1ccce5c8c648980554f41704526e1809d2545739f5872e75ec797db1c99f5682e2eda9363cb32aa367b7b363c782ddbacf874183cc15c8a2db068dd4093eebdd096ad33832a7939deb0a872279744f5a56dc001ba62fac973bf680f3b362bdd336add4dd102f462b773bf70bfce1921070a802a92025273a177186d1a643081b42175eb789ccddadb71033ef4feacbf6fd282ab622cf61669d73cda559e411c6ccdd8f003443b6933b7729b7a357aa4aa2fba0f365f829a4d497afb5dc2648a53bc9f3e786d955069d0a4781088a5463747dfe9958ea19ea444eae947ec6a67640955f710f93640084f3fbb8ad259b68dbc0ee0b7fab2d81bffd83ed8a6d33522dbfef43bec0a0fb4bdf1cb712dc4ced0680c0687fa240fd157baa232b1c84e14adce6421cf9270f9b3972f98fc67b344b8a4f1fb551e26f7f76d484ed9f8197f231dc5d9a44cc0ddce73d7f810a620851f4e97eb5037ab5135d7c3be5b80cc32d19910b8387aca64c93c02dc3e35238b78e6aff470722078982e58802844932b6041446bfdcc97ba640cbb86721bcd0f40f27b77aa6287ce5674ec1720134b9302875482c3269787e004b9edb483d44f326eef38c0e83cb46af96488c2e696bc2524567fb29c1e8edcd5a73615496d172d46a9d29e0505c0018b7bbb00165eca0389e09c4b1d73b6cc4a2f735a720650134a2e98e8105e20695cf231b92586237dfe0f99c897414e51c21627496276535f07abb53fb2b554376fe520fa45a3e944fd91dfe7a72aead08842b6b63d8edf861fb911954c83bd9a896eb9da4af5eff646455069d747facd4e77c254096843bff7c3e9031dbdf8dc37ea45f1122922fcbc322ec1378f3c7c1af0da62e1052e6210f1b23073f93a82d90e14cb20bc4501d487a1c848674d57a7c269b13590b3a99d8b8b4f6d0dfbd1d2cbbe7a32c0d5c84ae7ec438b0b19f3862d8fabaa828d06c7e3c6967405cd56a1ae90f38633e2ee0e3ecfca3df399fe12f029e0860a1a30da010300d0c94f0bf56091d00011488c1429928b21c739ebf50ba8be91116315d3173f6d2c56735722478c4d74392ba84d1727036b3d64e8c2263b0f33cb8086be587ca6b3940259c06afa2683868856529303ae12e91d7ca874568be7f2bfaa0656dfab0ed31ed90eaea10fb7f3433ec59a334abe6211d547fa0c825ac45d3691e749d15432008de83e9f6d98f368359137ae803d9189b3386f800c7c0cf4b615d1983cf82d9981a8105b60a80fe66c9b0d439b5ba153dd19e9e7483a01cf3b02b4597540b38e658d4eb8455e030b2bf2690bdd78c23f16fe5")
|
|
||||||
require.NoError(t, err)
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
|
||||||
require.Equal(t, metadata.Protocol, C.ProtocolQUIC)
|
|
||||||
require.Equal(t, metadata.Client, C.ClientChromium)
|
|
||||||
require.ErrorIs(t, err, sniff.ErrClientHelloFragmented)
|
|
||||||
pkt, err = hex.DecodeString("cc0000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea44894b626c685cd5d5c965f7e97b3a1bdc520b75813e747f37a3ae83ad38b9ca2acb0de4fc9424839a50c8fb815a62b498609fbbc59145698860e0509cc08a04d1b119daef844ba2f09c16e2665e5cc0b47624b71f7b950c54fd56b4a1fbb826cba44eeeee3949ced8f5de60d4c81b19ee59f75aa1abb33f22c6b13c27095eb1e99cff01fdc93e6e88da2622ee18c08a79f508befd7e33e99bca60e64bef9a47b764384bd93823daeeb6fcb4d7cfbc4ab53eff59b3636f6dcaaf229b5a94941b5712807166b9bd5e82cb4a9708a71451c4cd6f6e33fb2fe40c8c70dd51a30b37ff9c5e35783debde0093fde19ce074b4887b3c90980b107b9c0f32cf61a66f37c251b789abc4d27fc421207966846c8cc7faa42d9af6ad355a6bc94cb78223b612be8b3e2a4df61fee83a674a0ceb8b7c3a29b97102cda22fecdf6a4628e5b612bc17eab64d6f75feedd0b106c0419e484e66725759964cb5935ac5125e5ae920cd280bd40df57c1d7ae1845700bd4eb7b7ab12bc0850950bfe6e69edd6ac1daa5db2c2b07484327196e561c513462d72872dc6771c39f6b60d46a1f2c92343b7338450a0ef8e39f97fa70652b3a12cd04043698951627aaaa82cc95e76df92021d30e8014c984f12eea0143de8b17e5e4a36ec07bf4814251b391f168a59ef75afcd2319249aaba930f06bb7a11b9491e6f71b3d5774a6503a965e94edd0a67737282fc9cb0271779ff14151b7aa9267bb8f7d643185512515aeea513c0c98bfae782381a3317064195d8825cf8b25c17cdab5fced02612a3f2870e40df57e6ca3f08228a2b04e8de1425eb4b970118f9bbdc212223ff86a5d6b648cdf2366722f21de4b14a1014879eadb69215cdb1aa2a9f4f310ecfe3116214fe3ab0a23f4775a0a54b48d7dfd8f7283ed687b3ac7e1a7e42a0bdc3478aba8651c03e1e9cc9df17d106b8130afe854269b0103b7a696f452721887b19d8181830073c9f10684c65f96d3a6c6efbae044eec03d6399e001fa44d54635dc72f9b8ea6b87d0f452cad1e1e32273e2b47c40f2730235adcae8523b8282f86b8cf1ab63ae54aaa06130df3bbf6ecac7d7d1d43d2a87aea837267ff8ccfaa4b7e47b7ded909e6603d0b928a304f8915c839153598adc4178eb48bc0e98ad7793d7980275e1e491ba4847a4a04ae30fe7f5cc7d4b6f4f63a525e9964d72245860ca76a668a4654adb6619f16e9db79131e5675b93cafb96c92f1da8464d4fef2a22e7f9db695965fe2cc27ea30974629c8fe17cfa2f860179e1eb9faaa88a91ec9ce6da28c1a2894c3b932b5e1c807146718cc77ca13c61eaae00c7c99e019f599772064b198c5c2c5e863336367673630b417ac845ddb7c93b0856317e5d64bab208c5730abc2c63536784fbeaaec139dffc917e775715f1e42164ddef5138d4d163609ab3fbdcab968f8738385c0e7e34ff3cf7771a1dc5ba25a8850fdf96dabafa21f9065f307457ce9af4b7a73450c9d20a3b46fa8d3a1163d22bd01a7d17f0ec274181bf9640fa941427694bfeb1346089f7a851efe0fbb7a2041fa6bb6541ccbad77dd3e1a97999fc05f1fef070e7b5c4b385b8b2a8cc32483fdeba6a373970de2fa4139ba18e5916f949aab0aab2894")
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
|
||||||
require.ErrorIs(t, err, sniff.ErrClientHelloFragmented)
|
|
||||||
pkt, err = hex.DecodeString("c20000000108e241a0c601413b4f004046006d8f15dae9999edf39d58df6762822b9a2ab996d7f6a10044338af3b51b1814bc4ac0fa5a87c34c6ae604af8cabc5957c5240174deefc8e378719ffdab2ae4e15bf4514bea4489e2ff30c43a5f63beb2e4501ce7754085bcbe838003a0b4bccb53863c0766df7eac073c2bdc170772b157997945acdc2ab2e84750cc9aa0ffa0fdc023da7fc565a14f87f7c563dbc9183dd226aab79957d263f66e64b85a1b15a24516bd2c7c04eea4fa0a34ef9849c21585db2e4adb7c05e265c4f38d8ffe4cbed0f3b0e68f3693bf1f726c3fb135b8e32a5d22931d7c55fc2ff4b9a354933ab14544df3cdaf3e3217dfb8d7feb3465dc34df6320ea486f12e5b2d609aaa5f4515c20c86fc440f8087be0ee3d339835746ae2573c2afdee6bb6ef7e9eb541feae9209391b2902cfb0bdaccd9da8d290714638b7da588d4a656ca6eabba78b7363922d6037cf060b161a42019d4feb4156459103cffdeefd0e63114af2b0e0c39e70ebc7fecb8dd1ebb8d60b2137f509bb7dcef5f1d3e06ab1d391466652d57440a410fb4f58a6ce1fb62feb453241f64e110709f59a3d9ebdac94f811337d0e4a80fd6b56b2a70cd6eebbf98e1661291da6bf5beb8b8afc376dfd20eb76afe709e8e8f28e0ef82105954e346546ad25973df43f4acddbec0ffd9b215f62abebebf71305b5ea993560316f69430bf5afe50420340622f802b5830f3bcebffff04980c75a59d28902879e5d51a4fb21062a4ae13c42297075b21d54ee04303879c1157e7470c1451673c98a2f3921f2f3e8f6acfe85b01caaca66b59e5ebffbfe68e5e9ab17e9a1b857eb409df91cb76767fc1814fd3c522a9b117edd0b02526e469cb4afb291a4dcc74c79b47ec6e7ce558c597129366f83ec306b11d2598c705fd4ee9ee99df6b7039bef13b08fc6f26853ad213829d24f895747d45a47414f931c583fb6c3e4f6c27d0c2b81a5f3cee390ec6314e1fec637e8d28b675e97caafdfbf8c25d34a635083a7553d219dd80dbb39087d74c6ad6192ca6f48a3ff8d47db41b2a492c63fcd780012780931dae0a325f9dcbd772d09a700f132c4bc1d9809b25b9751b694eb72a8ba4db7208d2b1bab63e1845208e4f841ea30218a559db98751589716b6d059ca673378f5fe7c7d8a1c82e14a561c47313bbcc278412ba86ffb2b87ec308eab9df696f5b4b54f8e361731bf232820a02a35fda7e5d4bf01b8f005ad299a055116e7b23c181f15a66442cf6032ca477bccc55b79d424eb4f245847bd81a581dc369dd20b1a4892733bde3c38e492c0039f69f2b947a4dc251a49ee7ccc0f36b3b75a555fa1d126db75f94dab60f52f6b15a877a0c380b59f82d35c570bc5f8051e9ef87db51f52383d47b50829b7f9e947ccc67aa280566aa48b4a85c1c7eca6f542789d8abcc050f1aa3cc221b6859656a21454aa21c7bfb9d12115f61c3ed46263ade68a8d3679fa62a659a5da7817406bd16618fccf33ed208ada1b03584e8b485d3cb6ed80a0774e60b6cd55aff64169ea998cf8235997049515abac58e0169ca07fb1c8c4c8b2803ba9d27b44c045d0a1cac86e5e188195c68001f53eb44851b6d821fc01ccbb41e27f38e6ddd66540c2d62ed6e0d551e22c0f26b60078c74a6302a1ed3d9e8fc0861257a63f6ac4e759fd54bff088becd28e30944a6c15db4fc8ae6244346869add946d9d92c430d737e042fa18b28a8ed64d1e8987ad9061cdc1335f")
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = sniff.QUICClientHello(context.Background(), &metadata, pkt)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "www.google.com", metadata.Domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSniffQUICChromium(t *testing.T) {
|
func TestSniffQUICChromium(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
pkt, err := hex.DecodeString("c30000000108f40d654cc09b27f5000044d08a94548e57e43cc5483f129986187c432d58d46674830442988f869566a6e31e2ae37c9f7acbf61cc81621594fab0b3dfdc1635460b32389563dc8e74006315661cd22694114612973c1c45910621713a48b375854f095e8a77ccf3afa64e972f0f7f7002f50e0b014b1b146ea47c07fb20b73ad5587872b51a0b3fafdf1c4cf4fe6f8b112142392efa25d993abe2f42582be145148bdfe12edcd96c3655b65a4781b093e5594ba8e3ae5320f12e8314fc3ca374128cc43381046c322b964681ed4395c813b28534505118201459665a44b8f0abead877de322e9040631d20b05f15b81fa7ff785d4041aecc37c7e2ccdc5d1532787ce566517e8985fd5c200dbfd1e67bc255efaba94cfc07bb52fea4a90887413b134f2715b5643542aa897c6116486f428d82da64d2a2c1e1bdd40bd592558901a554b003d6966ac5a7b8b9413eddbf6ef21f28386c74981e3ce1d724c341e95494907626659692720c81114ca4acea35a14c402cfa3dc2228446e78dc1b81fa4325cf7e314a9cad6a6bdff33b3351dcba74eb15fae67f1227283aa4cdd64bcadf8f19358333f8549b596f4350297b5c65274565869d497398339947b9d3d064e5b06d39d34b436d8a41c1a3880de10bd26c3b1c5b4e2a49b0d4d07b8d90cd9e92bc611564d19ea8ec33099e92033caf21f5307dbeaa4708b99eb313bff99e2081ac25fd12d6a72e8335e0724f6718fe023cd0ad0d6e6a6309f09c9c391eec2bc08e9c3210a043c08e1759f354c121f6517fff4d6e20711a871e41285d48d930352fddffb92c96ba57df045ce99f8bfdfa8edc0969ce68a51e9fbb4f54b956d9df74a9e4af27ed2b27839bce1cffeca8333c0aaee81a570217442f9029ba8fedb84a2cf4be4d910982d891ea00e816c7fb98e8020e896a9c6fdd9106611da0a99dde18df1b7a8f6327acb1eed9ad93314451e48cb0dfb9571728521ca3db2ac0968159d5622556a55d51a422d11995b650949aaefc5d24c16080446dfc4fbc10353f9f93ce161ab513367bb89ab83988e0630b689e174e27bcfcc31996ee7b0bca909e251b82d69a28fee5a5d662e127508cd19dbbe5097b7d5b62a49203d66764197a527e472e2627e44a93d44177dace9d60e7d0e03305ddf4cfe47cdf2362e14de79ef46a6763ce696cd7854a48d9419a0817507a4713ffd4977b906d4f2b5fb6dbe1bd15bc505d5fea582190bf531a45d5ee026da8918547fd5105f15e5d061c7b0cf80a34990366ed8e91e13c2f0d85e5dad537298808d193cf54b7eaac33f10051f74cb6b75e52f81618c36f03d86aef613ba237a1a793ba1539938a38f62ccaf7bd5f6c5e0ce53cde4012fcf2b758214a0422d2faaa798e86e19d7481b42df2b36a73d287ff28c20cce01ce598771fec16a8f1f00305c06010126013a6c1de9f589b4e79d693717cd88ad1c42a2d99fa96617ba0bc6365b68e21a70ebc447904aa27979e1514433cfd83bfec09f137c747d47582cb63eb28f873fb94cf7a59ff764ddfbb687d79a58bb10f85949269f7f72c611a5e0fbb52adfa298ff060ec2eb7216fd7302ea8fb07798cbb3be25cb53ac8161aac2b5bbcfbcfb01c113d28bd1cb0333fb89ac82a95930f7abded0a2f5a623cc6a1f62bf3f38ef1b81c1e50a634f657dbb6770e4af45879e2fb1e00c742e7b52205c8015b5c0f5b1e40186ff9aa7288ab3e01a51fb87761f9bc6837082af109b39cc9f620")
|
pkt, err := hex.DecodeString("c30000000108f40d654cc09b27f5000044d08a94548e57e43cc5483f129986187c432d58d46674830442988f869566a6e31e2ae37c9f7acbf61cc81621594fab0b3dfdc1635460b32389563dc8e74006315661cd22694114612973c1c45910621713a48b375854f095e8a77ccf3afa64e972f0f7f7002f50e0b014b1b146ea47c07fb20b73ad5587872b51a0b3fafdf1c4cf4fe6f8b112142392efa25d993abe2f42582be145148bdfe12edcd96c3655b65a4781b093e5594ba8e3ae5320f12e8314fc3ca374128cc43381046c322b964681ed4395c813b28534505118201459665a44b8f0abead877de322e9040631d20b05f15b81fa7ff785d4041aecc37c7e2ccdc5d1532787ce566517e8985fd5c200dbfd1e67bc255efaba94cfc07bb52fea4a90887413b134f2715b5643542aa897c6116486f428d82da64d2a2c1e1bdd40bd592558901a554b003d6966ac5a7b8b9413eddbf6ef21f28386c74981e3ce1d724c341e95494907626659692720c81114ca4acea35a14c402cfa3dc2228446e78dc1b81fa4325cf7e314a9cad6a6bdff33b3351dcba74eb15fae67f1227283aa4cdd64bcadf8f19358333f8549b596f4350297b5c65274565869d497398339947b9d3d064e5b06d39d34b436d8a41c1a3880de10bd26c3b1c5b4e2a49b0d4d07b8d90cd9e92bc611564d19ea8ec33099e92033caf21f5307dbeaa4708b99eb313bff99e2081ac25fd12d6a72e8335e0724f6718fe023cd0ad0d6e6a6309f09c9c391eec2bc08e9c3210a043c08e1759f354c121f6517fff4d6e20711a871e41285d48d930352fddffb92c96ba57df045ce99f8bfdfa8edc0969ce68a51e9fbb4f54b956d9df74a9e4af27ed2b27839bce1cffeca8333c0aaee81a570217442f9029ba8fedb84a2cf4be4d910982d891ea00e816c7fb98e8020e896a9c6fdd9106611da0a99dde18df1b7a8f6327acb1eed9ad93314451e48cb0dfb9571728521ca3db2ac0968159d5622556a55d51a422d11995b650949aaefc5d24c16080446dfc4fbc10353f9f93ce161ab513367bb89ab83988e0630b689e174e27bcfcc31996ee7b0bca909e251b82d69a28fee5a5d662e127508cd19dbbe5097b7d5b62a49203d66764197a527e472e2627e44a93d44177dace9d60e7d0e03305ddf4cfe47cdf2362e14de79ef46a6763ce696cd7854a48d9419a0817507a4713ffd4977b906d4f2b5fb6dbe1bd15bc505d5fea582190bf531a45d5ee026da8918547fd5105f15e5d061c7b0cf80a34990366ed8e91e13c2f0d85e5dad537298808d193cf54b7eaac33f10051f74cb6b75e52f81618c36f03d86aef613ba237a1a793ba1539938a38f62ccaf7bd5f6c5e0ce53cde4012fcf2b758214a0422d2faaa798e86e19d7481b42df2b36a73d287ff28c20cce01ce598771fec16a8f1f00305c06010126013a6c1de9f589b4e79d693717cd88ad1c42a2d99fa96617ba0bc6365b68e21a70ebc447904aa27979e1514433cfd83bfec09f137c747d47582cb63eb28f873fb94cf7a59ff764ddfbb687d79a58bb10f85949269f7f72c611a5e0fbb52adfa298ff060ec2eb7216fd7302ea8fb07798cbb3be25cb53ac8161aac2b5bbcfbcfb01c113d28bd1cb0333fb89ac82a95930f7abded0a2f5a623cc6a1f62bf3f38ef1b81c1e50a634f657dbb6770e4af45879e2fb1e00c742e7b52205c8015b5c0f5b1e40186ff9aa7288ab3e01a51fb87761f9bc6837082af109b39cc9f620")
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ func TLSClientHello(ctx context.Context, metadata *adapter.InboundContext, reade
|
|||||||
if clientHello != nil {
|
if clientHello != nil {
|
||||||
metadata.Protocol = C.ProtocolTLS
|
metadata.Protocol = C.ProtocolTLS
|
||||||
metadata.Domain = clientHello.ServerName
|
metadata.Domain = clientHello.ServerName
|
||||||
|
metadata.ClientHello = clientHello
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/caddyserver/certmagic"
|
"github.com/caddyserver/certmagic"
|
||||||
"github.com/libdns/alidns"
|
"github.com/libdns/alidns"
|
||||||
"github.com/libdns/cloudflare"
|
"github.com/libdns/cloudflare"
|
||||||
"github.com/mholt/acmez/v3/acme"
|
"github.com/mholt/acmez/acme"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import (
|
|||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
func GenerateKeyPair(parent *x509.Certificate, parentKey any, timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||||
@@ -35,17 +38,30 @@ func GenerateCertificate(parent *x509.Certificate, parentKey any, timeFunc func(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
template := &x509.Certificate{
|
var template *x509.Certificate
|
||||||
SerialNumber: serialNumber,
|
if serverAddress := M.ParseAddr(serverName); serverAddress.IsValid() {
|
||||||
NotBefore: timeFunc().Add(time.Hour * -1),
|
template = &x509.Certificate{
|
||||||
NotAfter: expire,
|
SerialNumber: serialNumber,
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
IPAddresses: []net.IP{serverAddress.AsSlice()},
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||||
BasicConstraintsValid: true,
|
NotAfter: expire,
|
||||||
Subject: pkix.Name{
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
CommonName: serverName,
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
},
|
BasicConstraintsValid: true,
|
||||||
DNSNames: []string{serverName},
|
}
|
||||||
|
} else {
|
||||||
|
template = &x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||||
|
NotAfter: expire,
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: serverName,
|
||||||
|
},
|
||||||
|
DNSNames: []string{serverName},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
parent = template
|
parent = template
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const (
|
|||||||
DNSTypePreDefined = "predefined"
|
DNSTypePreDefined = "predefined"
|
||||||
DNSTypeFakeIP = "fakeip"
|
DNSTypeFakeIP = "fakeip"
|
||||||
DNSTypeDHCP = "dhcp"
|
DNSTypeDHCP = "dhcp"
|
||||||
DNSTypeTailscale = "tailscale"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ const (
|
|||||||
TypeVLESS = "vless"
|
TypeVLESS = "vless"
|
||||||
TypeTUIC = "tuic"
|
TypeTUIC = "tuic"
|
||||||
TypeHysteria2 = "hysteria2"
|
TypeHysteria2 = "hysteria2"
|
||||||
TypeTailscale = "tailscale"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
7
constant/script.go
Normal file
7
constant/script.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
ScriptTypeSurge = "surge"
|
||||||
|
ScriptSourceTypeLocal = "local"
|
||||||
|
ScriptSourceTypeRemote = "remote"
|
||||||
|
)
|
||||||
@@ -174,6 +174,7 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
|
|||||||
options.ClientSubnet = legacyTransport.LegacyClientSubnet()
|
options.ClientSubnet = legacyTransport.LegacyClientSubnet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||||
return transport, currentRule, currentRuleIndex
|
return transport, currentRule, currentRuleIndex
|
||||||
case *R.RuleActionDNSRouteOptions:
|
case *R.RuleActionDNSRouteOptions:
|
||||||
if action.Strategy != C.DomainStrategyAsIS {
|
if action.Strategy != C.DomainStrategyAsIS {
|
||||||
@@ -188,7 +189,9 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int,
|
|||||||
if action.ClientSubnet.IsValid() {
|
if action.ClientSubnet.IsValid() {
|
||||||
options.ClientSubnet = action.ClientSubnet
|
options.ClientSubnet = action.ClientSubnet
|
||||||
}
|
}
|
||||||
|
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||||
case *R.RuleActionReject:
|
case *R.RuleActionReject:
|
||||||
|
r.logger.DebugContext(ctx, "match[", displayRuleIndex, "] => ", currentRule.Action())
|
||||||
return nil, currentRule, currentRuleIndex
|
return nil, currentRule, currentRuleIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,7 +301,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 {
|
||||||
if transport == nil || transport.Type() != C.DNSTypeFakeIP {
|
if transport.Type() != C.DNSTypeFakeIP {
|
||||||
for _, answer := range response.Answer {
|
for _, answer := range response.Answer {
|
||||||
switch record := answer.(type) {
|
switch record := answer.(type) {
|
||||||
case *mDNS.A:
|
case *mDNS.A:
|
||||||
@@ -342,7 +345,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ
|
|||||||
r.logger.DebugContext(ctx, "lookup domain ", domain)
|
r.logger.DebugContext(ctx, "lookup domain ", domain)
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Destination = M.Socksaddr{}
|
metadata.Destination = M.Socksaddr{}
|
||||||
metadata.Domain = FqdnToDomain(domain)
|
metadata.Domain = domain
|
||||||
if options.Transport != nil {
|
if options.Transport != nil {
|
||||||
transport := options.Transport
|
transport := options.Transport
|
||||||
if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy {
|
if legacyTransport, isLegacy := transport.(adapter.LegacyDNSTransport); isLegacy {
|
||||||
|
|||||||
@@ -36,10 +36,7 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) Start(stage adapter.StartStage) error {
|
func (t *Transport) Start() error {
|
||||||
if stage != adapter.StartStateStart {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return t.store.Start()
|
return t.store.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +52,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
|||||||
if question.Qtype != mDNS.TypeA && question.Qtype != mDNS.TypeAAAA {
|
if question.Qtype != mDNS.TypeA && question.Qtype != mDNS.TypeAAAA {
|
||||||
return nil, E.New("only IP queries are supported by fakeip")
|
return nil, E.New("only IP queries are supported by fakeip")
|
||||||
}
|
}
|
||||||
address, err := t.store.Create(dns.FqdnToDomain(question.Name), question.Qtype == mDNS.TypeAAAA)
|
address, err := t.store.Create(question.Name, question.Qtype == mDNS.TypeAAAA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,11 +86,9 @@ func (t *Transport) exchangeParallel(ctx context.Context, systemConfig *dnsConfi
|
|||||||
results := make(chan queryResult)
|
results := make(chan queryResult)
|
||||||
startRacer := func(ctx context.Context, fqdn string) {
|
startRacer := func(ctx context.Context, fqdn string) {
|
||||||
response, err := t.tryOneName(ctx, systemConfig, fqdn, message)
|
response, err := t.tryOneName(ctx, systemConfig, fqdn, message)
|
||||||
if err == nil {
|
addresses, _ := dns.MessageToAddresses(response)
|
||||||
addresses, _ := dns.MessageToAddresses(response)
|
if len(addresses) == 0 {
|
||||||
if len(addresses) == 0 {
|
err = E.New(fqdn, ": empty result")
|
||||||
err = E.New(fqdn, ": empty result")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case results <- queryResult{response, err}:
|
case results <- queryResult{response, err}:
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func NewHTTP3(ctx context.Context, logger log.ContextLogger, tag string, options
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
destinationURL := url.URL{
|
destinationURL := url.URL{
|
||||||
Scheme: "https",
|
Scheme: "HTTP3",
|
||||||
Host: host,
|
Host: host,
|
||||||
}
|
}
|
||||||
if destinationURL.Host == "" {
|
if destinationURL.Host == "" {
|
||||||
|
|||||||
@@ -144,13 +144,6 @@ func (t *UDPTransport) exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M
|
|||||||
func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) {
|
func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) {
|
||||||
t.access.Lock()
|
t.access.Lock()
|
||||||
defer t.access.Unlock()
|
defer t.access.Unlock()
|
||||||
if t.conn != nil {
|
|
||||||
select {
|
|
||||||
case <-t.conn.done:
|
|
||||||
default:
|
|
||||||
return t.conn, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
conn, err := t.dialer.DialContext(ctx, N.NetworkUDP, t.serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -161,7 +154,6 @@ func (t *UDPTransport) open(ctx context.Context) (*dnsConnection, error) {
|
|||||||
callbacks: make(map[uint16]*dnsCallback),
|
callbacks: make(map[uint16]*dnsCallback),
|
||||||
}
|
}
|
||||||
go t.recvLoop(dnsConn)
|
go t.recvLoop(dnsConn)
|
||||||
t.conn = dnsConn
|
|
||||||
return dnsConn, nil
|
return dnsConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +201,8 @@ type dnsConnection struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *dnsConnection) Close(err error) {
|
func (c *dnsConnection) Close(err error) {
|
||||||
|
c.access.Lock()
|
||||||
|
defer c.access.Unlock()
|
||||||
c.closeOnce.Do(func() {
|
c.closeOnce.Do(func() {
|
||||||
close(c.done)
|
close(c.done)
|
||||||
c.err = err
|
c.err = err
|
||||||
|
|||||||
@@ -2,46 +2,7 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
#### 1.12.0-alpha.9
|
#### 1.12.0-alpha.3
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
#### 1.12.0-alpha.7
|
|
||||||
|
|
||||||
* Add Tailscale DNS server **1**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
See [Tailscale](/configuration/dns/server/tailscale/).
|
|
||||||
|
|
||||||
#### 1.12.0-alpha.6
|
|
||||||
|
|
||||||
* Add Tailscale endpoint **1**
|
|
||||||
* Drop support for go1.22 **2**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
See [Tailscale](/configuration/endpoint/tailscale/).
|
|
||||||
|
|
||||||
**2**:
|
|
||||||
|
|
||||||
Due to maintenance difficulties, sing-box 1.12.0 requires at least Go 1.23 to compile.
|
|
||||||
|
|
||||||
For Windows 7 users, legacy binaries now continue to compile with Go 1.23 and patches from [MetaCubeX/go](https://github.com/MetaCubeX/go).
|
|
||||||
|
|
||||||
### 1.11.3
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
_This version overwrites 1.11.2, as incorrect binaries were released due to a bug in the continuous integration process._
|
|
||||||
|
|
||||||
#### 1.12.0-alpha.5
|
|
||||||
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
### 1.11.1
|
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
@@ -75,18 +36,15 @@ and can be replaced by the new `domain_resolver` option.
|
|||||||
See [Dial Fields](/configuration/shared/dial/#domain_resolver) and
|
See [Dial Fields](/configuration/shared/dial/#domain_resolver) and
|
||||||
[Route](/configuration/route/#default_domain_resolver).
|
[Route](/configuration/route/#default_domain_resolver).
|
||||||
|
|
||||||
For migration,
|
For migration, see [Migrate outbound DNS rule items to domain resolver](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver).
|
||||||
see [Migrate outbound DNS rule items to domain resolver](/migration/#migrate-outbound-dns-rule-items-to-domain-resolver).
|
|
||||||
|
|
||||||
**3**:
|
**3**:
|
||||||
|
|
||||||
The new TLS fragment route options allow you to fragment TLS handshakes to bypass firewalls.
|
The new TLS fragment route options allow you to fragment TLS handshakes to bypass firewalls.
|
||||||
|
|
||||||
This feature is intended to circumvent simple firewalls based on **plaintext packet matching**, and should not be used
|
This feature is intended to circumvent simple firewalls based on **plaintext packet matching**, and should not be used to circumvent real censorship.
|
||||||
to circumvent real censorship.
|
|
||||||
|
|
||||||
Since it is not designed for performance, it should not be applied to all connections, but only to server names that are
|
Since it is not designed for performance, it should not be applied to all connections, but only to server names that are known to be blocked.
|
||||||
known to be blocked.
|
|
||||||
|
|
||||||
See [Route Action](/configuration/route/rule_action/#tls_fragment).
|
See [Route Action](/configuration/route/rule_action/#tls_fragment).
|
||||||
|
|
||||||
|
|||||||
@@ -11,15 +11,13 @@ icon: material/new-box
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": {
|
||||||
{
|
"type": "fakeip",
|
||||||
"type": "fakeip",
|
"tag": "",
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"inet4_range": "198.18.0.0/15",
|
"inet4_range": "198.18.0.0/15",
|
||||||
"inet6_range": "fc00::/18"
|
"inet6_range": "fc00::/18"
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,22 +11,20 @@ icon: material/new-box
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": {
|
||||||
{
|
"type": "h3",
|
||||||
"type": "h3",
|
"tag": "",
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"server": "",
|
"server": "",
|
||||||
"server_port": 443,
|
"server_port": 443,
|
||||||
|
|
||||||
"path": "",
|
"path": "",
|
||||||
"headers": {},
|
"headers": {},
|
||||||
|
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
// Dial Fields
|
// Dial Fields
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,22 +11,20 @@ icon: material/new-box
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": {
|
||||||
{
|
"type": "https",
|
||||||
"type": "https",
|
"tag": "",
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"server": "",
|
"server": "",
|
||||||
"server_port": 443,
|
"server_port": 443,
|
||||||
|
|
||||||
"path": "",
|
"path": "",
|
||||||
"headers": {},
|
"headers": {},
|
||||||
|
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
// Dial Fields
|
// Dial Fields
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,19 +11,17 @@ icon: material/new-box
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": {
|
||||||
{
|
"type": "quic",
|
||||||
"type": "quic",
|
"tag": "",
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"server": "",
|
"server": "",
|
||||||
"server_port": 853,
|
"server_port": 853,
|
||||||
|
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
// Dial Fields
|
// Dial Fields
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.12.0"
|
|
||||||
|
|
||||||
# Tailscale
|
|
||||||
|
|
||||||
### Structure
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dns": {
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"type": "tailscale",
|
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"endpoint": "ts-ep",
|
|
||||||
"accept_default_resolvers": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Fields
|
|
||||||
|
|
||||||
#### endpoint
|
|
||||||
|
|
||||||
==Required==
|
|
||||||
|
|
||||||
The tag of the Tailscale endpoint.
|
|
||||||
|
|
||||||
#### accept_default_resolvers
|
|
||||||
|
|
||||||
Indicates whether default DNS resolvers should be accepted for fallback queries in addition to MagicDNS。
|
|
||||||
|
|
||||||
if not enabled, NXDOMAIN will be returned for non-Tailscale domain queries.
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
=== "MagicDNS only"
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dns": {
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"type": "local",
|
|
||||||
"tag": "local"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tailscale",
|
|
||||||
"tag": "ts",
|
|
||||||
"endpoint": "ts-ep"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"ip_accept_any": true,
|
|
||||||
"server": "ts"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
=== "Use as global DNS"
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dns": {
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"type": "tailscale",
|
|
||||||
"endpoint": "ts-ep",
|
|
||||||
"accept_default_resolvers": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -11,17 +11,15 @@ icon: material/new-box
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": {
|
||||||
{
|
"type": "tcp",
|
||||||
"type": "tcp",
|
"tag": "",
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"server": "",
|
"server": "",
|
||||||
"server_port": 53,
|
"server_port": 53,
|
||||||
|
|
||||||
// Dial Fields
|
// Dial Fields
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,19 +11,17 @@ icon: material/new-box
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": {
|
||||||
{
|
"type": "tls",
|
||||||
"type": "tls",
|
"tag": "",
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"server": "",
|
"server": "",
|
||||||
"server_port": 853,
|
"server_port": 853,
|
||||||
|
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
|
||||||
// Dial Fields
|
// Dial Fields
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,17 +11,15 @@ icon: material/new-box
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": {
|
||||||
{
|
"type": "udp",
|
||||||
"type": "udp",
|
"tag": "",
|
||||||
"tag": "",
|
|
||||||
|
|
||||||
"server": "",
|
"server": "",
|
||||||
"server_port": 53,
|
"server_port": 53,
|
||||||
|
|
||||||
// Dial Fields
|
// Dial Fields
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.12.0"
|
|
||||||
|
|
||||||
### Structure
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "tailscale",
|
|
||||||
"tag": "ts-ep",
|
|
||||||
"state_directory": "",
|
|
||||||
"auth_key": "",
|
|
||||||
"control_url": "",
|
|
||||||
"ephemeral": false,
|
|
||||||
"hostname": "",
|
|
||||||
"exit_node": "",
|
|
||||||
"exit_node_allow_lan_access": false,
|
|
||||||
"advertise_routes": [],
|
|
||||||
"advertise_exit_node": false,
|
|
||||||
"udp_timeout": "5m",
|
|
||||||
|
|
||||||
... // Dial Fields
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Fields
|
|
||||||
|
|
||||||
#### state_directory
|
|
||||||
|
|
||||||
The directory where the Tailscale state is stored.
|
|
||||||
|
|
||||||
`tailscale` is used by default.
|
|
||||||
|
|
||||||
Example: `$HOME/.tailscale`
|
|
||||||
|
|
||||||
#### auth_key
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
|
|
||||||
Auth key is not required. By default, sing-box will log the login URL (or popup a notification on graphical clients).
|
|
||||||
|
|
||||||
The auth key to create the node. If the node is already created (from state previously stored), then this field is not
|
|
||||||
used.
|
|
||||||
|
|
||||||
#### control_url
|
|
||||||
|
|
||||||
The coordination server URL.
|
|
||||||
|
|
||||||
`https://controlplane.tailscale.com` is used by default.
|
|
||||||
|
|
||||||
#### ephemeral
|
|
||||||
|
|
||||||
Indicates whether the instance should register as an Ephemeral node (https://tailscale.com/s/ephemeral-nodes).
|
|
||||||
|
|
||||||
#### hostname
|
|
||||||
|
|
||||||
The hostname of the node.
|
|
||||||
|
|
||||||
System hostname is used by default.
|
|
||||||
|
|
||||||
Example: `localhost`
|
|
||||||
|
|
||||||
#### exit_node
|
|
||||||
|
|
||||||
The exit node name or IP address to use.
|
|
||||||
|
|
||||||
#### exit_node_allow_lan_access
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
|
|
||||||
When the exit node does not have a corresponding advertised route, private traffics cannot be routed to the exit node even if `exit_node_allow_lan_access is` set.
|
|
||||||
|
|
||||||
Indicates whether locally accessible subnets should be routed directly or via the exit node.
|
|
||||||
|
|
||||||
#### advertise_routes
|
|
||||||
|
|
||||||
CIDR prefixes to advertise into the Tailscale network as reachable through the current node.
|
|
||||||
|
|
||||||
Example: `["192.168.1.1/24"]`
|
|
||||||
|
|
||||||
#### advertise_exit_node
|
|
||||||
|
|
||||||
Indicates whether the node should advertise itself as an exit node.
|
|
||||||
|
|
||||||
#### udp_timeout
|
|
||||||
|
|
||||||
UDP NAT expiration time.
|
|
||||||
|
|
||||||
`5m` will be used by default.
|
|
||||||
|
|
||||||
### Dial Fields
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
|
|
||||||
Dial Fields in Tailscale endpoints only control how it connects to the control plane and have nothing to do with actual connections.
|
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial/) for details.
|
|
||||||
@@ -8,7 +8,6 @@ sing-box 使用 JSON 作为配置文件格式。
|
|||||||
{
|
{
|
||||||
"log": {},
|
"log": {},
|
||||||
"dns": {},
|
"dns": {},
|
||||||
"ntp": {},
|
|
||||||
"endpoints": [],
|
"endpoints": [],
|
||||||
"inbounds": [],
|
"inbounds": [],
|
||||||
"outbounds": [],
|
"outbounds": [],
|
||||||
@@ -23,7 +22,6 @@ sing-box 使用 JSON 作为配置文件格式。
|
|||||||
|----------------|------------------------|
|
|----------------|------------------------|
|
||||||
| `log` | [日志](./log/) |
|
| `log` | [日志](./log/) |
|
||||||
| `dns` | [DNS](./dns/) |
|
| `dns` | [DNS](./dns/) |
|
||||||
| `ntp` | [NTP](./ntp/) |
|
|
||||||
| `endpoints` | [端点](./endpoint/) |
|
| `endpoints` | [端点](./endpoint/) |
|
||||||
| `inbounds` | [入站](./inbound/) |
|
| `inbounds` | [入站](./inbound/) |
|
||||||
| `outbounds` | [出站](./outbound/) |
|
| `outbounds` | [出站](./outbound/) |
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ icon: material/delete-clock
|
|||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json F
|
||||||
{
|
{
|
||||||
"type": "block",
|
"type": "block",
|
||||||
"tag": "block"
|
"tag": "block"
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.12.0"
|
|
||||||
|
|
||||||
:material-plus: [server_ports](#server_ports)
|
|
||||||
:material-plus: [hop_interval](#hop_interval)
|
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -16,10 +7,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
"server_ports": [
|
|
||||||
"2080:3000"
|
|
||||||
],
|
|
||||||
"hop_interval": "",
|
|
||||||
"up": "100 Mbps",
|
"up": "100 Mbps",
|
||||||
"up_mbps": 100,
|
"up_mbps": 100,
|
||||||
"down": "100 Mbps",
|
"down": "100 Mbps",
|
||||||
@@ -51,22 +38,6 @@ The server address.
|
|||||||
|
|
||||||
The server port.
|
The server port.
|
||||||
|
|
||||||
#### server_ports
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.12.0"
|
|
||||||
|
|
||||||
Server port range list.
|
|
||||||
|
|
||||||
Conflicts with `server_port`.
|
|
||||||
|
|
||||||
#### hop_interval
|
|
||||||
|
|
||||||
!!! question "Since sing-box 1.12.0"
|
|
||||||
|
|
||||||
Port hopping interval.
|
|
||||||
|
|
||||||
`30s` is used by default.
|
|
||||||
|
|
||||||
#### up, down
|
#### up, down
|
||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
---
|
|
||||||
icon: material/new-box
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! quote "sing-box 1.12.0 中的更改"
|
|
||||||
|
|
||||||
:material-plus: [server_ports](#server_ports)
|
|
||||||
:material-plus: [hop_interval](#hop_interval)
|
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -16,10 +7,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 1080,
|
"server_port": 1080,
|
||||||
"server_ports": [
|
|
||||||
"2080:3000"
|
|
||||||
],
|
|
||||||
"hop_interval": "",
|
|
||||||
"up": "100 Mbps",
|
"up": "100 Mbps",
|
||||||
"up_mbps": 100,
|
"up_mbps": 100,
|
||||||
"down": "100 Mbps",
|
"down": "100 Mbps",
|
||||||
@@ -51,22 +38,6 @@ icon: material/new-box
|
|||||||
|
|
||||||
服务器端口。
|
服务器端口。
|
||||||
|
|
||||||
#### server_ports
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
|
||||||
|
|
||||||
服务器端口范围列表。
|
|
||||||
|
|
||||||
与 `server_port` 冲突。
|
|
||||||
|
|
||||||
#### hop_interval
|
|
||||||
|
|
||||||
!!! question "自 sing-box 1.12.0 起"
|
|
||||||
|
|
||||||
端口跳跃间隔。
|
|
||||||
|
|
||||||
默认使用 `30s`。
|
|
||||||
|
|
||||||
#### up, down
|
#### up, down
|
||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ Tag of rule-set.
|
|||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
List of [Headless Rule](./headless-rule/).
|
List of [Headless Rule](../headless-rule/).
|
||||||
|
|
||||||
### Local or Remote Fields
|
### Local or Remote Fields
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|
||||||
一组 [无头规则](./headless-rule/).
|
一组 [无头规则](../headless-rule/).
|
||||||
|
|
||||||
### 本地或远程字段
|
### 本地或远程字段
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,5 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
|
|||||||
| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
|
| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
|
||||||
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
|
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
|
||||||
| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
|
| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
|
||||||
| `with_tailscale` | :material-check: | Build with Tailscale support, see [Tailscale endpoint](/configuration/endpoint/tailscale) |
|
|
||||||
|
|
||||||
It is not recommended to change the default build tag list unless you really know what you are adding.
|
It is not recommended to change the default build tag list unless you really know what you are adding.
|
||||||
|
|||||||
@@ -60,6 +60,5 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
|
|||||||
| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
|
| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
|
||||||
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
|
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
|
||||||
| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
|
| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
|
||||||
| `with_tailscale` | :material-check: | Build with Tailscale support, see [Tailscale endpoint](/configuration/endpoint/tailscale) |
|
|
||||||
|
|
||||||
除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。
|
除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。
|
||||||
|
|||||||
@@ -692,7 +692,7 @@ Inbound fields are deprecated and can be replaced by rule actions.
|
|||||||
|
|
||||||
!!! info "References"
|
!!! info "References"
|
||||||
|
|
||||||
[Listen Fields](/configuration/shared/listen/) /
|
[Listen Fields](/configuration/inbound/listen/) /
|
||||||
[Rule](/configuration/route/rule/) /
|
[Rule](/configuration/route/rule/) /
|
||||||
[Rule Action](/configuration/route/rule_action/) /
|
[Rule Action](/configuration/route/rule_action/) /
|
||||||
[DNS Rule](/configuration/dns/rule/) /
|
[DNS Rule](/configuration/dns/rule/) /
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bucketSelected = []byte("selected")
|
bucketSelected = []byte("selected")
|
||||||
bucketExpand = []byte("group_expand")
|
bucketExpand = []byte("group_expand")
|
||||||
bucketMode = []byte("clash_mode")
|
bucketMode = []byte("clash_mode")
|
||||||
bucketRuleSet = []byte("rule_set")
|
bucketRuleSet = []byte("rule_set")
|
||||||
|
bucketScript = []byte("script")
|
||||||
|
bucketSgPersistentStore = []byte("sg_persistent_store")
|
||||||
|
|
||||||
bucketNameList = []string{
|
bucketNameList = []string{
|
||||||
string(bucketSelected),
|
string(bucketSelected),
|
||||||
@@ -316,3 +318,70 @@ func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error {
|
|||||||
return bucket.Put([]byte(tag), setBinary)
|
return bucket.Put([]byte(tag), setBinary)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) LoadScript(tag string) *adapter.SavedBinary {
|
||||||
|
var savedSet adapter.SavedBinary
|
||||||
|
err := c.DB.View(func(t *bbolt.Tx) error {
|
||||||
|
bucket := c.bucket(t, bucketScript)
|
||||||
|
if bucket == nil {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
scriptBinary := bucket.Get([]byte(tag))
|
||||||
|
if len(scriptBinary) == 0 {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
return savedSet.UnmarshalBinary(scriptBinary)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &savedSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SaveScript(tag string, set *adapter.SavedBinary) error {
|
||||||
|
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||||
|
bucket, err := c.createBucket(t, bucketScript)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
scriptBinary, err := set.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return bucket.Put([]byte(tag), scriptBinary)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SurgePersistentStoreRead(key string) string {
|
||||||
|
var value string
|
||||||
|
_ = c.DB.View(func(t *bbolt.Tx) error {
|
||||||
|
bucket := c.bucket(t, bucketSgPersistentStore)
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
valueBinary := bucket.Get([]byte(key))
|
||||||
|
if len(valueBinary) > 0 {
|
||||||
|
value = string(valueBinary)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SurgePersistentStoreWrite(key string, value string) error {
|
||||||
|
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||||
|
if value != "" {
|
||||||
|
bucket, err := c.createBucket(t, bucketSgPersistentStore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return bucket.Put([]byte(key), []byte(value))
|
||||||
|
} else {
|
||||||
|
bucket := c.bucket(t, bucketSgPersistentStore)
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return bucket.Delete([]byte(key))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
84
experimental/clashapi/mitm.go
Normal file
84
experimental/clashapi/mitm.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package clashapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
"github.com/gofrs/uuid/v5"
|
||||||
|
"howett.net/plist"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mitmRouter(ctx context.Context) http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Get("/mobileconfig", getMobileConfig(ctx))
|
||||||
|
r.Get("/certificate", getCertificate(ctx))
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMobileConfig(ctx context.Context) http.HandlerFunc {
|
||||||
|
return func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
engine := service.FromContext[adapter.MITMEngine](ctx)
|
||||||
|
if engine == nil {
|
||||||
|
http.NotFound(writer, request)
|
||||||
|
render.PlainText(writer, request, "MITM not enabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
certificate := engine.ExportCertificate()
|
||||||
|
if certificate == nil {
|
||||||
|
http.NotFound(writer, request)
|
||||||
|
render.PlainText(writer, request, "Certificate not configured")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Header().Set("Content-Type", "application/x-apple-aspen-config")
|
||||||
|
uuidGen := common.Must1(uuid.NewV4()).String()
|
||||||
|
mobileConfig := map[string]interface{}{
|
||||||
|
"PayloadContent": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"PayloadCertificateFileName": "Certificates.cer",
|
||||||
|
"PayloadContent": certificate.Raw,
|
||||||
|
"PayloadDescription": "Adds a root certificate",
|
||||||
|
"PayloadDisplayName": certificate.Subject.CommonName,
|
||||||
|
"PayloadIdentifier": "com.apple.security.root." + uuidGen,
|
||||||
|
"PayloadType": "com.apple.security.root",
|
||||||
|
"PayloadUUID": uuidGen,
|
||||||
|
"PayloadVersion": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"PayloadDisplayName": certificate.Subject.CommonName,
|
||||||
|
"PayloadIdentifier": "io.nekohasekai.sfa.ca.profile." + uuidGen,
|
||||||
|
"PayloadRemovalDisallowed": false,
|
||||||
|
"PayloadType": "Configuration",
|
||||||
|
"PayloadUUID": uuidGen,
|
||||||
|
"PayloadVersion": 1,
|
||||||
|
}
|
||||||
|
encoder := plist.NewEncoder(writer)
|
||||||
|
encoder.Indent("\t")
|
||||||
|
encoder.Encode(mobileConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCertificate(ctx context.Context) http.HandlerFunc {
|
||||||
|
return func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
engine := service.FromContext[adapter.MITMEngine](ctx)
|
||||||
|
if engine == nil {
|
||||||
|
http.NotFound(writer, request)
|
||||||
|
render.PlainText(writer, request, "MITM not enabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
certificate := engine.ExportCertificate()
|
||||||
|
if certificate == nil {
|
||||||
|
http.NotFound(writer, request)
|
||||||
|
render.PlainText(writer, request, "Certificate not configured")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Header().Set("Content-Type", "application/x-x509-ca-cert")
|
||||||
|
writer.Header().Set("Content-Disposition", "attachment; filename=Certificate.crt")
|
||||||
|
writer.Write(certificate.Raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -124,6 +124,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
|||||||
r.Mount("/profile", profileRouter())
|
r.Mount("/profile", profileRouter())
|
||||||
r.Mount("/cache", cacheRouter(ctx))
|
r.Mount("/cache", cacheRouter(ctx))
|
||||||
r.Mount("/dns", dnsRouter(s.dnsRouter))
|
r.Mount("/dns", dnsRouter(s.dnsRouter))
|
||||||
|
r.Mount("/mitm", mitmRouter(ctx))
|
||||||
|
|
||||||
s.setupMetaAPI(r)
|
s.setupMetaAPI(r)
|
||||||
})
|
})
|
||||||
@@ -131,7 +132,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
|
|||||||
s.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
s.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
||||||
chiRouter.Group(func(r chi.Router) {
|
chiRouter.Group(func(r chi.Router) {
|
||||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
||||||
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(Dir(s.externalUI))))
|
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(http.Dir(s.externalUI))))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package clashapi
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type Dir http.Dir
|
|
||||||
|
|
||||||
func (d Dir) Open(name string) (http.File, error) {
|
|
||||||
file, err := http.Dir(d).Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &fileWrapper{file}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// workaround for #2345 #2596
|
|
||||||
type fileWrapper struct {
|
|
||||||
http.File
|
|
||||||
}
|
|
||||||
@@ -43,6 +43,7 @@ func (s *Server) downloadExternalUI() error {
|
|||||||
} else {
|
} else {
|
||||||
downloadURL = "https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip"
|
downloadURL = "https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip"
|
||||||
}
|
}
|
||||||
|
s.logger.Info("downloading external ui")
|
||||||
var detour adapter.Outbound
|
var detour adapter.Outbound
|
||||||
if s.externalUIDownloadDetour != "" {
|
if s.externalUIDownloadDetour != "" {
|
||||||
outbound, loaded := s.outbound.Outbound(s.externalUIDownloadDetour)
|
outbound, loaded := s.outbound.Outbound(s.externalUIDownloadDetour)
|
||||||
@@ -54,7 +55,6 @@ func (s *Server) downloadExternalUI() error {
|
|||||||
outbound := s.outbound.Default()
|
outbound := s.outbound.Default()
|
||||||
detour = outbound
|
detour = outbound
|
||||||
}
|
}
|
||||||
s.logger.Info("downloading external ui using outbound/", detour.Type(), "[", detour.Tag(), "]")
|
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
|
|||||||
@@ -32,4 +32,9 @@ type Notification struct {
|
|||||||
Subtitle string
|
Subtitle string
|
||||||
Body string
|
Body string
|
||||||
OpenURL string
|
OpenURL string
|
||||||
|
Clipboard string
|
||||||
|
MediaURL string
|
||||||
|
MediaData []byte
|
||||||
|
MediaType string
|
||||||
|
Timeout int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package v2rayapi
|
|||||||
import (
|
import (
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
sync "sync"
|
sync "sync"
|
||||||
unsafe "unsafe"
|
|
||||||
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
@@ -17,20 +16,23 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type GetStatsRequest struct {
|
type GetStatsRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
// Name of the stat counter.
|
// Name of the stat counter.
|
||||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
// Whether or not to reset the counter to fetching its value.
|
// Whether or not to reset the counter to fetching its value.
|
||||||
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
|
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetStatsRequest) Reset() {
|
func (x *GetStatsRequest) Reset() {
|
||||||
*x = GetStatsRequest{}
|
*x = GetStatsRequest{}
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0]
|
if protoimpl.UnsafeEnabled {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0]
|
||||||
ms.StoreMessageInfo(mi)
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetStatsRequest) String() string {
|
func (x *GetStatsRequest) String() string {
|
||||||
@@ -41,7 +43,7 @@ func (*GetStatsRequest) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *GetStatsRequest) ProtoReflect() protoreflect.Message {
|
func (x *GetStatsRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0]
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[0]
|
||||||
if x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -71,18 +73,21 @@ func (x *GetStatsRequest) GetReset_() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Stat struct {
|
type Stat struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState
|
||||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
|
||||||
Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Stat) Reset() {
|
func (x *Stat) Reset() {
|
||||||
*x = Stat{}
|
*x = Stat{}
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1]
|
if protoimpl.UnsafeEnabled {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1]
|
||||||
ms.StoreMessageInfo(mi)
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Stat) String() string {
|
func (x *Stat) String() string {
|
||||||
@@ -93,7 +98,7 @@ func (*Stat) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *Stat) ProtoReflect() protoreflect.Message {
|
func (x *Stat) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1]
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[1]
|
||||||
if x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -123,17 +128,20 @@ func (x *Stat) GetValue() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetStatsResponse struct {
|
type GetStatsResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState
|
||||||
Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Stat *Stat `protobuf:"bytes,1,opt,name=stat,proto3" json:"stat,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetStatsResponse) Reset() {
|
func (x *GetStatsResponse) Reset() {
|
||||||
*x = GetStatsResponse{}
|
*x = GetStatsResponse{}
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2]
|
if protoimpl.UnsafeEnabled {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2]
|
||||||
ms.StoreMessageInfo(mi)
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetStatsResponse) String() string {
|
func (x *GetStatsResponse) String() string {
|
||||||
@@ -144,7 +152,7 @@ func (*GetStatsResponse) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *GetStatsResponse) ProtoReflect() protoreflect.Message {
|
func (x *GetStatsResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2]
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[2]
|
||||||
if x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -167,21 +175,24 @@ func (x *GetStatsResponse) GetStat() *Stat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type QueryStatsRequest struct {
|
type QueryStatsRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState
|
||||||
// Deprecated, use Patterns instead
|
|
||||||
Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
|
|
||||||
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
|
|
||||||
Patterns []string `protobuf:"bytes,3,rep,name=patterns,proto3" json:"patterns,omitempty"`
|
|
||||||
Regexp bool `protobuf:"varint,4,opt,name=regexp,proto3" json:"regexp,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// Deprecated, use Patterns instead
|
||||||
|
Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
|
||||||
|
Reset_ bool `protobuf:"varint,2,opt,name=reset,proto3" json:"reset,omitempty"`
|
||||||
|
Patterns []string `protobuf:"bytes,3,rep,name=patterns,proto3" json:"patterns,omitempty"`
|
||||||
|
Regexp bool `protobuf:"varint,4,opt,name=regexp,proto3" json:"regexp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *QueryStatsRequest) Reset() {
|
func (x *QueryStatsRequest) Reset() {
|
||||||
*x = QueryStatsRequest{}
|
*x = QueryStatsRequest{}
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3]
|
if protoimpl.UnsafeEnabled {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3]
|
||||||
ms.StoreMessageInfo(mi)
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *QueryStatsRequest) String() string {
|
func (x *QueryStatsRequest) String() string {
|
||||||
@@ -192,7 +203,7 @@ func (*QueryStatsRequest) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *QueryStatsRequest) ProtoReflect() protoreflect.Message {
|
func (x *QueryStatsRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3]
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[3]
|
||||||
if x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -236,17 +247,20 @@ func (x *QueryStatsRequest) GetRegexp() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type QueryStatsResponse struct {
|
type QueryStatsResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState
|
||||||
Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Stat []*Stat `protobuf:"bytes,1,rep,name=stat,proto3" json:"stat,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *QueryStatsResponse) Reset() {
|
func (x *QueryStatsResponse) Reset() {
|
||||||
*x = QueryStatsResponse{}
|
*x = QueryStatsResponse{}
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4]
|
if protoimpl.UnsafeEnabled {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4]
|
||||||
ms.StoreMessageInfo(mi)
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *QueryStatsResponse) String() string {
|
func (x *QueryStatsResponse) String() string {
|
||||||
@@ -257,7 +271,7 @@ func (*QueryStatsResponse) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *QueryStatsResponse) ProtoReflect() protoreflect.Message {
|
func (x *QueryStatsResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4]
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[4]
|
||||||
if x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -280,16 +294,18 @@ func (x *QueryStatsResponse) GetStat() []*Stat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SysStatsRequest struct {
|
type SysStatsRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SysStatsRequest) Reset() {
|
func (x *SysStatsRequest) Reset() {
|
||||||
*x = SysStatsRequest{}
|
*x = SysStatsRequest{}
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5]
|
if protoimpl.UnsafeEnabled {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5]
|
||||||
ms.StoreMessageInfo(mi)
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SysStatsRequest) String() string {
|
func (x *SysStatsRequest) String() string {
|
||||||
@@ -300,7 +316,7 @@ func (*SysStatsRequest) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *SysStatsRequest) ProtoReflect() protoreflect.Message {
|
func (x *SysStatsRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5]
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[5]
|
||||||
if x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -316,26 +332,29 @@ func (*SysStatsRequest) Descriptor() ([]byte, []int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SysStatsResponse struct {
|
type SysStatsResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState
|
||||||
NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"`
|
|
||||||
NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"`
|
|
||||||
Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"`
|
|
||||||
TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"`
|
|
||||||
Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"`
|
|
||||||
Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"`
|
|
||||||
Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"`
|
|
||||||
LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"`
|
|
||||||
PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"`
|
|
||||||
Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
NumGoroutine uint32 `protobuf:"varint,1,opt,name=NumGoroutine,proto3" json:"NumGoroutine,omitempty"`
|
||||||
|
NumGC uint32 `protobuf:"varint,2,opt,name=NumGC,proto3" json:"NumGC,omitempty"`
|
||||||
|
Alloc uint64 `protobuf:"varint,3,opt,name=Alloc,proto3" json:"Alloc,omitempty"`
|
||||||
|
TotalAlloc uint64 `protobuf:"varint,4,opt,name=TotalAlloc,proto3" json:"TotalAlloc,omitempty"`
|
||||||
|
Sys uint64 `protobuf:"varint,5,opt,name=Sys,proto3" json:"Sys,omitempty"`
|
||||||
|
Mallocs uint64 `protobuf:"varint,6,opt,name=Mallocs,proto3" json:"Mallocs,omitempty"`
|
||||||
|
Frees uint64 `protobuf:"varint,7,opt,name=Frees,proto3" json:"Frees,omitempty"`
|
||||||
|
LiveObjects uint64 `protobuf:"varint,8,opt,name=LiveObjects,proto3" json:"LiveObjects,omitempty"`
|
||||||
|
PauseTotalNs uint64 `protobuf:"varint,9,opt,name=PauseTotalNs,proto3" json:"PauseTotalNs,omitempty"`
|
||||||
|
Uptime uint32 `protobuf:"varint,10,opt,name=Uptime,proto3" json:"Uptime,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SysStatsResponse) Reset() {
|
func (x *SysStatsResponse) Reset() {
|
||||||
*x = SysStatsResponse{}
|
*x = SysStatsResponse{}
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6]
|
if protoimpl.UnsafeEnabled {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6]
|
||||||
ms.StoreMessageInfo(mi)
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SysStatsResponse) String() string {
|
func (x *SysStatsResponse) String() string {
|
||||||
@@ -346,7 +365,7 @@ func (*SysStatsResponse) ProtoMessage() {}
|
|||||||
|
|
||||||
func (x *SysStatsResponse) ProtoReflect() protoreflect.Message {
|
func (x *SysStatsResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6]
|
mi := &file_experimental_v2rayapi_stats_proto_msgTypes[6]
|
||||||
if x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
@@ -433,7 +452,7 @@ func (x *SysStatsResponse) GetUptime() uint32 {
|
|||||||
|
|
||||||
var File_experimental_v2rayapi_stats_proto protoreflect.FileDescriptor
|
var File_experimental_v2rayapi_stats_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_experimental_v2rayapi_stats_proto_rawDesc = string([]byte{
|
var file_experimental_v2rayapi_stats_proto_rawDesc = []byte{
|
||||||
0x0a, 0x21, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76,
|
0x0a, 0x21, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76,
|
||||||
0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72,
|
0x32, 0x72, 0x61, 0x79, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x12, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61,
|
0x6f, 0x74, 0x6f, 0x12, 0x15, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61,
|
||||||
@@ -504,23 +523,23 @@ var file_experimental_v2rayapi_stats_proto_rawDesc = string([]byte{
|
|||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x2f, 0x65, 0x78,
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x78, 0x2f, 0x65, 0x78,
|
||||||
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79,
|
0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79,
|
||||||
0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
})
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_experimental_v2rayapi_stats_proto_rawDescOnce sync.Once
|
file_experimental_v2rayapi_stats_proto_rawDescOnce sync.Once
|
||||||
file_experimental_v2rayapi_stats_proto_rawDescData []byte
|
file_experimental_v2rayapi_stats_proto_rawDescData = file_experimental_v2rayapi_stats_proto_rawDesc
|
||||||
)
|
)
|
||||||
|
|
||||||
func file_experimental_v2rayapi_stats_proto_rawDescGZIP() []byte {
|
func file_experimental_v2rayapi_stats_proto_rawDescGZIP() []byte {
|
||||||
file_experimental_v2rayapi_stats_proto_rawDescOnce.Do(func() {
|
file_experimental_v2rayapi_stats_proto_rawDescOnce.Do(func() {
|
||||||
file_experimental_v2rayapi_stats_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_experimental_v2rayapi_stats_proto_rawDesc), len(file_experimental_v2rayapi_stats_proto_rawDesc)))
|
file_experimental_v2rayapi_stats_proto_rawDescData = protoimpl.X.CompressGZIP(file_experimental_v2rayapi_stats_proto_rawDescData)
|
||||||
})
|
})
|
||||||
return file_experimental_v2rayapi_stats_proto_rawDescData
|
return file_experimental_v2rayapi_stats_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_experimental_v2rayapi_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
file_experimental_v2rayapi_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
file_experimental_v2rayapi_stats_proto_goTypes = []any{
|
file_experimental_v2rayapi_stats_proto_goTypes = []interface{}{
|
||||||
(*GetStatsRequest)(nil), // 0: experimental.v2rayapi.GetStatsRequest
|
(*GetStatsRequest)(nil), // 0: experimental.v2rayapi.GetStatsRequest
|
||||||
(*Stat)(nil), // 1: experimental.v2rayapi.Stat
|
(*Stat)(nil), // 1: experimental.v2rayapi.Stat
|
||||||
(*GetStatsResponse)(nil), // 2: experimental.v2rayapi.GetStatsResponse
|
(*GetStatsResponse)(nil), // 2: experimental.v2rayapi.GetStatsResponse
|
||||||
@@ -552,11 +571,97 @@ func file_experimental_v2rayapi_stats_proto_init() {
|
|||||||
if File_experimental_v2rayapi_stats_proto != nil {
|
if File_experimental_v2rayapi_stats_proto != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_experimental_v2rayapi_stats_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GetStatsRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_experimental_v2rayapi_stats_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Stat); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_experimental_v2rayapi_stats_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GetStatsResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_experimental_v2rayapi_stats_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*QueryStatsRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_experimental_v2rayapi_stats_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*QueryStatsResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_experimental_v2rayapi_stats_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*SysStatsRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_experimental_v2rayapi_stats_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*SysStatsResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_experimental_v2rayapi_stats_proto_rawDesc), len(file_experimental_v2rayapi_stats_proto_rawDesc)),
|
RawDescriptor: file_experimental_v2rayapi_stats_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 7,
|
NumMessages: 7,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
@@ -567,6 +672,7 @@ func file_experimental_v2rayapi_stats_proto_init() {
|
|||||||
MessageInfos: file_experimental_v2rayapi_stats_proto_msgTypes,
|
MessageInfos: file_experimental_v2rayapi_stats_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_experimental_v2rayapi_stats_proto = out.File
|
File_experimental_v2rayapi_stats_proto = out.File
|
||||||
|
file_experimental_v2rayapi_stats_proto_rawDesc = nil
|
||||||
file_experimental_v2rayapi_stats_proto_goTypes = nil
|
file_experimental_v2rayapi_stats_proto_goTypes = nil
|
||||||
file_experimental_v2rayapi_stats_proto_depIdxs = nil
|
file_experimental_v2rayapi_stats_proto_depIdxs = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
// is compatible with the grpc package it is being compiled against.
|
// is compatible with the grpc package it is being compiled against.
|
||||||
// Requires gRPC-Go v1.64.0 or later.
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
const _ = grpc.SupportPackageIsVersion9
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StatsService_GetStats_FullMethodName = "/experimental.v2rayapi.StatsService/GetStats"
|
StatsService_GetStats_FullMethodName = "/experimental.v2rayapi.StatsService/GetStats"
|
||||||
@@ -37,9 +37,8 @@ func NewStatsServiceClient(cc grpc.ClientConnInterface) StatsServiceClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
|
func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(GetStatsResponse)
|
out := new(GetStatsResponse)
|
||||||
err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, cOpts...)
|
err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -47,9 +46,8 @@ func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) {
|
func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(QueryStatsResponse)
|
out := new(QueryStatsResponse)
|
||||||
err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, cOpts...)
|
err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -57,9 +55,8 @@ func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) {
|
func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
|
||||||
out := new(SysStatsResponse)
|
out := new(SysStatsResponse)
|
||||||
err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, cOpts...)
|
err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -68,7 +65,7 @@ func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsReques
|
|||||||
|
|
||||||
// StatsServiceServer is the server API for StatsService service.
|
// StatsServiceServer is the server API for StatsService service.
|
||||||
// All implementations must embed UnimplementedStatsServiceServer
|
// All implementations must embed UnimplementedStatsServiceServer
|
||||||
// for forward compatibility.
|
// for forward compatibility
|
||||||
type StatsServiceServer interface {
|
type StatsServiceServer interface {
|
||||||
GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error)
|
||||||
QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error)
|
QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error)
|
||||||
@@ -76,11 +73,7 @@ type StatsServiceServer interface {
|
|||||||
mustEmbedUnimplementedStatsServiceServer()
|
mustEmbedUnimplementedStatsServiceServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnimplementedStatsServiceServer must be embedded to have
|
// UnimplementedStatsServiceServer must be embedded to have forward compatible implementations.
|
||||||
// forward compatible implementations.
|
|
||||||
//
|
|
||||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
|
||||||
// pointer dereference when methods are called.
|
|
||||||
type UnimplementedStatsServiceServer struct{}
|
type UnimplementedStatsServiceServer struct{}
|
||||||
|
|
||||||
func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) {
|
func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) {
|
||||||
@@ -95,7 +88,6 @@ func (UnimplementedStatsServiceServer) GetSysStats(context.Context, *SysStatsReq
|
|||||||
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetSysStats not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
|
func (UnimplementedStatsServiceServer) mustEmbedUnimplementedStatsServiceServer() {}
|
||||||
func (UnimplementedStatsServiceServer) testEmbeddedByValue() {}
|
|
||||||
|
|
||||||
// UnsafeStatsServiceServer may be embedded to opt out of forward compatibility for this service.
|
// UnsafeStatsServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
// Use of this interface is not recommended, as added methods to StatsServiceServer will
|
// Use of this interface is not recommended, as added methods to StatsServiceServer will
|
||||||
@@ -105,13 +97,6 @@ type UnsafeStatsServiceServer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) {
|
func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) {
|
||||||
// If the following call pancis, it indicates UnimplementedStatsServiceServer 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(&StatsService_ServiceDesc, srv)
|
s.RegisterService(&StatsService_ServiceDesc, srv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
128
go.mod
128
go.mod
@@ -1,22 +1,24 @@
|
|||||||
module github.com/sagernet/sing-box
|
module github.com/sagernet/sing-box
|
||||||
|
|
||||||
go 1.23.1
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caddyserver/certmagic v0.21.7
|
github.com/adhocore/gronx v1.19.5
|
||||||
github.com/cloudflare/circl v1.6.0
|
github.com/caddyserver/certmagic v0.20.0
|
||||||
|
github.com/cloudflare/circl v1.3.7
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/go-chi/chi/v5 v5.2.1
|
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17
|
||||||
|
github.com/go-chi/chi/v5 v5.1.0
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/gofrs/uuid/v5 v5.3.1
|
github.com/gofrs/uuid/v5 v5.3.0
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
|
||||||
github.com/libdns/alidns v1.0.3
|
github.com/libdns/alidns v1.0.3
|
||||||
github.com/libdns/cloudflare v0.1.1
|
github.com/libdns/cloudflare v0.1.1
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa
|
||||||
github.com/mholt/acmez/v3 v3.0.1
|
github.com/mholt/acmez v1.2.0
|
||||||
github.com/miekg/dns v1.1.63
|
github.com/miekg/dns v1.1.62
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1
|
github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||||
@@ -26,120 +28,78 @@ require (
|
|||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
||||||
github.com/sagernet/quic-go v0.49.0-beta.1
|
github.com/sagernet/quic-go v0.49.0-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff
|
github.com/sagernet/sing v0.6.0-beta.12.0.20250130112616-23af22fe01ff
|
||||||
github.com/sagernet/sing-mux v0.3.1
|
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||||
github.com/sagernet/sing-quic v0.4.1-beta.1
|
github.com/sagernet/sing-quic v0.4.0-beta.4
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
|
||||||
github.com/sagernet/sing-tun v0.6.1
|
github.com/sagernet/sing-tun v0.6.0-beta.8
|
||||||
github.com/sagernet/sing-vmess v0.2.0
|
github.com/sagernet/sing-vmess v0.2.0-beta.2
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||||
github.com/sagernet/tailscale v1.79.0-mod.1
|
|
||||||
github.com/sagernet/utls v1.6.7
|
github.com/sagernet/utls v1.6.7
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.5
|
github.com/sagernet/wireguard-go v0.0.1-beta.5
|
||||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.9.0
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.33.0
|
golang.org/x/crypto v0.32.0
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||||
golang.org/x/mod v0.23.0
|
golang.org/x/mod v0.20.0
|
||||||
golang.org/x/net v0.35.0
|
golang.org/x/net v0.34.0
|
||||||
golang.org/x/sys v0.30.0
|
golang.org/x/sys v0.29.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
google.golang.org/grpc v1.70.0
|
google.golang.org/grpc v1.63.2
|
||||||
google.golang.org/protobuf v1.36.5
|
google.golang.org/protobuf v1.33.0
|
||||||
howett.net/plist v1.0.1
|
howett.net/plist v1.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
//replace github.com/sagernet/sing => ../sing
|
//replace github.com/sagernet/sing => ../sing
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/akutz/memconn v0.1.0 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
|
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
|
||||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
|
||||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/coder/websocket v1.8.12 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
|
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
|
||||||
github.com/gaissmai/bart v0.11.1 // indirect
|
|
||||||
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect
|
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/gobwas/httphead v0.1.0 // indirect
|
github.com/gobwas/httphead v0.1.0 // indirect
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
|
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/gorilla/csrf v1.7.2 // indirect
|
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
|
||||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
|
||||||
github.com/illarion/gonotify/v2 v2.0.3 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
|
||||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
|
|
||||||
github.com/libdns/libdns v0.2.2 // indirect
|
github.com/libdns/libdns v0.2.2 // indirect
|
||||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
|
||||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
github.com/mdlayher/sdnotify v1.0.0 // indirect
|
github.com/mdlayher/socket v0.4.1 // indirect
|
||||||
github.com/mdlayher/socket v0.5.1 // indirect
|
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.17.2 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
|
||||||
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||||
github.com/safchain/ethtool v0.3.0 // indirect
|
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
|
||||||
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 // indirect
|
|
||||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
|
||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
|
||||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
|
|
||||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect
|
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect
|
|
||||||
github.com/tcnksm/go-httpstat v0.2.0 // indirect
|
|
||||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
|
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap/exp v0.3.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
golang.org/x/sync v0.11.0 // indirect
|
|
||||||
golang.org/x/term v0.29.0 // indirect
|
|
||||||
golang.org/x/text v0.22.0 // indirect
|
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
//replace github.com/sagernet/sing => ../sing
|
|
||||||
|
|||||||
295
go.sum
295
go.sum
@@ -1,75 +1,48 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
github.com/adhocore/gronx v1.19.5 h1:cwIG4nT1v9DvadxtHBe6MzE+FZ1JDvAUC45U2fl4eSQ=
|
||||||
|
github.com/adhocore/gronx v1.19.5/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
|
||||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
|
||||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
|
||||||
github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg=
|
|
||||||
github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI=
|
|
||||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
|
||||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
|
||||||
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
|
||||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
|
||||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
|
||||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
|
|
||||||
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
||||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
|
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
|
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
|
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
|
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17 h1:spJaibPy2sZNwo6Q0HjBVufq7hBUj5jNFOKRoogCBow=
|
||||||
|
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
|
|
||||||
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
|
||||||
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
|
|
||||||
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
|
|
||||||
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
|
||||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
|
||||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||||
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA=
|
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
|
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
||||||
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
|
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
github.com/gofrs/uuid/v5 v5.3.1 h1:aPx49MwJbekCzOyhZDjJVb0hx3A0KLjlbLx6p2gY0p0=
|
|
||||||
github.com/gofrs/uuid/v5 v5.3.1/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@@ -77,40 +50,23 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI=
|
|
||||||
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
|
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
|
|
||||||
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
|
|
||||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
|
||||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
|
||||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||||
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
|
||||||
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
|
||||||
github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vyxM+9A=
|
|
||||||
github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
|
||||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
|
|
||||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
|
|
||||||
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
||||||
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
||||||
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
||||||
@@ -120,45 +76,30 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
|||||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
|
||||||
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
|
||||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||||
github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c=
|
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||||
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
|
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4=
|
||||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||||
github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8=
|
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||||
github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
||||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
||||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
|
|
||||||
github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
|
|
||||||
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
|
||||||
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
|
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
|
|
||||||
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
|
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
|
||||||
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
|
||||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4=
|
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 h1:qi+ijeREa0yfAaO+NOcZ81gv4uzOfALUIdhkiIFvmG4=
|
||||||
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU=
|
github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1/go.mod h1:JULDuzTMn2gyZFcjpTVZP4/UuwAdbHJ0bum2RdjXojU=
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
||||||
@@ -182,26 +123,24 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W
|
|||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff h1:5UGghwx8cI14qFa0ienrLekAYfhdKAiWvJUkY7rHmsI=
|
github.com/sagernet/sing v0.6.0-beta.12.0.20250130112616-23af22fe01ff h1:aZeWJw/NkI3/NoXbNHzv2435nWYTV+ZDNDWXTnN9FjQ=
|
||||||
github.com/sagernet/sing v0.6.2-0.20250210072154-8dff604468ff/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-beta.12.0.20250130112616-23af22fe01ff/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||||
github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
|
||||||
github.com/sagernet/sing-quic v0.4.1-beta.1 h1:V2VfMckT3EQR3ZdfSzJgZZDsvfZZH42QAZpnOnHKa0s=
|
github.com/sagernet/sing-quic v0.4.0-beta.4 h1:kKiMLGaxvVLDCSvCMYo4PtWd1xU6FTL7xvUAQfXO09g=
|
||||||
github.com/sagernet/sing-quic v0.4.1-beta.1/go.mod h1:c+CytOEyeN20KCTFIP8YQUkNDVFLSzjrEPqP7Hlnxys=
|
github.com/sagernet/sing-quic v0.4.0-beta.4/go.mod h1:1UNObFodd8CnS3aCT53x9cigjPSCl3P//8dfBMCwBDM=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
|
github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA=
|
||||||
github.com/sagernet/sing-tun v0.6.1 h1:4l0+gnEKcGjlWfUVTD+W0BRApqIny/lU2ZliurE+VMo=
|
github.com/sagernet/sing-tun v0.6.0-beta.8 h1:GFNt/w8r1v30zC/hfCytk8C9+N/f1DfvosFXJkyJlrw=
|
||||||
github.com/sagernet/sing-tun v0.6.1/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
github.com/sagernet/sing-tun v0.6.0-beta.8/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||||
github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
|
github.com/sagernet/sing-vmess v0.2.0-beta.2 h1:obAkAL35X7ql4RnGzDg4dBYIRpGXRKqcN4LyLZpZGSs=
|
||||||
github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
github.com/sagernet/sing-vmess v0.2.0-beta.2/go.mod h1:HGhf9XUdeE2iOWrX0hQNFgXPbKyGlzpeYFyX0c/pykk=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||||
github.com/sagernet/tailscale v1.79.0-mod.1 h1:wIuAH7VqBYJNk0h2+bTyk4F0OlSqHvyLDCBrD3i+XNI=
|
|
||||||
github.com/sagernet/tailscale v1.79.0-mod.1/go.mod h1:RKY5WjYLj3JJ7VO/8ZCw8eAFa4+kWU6A1Ftdk84uB14=
|
|
||||||
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
|
||||||
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
|
||||||
@@ -213,122 +152,76 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k
|
|||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||||
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
|
|
||||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
|
|
||||||
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 h1:rXZGgEa+k2vJM8xT0PoSKfVXwFGPQ3z3CJfmnHJkZZw=
|
|
||||||
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
|
|
||||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
|
|
||||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
|
|
||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
|
||||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
|
||||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
|
|
||||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
|
||||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w=
|
|
||||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU=
|
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g=
|
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
|
||||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
|
||||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
|
||||||
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
|
||||||
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
|
|
||||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw=
|
|
||||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
|
||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
|
||||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
||||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
|
||||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
|
||||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
|
||||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
|
||||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
|
||||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
|
||||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
|
||||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
|
|
||||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
|
||||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
|
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -336,5 +229,3 @@ howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
|||||||
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
|
||||||
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ func EndpointRegistry() *endpoint.Registry {
|
|||||||
registry := endpoint.NewRegistry()
|
registry := endpoint.NewRegistry()
|
||||||
|
|
||||||
registerWireGuardEndpoint(registry)
|
registerWireGuardEndpoint(registry)
|
||||||
registerTailscaleEndpoint(registry)
|
|
||||||
|
|
||||||
return registry
|
return registry
|
||||||
}
|
}
|
||||||
@@ -111,7 +110,6 @@ func DNSTransportRegistry() *dns.TransportRegistry {
|
|||||||
|
|
||||||
registerQUICTransports(registry)
|
registerQUICTransports(registry)
|
||||||
registerDHCPTransport(registry)
|
registerDHCPTransport(registry)
|
||||||
registerTailscaleTransport(registry)
|
|
||||||
|
|
||||||
return registry
|
return registry
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
//go:build with_tailscale
|
|
||||||
|
|
||||||
package include
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
|
||||||
"github.com/sagernet/sing-box/dns"
|
|
||||||
"github.com/sagernet/sing-box/protocol/tailscale"
|
|
||||||
)
|
|
||||||
|
|
||||||
func registerTailscaleEndpoint(registry *endpoint.Registry) {
|
|
||||||
tailscale.RegisterEndpoint(registry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerTailscaleTransport(registry *dns.TransportRegistry) {
|
|
||||||
tailscale.RegistryTransport(registry)
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//go:build !with_tailscale
|
|
||||||
|
|
||||||
package include
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/dns"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
func registerTailscaleEndpoint(registry *endpoint.Registry) {
|
|
||||||
endpoint.Register[option.TailscaleEndpointOptions](registry, C.TypeTailscale, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
|
|
||||||
return nil, E.New(`Tailscale is not included in this build, rebuild with -tags with_tailscale`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerTailscaleTransport(registry *dns.TransportRegistry) {
|
|
||||||
dns.RegisterTransport[option.TailscaleDNSServerOptions](registry, C.DNSTypeTailscale, func(ctx context.Context, logger log.ContextLogger, tag string, options option.TailscaleDNSServerOptions) (adapter.DNSTransport, error) {
|
|
||||||
return nil, E.New(`Tailscale is not included in this build, rebuild with -tags with_tailscale`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,10 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultTimeFormat = "-0700 2006-01-02 15:04:05"
|
||||||
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Context context.Context
|
Context context.Context
|
||||||
Options option.LogOptions
|
Options option.LogOptions
|
||||||
@@ -47,7 +51,7 @@ func New(options Options) (Factory, error) {
|
|||||||
DisableColors: logOptions.DisableColor || logFilePath != "",
|
DisableColors: logOptions.DisableColor || logFilePath != "",
|
||||||
DisableTimestamp: !logOptions.Timestamp && logFilePath != "",
|
DisableTimestamp: !logOptions.Timestamp && logFilePath != "",
|
||||||
FullTimestamp: logOptions.Timestamp,
|
FullTimestamp: logOptions.Timestamp,
|
||||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
TimestampFormat: DefaultTimeFormat,
|
||||||
}
|
}
|
||||||
factory := NewDefaultFactory(
|
factory := NewDefaultFactory(
|
||||||
options.Context,
|
options.Context,
|
||||||
|
|||||||
11
mitm/constants.go
Normal file
11
mitm/constants.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package mitm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var surgeTinyGif = common.OnceValue(func() []byte {
|
||||||
|
return common.Must1(base64.StdEncoding.DecodeString("R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAIBAAA="))
|
||||||
|
})
|
||||||
1121
mitm/engine.go
Normal file
1121
mitm/engine.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -93,7 +93,6 @@ nav:
|
|||||||
- Predefined: configuration/dns/server/predefined.md
|
- Predefined: configuration/dns/server/predefined.md
|
||||||
- DHCP: configuration/dns/server/dhcp.md
|
- DHCP: configuration/dns/server/dhcp.md
|
||||||
- FakeIP: configuration/dns/server/fakeip.md
|
- FakeIP: configuration/dns/server/fakeip.md
|
||||||
- Tailscale: configuration/dns/server/tailscale.md
|
|
||||||
- DNS Rule: configuration/dns/rule.md
|
- DNS Rule: configuration/dns/rule.md
|
||||||
- DNS Rule Action: configuration/dns/rule_action.md
|
- DNS Rule Action: configuration/dns/rule_action.md
|
||||||
- FakeIP: configuration/dns/fakeip.md
|
- FakeIP: configuration/dns/fakeip.md
|
||||||
@@ -128,7 +127,6 @@ nav:
|
|||||||
- Endpoint:
|
- Endpoint:
|
||||||
- configuration/endpoint/index.md
|
- configuration/endpoint/index.md
|
||||||
- WireGuard: configuration/endpoint/wireguard.md
|
- WireGuard: configuration/endpoint/wireguard.md
|
||||||
- Tailscale: configuration/endpoint/tailscale.md
|
|
||||||
- Inbound:
|
- Inbound:
|
||||||
- configuration/inbound/index.md
|
- configuration/inbound/index.md
|
||||||
- Direct: configuration/inbound/direct.md
|
- Direct: configuration/inbound/direct.md
|
||||||
|
|||||||
@@ -126,12 +126,7 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error {
|
|||||||
if serverURL.Scheme != "" {
|
if serverURL.Scheme != "" {
|
||||||
serverType = serverURL.Scheme
|
serverType = serverURL.Scheme
|
||||||
} else {
|
} else {
|
||||||
switch options.Address {
|
serverType = C.DNSTypeUDP
|
||||||
case "local", "fakeip":
|
|
||||||
serverType = options.Address
|
|
||||||
default:
|
|
||||||
serverType = C.DNSTypeUDP
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var remoteOptions RemoteDNSServerOptions
|
var remoteOptions RemoteDNSServerOptions
|
||||||
if options.Detour == "" {
|
if options.Detour == "" {
|
||||||
@@ -163,9 +158,6 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch serverType {
|
switch serverType {
|
||||||
case C.DNSTypeLocal:
|
|
||||||
o.Type = C.DNSTypeLocal
|
|
||||||
o.Options = &remoteOptions.LocalDNSServerOptions
|
|
||||||
case C.DNSTypeUDP:
|
case C.DNSTypeUDP:
|
||||||
o.Type = C.DNSTypeUDP
|
o.Type = C.DNSTypeUDP
|
||||||
o.Options = &remoteOptions
|
o.Options = &remoteOptions
|
||||||
@@ -182,8 +174,6 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error {
|
|||||||
if serverAddr.Port != 0 && serverAddr.Port != 53 {
|
if serverAddr.Port != 0 && serverAddr.Port != 53 {
|
||||||
remoteOptions.ServerPort = serverAddr.Port
|
remoteOptions.ServerPort = serverAddr.Port
|
||||||
}
|
}
|
||||||
remoteOptions.Server = serverAddr.AddrString()
|
|
||||||
remoteOptions.ServerPort = serverAddr.Port
|
|
||||||
case C.DNSTypeTCP:
|
case C.DNSTypeTCP:
|
||||||
o.Type = C.DNSTypeTCP
|
o.Type = C.DNSTypeTCP
|
||||||
o.Options = &remoteOptions
|
o.Options = &remoteOptions
|
||||||
@@ -195,20 +185,19 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error {
|
|||||||
if serverAddr.Port != 0 && serverAddr.Port != 53 {
|
if serverAddr.Port != 0 && serverAddr.Port != 53 {
|
||||||
remoteOptions.ServerPort = serverAddr.Port
|
remoteOptions.ServerPort = serverAddr.Port
|
||||||
}
|
}
|
||||||
remoteOptions.Server = serverAddr.AddrString()
|
|
||||||
remoteOptions.ServerPort = serverAddr.Port
|
|
||||||
case C.DNSTypeTLS, C.DNSTypeQUIC:
|
case C.DNSTypeTLS, C.DNSTypeQUIC:
|
||||||
o.Type = serverType
|
o.Type = serverType
|
||||||
|
tlsOptions := RemoteTLSDNSServerOptions{
|
||||||
|
RemoteDNSServerOptions: remoteOptions,
|
||||||
|
}
|
||||||
|
o.Options = &tlsOptions
|
||||||
serverAddr := M.ParseSocksaddr(serverURL.Host)
|
serverAddr := M.ParseSocksaddr(serverURL.Host)
|
||||||
if !serverAddr.IsValid() {
|
if !serverAddr.IsValid() {
|
||||||
return E.New("invalid server address")
|
return E.New("invalid server address")
|
||||||
}
|
}
|
||||||
remoteOptions.Server = serverAddr.Addr.String()
|
tlsOptions.Server = serverAddr.Addr.String()
|
||||||
if serverAddr.Port != 0 && serverAddr.Port != 853 {
|
if serverAddr.Port != 0 && serverAddr.Port != 853 {
|
||||||
remoteOptions.ServerPort = serverAddr.Port
|
tlsOptions.ServerPort = serverAddr.Port
|
||||||
}
|
|
||||||
o.Options = &RemoteTLSDNSServerOptions{
|
|
||||||
RemoteDNSServerOptions: remoteOptions,
|
|
||||||
}
|
}
|
||||||
case C.DNSTypeHTTPS, C.DNSTypeHTTP3:
|
case C.DNSTypeHTTPS, C.DNSTypeHTTP3:
|
||||||
o.Type = serverType
|
o.Type = serverType
|
||||||
@@ -255,14 +244,14 @@ func (o *NewDNSServerOptions) Upgrade(ctx context.Context) error {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case C.DNSTypeDHCP:
|
case "dhcp":
|
||||||
o.Type = C.DNSTypeDHCP
|
o.Type = C.DNSTypeDHCP
|
||||||
dhcpOptions := DHCPDNSServerOptions{}
|
dhcpOptions := DHCPDNSServerOptions{}
|
||||||
if serverURL.Host != "" && serverURL.Host != "auto" {
|
if serverURL.Host != "" && serverURL.Host != "auto" {
|
||||||
dhcpOptions.Interface = serverURL.Host
|
dhcpOptions.Interface = serverURL.Host
|
||||||
}
|
}
|
||||||
o.Options = &dhcpOptions
|
o.Options = &dhcpOptions
|
||||||
case C.DNSTypeFakeIP:
|
case "fakeip":
|
||||||
o.Type = C.DNSTypeFakeIP
|
o.Type = C.DNSTypeFakeIP
|
||||||
fakeipOptions := FakeIPDNSServerOptions{}
|
fakeipOptions := FakeIPDNSServerOptions{}
|
||||||
if legacyOptions, loaded := ctx.Value((*LegacyDNSFakeIPOptions)(nil)).(*LegacyDNSFakeIPOptions); loaded {
|
if legacyOptions, loaded := ctx.Value((*LegacyDNSFakeIPOptions)(nil)).(*LegacyDNSFakeIPOptions); loaded {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
"github.com/sagernet/sing/common/json/badoption"
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
@@ -136,9 +135,6 @@ func (o *DNSRecordOptions) UnmarshalJSON(data []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if a, isA := record.(*dns.A); isA {
|
|
||||||
a.A = M.AddrFromIP(a.A).Unmap().AsSlice()
|
|
||||||
}
|
|
||||||
o.RR = record
|
o.RR = record
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
import "github.com/sagernet/sing/common/json/badoption"
|
|
||||||
|
|
||||||
type HysteriaInboundOptions struct {
|
type HysteriaInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Up string `json:"up,omitempty"`
|
Up string `json:"up,omitempty"`
|
||||||
@@ -26,18 +24,16 @@ type HysteriaUser struct {
|
|||||||
type HysteriaOutboundOptions struct {
|
type HysteriaOutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
ServerPorts badoption.Listable[string] `json:"server_ports,omitempty"`
|
Up string `json:"up,omitempty"`
|
||||||
HopInterval badoption.Duration `json:"hop_interval,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
Up string `json:"up,omitempty"`
|
Down string `json:"down,omitempty"`
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
DownMbps int `json:"down_mbps,omitempty"`
|
||||||
Down string `json:"down,omitempty"`
|
Obfs string `json:"obfs,omitempty"`
|
||||||
DownMbps int `json:"down_mbps,omitempty"`
|
Auth []byte `json:"auth,omitempty"`
|
||||||
Obfs string `json:"obfs,omitempty"`
|
AuthString string `json:"auth_str,omitempty"`
|
||||||
Auth []byte `json:"auth,omitempty"`
|
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
||||||
AuthString string `json:"auth_str,omitempty"`
|
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
||||||
ReceiveWindowConn uint64 `json:"recv_window_conn,omitempty"`
|
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
||||||
ReceiveWindow uint64 `json:"recv_window,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
DisableMTUDiscovery bool `json:"disable_mtu_discovery,omitempty"`
|
|
||||||
Network NetworkList `json:"network,omitempty"`
|
|
||||||
OutboundTLSOptionsContainer
|
OutboundTLSOptionsContainer
|
||||||
}
|
}
|
||||||
|
|||||||
38
option/mitm.go
Normal file
38
option/mitm.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MITMOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
HTTP2Enabled bool `json:"http2_enabled,omitempty"`
|
||||||
|
TLSDecryptionOptions *TLSDecryptionOptions `json:"tls_decryption,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSDecryptionOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
KeyPair string `json:"key_pair_p12,omitempty"`
|
||||||
|
KeyPassword string `json:"key_password,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MITMRouteOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
Print bool `json:"print,omitempty"`
|
||||||
|
Script badoption.Listable[MITMRouteSurgeScriptOptions] `json:"sg_script,omitempty"`
|
||||||
|
SurgeURLRewrite badoption.Listable[SurgeURLRewriteLine] `json:"sg_url_rewrite,omitempty"`
|
||||||
|
SurgeHeaderRewrite badoption.Listable[SurgeHeaderRewriteLine] `json:"sg_header_rewrite,omitempty"`
|
||||||
|
SurgeBodyRewrite badoption.Listable[SurgeBodyRewriteLine] `json:"sg_body_rewrite,omitempty"`
|
||||||
|
SurgeMapLocal badoption.Listable[SurgeMapLocalLine] `json:"sg_map_local,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MITMRouteSurgeScriptOptions struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Type badoption.Listable[string] `json:"type"`
|
||||||
|
Pattern badoption.Listable[*badoption.Regexp] `json:"pattern"`
|
||||||
|
Timeout badoption.Duration `json:"timeout,omitempty"`
|
||||||
|
RequiresBody bool `json:"requires_body,omitempty"`
|
||||||
|
MaxSize int64 `json:"max_size,omitempty"`
|
||||||
|
BinaryBodyMode bool `json:"binary_body_mode,omitempty"`
|
||||||
|
Arguments badoption.Listable[string] `json:"arguments,omitempty"`
|
||||||
|
}
|
||||||
444
option/mitm_surge_urlrewrite.go
Normal file
444
option/mitm_surge_urlrewrite.go
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SurgeURLRewriteLine struct {
|
||||||
|
Pattern *regexp.Regexp
|
||||||
|
Destination *url.URL
|
||||||
|
Redirect bool
|
||||||
|
Reject bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeURLRewriteLine) String() string {
|
||||||
|
var fields []string
|
||||||
|
fields = append(fields, l.Pattern.String())
|
||||||
|
if l.Reject {
|
||||||
|
fields = append(fields, "_")
|
||||||
|
} else {
|
||||||
|
fields = append(fields, l.Destination.String())
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case l.Redirect:
|
||||||
|
fields = append(fields, "302")
|
||||||
|
case l.Reject:
|
||||||
|
fields = append(fields, "reject")
|
||||||
|
default:
|
||||||
|
fields = append(fields, "header")
|
||||||
|
}
|
||||||
|
return encodeSurgeKeys(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeURLRewriteLine) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(l.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SurgeURLRewriteLine) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var stringValue string
|
||||||
|
err := json.Unmarshal(bytes, &stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fields, err := surgeFields(stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_url_rewrite line: ", stringValue)
|
||||||
|
} else if len(fields) < 2 || len(fields) > 3 {
|
||||||
|
return E.New("invalid surge_url_rewrite line: ", stringValue)
|
||||||
|
}
|
||||||
|
pattern, err := regexp.Compile(fields[0].Key)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_url_rewrite line: invalid pattern: ", stringValue)
|
||||||
|
}
|
||||||
|
l.Pattern = pattern
|
||||||
|
l.Destination, err = url.Parse(fields[1].Key)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_url_rewrite line: invalid destination: ", stringValue)
|
||||||
|
}
|
||||||
|
if len(fields) == 3 {
|
||||||
|
switch fields[2].Key {
|
||||||
|
case "header":
|
||||||
|
case "302":
|
||||||
|
l.Redirect = true
|
||||||
|
case "reject":
|
||||||
|
l.Reject = true
|
||||||
|
default:
|
||||||
|
return E.New("invalid surge_url_rewrite line: invalid action: ", stringValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SurgeHeaderRewriteLine struct {
|
||||||
|
Response bool
|
||||||
|
Pattern *regexp.Regexp
|
||||||
|
Add bool
|
||||||
|
Delete bool
|
||||||
|
Replace bool
|
||||||
|
ReplaceRegex bool
|
||||||
|
Key string
|
||||||
|
Match *regexp.Regexp
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeHeaderRewriteLine) String() string {
|
||||||
|
var fields []string
|
||||||
|
if !l.Response {
|
||||||
|
fields = append(fields, "http-request")
|
||||||
|
} else {
|
||||||
|
fields = append(fields, "http-response")
|
||||||
|
}
|
||||||
|
fields = append(fields, l.Pattern.String())
|
||||||
|
if l.Add {
|
||||||
|
fields = append(fields, "header-add")
|
||||||
|
} else if l.Delete {
|
||||||
|
fields = append(fields, "header-del")
|
||||||
|
} else if l.Replace {
|
||||||
|
fields = append(fields, "header-replace")
|
||||||
|
} else if l.ReplaceRegex {
|
||||||
|
fields = append(fields, "header-replace-regex")
|
||||||
|
}
|
||||||
|
fields = append(fields, l.Key)
|
||||||
|
if l.Add || l.Replace {
|
||||||
|
fields = append(fields, l.Value)
|
||||||
|
} else if l.ReplaceRegex {
|
||||||
|
fields = append(fields, l.Match.String(), l.Value)
|
||||||
|
}
|
||||||
|
return encodeSurgeKeys(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeHeaderRewriteLine) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(l.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SurgeHeaderRewriteLine) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var stringValue string
|
||||||
|
err := json.Unmarshal(bytes, &stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fields, err := surgeFields(stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_header_rewrite line: ", stringValue)
|
||||||
|
} else if len(fields) < 4 {
|
||||||
|
return E.New("invalid surge_header_rewrite line: ", stringValue)
|
||||||
|
}
|
||||||
|
switch fields[0].Key {
|
||||||
|
case "http-request":
|
||||||
|
case "http-response":
|
||||||
|
l.Response = true
|
||||||
|
default:
|
||||||
|
return E.New("invalid surge_header_rewrite line: invalid type: ", stringValue)
|
||||||
|
}
|
||||||
|
l.Pattern, err = regexp.Compile(fields[1].Key)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_header_rewrite line: invalid pattern: ", stringValue)
|
||||||
|
}
|
||||||
|
switch fields[2].Key {
|
||||||
|
case "header-add":
|
||||||
|
l.Add = true
|
||||||
|
if len(fields) != 5 {
|
||||||
|
return E.New("invalid surge_header_rewrite line: " + stringValue)
|
||||||
|
}
|
||||||
|
l.Key = fields[3].Key
|
||||||
|
l.Value = fields[4].Key
|
||||||
|
case "header-del":
|
||||||
|
l.Delete = true
|
||||||
|
l.Key = fields[3].Key
|
||||||
|
case "header-replace":
|
||||||
|
l.Replace = true
|
||||||
|
if len(fields) != 5 {
|
||||||
|
return E.New("invalid surge_header_rewrite line: " + stringValue)
|
||||||
|
}
|
||||||
|
l.Key = fields[3].Key
|
||||||
|
l.Value = fields[4].Key
|
||||||
|
case "header-replace-regex":
|
||||||
|
l.ReplaceRegex = true
|
||||||
|
if len(fields) != 6 {
|
||||||
|
return E.New("invalid surge_header_rewrite line: " + stringValue)
|
||||||
|
}
|
||||||
|
l.Key = fields[3].Key
|
||||||
|
l.Match, err = regexp.Compile(fields[4].Key)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_header_rewrite line: invalid match: ", stringValue)
|
||||||
|
}
|
||||||
|
l.Value = fields[5].Key
|
||||||
|
default:
|
||||||
|
return E.New("invalid surge_header_rewrite line: invalid action: ", stringValue)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SurgeBodyRewriteLine struct {
|
||||||
|
Response bool
|
||||||
|
Pattern *regexp.Regexp
|
||||||
|
Match []*regexp.Regexp
|
||||||
|
Replace []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeBodyRewriteLine) String() string {
|
||||||
|
var fields []string
|
||||||
|
if !l.Response {
|
||||||
|
fields = append(fields, "http-request")
|
||||||
|
} else {
|
||||||
|
fields = append(fields, "http-response")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(l.Match); i += 2 {
|
||||||
|
fields = append(fields, l.Match[i].String(), l.Replace[i])
|
||||||
|
}
|
||||||
|
return strings.Join(fields, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeBodyRewriteLine) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(l.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SurgeBodyRewriteLine) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var stringValue string
|
||||||
|
err := json.Unmarshal(bytes, &stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fields, err := surgeFields(stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_body_rewrite line: ", stringValue)
|
||||||
|
} else if len(fields) < 4 {
|
||||||
|
return E.New("invalid surge_body_rewrite line: ", stringValue)
|
||||||
|
} else if len(fields)%2 != 0 {
|
||||||
|
return E.New("invalid surge_body_rewrite line: ", stringValue)
|
||||||
|
}
|
||||||
|
switch fields[0].Key {
|
||||||
|
case "http-request":
|
||||||
|
case "http-response":
|
||||||
|
l.Response = true
|
||||||
|
default:
|
||||||
|
return E.New("invalid surge_body_rewrite line: invalid type: ", stringValue)
|
||||||
|
}
|
||||||
|
l.Pattern, err = regexp.Compile(fields[1].Key)
|
||||||
|
for i := 2; i < len(fields); i += 2 {
|
||||||
|
var match *regexp.Regexp
|
||||||
|
match, err = regexp.Compile(fields[i].Key)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_body_rewrite line: invalid match: ", stringValue)
|
||||||
|
}
|
||||||
|
l.Match = append(l.Match, match)
|
||||||
|
l.Replace = append(l.Replace, fields[i+1].Key)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SurgeMapLocalLine struct {
|
||||||
|
Pattern *regexp.Regexp
|
||||||
|
StatusCode int
|
||||||
|
File bool
|
||||||
|
Text bool
|
||||||
|
TinyGif bool
|
||||||
|
Base64 bool
|
||||||
|
Data string
|
||||||
|
Base64Data []byte
|
||||||
|
Headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeMapLocalLine) String() string {
|
||||||
|
var fields []surgeField
|
||||||
|
fields = append(fields, surgeField{Key: l.Pattern.String()})
|
||||||
|
if l.File {
|
||||||
|
fields = append(fields, surgeField{Key: "data-type", Value: "file"})
|
||||||
|
fields = append(fields, surgeField{Key: "data", Value: l.Data})
|
||||||
|
} else if l.Text {
|
||||||
|
fields = append(fields, surgeField{Key: "data-type", Value: "text"})
|
||||||
|
fields = append(fields, surgeField{Key: "data", Value: l.Data})
|
||||||
|
} else if l.TinyGif {
|
||||||
|
fields = append(fields, surgeField{Key: "data-type", Value: "tiny-gif"})
|
||||||
|
} else if l.Base64 {
|
||||||
|
fields = append(fields, surgeField{Key: "data-type", Value: "base64"})
|
||||||
|
fields = append(fields, surgeField{Key: "data-type", Value: base64.StdEncoding.EncodeToString(l.Base64Data)})
|
||||||
|
}
|
||||||
|
fields = append(fields, surgeField{Key: "status-code", Value: F.ToString(l.StatusCode), ValueSet: true})
|
||||||
|
if len(l.Headers) > 0 {
|
||||||
|
var headers []string
|
||||||
|
for key, values := range l.Headers {
|
||||||
|
for _, value := range values {
|
||||||
|
headers = append(headers, key+":"+value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields = append(fields, surgeField{Key: "headers", Value: strings.Join(headers, "|")})
|
||||||
|
}
|
||||||
|
return encodeSurgeFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l SurgeMapLocalLine) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(l.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SurgeMapLocalLine) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var stringValue string
|
||||||
|
err := json.Unmarshal(bytes, &stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fields, err := surgeFields(stringValue)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_map_local line: ", stringValue)
|
||||||
|
} else if len(fields) < 1 {
|
||||||
|
return E.New("invalid surge_map_local line: ", stringValue)
|
||||||
|
}
|
||||||
|
l.Pattern, err = regexp.Compile(fields[0].Key)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "invalid surge_map_local line: invalid pattern: ", stringValue)
|
||||||
|
}
|
||||||
|
dataTypeField := common.Find(fields, func(it surgeField) bool {
|
||||||
|
return it.Key == "data-type"
|
||||||
|
})
|
||||||
|
if !dataTypeField.ValueSet {
|
||||||
|
return E.New("invalid surge_map_local line: missing data-type: ", stringValue)
|
||||||
|
}
|
||||||
|
switch dataTypeField.Value {
|
||||||
|
case "file":
|
||||||
|
l.File = true
|
||||||
|
case "text":
|
||||||
|
l.Text = true
|
||||||
|
case "tiny-gif":
|
||||||
|
l.TinyGif = true
|
||||||
|
case "base64":
|
||||||
|
l.Base64 = true
|
||||||
|
}
|
||||||
|
for i := 1; i < len(fields); i++ {
|
||||||
|
switch fields[i].Key {
|
||||||
|
case "data-type":
|
||||||
|
continue
|
||||||
|
case "data":
|
||||||
|
if l.File {
|
||||||
|
l.Data = fields[i].Value
|
||||||
|
} else if l.Text {
|
||||||
|
l.Data = fields[i].Value
|
||||||
|
} else if l.Base64 {
|
||||||
|
l.Base64Data, err = base64.StdEncoding.DecodeString(fields[i].Value)
|
||||||
|
if err != nil {
|
||||||
|
return E.New("invalid surge_map_local line: invalid base64 data: ", stringValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "status-code":
|
||||||
|
statusCode, err := strconv.ParseInt(fields[i].Value, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return E.New("invalid surge_map_local line: invalid status code: ", stringValue)
|
||||||
|
}
|
||||||
|
l.StatusCode = int(statusCode)
|
||||||
|
case "headers":
|
||||||
|
headers := make(http.Header)
|
||||||
|
for _, headerLine := range strings.Split(fields[i].Value, "|") {
|
||||||
|
if !strings.Contains(headerLine, ":") {
|
||||||
|
return E.New("invalid surge_map_local line: headers: missing `:` in item: ", stringValue, ": ", headerLine)
|
||||||
|
}
|
||||||
|
headers.Add(common.SubstringBefore(headerLine, ":"), common.SubstringAfter(headerLine, ":"))
|
||||||
|
}
|
||||||
|
l.Headers = headers
|
||||||
|
default:
|
||||||
|
return E.New("invalid surge_map_local line: unknown options: ", stringValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type surgeField struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
ValueSet bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeSurgeKeys(keys []string) string {
|
||||||
|
keys = common.Map(keys, func(it string) string {
|
||||||
|
if strings.ContainsFunc(it, unicode.IsSpace) {
|
||||||
|
return "\"" + it + "\""
|
||||||
|
} else {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return strings.Join(keys, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeSurgeFields(fields []surgeField) string {
|
||||||
|
return strings.Join(common.Map(fields, func(it surgeField) string {
|
||||||
|
if !it.ValueSet {
|
||||||
|
if strings.ContainsFunc(it.Key, unicode.IsSpace) {
|
||||||
|
return "\"" + it.Key + "\""
|
||||||
|
} else {
|
||||||
|
return it.Key
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if strings.ContainsFunc(it.Value, unicode.IsSpace) {
|
||||||
|
return it.Key + "=\"" + it.Value + "\""
|
||||||
|
} else {
|
||||||
|
return it.Key + "=" + it.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func surgeFields(s string) ([]surgeField, error) {
|
||||||
|
var (
|
||||||
|
fields []surgeField
|
||||||
|
currentField *surgeField
|
||||||
|
)
|
||||||
|
for _, field := range strings.Fields(s) {
|
||||||
|
if currentField != nil {
|
||||||
|
field = " " + field
|
||||||
|
if strings.HasSuffix(field, "\"") {
|
||||||
|
field = field[:len(field)-1]
|
||||||
|
if !currentField.ValueSet {
|
||||||
|
currentField.Key += field
|
||||||
|
} else {
|
||||||
|
currentField.Value += field
|
||||||
|
}
|
||||||
|
fields = append(fields, *currentField)
|
||||||
|
currentField = nil
|
||||||
|
} else {
|
||||||
|
if !currentField.ValueSet {
|
||||||
|
currentField.Key += field
|
||||||
|
} else {
|
||||||
|
currentField.Value += " " + field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !strings.Contains(field, "=") {
|
||||||
|
if strings.HasPrefix(field, "\"") {
|
||||||
|
field = field[1:]
|
||||||
|
if strings.HasSuffix(field, "\"") {
|
||||||
|
field = field[:len(field)-1]
|
||||||
|
} else {
|
||||||
|
currentField = &surgeField{Key: field}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields = append(fields, surgeField{Key: field})
|
||||||
|
} else {
|
||||||
|
key := common.SubstringBefore(field, "=")
|
||||||
|
value := common.SubstringAfter(field, "=")
|
||||||
|
if strings.HasPrefix(value, "\"") {
|
||||||
|
value = value[1:]
|
||||||
|
if strings.HasSuffix(field, "\"") {
|
||||||
|
value = value[:len(value)-1]
|
||||||
|
} else {
|
||||||
|
currentField = &surgeField{Key: key, Value: field, ValueSet: true}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields = append(fields, surgeField{Key: key, Value: value, ValueSet: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currentField != nil {
|
||||||
|
return nil, E.New("invalid surge fields line: ", s)
|
||||||
|
}
|
||||||
|
return fields, nil
|
||||||
|
}
|
||||||
@@ -12,13 +12,15 @@ type _Options struct {
|
|||||||
Schema string `json:"$schema,omitempty"`
|
Schema string `json:"$schema,omitempty"`
|
||||||
Log *LogOptions `json:"log,omitempty"`
|
Log *LogOptions `json:"log,omitempty"`
|
||||||
DNS *DNSOptions `json:"dns,omitempty"`
|
DNS *DNSOptions `json:"dns,omitempty"`
|
||||||
NTP *NTPOptions `json:"ntp,omitempty"`
|
|
||||||
Certificate *CertificateOptions `json:"certificate,omitempty"`
|
|
||||||
Endpoints []Endpoint `json:"endpoints,omitempty"`
|
Endpoints []Endpoint `json:"endpoints,omitempty"`
|
||||||
Inbounds []Inbound `json:"inbounds,omitempty"`
|
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||||
Outbounds []Outbound `json:"outbounds,omitempty"`
|
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||||
Route *RouteOptions `json:"route,omitempty"`
|
Route *RouteOptions `json:"route,omitempty"`
|
||||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||||
|
NTP *NTPOptions `json:"ntp,omitempty"`
|
||||||
|
Certificate *CertificateOptions `json:"certificate,omitempty"`
|
||||||
|
MITM *MITMOptions `json:"mitm,omitempty"`
|
||||||
|
Scripts []Script `json:"scripts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options _Options
|
type Options _Options
|
||||||
|
|||||||
@@ -153,6 +153,8 @@ type RawRouteOptionsActionOptions struct {
|
|||||||
|
|
||||||
TLSFragment bool `json:"tls_fragment,omitempty"`
|
TLSFragment bool `json:"tls_fragment,omitempty"`
|
||||||
TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
|
TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
|
||||||
|
|
||||||
|
MITM *MITMRouteOptions `json:"mitm,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
||||||
|
|||||||
128
option/script.go
Normal file
128
option/script.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/json"
|
||||||
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _ScriptSourceOptions struct {
|
||||||
|
Source string `json:"source"`
|
||||||
|
LocalOptions LocalScriptSource `json:"-"`
|
||||||
|
RemoteOptions RemoteScriptSource `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalScriptSource struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoteScriptSource struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
DownloadDetour string `json:"download_detour,omitempty"`
|
||||||
|
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScriptSourceOptions _ScriptSourceOptions
|
||||||
|
|
||||||
|
func (o ScriptSourceOptions) MarshalJSON() ([]byte, error) {
|
||||||
|
var source any
|
||||||
|
switch o.Source {
|
||||||
|
case C.ScriptSourceTypeLocal:
|
||||||
|
source = o.LocalOptions
|
||||||
|
case C.ScriptSourceTypeRemote:
|
||||||
|
source = o.RemoteOptions
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown script source: ", o.Source)
|
||||||
|
}
|
||||||
|
return badjson.MarshallObjects((_ScriptSourceOptions)(o), source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ScriptSourceOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
|
err := json.Unmarshal(bytes, (*_ScriptSourceOptions)(o))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var source any
|
||||||
|
switch o.Source {
|
||||||
|
case C.ScriptSourceTypeLocal:
|
||||||
|
source = &o.LocalOptions
|
||||||
|
case C.ScriptSourceTypeRemote:
|
||||||
|
source = &o.RemoteOptions
|
||||||
|
default:
|
||||||
|
return E.New("unknown script source: ", o.Source)
|
||||||
|
}
|
||||||
|
return json.Unmarshal(bytes, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make struct in order
|
||||||
|
type Script struct {
|
||||||
|
ScriptSourceOptions
|
||||||
|
ScriptOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Script) MarshalJSON() ([]byte, error) {
|
||||||
|
return badjson.MarshallObjects(s.ScriptSourceOptions, s.ScriptOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Script) UnmarshalJSON(bytes []byte) error {
|
||||||
|
err := json.Unmarshal(bytes, &s.ScriptSourceOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return badjson.UnmarshallExcluded(bytes, &s.ScriptSourceOptions, &s.ScriptOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ScriptOptions struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
SurgeOptions SurgeScriptOptions `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScriptOptions _ScriptOptions
|
||||||
|
|
||||||
|
func (o ScriptOptions) MarshalJSON() ([]byte, error) {
|
||||||
|
var v any
|
||||||
|
switch o.Type {
|
||||||
|
case C.ScriptTypeSurge:
|
||||||
|
v = &o.SurgeOptions
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown script type: ", o.Type)
|
||||||
|
}
|
||||||
|
if v == nil {
|
||||||
|
return badjson.MarshallObjects((_ScriptOptions)(o))
|
||||||
|
}
|
||||||
|
return badjson.MarshallObjects((_ScriptOptions)(o), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ScriptOptions) UnmarshalJSON(bytes []byte) error {
|
||||||
|
err := json.Unmarshal(bytes, (*_ScriptOptions)(o))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var v any
|
||||||
|
switch o.Type {
|
||||||
|
case C.ScriptTypeSurge:
|
||||||
|
v = &o.SurgeOptions
|
||||||
|
case "":
|
||||||
|
return E.New("missing script type")
|
||||||
|
default:
|
||||||
|
return E.New("unknown script type: ", o.Type)
|
||||||
|
}
|
||||||
|
if v == nil {
|
||||||
|
// check unknown fields
|
||||||
|
return json.UnmarshalDisallowUnknownFields(bytes, &_ScriptOptions{})
|
||||||
|
}
|
||||||
|
return badjson.UnmarshallExcluded(bytes, (*_ScriptOptions)(o), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SurgeScriptOptions struct {
|
||||||
|
CronOptions *CronScriptOptions `json:"cron,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CronScriptOptions struct {
|
||||||
|
Expression string `json:"expression"`
|
||||||
|
Arguments []string `json:"arguments,omitempty"`
|
||||||
|
Timeout badoption.Duration `json:"timeout,omitempty"`
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package option
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TailscaleEndpointOptions struct {
|
|
||||||
DialerOptions
|
|
||||||
StateDirectory string `json:"state_directory,omitempty"`
|
|
||||||
AuthKey string `json:"auth_key,omitempty"`
|
|
||||||
ControlURL string `json:"control_url,omitempty"`
|
|
||||||
Ephemeral bool `json:"ephemeral,omitempty"`
|
|
||||||
Hostname string `json:"hostname,omitempty"`
|
|
||||||
ExitNode string `json:"exit_node,omitempty"`
|
|
||||||
ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"`
|
|
||||||
AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"`
|
|
||||||
AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"`
|
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TailscaleDNSServerOptions struct {
|
|
||||||
Endpoint string `json:"endpoint,omitempty"`
|
|
||||||
AcceptDefaultResolvers bool `json:"accept_default_resolvers,omitempty"`
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/adapter/outbound"
|
"github.com/sagernet/sing-box/adapter/outbound"
|
||||||
@@ -77,18 +76,17 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
client, err := hysteria.NewClient(hysteria.ClientOptions{
|
client, err := hysteria.NewClient(hysteria.ClientOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Dialer: outboundDialer,
|
Dialer: outboundDialer,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
ServerAddress: options.ServerOptions.Build(),
|
ServerAddress: options.ServerOptions.Build(),
|
||||||
ServerPorts: options.ServerPorts,
|
SendBPS: sendBps,
|
||||||
HopInterval: time.Duration(options.HopInterval),
|
ReceiveBPS: receiveBps,
|
||||||
SendBPS: sendBps,
|
XPlusPassword: options.Obfs,
|
||||||
ReceiveBPS: receiveBps,
|
Password: password,
|
||||||
XPlusPassword: options.Obfs,
|
TLSConfig: tlsConfig,
|
||||||
Password: password,
|
UDPDisabled: !common.Contains(networkList, N.NetworkUDP),
|
||||||
TLSConfig: tlsConfig,
|
|
||||||
UDPDisabled: !common.Contains(networkList, N.NetworkUDP),
|
|
||||||
ConnReceiveWindow: options.ReceiveWindowConn,
|
ConnReceiveWindow: options.ReceiveWindowConn,
|
||||||
StreamReceiveWindow: options.ReceiveWindow,
|
StreamReceiveWindow: options.ReceiveWindow,
|
||||||
DisableMTUDiscovery: options.DisableMTUDiscovery,
|
DisableMTUDiscovery: options.DisableMTUDiscovery,
|
||||||
|
|||||||
@@ -1,320 +0,0 @@
|
|||||||
package tailscale
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/netip"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/dns"
|
|
||||||
"github.com/sagernet/sing-box/dns/transport"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/service"
|
|
||||||
nDNS "github.com/sagernet/tailscale/net/dns"
|
|
||||||
"github.com/sagernet/tailscale/types/dnstype"
|
|
||||||
"github.com/sagernet/tailscale/wgengine/router"
|
|
||||||
"github.com/sagernet/tailscale/wgengine/wgcfg"
|
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
|
||||||
"go4.org/netipx"
|
|
||||||
"golang.org/x/net/http2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RegistryTransport(registry *dns.TransportRegistry) {
|
|
||||||
dns.RegisterTransport[option.TailscaleDNSServerOptions](registry, C.DNSTypeTailscale, NewDNSTransport)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DNSTransport struct {
|
|
||||||
dns.TransportAdapter
|
|
||||||
ctx context.Context
|
|
||||||
logger logger.ContextLogger
|
|
||||||
endpointTag string
|
|
||||||
acceptDefaultResolvers bool
|
|
||||||
dnsRouter adapter.DNSRouter
|
|
||||||
endpointManager adapter.EndpointManager
|
|
||||||
cfg *wgcfg.Config
|
|
||||||
dnsCfg *nDNS.Config
|
|
||||||
endpoint *Endpoint
|
|
||||||
routePrefixes []netip.Prefix
|
|
||||||
routes map[string][]adapter.DNSTransport
|
|
||||||
hosts map[string][]netip.Addr
|
|
||||||
defaultResolvers []adapter.DNSTransport
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDNSTransport(ctx context.Context, logger log.ContextLogger, tag string, options option.TailscaleDNSServerOptions) (adapter.DNSTransport, error) {
|
|
||||||
if options.Endpoint == "" {
|
|
||||||
return nil, E.New("missing tailscale endpoint tag")
|
|
||||||
}
|
|
||||||
return &DNSTransport{
|
|
||||||
TransportAdapter: dns.NewTransportAdapter(C.DNSTypeTailscale, tag, nil),
|
|
||||||
ctx: ctx,
|
|
||||||
logger: logger,
|
|
||||||
endpointTag: options.Endpoint,
|
|
||||||
acceptDefaultResolvers: options.AcceptDefaultResolvers,
|
|
||||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
|
||||||
endpointManager: service.FromContext[adapter.EndpointManager](ctx),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) Start(stage adapter.StartStage) error {
|
|
||||||
if stage != adapter.StartStateInitialize {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rawOutbound, loaded := t.endpointManager.Get(t.endpointTag)
|
|
||||||
if !loaded {
|
|
||||||
return E.New("endpoint not found: ", t.endpointTag)
|
|
||||||
}
|
|
||||||
ep, isTailscale := rawOutbound.(*Endpoint)
|
|
||||||
if !isTailscale {
|
|
||||||
return E.New("endpoint is not Tailscale: ", t.endpointTag)
|
|
||||||
}
|
|
||||||
if ep.onReconfig != nil {
|
|
||||||
return E.New("only one Tailscale DNS server is allowed for single endpoint")
|
|
||||||
}
|
|
||||||
ep.onReconfig = t.onReconfig
|
|
||||||
t.endpoint = ep
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) Reset() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *nDNS.Config) {
|
|
||||||
if cfg == nil || dnsCfg == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.cfg = cfg
|
|
||||||
t.dnsCfg = dnsCfg
|
|
||||||
err := t.updateDNSServers(routerCfg, dnsCfg)
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error(E.Cause(err, "update DNS servers"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) updateDNSServers(routeConfig *router.Config, dnsConfig *nDNS.Config) error {
|
|
||||||
t.routePrefixes = buildRoutePrefixes(routeConfig)
|
|
||||||
directDialerOnce := sync.OnceValue(func() N.Dialer {
|
|
||||||
directDialer := common.Must1(dialer.NewDefault(t.ctx, option.DialerOptions{}))
|
|
||||||
return &DNSDialer{transport: t, fallbackDialer: directDialer}
|
|
||||||
})
|
|
||||||
routes := make(map[string][]adapter.DNSTransport)
|
|
||||||
for domain, resolvers := range dnsConfig.Routes {
|
|
||||||
var myResolvers []adapter.DNSTransport
|
|
||||||
for _, resolver := range resolvers {
|
|
||||||
myResolver, err := t.createResolver(directDialerOnce, resolver)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
myResolvers = append(myResolvers, myResolver)
|
|
||||||
}
|
|
||||||
routes[domain.WithTrailingDot()] = myResolvers
|
|
||||||
}
|
|
||||||
hosts := make(map[string][]netip.Addr)
|
|
||||||
for domain, addresses := range dnsConfig.Hosts {
|
|
||||||
hosts[domain.WithTrailingDot()] = addresses
|
|
||||||
}
|
|
||||||
var defaultResolvers []adapter.DNSTransport
|
|
||||||
for _, resolver := range dnsConfig.DefaultResolvers {
|
|
||||||
myResolver, err := t.createResolver(directDialerOnce, resolver)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defaultResolvers = append(defaultResolvers, myResolver)
|
|
||||||
}
|
|
||||||
t.routes = routes
|
|
||||||
t.hosts = hosts
|
|
||||||
t.defaultResolvers = defaultResolvers
|
|
||||||
if len(defaultResolvers) > 0 {
|
|
||||||
t.logger.Info("updated ", len(routes), " routes, ", len(hosts), " hosts, default resolvers: ",
|
|
||||||
strings.Join(common.Map(dnsConfig.DefaultResolvers, func(it *dnstype.Resolver) string { return it.Addr }), " "))
|
|
||||||
} else {
|
|
||||||
t.logger.Info("updated ", len(routes), " routes, ", len(hosts), " hosts")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dnstype.Resolver) (adapter.DNSTransport, error) {
|
|
||||||
serverURL, parseURLErr := url.Parse(resolver.Addr)
|
|
||||||
var myDialer N.Dialer
|
|
||||||
if parseURLErr == nil && serverURL.Scheme == "http" {
|
|
||||||
myDialer = t.endpoint
|
|
||||||
} else {
|
|
||||||
myDialer = directDialer()
|
|
||||||
}
|
|
||||||
if len(resolver.BootstrapResolution) > 0 {
|
|
||||||
bootstrapTransport := transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, M.SocksaddrFrom(resolver.BootstrapResolution[0], 53))
|
|
||||||
myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", adapter.DNSQueryOptions{Transport: bootstrapTransport}, 0)
|
|
||||||
}
|
|
||||||
if serverAddr := M.ParseSocksaddr(resolver.Addr); serverAddr.IsValid() {
|
|
||||||
if serverAddr.Port == 0 {
|
|
||||||
serverAddr.Port = 53
|
|
||||||
}
|
|
||||||
return transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, serverAddr), nil
|
|
||||||
} else if parseURLErr != nil {
|
|
||||||
return nil, E.Cause(parseURLErr, "parse resolver address")
|
|
||||||
} else {
|
|
||||||
switch serverURL.Scheme {
|
|
||||||
case "https":
|
|
||||||
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
|
|
||||||
if serverAddr.Port == 0 {
|
|
||||||
serverAddr.Port = 443
|
|
||||||
}
|
|
||||||
tlsConfig := common.Must1(tls.NewClient(t.ctx, serverAddr.AddrString(), option.OutboundTLSOptions{
|
|
||||||
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
|
|
||||||
}))
|
|
||||||
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
|
|
||||||
case "http":
|
|
||||||
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
|
|
||||||
if serverAddr.Port == 0 {
|
|
||||||
serverAddr.Port = 80
|
|
||||||
}
|
|
||||||
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, nil), nil
|
|
||||||
// case "tls":
|
|
||||||
default:
|
|
||||||
return nil, E.New("unknown resolver scheme: ", serverURL.Scheme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildRoutePrefixes(routeConfig *router.Config) []netip.Prefix {
|
|
||||||
var builder netipx.IPSetBuilder
|
|
||||||
for _, localAddr := range routeConfig.LocalAddrs {
|
|
||||||
builder.AddPrefix(localAddr)
|
|
||||||
}
|
|
||||||
for _, route := range routeConfig.Routes {
|
|
||||||
builder.AddPrefix(route)
|
|
||||||
}
|
|
||||||
for _, route := range routeConfig.LocalRoutes {
|
|
||||||
builder.AddPrefix(route)
|
|
||||||
}
|
|
||||||
for _, route := range routeConfig.SubnetRoutes {
|
|
||||||
builder.AddPrefix(route)
|
|
||||||
}
|
|
||||||
ipSet, err := builder.IPSet()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ipSet.Prefixes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) Raw() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
|
||||||
if len(message.Question) != 1 {
|
|
||||||
return nil, os.ErrInvalid
|
|
||||||
}
|
|
||||||
question := message.Question[0]
|
|
||||||
addresses, hostsLoaded := t.hosts[question.Name]
|
|
||||||
if hostsLoaded {
|
|
||||||
switch question.Qtype {
|
|
||||||
case mDNS.TypeA:
|
|
||||||
addresses4 := common.Filter(addresses, func(addr netip.Addr) bool {
|
|
||||||
return addr.Is4()
|
|
||||||
})
|
|
||||||
if len(addresses4) > 0 {
|
|
||||||
return dns.FixedResponse(message.Id, question, addresses4, C.DefaultDNSTTL), nil
|
|
||||||
}
|
|
||||||
case mDNS.TypeAAAA:
|
|
||||||
addresses6 := common.Filter(addresses, func(addr netip.Addr) bool {
|
|
||||||
return addr.Is6()
|
|
||||||
})
|
|
||||||
if len(addresses6) > 0 {
|
|
||||||
return dns.FixedResponse(message.Id, question, addresses6, C.DefaultDNSTTL), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for domainSuffix, transports := range t.routes {
|
|
||||||
if strings.HasSuffix(question.Name, domainSuffix) {
|
|
||||||
if len(transports) == 0 {
|
|
||||||
return &mDNS.Msg{
|
|
||||||
MsgHdr: mDNS.MsgHdr{
|
|
||||||
Id: message.Id,
|
|
||||||
Rcode: mDNS.RcodeNameError,
|
|
||||||
Response: true,
|
|
||||||
},
|
|
||||||
Question: []mDNS.Question{question},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
var lastErr error
|
|
||||||
for _, dnsTransport := range transports {
|
|
||||||
response, err := dnsTransport.Exchange(ctx, message)
|
|
||||||
if err != nil {
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
return nil, lastErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t.acceptDefaultResolvers {
|
|
||||||
if len(t.defaultResolvers) > 0 {
|
|
||||||
var lastErr error
|
|
||||||
for _, resolver := range t.defaultResolvers {
|
|
||||||
response, err := resolver.Exchange(ctx, message)
|
|
||||||
if err != nil {
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return response, nil
|
|
||||||
}
|
|
||||||
return nil, lastErr
|
|
||||||
} else {
|
|
||||||
return nil, E.New("missing default resolvers")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, dns.RCodeNameError
|
|
||||||
}
|
|
||||||
|
|
||||||
type DNSDialer struct {
|
|
||||||
transport *DNSTransport
|
|
||||||
fallbackDialer N.Dialer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
if destination.IsFqdn() {
|
|
||||||
panic("invalid request here")
|
|
||||||
}
|
|
||||||
for _, prefix := range d.transport.routePrefixes {
|
|
||||||
if prefix.Contains(destination.Addr) {
|
|
||||||
return d.transport.endpoint.DialContext(ctx, network, destination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return d.fallbackDialer.DialContext(ctx, network, destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
|
||||||
if destination.IsFqdn() {
|
|
||||||
panic("invalid request here")
|
|
||||||
}
|
|
||||||
for _, prefix := range d.transport.routePrefixes {
|
|
||||||
if prefix.Contains(destination.Addr) {
|
|
||||||
return d.transport.endpoint.ListenPacket(ctx, destination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return d.fallbackDialer.ListenPacket(ctx, destination)
|
|
||||||
}
|
|
||||||
@@ -1,473 +0,0 @@
|
|||||||
package tailscale
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync/atomic"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/adapter/endpoint"
|
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
"github.com/sagernet/sing-tun"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
"github.com/sagernet/sing/common/control"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/service"
|
|
||||||
"github.com/sagernet/sing/service/filemanager"
|
|
||||||
"github.com/sagernet/tailscale/ipn"
|
|
||||||
"github.com/sagernet/tailscale/net/netmon"
|
|
||||||
"github.com/sagernet/tailscale/net/tsaddr"
|
|
||||||
"github.com/sagernet/tailscale/tsnet"
|
|
||||||
"github.com/sagernet/tailscale/types/ipproto"
|
|
||||||
"github.com/sagernet/tailscale/version"
|
|
||||||
"github.com/sagernet/tailscale/wgengine"
|
|
||||||
"github.com/sagernet/tailscale/wgengine/filter"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
version.SetVersion("sing-box " + C.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterEndpoint(registry *endpoint.Registry) {
|
|
||||||
endpoint.Register[option.TailscaleEndpointOptions](registry, C.TypeTailscale, NewEndpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Endpoint struct {
|
|
||||||
endpoint.Adapter
|
|
||||||
ctx context.Context
|
|
||||||
router adapter.Router
|
|
||||||
logger logger.ContextLogger
|
|
||||||
dnsRouter adapter.DNSRouter
|
|
||||||
network adapter.NetworkManager
|
|
||||||
platformInterface platform.Interface
|
|
||||||
server *tsnet.Server
|
|
||||||
stack *stack.Stack
|
|
||||||
filter *atomic.Pointer[filter.Filter]
|
|
||||||
onReconfig wgengine.ReconfigListener
|
|
||||||
|
|
||||||
exitNode string
|
|
||||||
exitNodeAllowLANAccess bool
|
|
||||||
advertiseRoutes []netip.Prefix
|
|
||||||
advertiseExitNode bool
|
|
||||||
|
|
||||||
udpTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) {
|
|
||||||
stateDirectory := options.StateDirectory
|
|
||||||
if stateDirectory == "" {
|
|
||||||
stateDirectory = "tailscale"
|
|
||||||
}
|
|
||||||
hostname := options.Hostname
|
|
||||||
if hostname == "" {
|
|
||||||
osHostname, _ := os.Hostname()
|
|
||||||
osHostname = strings.TrimSpace(osHostname)
|
|
||||||
hostname = osHostname
|
|
||||||
}
|
|
||||||
if hostname == "" {
|
|
||||||
hostname = "sing-box"
|
|
||||||
}
|
|
||||||
stateDirectory = filemanager.BasePath(ctx, os.ExpandEnv(stateDirectory))
|
|
||||||
stateDirectory, _ = filepath.Abs(stateDirectory)
|
|
||||||
for _, advertiseRoute := range options.AdvertiseRoutes {
|
|
||||||
if advertiseRoute.Addr().IsUnspecified() && advertiseRoute.Bits() == 0 {
|
|
||||||
return nil, E.New("`advertise_routes` cannot be default, use `advertise_exit_node` instead.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if options.AdvertiseExitNode && options.ExitNode != "" {
|
|
||||||
return nil, E.New("cannot advertise an exit node and use an exit node at the same time.")
|
|
||||||
}
|
|
||||||
var udpTimeout time.Duration
|
|
||||||
if options.UDPTimeout != 0 {
|
|
||||||
udpTimeout = time.Duration(options.UDPTimeout)
|
|
||||||
} else {
|
|
||||||
udpTimeout = C.UDPTimeout
|
|
||||||
}
|
|
||||||
var remoteIsDomain bool
|
|
||||||
if options.ControlURL != "" {
|
|
||||||
controlURL, err := url.Parse(options.ControlURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "parse control URL")
|
|
||||||
}
|
|
||||||
remoteIsDomain = M.IsDomainName(controlURL.Hostname())
|
|
||||||
} else {
|
|
||||||
// controlplane.tailscale.com
|
|
||||||
remoteIsDomain = true
|
|
||||||
}
|
|
||||||
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
|
|
||||||
Context: ctx,
|
|
||||||
Options: options.DialerOptions,
|
|
||||||
RemoteIsDomain: remoteIsDomain,
|
|
||||||
ResolverOnDetour: true,
|
|
||||||
NewDialer: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dnsRouter := service.FromContext[adapter.DNSRouter](ctx)
|
|
||||||
server := &tsnet.Server{
|
|
||||||
Dir: stateDirectory,
|
|
||||||
Hostname: hostname,
|
|
||||||
Logf: func(format string, args ...any) {
|
|
||||||
logger.Trace(fmt.Sprintf(format, args...))
|
|
||||||
},
|
|
||||||
UserLogf: func(format string, args ...any) {
|
|
||||||
logger.Debug(fmt.Sprintf(format, args...))
|
|
||||||
},
|
|
||||||
Ephemeral: options.Ephemeral,
|
|
||||||
AuthKey: options.AuthKey,
|
|
||||||
ControlURL: options.ControlURL,
|
|
||||||
Dialer: &endpointDialer{Dialer: outboundDialer, logger: logger},
|
|
||||||
LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) {
|
|
||||||
return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return &Endpoint{
|
|
||||||
Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP}, nil),
|
|
||||||
ctx: ctx,
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
dnsRouter: dnsRouter,
|
|
||||||
network: service.FromContext[adapter.NetworkManager](ctx),
|
|
||||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
|
||||||
server: server,
|
|
||||||
exitNode: options.ExitNode,
|
|
||||||
exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess,
|
|
||||||
advertiseRoutes: options.AdvertiseRoutes,
|
|
||||||
advertiseExitNode: options.AdvertiseExitNode,
|
|
||||||
udpTimeout: udpTimeout,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) Start(stage adapter.StartStage) error {
|
|
||||||
if stage != adapter.StartStateStart {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if t.platformInterface != nil {
|
|
||||||
err := t.network.UpdateInterfaces()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
netmon.RegisterInterfaceGetter(func() ([]netmon.Interface, error) {
|
|
||||||
return common.Map(t.network.InterfaceFinder().Interfaces(), func(it control.Interface) netmon.Interface {
|
|
||||||
return netmon.Interface{
|
|
||||||
Interface: &net.Interface{
|
|
||||||
Index: it.Index,
|
|
||||||
MTU: it.MTU,
|
|
||||||
Name: it.Name,
|
|
||||||
HardwareAddr: it.HardwareAddr,
|
|
||||||
Flags: it.Flags,
|
|
||||||
},
|
|
||||||
AltAddrs: common.Map(it.Addresses, func(it netip.Prefix) net.Addr {
|
|
||||||
return &net.IPNet{
|
|
||||||
IP: it.Addr().AsSlice(),
|
|
||||||
Mask: net.CIDRMask(it.Bits(), it.Addr().BitLen()),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}), nil
|
|
||||||
})
|
|
||||||
if runtime.GOOS == "android" {
|
|
||||||
setAndroidProtectFunc(t.platformInterface)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := t.server.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if t.onReconfig != nil {
|
|
||||||
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
ipStack := t.server.ExportNetstack().ExportIPStack()
|
|
||||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
|
|
||||||
udpForwarder := tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout)
|
|
||||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
|
||||||
t.stack = ipStack
|
|
||||||
|
|
||||||
localBackend := t.server.ExportLocalBackend()
|
|
||||||
perfs := &ipn.MaskedPrefs{
|
|
||||||
ExitNodeIPSet: true,
|
|
||||||
AdvertiseRoutesSet: true,
|
|
||||||
}
|
|
||||||
if len(t.advertiseRoutes) > 0 {
|
|
||||||
perfs.AdvertiseRoutes = t.advertiseRoutes
|
|
||||||
}
|
|
||||||
if t.advertiseExitNode {
|
|
||||||
perfs.AdvertiseRoutes = append(perfs.AdvertiseRoutes, tsaddr.ExitRoutes()...)
|
|
||||||
}
|
|
||||||
_, err = localBackend.EditPrefs(perfs)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "update prefs")
|
|
||||||
}
|
|
||||||
t.filter = localBackend.ExportFilter()
|
|
||||||
|
|
||||||
go t.watchState()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) watchState() {
|
|
||||||
localBackend := t.server.ExportLocalBackend()
|
|
||||||
localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState, nil, func(roNotify *ipn.Notify) (keepGoing bool) {
|
|
||||||
if roNotify.State != nil && *roNotify.State != ipn.NeedsLogin && *roNotify.State != ipn.NoState {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
authURL := localBackend.StatusWithoutPeers().AuthURL
|
|
||||||
if authURL != "" {
|
|
||||||
t.logger.Info("Waiting for authentication: ", authURL)
|
|
||||||
if t.platformInterface != nil {
|
|
||||||
err := t.platformInterface.SendNotification(&platform.Notification{
|
|
||||||
Identifier: "tailscale-authentication",
|
|
||||||
TypeName: "Tailscale Authentication Notifications",
|
|
||||||
TypeID: 10,
|
|
||||||
Title: "Tailscale Authentication",
|
|
||||||
Body: F.ToString("Tailscale outbound[", t.Tag(), "] is waiting for authentication."),
|
|
||||||
OpenURL: authURL,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error("send authentication notification: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if t.exitNode != "" {
|
|
||||||
localBackend.WatchNotifications(t.ctx, ipn.NotifyInitialState, nil, func(roNotify *ipn.Notify) (keepGoing bool) {
|
|
||||||
if roNotify.State == nil || *roNotify.State != ipn.Running {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
status, err := common.Must1(t.server.LocalClient()).Status(t.ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error("set exit node: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
perfs := &ipn.MaskedPrefs{
|
|
||||||
Prefs: ipn.Prefs{
|
|
||||||
ExitNodeAllowLANAccess: t.exitNodeAllowLANAccess,
|
|
||||||
},
|
|
||||||
ExitNodeIPSet: true,
|
|
||||||
ExitNodeAllowLANAccessSet: true,
|
|
||||||
}
|
|
||||||
err = perfs.SetExitNodeIP(t.exitNode, status)
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error("set exit node: ", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
_, err = localBackend.EditPrefs(perfs)
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error("set exit node: ", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) Close() error {
|
|
||||||
netmon.RegisterInterfaceGetter(nil)
|
|
||||||
if runtime.GOOS == "android" {
|
|
||||||
setAndroidProtectFunc(nil)
|
|
||||||
}
|
|
||||||
return common.Close(common.PtrOrNil(t.server))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
switch network {
|
|
||||||
case N.NetworkTCP:
|
|
||||||
t.logger.InfoContext(ctx, "outbound connection to ", destination)
|
|
||||||
case N.NetworkUDP:
|
|
||||||
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
|
||||||
}
|
|
||||||
if destination.IsFqdn() {
|
|
||||||
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
|
|
||||||
}
|
|
||||||
addr := tcpip.FullAddress{
|
|
||||||
NIC: 1,
|
|
||||||
Port: destination.Port,
|
|
||||||
Addr: addressFromAddr(destination.Addr),
|
|
||||||
}
|
|
||||||
var networkProtocol tcpip.NetworkProtocolNumber
|
|
||||||
if destination.IsIPv4() {
|
|
||||||
networkProtocol = header.IPv4ProtocolNumber
|
|
||||||
} else {
|
|
||||||
networkProtocol = header.IPv6ProtocolNumber
|
|
||||||
}
|
|
||||||
switch N.NetworkName(network) {
|
|
||||||
case N.NetworkTCP:
|
|
||||||
tcpConn, err := gonet.DialContextTCP(ctx, t.stack, addr, networkProtocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return tcpConn, nil
|
|
||||||
case N.NetworkUDP:
|
|
||||||
udpConn, err := gonet.DialUDP(t.stack, nil, &addr, networkProtocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return udpConn, nil
|
|
||||||
default:
|
|
||||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
|
||||||
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
|
||||||
if destination.IsFqdn() {
|
|
||||||
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
packetConn, _, err := N.ListenSerial(ctx, t, destination, destinationAddresses)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return packetConn, err
|
|
||||||
}
|
|
||||||
addr4, addr6 := t.server.TailscaleIPs()
|
|
||||||
bind := tcpip.FullAddress{
|
|
||||||
NIC: 1,
|
|
||||||
}
|
|
||||||
var networkProtocol tcpip.NetworkProtocolNumber
|
|
||||||
if destination.IsIPv4() {
|
|
||||||
if !addr4.IsValid() {
|
|
||||||
return nil, E.New("missing Tailscale IPv4 address")
|
|
||||||
}
|
|
||||||
networkProtocol = header.IPv4ProtocolNumber
|
|
||||||
bind.Addr = addressFromAddr(addr4)
|
|
||||||
} else {
|
|
||||||
if !addr6.IsValid() {
|
|
||||||
return nil, E.New("missing Tailscale IPv6 address")
|
|
||||||
}
|
|
||||||
networkProtocol = header.IPv6ProtocolNumber
|
|
||||||
bind.Addr = addressFromAddr(addr6)
|
|
||||||
}
|
|
||||||
udpConn, err := gonet.DialUDP(t.stack, &bind, nil, networkProtocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return udpConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
|
||||||
tsFilter := t.filter.Load()
|
|
||||||
if tsFilter != nil {
|
|
||||||
var ipProto ipproto.Proto
|
|
||||||
switch N.NetworkName(network) {
|
|
||||||
case N.NetworkTCP:
|
|
||||||
ipProto = ipproto.TCP
|
|
||||||
case N.NetworkUDP:
|
|
||||||
ipProto = ipproto.UDP
|
|
||||||
}
|
|
||||||
response := tsFilter.Check(source.Addr, destination.Addr, destination.Port, ipProto)
|
|
||||||
switch response {
|
|
||||||
case filter.Drop:
|
|
||||||
return syscall.ECONNRESET
|
|
||||||
case filter.DropSilently:
|
|
||||||
return tun.ErrDrop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.router.PreMatch(adapter.InboundContext{
|
|
||||||
Inbound: t.Tag(),
|
|
||||||
InboundType: t.Type(),
|
|
||||||
Network: network,
|
|
||||||
Source: source,
|
|
||||||
Destination: destination,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
metadata.Inbound = t.Tag()
|
|
||||||
metadata.InboundType = t.Type()
|
|
||||||
metadata.Source = source
|
|
||||||
addr4, addr6 := t.server.TailscaleIPs()
|
|
||||||
switch destination.Addr {
|
|
||||||
case addr4:
|
|
||||||
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
|
|
||||||
case addr6:
|
|
||||||
destination.Addr = netip.IPv6Loopback()
|
|
||||||
}
|
|
||||||
metadata.Destination = destination
|
|
||||||
t.logger.InfoContext(ctx, "inbound connection from ", source)
|
|
||||||
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
|
||||||
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
metadata.Inbound = t.Tag()
|
|
||||||
metadata.InboundType = t.Type()
|
|
||||||
metadata.Source = source
|
|
||||||
metadata.Destination = destination
|
|
||||||
addr4, addr6 := t.server.TailscaleIPs()
|
|
||||||
switch destination.Addr {
|
|
||||||
case addr4:
|
|
||||||
metadata.OriginDestination = destination
|
|
||||||
destination.Addr = netip.AddrFrom4([4]uint8{127, 0, 0, 1})
|
|
||||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
|
|
||||||
case addr6:
|
|
||||||
metadata.OriginDestination = destination
|
|
||||||
destination.Addr = netip.IPv6Loopback()
|
|
||||||
conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination)
|
|
||||||
}
|
|
||||||
t.logger.InfoContext(ctx, "inbound packet connection from ", source)
|
|
||||||
t.logger.InfoContext(ctx, "inbound packet connection to ", destination)
|
|
||||||
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addressFromAddr(destination netip.Addr) tcpip.Address {
|
|
||||||
if destination.Is6() {
|
|
||||||
return tcpip.AddrFrom16(destination.As16())
|
|
||||||
} else {
|
|
||||||
return tcpip.AddrFrom4(destination.As4())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type endpointDialer struct {
|
|
||||||
N.Dialer
|
|
||||||
logger logger.ContextLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *endpointDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
switch N.NetworkName(network) {
|
|
||||||
case N.NetworkTCP:
|
|
||||||
d.logger.InfoContext(ctx, "output connection to ", destination)
|
|
||||||
case N.NetworkUDP:
|
|
||||||
d.logger.InfoContext(ctx, "output packet connection to ", destination)
|
|
||||||
}
|
|
||||||
return d.Dialer.DialContext(ctx, network, destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *endpointDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
|
||||||
d.logger.InfoContext(ctx, "output packet connection")
|
|
||||||
return d.Dialer.ListenPacket(ctx, destination)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package tailscale
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
"github.com/sagernet/tailscale/net/netns"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setAndroidProtectFunc(platformInterface platform.Interface) {
|
|
||||||
if platformInterface != nil {
|
|
||||||
netns.SetAndroidProtectFunc(func(fd int) error {
|
|
||||||
return platformInterface.AutoDetectInterfaceControl(fd)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
netns.SetAndroidProtectFunc(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package tailscale
|
|
||||||
|
|
||||||
import "github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
|
|
||||||
func setAndroidProtectFunc(platformInterface platform.Interface) {
|
|
||||||
}
|
|
||||||
@@ -312,11 +312,9 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
|
|||||||
if len(ipSets) == 0 {
|
if len(ipSets) == 0 {
|
||||||
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeRuleSet.Name())
|
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeRuleSet.Name())
|
||||||
}
|
}
|
||||||
|
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
||||||
routeRuleSet.DecRef()
|
routeRuleSet.DecRef()
|
||||||
t.routeAddressSet = append(t.routeAddressSet, ipSets...)
|
t.routeAddressSet = append(t.routeAddressSet, ipSets...)
|
||||||
if t.autoRedirect != nil {
|
|
||||||
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
|
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
|
||||||
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
|
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
|
||||||
@@ -324,11 +322,9 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
|
|||||||
if len(ipSets) == 0 {
|
if len(ipSets) == 0 {
|
||||||
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeExcludeRuleSet.Name())
|
t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeExcludeRuleSet.Name())
|
||||||
}
|
}
|
||||||
|
t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
||||||
routeExcludeRuleSet.DecRef()
|
routeExcludeRuleSet.DecRef()
|
||||||
t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...)
|
t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...)
|
||||||
if t.autoRedirect != nil {
|
|
||||||
t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -21,23 +21,31 @@ import (
|
|||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/x/list"
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.ConnectionManager = (*ConnectionManager)(nil)
|
var _ adapter.ConnectionManager = (*ConnectionManager)(nil)
|
||||||
|
|
||||||
type ConnectionManager struct {
|
type ConnectionManager struct {
|
||||||
|
ctx context.Context
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
|
mitm adapter.MITMEngine
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
connections list.List[io.Closer]
|
connections list.List[io.Closer]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnectionManager(logger logger.ContextLogger) *ConnectionManager {
|
func NewConnectionManager(ctx context.Context, logger logger.ContextLogger) *ConnectionManager {
|
||||||
return &ConnectionManager{
|
return &ConnectionManager{
|
||||||
|
ctx: ctx,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConnectionManager) Start(stage adapter.StartStage) error {
|
func (m *ConnectionManager) Start(stage adapter.StartStage) error {
|
||||||
|
switch stage {
|
||||||
|
case adapter.StartStateInitialize:
|
||||||
|
m.mitm = service.FromContext[adapter.MITMEngine](m.ctx)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +60,14 @@ func (m *ConnectionManager) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
func (m *ConnectionManager) NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
|
if metadata.MITM != nil && metadata.MITM.Enabled {
|
||||||
|
if m.mitm == nil {
|
||||||
|
m.logger.WarnContext(ctx, "MITM disabled")
|
||||||
|
} else {
|
||||||
|
m.mitm.NewConnection(ctx, this, conn, metadata, onClose)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
ctx = adapter.WithContext(ctx, &metadata)
|
||||||
var (
|
var (
|
||||||
remoteConn net.Conn
|
remoteConn net.Conn
|
||||||
|
|||||||
@@ -458,6 +458,9 @@ match:
|
|||||||
metadata.TLSFragment = true
|
metadata.TLSFragment = true
|
||||||
metadata.TLSFragmentFallbackDelay = routeOptions.TLSFragmentFallbackDelay
|
metadata.TLSFragmentFallbackDelay = routeOptions.TLSFragmentFallbackDelay
|
||||||
}
|
}
|
||||||
|
if routeOptions.MITM != nil && routeOptions.MITM.Enabled {
|
||||||
|
metadata.MITM = routeOptions.MITM
|
||||||
|
}
|
||||||
}
|
}
|
||||||
switch action := currentRule.Action().(type) {
|
switch action := currentRule.Action().(type) {
|
||||||
case *rule.RuleActionSniff:
|
case *rule.RuleActionSniff:
|
||||||
@@ -493,8 +496,12 @@ match:
|
|||||||
break match
|
break match
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !preMatch && inputPacketConn != nil && (metadata.InboundType == C.TypeSOCKS || metadata.InboundType == C.TypeMixed) && !metadata.Destination.IsFqdn() && !metadata.Destination.Addr.IsGlobalUnicast() {
|
if !preMatch && inputPacketConn != nil && !metadata.Destination.IsFqdn() && !metadata.Destination.Addr.IsGlobalUnicast() {
|
||||||
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{Timeout: C.TCPTimeout}, inputConn, inputPacketConn)
|
var timeout time.Duration
|
||||||
|
if metadata.InboundType == C.TypeSOCKS {
|
||||||
|
timeout = C.TCPTimeout
|
||||||
|
}
|
||||||
|
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{Timeout: timeout}, inputConn, inputPacketConn)
|
||||||
if newErr != nil {
|
if newErr != nil {
|
||||||
fatalErr = newErr
|
fatalErr = newErr
|
||||||
return
|
return
|
||||||
@@ -590,7 +597,7 @@ func (r *Router) actionSniff(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (metadata.InboundType == C.TypeSOCKS || metadata.InboundType == C.TypeMixed) && !metadata.Destination.IsFqdn() && !metadata.Destination.Addr.IsGlobalUnicast() && !metadata.RouteOriginalDestination.IsValid() {
|
if !metadata.Destination.Addr.IsGlobalUnicast() && !metadata.RouteOriginalDestination.IsValid() {
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
}
|
}
|
||||||
if len(packetBuffers) > 0 {
|
if len(packetBuffers) > 0 {
|
||||||
@@ -626,7 +633,7 @@ func (r *Router) actionSniff(
|
|||||||
Destination: destination,
|
Destination: destination,
|
||||||
}
|
}
|
||||||
packetBuffers = append(packetBuffers, packetBuffer)
|
packetBuffers = append(packetBuffers, packetBuffer)
|
||||||
if E.IsMulti(err, sniff.ErrClientHelloFragmented) {
|
if E.IsMulti(err, sniff.ErrClientHelloFragmented) && len(packetBuffers) == 0 {
|
||||||
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
|
r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
|||||||
UDPConnect: action.RouteOptions.UDPConnect,
|
UDPConnect: action.RouteOptions.UDPConnect,
|
||||||
TLSFragment: action.RouteOptions.TLSFragment,
|
TLSFragment: action.RouteOptions.TLSFragment,
|
||||||
TLSFragmentFallbackDelay: time.Duration(action.RouteOptions.TLSFragmentFallbackDelay),
|
TLSFragmentFallbackDelay: time.Duration(action.RouteOptions.TLSFragmentFallbackDelay),
|
||||||
|
MITM: action.RouteOptions.MITM,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
case C.RuleActionTypeRouteOptions:
|
case C.RuleActionTypeRouteOptions:
|
||||||
@@ -51,6 +52,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
|||||||
UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
|
UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
|
||||||
TLSFragment: action.RouteOptionsOptions.TLSFragment,
|
TLSFragment: action.RouteOptionsOptions.TLSFragment,
|
||||||
TLSFragmentFallbackDelay: time.Duration(action.RouteOptionsOptions.TLSFragmentFallbackDelay),
|
TLSFragmentFallbackDelay: time.Duration(action.RouteOptionsOptions.TLSFragmentFallbackDelay),
|
||||||
|
MITM: action.RouteOptionsOptions.MITM,
|
||||||
}, nil
|
}, nil
|
||||||
case C.RuleActionTypeDirect:
|
case C.RuleActionTypeDirect:
|
||||||
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false)
|
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false)
|
||||||
@@ -140,15 +142,7 @@ func (r *RuleActionRoute) Type() string {
|
|||||||
func (r *RuleActionRoute) String() string {
|
func (r *RuleActionRoute) String() string {
|
||||||
var descriptions []string
|
var descriptions []string
|
||||||
descriptions = append(descriptions, r.Outbound)
|
descriptions = append(descriptions, r.Outbound)
|
||||||
if r.UDPDisableDomainUnmapping {
|
descriptions = append(descriptions, r.Descriptions()...)
|
||||||
descriptions = append(descriptions, "udp-disable-domain-unmapping")
|
|
||||||
}
|
|
||||||
if r.UDPConnect {
|
|
||||||
descriptions = append(descriptions, "udp-connect")
|
|
||||||
}
|
|
||||||
if r.TLSFragment {
|
|
||||||
descriptions = append(descriptions, "tls-fragment")
|
|
||||||
}
|
|
||||||
return F.ToString("route(", strings.Join(descriptions, ","), ")")
|
return F.ToString("route(", strings.Join(descriptions, ","), ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,13 +158,14 @@ type RuleActionRouteOptions struct {
|
|||||||
UDPTimeout time.Duration
|
UDPTimeout time.Duration
|
||||||
TLSFragment bool
|
TLSFragment bool
|
||||||
TLSFragmentFallbackDelay time.Duration
|
TLSFragmentFallbackDelay time.Duration
|
||||||
|
MITM *option.MITMRouteOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleActionRouteOptions) Type() string {
|
func (r *RuleActionRouteOptions) Type() string {
|
||||||
return C.RuleActionTypeRouteOptions
|
return C.RuleActionTypeRouteOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleActionRouteOptions) String() string {
|
func (r *RuleActionRouteOptions) Descriptions() []string {
|
||||||
var descriptions []string
|
var descriptions []string
|
||||||
if r.OverrideAddress.IsValid() {
|
if r.OverrideAddress.IsValid() {
|
||||||
descriptions = append(descriptions, F.ToString("override-address=", r.OverrideAddress.AddrString()))
|
descriptions = append(descriptions, F.ToString("override-address=", r.OverrideAddress.AddrString()))
|
||||||
@@ -197,9 +192,22 @@ func (r *RuleActionRouteOptions) String() string {
|
|||||||
descriptions = append(descriptions, "udp-connect")
|
descriptions = append(descriptions, "udp-connect")
|
||||||
}
|
}
|
||||||
if r.UDPTimeout > 0 {
|
if r.UDPTimeout > 0 {
|
||||||
descriptions = append(descriptions, "udp-timeout")
|
descriptions = append(descriptions, F.ToString("udp-timeout=", r.UDPTimeout))
|
||||||
}
|
}
|
||||||
return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
|
if r.TLSFragment {
|
||||||
|
descriptions = append(descriptions, "tls-fragment")
|
||||||
|
if r.TLSFragmentFallbackDelay > 0 {
|
||||||
|
descriptions = append(descriptions, F.ToString("tls-fragment-fallbac-delay=", r.TLSFragmentFallbackDelay.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.MITM != nil && r.MITM.Enabled {
|
||||||
|
descriptions = append(descriptions, "mitm")
|
||||||
|
}
|
||||||
|
return descriptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RuleActionRouteOptions) String() string {
|
||||||
|
return F.ToString("route-options(", strings.Join(r.Descriptions(), ","), ")")
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleActionDNSRoute struct {
|
type RuleActionDNSRoute struct {
|
||||||
|
|||||||
@@ -307,10 +307,8 @@ func (s *RemoteRuleSet) fetchOnce(ctx context.Context, startContext *adapter.HTT
|
|||||||
|
|
||||||
func (s *RemoteRuleSet) Close() error {
|
func (s *RemoteRuleSet) Close() error {
|
||||||
s.rules = nil
|
s.rules = nil
|
||||||
|
s.updateTicker.Stop()
|
||||||
s.cancel()
|
s.cancel()
|
||||||
if s.updateTicker != nil {
|
|
||||||
s.updateTicker.Stop()
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
script/jsc/array.go
Normal file
23
script/jsc/array.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package jsc
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewUint8Array(runtime *goja.Runtime, data []byte) goja.Value {
|
||||||
|
buffer := runtime.NewArrayBuffer(data)
|
||||||
|
ctor, loaded := goja.AssertConstructor(runtimeGetUint8Array(runtime))
|
||||||
|
if !loaded {
|
||||||
|
panic(runtime.NewTypeError("missing UInt8Array constructor"))
|
||||||
|
}
|
||||||
|
array, err := ctor(nil, runtime.ToValue(buffer))
|
||||||
|
if err != nil {
|
||||||
|
panic(runtime.NewGoError(err))
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname runtimeGetUint8Array github.com/dop251/goja.(*Runtime).getUint8Array
|
||||||
|
func runtimeGetUint8Array(r *goja.Runtime) *goja.Object
|
||||||
18
script/jsc/array_test.go
Normal file
18
script/jsc/array_test.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package jsc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/script/jsc"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewUInt8Array(t *testing.T) {
|
||||||
|
runtime := goja.New()
|
||||||
|
runtime.Set("hello", jsc.NewUint8Array(runtime, []byte("world")))
|
||||||
|
result, err := runtime.RunString("hello instanceof Uint8Array")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, result.ToBoolean())
|
||||||
|
}
|
||||||
124
script/jsc/assert.go
Normal file
124
script/jsc/assert.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package jsc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsNil(value goja.Value) bool {
|
||||||
|
return value == nil || goja.IsUndefined(value) || goja.IsNull(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertObject(vm *goja.Runtime, value goja.Value, name string, nilable bool) *goja.Object {
|
||||||
|
if IsNil(value) {
|
||||||
|
if nilable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: missing ", name)))
|
||||||
|
}
|
||||||
|
objectValue, isObject := value.(*goja.Object)
|
||||||
|
if !isObject {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: ", name, ": expected object, but got ", value)))
|
||||||
|
}
|
||||||
|
return objectValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertString(vm *goja.Runtime, value goja.Value, name string, nilable bool) string {
|
||||||
|
if IsNil(value) {
|
||||||
|
if nilable {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: missing ", name)))
|
||||||
|
}
|
||||||
|
stringValue, isString := value.Export().(string)
|
||||||
|
if !isString {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: ", name, ": expected string, but got ", value)))
|
||||||
|
}
|
||||||
|
return stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertInt(vm *goja.Runtime, value goja.Value, name string, nilable bool) int64 {
|
||||||
|
if IsNil(value) {
|
||||||
|
if nilable {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: missing ", name)))
|
||||||
|
}
|
||||||
|
integerValue, isNumber := value.Export().(int64)
|
||||||
|
if !isNumber {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: ", name, ": expected integer, but got ", value)))
|
||||||
|
}
|
||||||
|
return integerValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertBool(vm *goja.Runtime, value goja.Value, name string, nilable bool) bool {
|
||||||
|
if IsNil(value) {
|
||||||
|
if nilable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: missing ", name)))
|
||||||
|
}
|
||||||
|
boolValue, isBool := value.Export().(bool)
|
||||||
|
if !isBool {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: ", name, ": expected boolean, but got ", value)))
|
||||||
|
}
|
||||||
|
return boolValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertBinary(vm *goja.Runtime, value goja.Value, name string, nilable bool) []byte {
|
||||||
|
if IsNil(value) {
|
||||||
|
if nilable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: missing ", name)))
|
||||||
|
}
|
||||||
|
switch exportedValue := value.Export().(type) {
|
||||||
|
case []byte:
|
||||||
|
return exportedValue
|
||||||
|
case goja.ArrayBuffer:
|
||||||
|
return exportedValue.Bytes()
|
||||||
|
default:
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: ", name, ": expected Uint8Array or ArrayBuffer, but got ", value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertStringBinary(vm *goja.Runtime, value goja.Value, name string, nilable bool) []byte {
|
||||||
|
if IsNil(value) {
|
||||||
|
if nilable {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: missing ", name)))
|
||||||
|
}
|
||||||
|
switch exportedValue := value.Export().(type) {
|
||||||
|
case string:
|
||||||
|
return []byte(exportedValue)
|
||||||
|
case []byte:
|
||||||
|
return exportedValue
|
||||||
|
case goja.ArrayBuffer:
|
||||||
|
return exportedValue.Bytes()
|
||||||
|
default:
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: ", name, ": expected string, Uint8Array or ArrayBuffer, but got ", value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertFunction(vm *goja.Runtime, value goja.Value, name string) goja.Callable {
|
||||||
|
if IsNil(value) {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: missing ", name)))
|
||||||
|
}
|
||||||
|
functionValue, isFunction := goja.AssertFunction(value)
|
||||||
|
if !isFunction {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid argument: ", name, ": expected function, but got ", value)))
|
||||||
|
}
|
||||||
|
return functionValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertHTTPHeader(vm *goja.Runtime, value goja.Value, name string) http.Header {
|
||||||
|
headersObject := AssertObject(vm, value, name, true)
|
||||||
|
if headersObject == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ObjectToHeaders(vm, headersObject, name)
|
||||||
|
}
|
||||||
192
script/jsc/class.go
Normal file
192
script/jsc/class.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
package jsc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Module interface {
|
||||||
|
Runtime() *goja.Runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
type Class[M Module, C any] interface {
|
||||||
|
Module() M
|
||||||
|
Runtime() *goja.Runtime
|
||||||
|
DefineField(name string, getter func(this C) any, setter func(this C, value goja.Value))
|
||||||
|
DefineMethod(name string, method func(this C, call goja.FunctionCall) any)
|
||||||
|
DefineStaticMethod(name string, method func(c Class[M, C], call goja.FunctionCall) any)
|
||||||
|
DefineConstructor(constructor func(c Class[M, C], call goja.ConstructorCall) C)
|
||||||
|
ToValue() goja.Value
|
||||||
|
New(instance C) *goja.Object
|
||||||
|
Prototype() *goja.Object
|
||||||
|
Is(value goja.Value) bool
|
||||||
|
As(value goja.Value) C
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClass[M Module, C any](runtime *goja.Runtime, exports *goja.Object, className string) Class[M, C] {
|
||||||
|
objectValue := exports.Get(className)
|
||||||
|
if objectValue == nil {
|
||||||
|
panic(runtime.NewTypeError("Missing class: " + className))
|
||||||
|
}
|
||||||
|
object, isObject := objectValue.(*goja.Object)
|
||||||
|
if !isObject {
|
||||||
|
panic(runtime.NewTypeError("Invalid class: " + className))
|
||||||
|
}
|
||||||
|
classObject, isClass := object.Get("_class").(*goja.Object)
|
||||||
|
if !isClass {
|
||||||
|
panic(runtime.NewTypeError("Invalid class: " + className))
|
||||||
|
}
|
||||||
|
class, isClass := classObject.Export().(Class[M, C])
|
||||||
|
if !isClass {
|
||||||
|
panic(runtime.NewTypeError("Invalid class: " + className))
|
||||||
|
}
|
||||||
|
return class
|
||||||
|
}
|
||||||
|
|
||||||
|
type goClass[M Module, C any] struct {
|
||||||
|
m M
|
||||||
|
prototype *goja.Object
|
||||||
|
constructor goja.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClass[M Module, C any](module M) Class[M, C] {
|
||||||
|
class := &goClass[M, C]{
|
||||||
|
m: module,
|
||||||
|
prototype: module.Runtime().NewObject(),
|
||||||
|
}
|
||||||
|
clazz := module.Runtime().ToValue(class).(*goja.Object)
|
||||||
|
clazz.Set("toString", module.Runtime().ToValue(func(call goja.FunctionCall) goja.Value {
|
||||||
|
return module.Runtime().ToValue("[sing-box Class]")
|
||||||
|
}))
|
||||||
|
class.prototype.DefineAccessorProperty("_class", class.Runtime().ToValue(func(call goja.FunctionCall) goja.Value { return clazz }), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)
|
||||||
|
return class
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) Module() M {
|
||||||
|
return c.m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) Runtime() *goja.Runtime {
|
||||||
|
return c.m.Runtime()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) DefineField(name string, getter func(this C) any, setter func(this C, value goja.Value)) {
|
||||||
|
var (
|
||||||
|
getterValue goja.Value
|
||||||
|
setterValue goja.Value
|
||||||
|
)
|
||||||
|
if getter != nil {
|
||||||
|
getterValue = c.Runtime().ToValue(func(call goja.FunctionCall) goja.Value {
|
||||||
|
this, isThis := call.This.Export().(C)
|
||||||
|
if !isThis {
|
||||||
|
panic(c.Runtime().NewTypeError("Illegal this value: " + call.This.ExportType().String()))
|
||||||
|
}
|
||||||
|
return c.toValue(getter(this), goja.Null())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if setter != nil {
|
||||||
|
setterValue = c.Runtime().ToValue(func(call goja.FunctionCall) goja.Value {
|
||||||
|
this, isThis := call.This.Export().(C)
|
||||||
|
if !isThis {
|
||||||
|
panic(c.Runtime().NewTypeError("Illegal this value: " + call.This.String()))
|
||||||
|
}
|
||||||
|
setter(this, call.Argument(0))
|
||||||
|
return goja.Undefined()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
c.prototype.DefineAccessorProperty(name, getterValue, setterValue, goja.FLAG_FALSE, goja.FLAG_TRUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) DefineMethod(name string, method func(this C, call goja.FunctionCall) any) {
|
||||||
|
methodValue := c.Runtime().ToValue(func(call goja.FunctionCall) goja.Value {
|
||||||
|
this, isThis := call.This.Export().(C)
|
||||||
|
if !isThis {
|
||||||
|
panic(c.Runtime().NewTypeError("Illegal this value: " + call.This.String()))
|
||||||
|
}
|
||||||
|
return c.toValue(method(this, call), goja.Undefined())
|
||||||
|
})
|
||||||
|
c.prototype.Set(name, methodValue)
|
||||||
|
if name == "entries" {
|
||||||
|
c.prototype.DefineDataPropertySymbol(goja.SymIterator, methodValue, goja.FLAG_TRUE, goja.FLAG_FALSE, goja.FLAG_TRUE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) DefineStaticMethod(name string, method func(c Class[M, C], call goja.FunctionCall) any) {
|
||||||
|
c.prototype.Set(name, c.Runtime().ToValue(func(call goja.FunctionCall) goja.Value {
|
||||||
|
return c.toValue(method(c, call), goja.Undefined())
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) DefineConstructor(constructor func(c Class[M, C], call goja.ConstructorCall) C) {
|
||||||
|
constructorObject := c.Runtime().ToValue(func(call goja.ConstructorCall) *goja.Object {
|
||||||
|
value := constructor(c, call)
|
||||||
|
object := c.toValue(value, goja.Undefined()).(*goja.Object)
|
||||||
|
object.SetPrototype(call.This.Prototype())
|
||||||
|
return object
|
||||||
|
}).(*goja.Object)
|
||||||
|
constructorObject.SetPrototype(c.prototype)
|
||||||
|
c.prototype.DefineDataProperty("constructor", constructorObject, goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_FALSE)
|
||||||
|
c.constructor = constructorObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) toValue(rawValue any, defaultValue goja.Value) goja.Value {
|
||||||
|
switch value := rawValue.(type) {
|
||||||
|
case nil:
|
||||||
|
return defaultValue
|
||||||
|
case time.Time:
|
||||||
|
return TimeToValue(c.Runtime(), value)
|
||||||
|
default:
|
||||||
|
return c.Runtime().ToValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) ToValue() goja.Value {
|
||||||
|
if c.constructor == nil {
|
||||||
|
constructorObject := c.Runtime().ToValue(func(call goja.ConstructorCall) *goja.Object {
|
||||||
|
panic(c.Runtime().NewTypeError("Illegal constructor call"))
|
||||||
|
}).(*goja.Object)
|
||||||
|
constructorObject.SetPrototype(c.prototype)
|
||||||
|
c.prototype.DefineDataProperty("constructor", constructorObject, goja.FLAG_FALSE, goja.FLAG_FALSE, goja.FLAG_FALSE)
|
||||||
|
c.constructor = constructorObject
|
||||||
|
}
|
||||||
|
return c.constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) New(instance C) *goja.Object {
|
||||||
|
object := c.Runtime().ToValue(instance).(*goja.Object)
|
||||||
|
object.SetPrototype(c.prototype)
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) Prototype() *goja.Object {
|
||||||
|
return c.prototype
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) Is(value goja.Value) bool {
|
||||||
|
object, isObject := value.(*goja.Object)
|
||||||
|
if !isObject {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
prototype := object.Prototype()
|
||||||
|
for prototype != nil {
|
||||||
|
if prototype == c.prototype {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
prototype = prototype.Prototype()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goClass[M, C]) As(value goja.Value) C {
|
||||||
|
object, isObject := value.(*goja.Object)
|
||||||
|
if !isObject {
|
||||||
|
return common.DefaultValue[C]()
|
||||||
|
}
|
||||||
|
if !c.Is(object) {
|
||||||
|
return common.DefaultValue[C]()
|
||||||
|
}
|
||||||
|
return object.Export().(C)
|
||||||
|
}
|
||||||
56
script/jsc/headers.go
Normal file
56
script/jsc/headers.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package jsc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HeadersToValue(runtime *goja.Runtime, headers http.Header) goja.Value {
|
||||||
|
object := runtime.NewObject()
|
||||||
|
for key, value := range headers {
|
||||||
|
if len(value) == 1 {
|
||||||
|
object.Set(key, value[0])
|
||||||
|
} else {
|
||||||
|
object.Set(key, ArrayToValue(runtime, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
|
||||||
|
func ArrayToValue[T any](runtime *goja.Runtime, values []T) goja.Value {
|
||||||
|
return runtime.NewArray(common.Map(values, func(it T) any { return it })...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjectToHeaders(vm *goja.Runtime, object *goja.Object, name string) http.Header {
|
||||||
|
headers := make(http.Header)
|
||||||
|
for _, key := range object.Keys() {
|
||||||
|
valueObject := object.Get(key)
|
||||||
|
switch headerValue := valueObject.(type) {
|
||||||
|
case goja.String:
|
||||||
|
headers.Set(key, headerValue.String())
|
||||||
|
case *goja.Object:
|
||||||
|
values := headerValue.Export()
|
||||||
|
valueArray, isArray := values.([]any)
|
||||||
|
if !isArray {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid value: ", name, ".", key, "expected string or string array, got ", valueObject.String())))
|
||||||
|
}
|
||||||
|
newValues := make([]string, 0, len(valueArray))
|
||||||
|
for _, value := range valueArray {
|
||||||
|
stringValue, isString := value.(string)
|
||||||
|
if !isString {
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid value: ", name, ".", key, " expected string or string array, got array item type: ", reflect.TypeOf(value))))
|
||||||
|
}
|
||||||
|
newValues = append(newValues, stringValue)
|
||||||
|
}
|
||||||
|
headers[key] = newValues
|
||||||
|
default:
|
||||||
|
panic(vm.NewTypeError(F.ToString("invalid value: ", name, ".", key, " expected string or string array, got ", valueObject.String())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
31
script/jsc/headers_test.go
Normal file
31
script/jsc/headers_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package jsc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/script/jsc"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHeaders(t *testing.T) {
|
||||||
|
runtime := goja.New()
|
||||||
|
runtime.Set("headers", jsc.HeadersToValue(runtime, http.Header{
|
||||||
|
"My-Header": []string{"My-Value1", "My-Value2"},
|
||||||
|
}))
|
||||||
|
headers := runtime.Get("headers").(*goja.Object).Get("My-Header").(*goja.Object)
|
||||||
|
fmt.Println(reflect.ValueOf(headers.Export()).Type().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBody(t *testing.T) {
|
||||||
|
runtime := goja.New()
|
||||||
|
_, err := runtime.RunString(`
|
||||||
|
var responseBody = new Uint8Array([1, 2, 3, 4, 5])
|
||||||
|
`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fmt.Println(reflect.TypeOf(runtime.Get("responseBody").Export()))
|
||||||
|
}
|
||||||
36
script/jsc/iterator.go
Normal file
36
script/jsc/iterator.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package jsc
|
||||||
|
|
||||||
|
import "github.com/dop251/goja"
|
||||||
|
|
||||||
|
type Iterator[M Module, T any] struct {
|
||||||
|
c Class[M, *Iterator[M, T]]
|
||||||
|
values []T
|
||||||
|
block func(this T) any
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIterator[M Module, T any](class Class[M, *Iterator[M, T]], values []T, block func(this T) any) goja.Value {
|
||||||
|
return class.New(&Iterator[M, T]{class, values, block})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateIterator[M Module, T any](module M) Class[M, *Iterator[M, T]] {
|
||||||
|
class := NewClass[M, *Iterator[M, T]](module)
|
||||||
|
class.DefineMethod("next", (*Iterator[M, T]).next)
|
||||||
|
class.DefineMethod("toString", (*Iterator[M, T]).toString)
|
||||||
|
return class
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iterator[M, T]) next(call goja.FunctionCall) any {
|
||||||
|
result := i.c.Runtime().NewObject()
|
||||||
|
if len(i.values) == 0 {
|
||||||
|
result.Set("done", true)
|
||||||
|
} else {
|
||||||
|
result.Set("done", false)
|
||||||
|
result.Set("value", i.block(i.values[0]))
|
||||||
|
i.values = i.values[1:]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iterator[M, T]) toString(call goja.FunctionCall) any {
|
||||||
|
return "[sing-box Iterator]"
|
||||||
|
}
|
||||||
18
script/jsc/time.go
Normal file
18
script/jsc/time.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package jsc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/dop251/goja"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TimeToValue(runtime *goja.Runtime, time time.Time) goja.Value {
|
||||||
|
return runtimeNewDateObject(runtime, time, true, runtimeGetDatePrototype(runtime))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname runtimeNewDateObject github.com/dop251/goja.(*Runtime).newDateObject
|
||||||
|
func runtimeNewDateObject(r *goja.Runtime, t time.Time, isSet bool, proto *goja.Object) *goja.Object
|
||||||
|
|
||||||
|
//go:linkname runtimeGetDatePrototype github.com/dop251/goja.(*Runtime).getDatePrototype
|
||||||
|
func runtimeGetDatePrototype(r *goja.Runtime) *goja.Object
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user