mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 10:07:20 +10:00
Compare commits
67 Commits
v1.3.0
...
v1.4.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a45707eb7 | ||
|
|
2b4787cd5d | ||
|
|
7666dba634 | ||
|
|
c4c1e1197a | ||
|
|
5f5b3e278c | ||
|
|
3983f88a55 | ||
|
|
4925553aa3 | ||
|
|
422bee9078 | ||
|
|
ea2cf051ff | ||
|
|
5b00f66533 | ||
|
|
825952e0bb | ||
|
|
43107187cd | ||
|
|
4dce25c6cc | ||
|
|
408903faab | ||
|
|
58413bc010 | ||
|
|
298d3cc054 | ||
|
|
6ec8ff79b5 | ||
|
|
2675aff98a | ||
|
|
09ffa2c66e | ||
|
|
9fba4f02b6 | ||
|
|
59987747e5 | ||
|
|
c40140bbae | ||
|
|
2123b216c0 | ||
|
|
1983f54907 | ||
|
|
8d629ef323 | ||
|
|
f57bee2f4b | ||
|
|
679739683e | ||
|
|
4fcce1f073 | ||
|
|
ff14220e08 | ||
|
|
a7b7a5c3c5 | ||
|
|
b054441f34 | ||
|
|
1e31d26e03 | ||
|
|
ffe515d0e0 | ||
|
|
aad021f521 | ||
|
|
4a986459ee | ||
|
|
9532d0cba4 | ||
|
|
cadc34f3ad | ||
|
|
db23a48b36 | ||
|
|
407cf68e59 | ||
|
|
e0058ca9c5 | ||
|
|
8140af01aa | ||
|
|
98bf696d01 | ||
|
|
e075bb5c8d | ||
|
|
c6baabedef | ||
|
|
6e6998dab7 | ||
|
|
1a29c23263 | ||
|
|
0f87396ab6 | ||
|
|
ffde948860 | ||
|
|
69b5dbdcc3 | ||
|
|
1121517755 | ||
|
|
6879def619 | ||
|
|
5c0f6d0a6f | ||
|
|
d74abbd20e | ||
|
|
120dae4eed | ||
|
|
bb651db2d2 | ||
|
|
e929dde13e | ||
|
|
9d75385bbb | ||
|
|
1c526feec1 | ||
|
|
7df26986de | ||
|
|
5f2d23a12d | ||
|
|
d9e65c0969 | ||
|
|
ec1160924f | ||
|
|
230e8f895d | ||
|
|
af79378734 | ||
|
|
07ce5e0d22 | ||
|
|
9c8565cf21 | ||
|
|
5ad0ea2b5a |
109
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
109
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,70 +1,77 @@
|
||||
name: Bug Report
|
||||
description: "Create a report to help us improve."
|
||||
name: Bug report
|
||||
description: "Report sing-box bug"
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Welcome
|
||||
label: Operating system
|
||||
description: Operating system type
|
||||
options:
|
||||
- label: Yes, I'm using the latest major release. Only such installations are supported.
|
||||
required: true
|
||||
- label: Yes, I'm using the latest Golang release. Only such installations are supported.
|
||||
required: true
|
||||
- label: Yes, I've searched similar issues on GitHub and didn't find any.
|
||||
required: true
|
||||
- label: Yes, I've included all information below (version, **FULL** config, **FULL** log, etc).
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Description of the problem
|
||||
placeholder: Your problem description
|
||||
- iOS
|
||||
- macOS
|
||||
- Apple tvOS
|
||||
- Android
|
||||
- Windows
|
||||
- Linux
|
||||
- Others
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: version
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version of sing-box
|
||||
label: System version
|
||||
description: Please provide the operating system version
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Installation type
|
||||
description: Please provide the sing-box installation type
|
||||
options:
|
||||
- Original sing-box Command Line
|
||||
- sing-box for iOS Graphical Client
|
||||
- sing-box for macOS Graphical Client
|
||||
- sing-box for Apple tvOS Graphical Client
|
||||
- sing-box for Android Graphical Client
|
||||
- Third-party graphical clients that advertise themselves as using sing-box (Windows)
|
||||
- Third-party graphical clients that advertise themselves as using sing-box (Android)
|
||||
- Others
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
description: Graphical client version
|
||||
label: If you are using a graphical client, please provide the version of the client.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Version
|
||||
description: If you are using the original command line program, please provide the output of the `sing-box version` command.
|
||||
value: |-
|
||||
<details>
|
||||
|
||||
```console
|
||||
$ sing-box version
|
||||
# Paste output here
|
||||
# Replace this line with the output
|
||||
```
|
||||
|
||||
</details>
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please provide a detailed description of the error.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
label: Server and client configuration file
|
||||
label: Reproduction
|
||||
description: Please provide the steps to reproduce the error, including the configuration files and procedures that can locally (not dependent on the remote server) reproduce the error using the original command line program of sing-box.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Logs
|
||||
description: |-
|
||||
If you encounter a crash with the graphical client, please provide crash logs.
|
||||
For Apple platform clients, please check `Settings - View Service Log` for crash logs.
|
||||
For the Android client, please check the `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` file for crash logs.
|
||||
value: |-
|
||||
<details>
|
||||
|
||||
```console
|
||||
# paste json here
|
||||
# Replace this line with logs
|
||||
```
|
||||
|
||||
</details>
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Server and client log file
|
||||
value: |-
|
||||
<details>
|
||||
|
||||
```console
|
||||
# paste log here
|
||||
```
|
||||
|
||||
</details>
|
||||
validations:
|
||||
required: true
|
||||
</details>
|
||||
77
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
Normal file
77
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: 错误反馈
|
||||
description: "提交 sing-box 漏洞"
|
||||
body:
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: 操作系统
|
||||
description: 请提供操作系统类型
|
||||
options:
|
||||
- iOS
|
||||
- macOS
|
||||
- Apple tvOS
|
||||
- Android
|
||||
- Windows
|
||||
- Linux
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: 系统版本
|
||||
description: 请提供操作系统版本
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: 安装类型
|
||||
description: 请提供该 sing-box 安装类型
|
||||
options:
|
||||
- sing-box 原始命令行程序
|
||||
- sing-box for iOS 图形客户端程序
|
||||
- sing-box for macOS 图形客户端程序
|
||||
- sing-box for Apple tvOS 图形客户端程序
|
||||
- sing-box for Android 图形客户端程序
|
||||
- 宣传使用 sing-box 的第三方图形客户端程序 (Windows)
|
||||
- 宣传使用 sing-box 的第三方图形客户端程序 (Android)
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
description: 图形客户端版本
|
||||
label: 如果您使用图形客户端程序,请提供该程序版本。
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 版本
|
||||
description: 如果您使用原始命令行程序,请提供 `sing-box version` 命令的输出。
|
||||
value: |-
|
||||
<details>
|
||||
```console
|
||||
# 使用输出内容覆盖此行
|
||||
```
|
||||
</details>
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请提供错误的详细描述。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 重现方式
|
||||
description: 请提供重现错误的步骤,必须包括可以在本地(不依赖与远程服务器)使用 sing-box 原始命令行程序重现错误的配置文件与流程。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 日志
|
||||
description: |-
|
||||
如果您遭遇图形界面应用程序崩溃,请提供崩溃日志。
|
||||
对于 Apple 平台图形客户端程序,请检查 `Settings - View Service Log` 以导出崩溃日志。
|
||||
对于 Android 图形客户端程序,请检查 `/sdcard/Android/data/io.nekohasekai.sfa/files/stderr.log` 文件以导出崩溃日志。
|
||||
value: |-
|
||||
<details>
|
||||
```console
|
||||
# 使用日志内容覆盖此行
|
||||
```
|
||||
</details>
|
||||
22
.github/workflows/debug.yml
vendored
22
.github/workflows/debug.yml
vendored
@@ -62,7 +62,27 @@ jobs:
|
||||
~/go/pkg/mod
|
||||
key: go118-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: make
|
||||
run: make ci_build_go118
|
||||
build_go120:
|
||||
name: Debug build (Go 1.20)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.7
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go118-${{ hashFiles('**/go.sum') }}
|
||||
- name: Run Test
|
||||
run: make ci_build
|
||||
cross:
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.20-alpine AS builder
|
||||
FROM golang:1.21-alpine AS builder
|
||||
LABEL maintainer="nekohasekai <contact-git@sekai.icu>"
|
||||
COPY . /go/src/github.com/sagernet/sing-box
|
||||
WORKDIR /go/src/github.com/sagernet/sing-box
|
||||
|
||||
18
Makefile
18
Makefile
@@ -1,20 +1,30 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_reality_server,with_clash_api
|
||||
TAGS_GO118 = with_gvisor,with_dhcp,with_wireguard,with_utls,with_reality_server,with_clash_api
|
||||
TAGS_GO120 ?= with_quic
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server,with_shadowsocksr
|
||||
|
||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||
VERSION=$(shell CGO_ENABLED=0 GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) go run ./cmd/internal/read_tag)
|
||||
|
||||
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid="
|
||||
PARAMS = -v -trimpath -ldflags "-X 'github.com/sagernet/sing-box/constant.Version=$(VERSION)' -s -w -buildid="
|
||||
MAIN_PARAMS = $(PARAMS) -tags "$(TAGS_GO118),$(TAGS_GO120)"
|
||||
MAIN = ./cmd/sing-box
|
||||
PREFIX ?= $(shell go env GOPATH)
|
||||
|
||||
.PHONY: test release
|
||||
|
||||
build:
|
||||
go build $(MAIN_PARAMS) $(MAIN)
|
||||
|
||||
ci_build_go118:
|
||||
go build $(PARAMS) $(MAIN)
|
||||
go build $(PARAMS) -tags "$(TAGS_GO118)" $(MAIN)
|
||||
|
||||
ci_build:
|
||||
go build $(PARAMS) $(MAIN)
|
||||
go build $(MAIN_PARAMS) $(MAIN)
|
||||
|
||||
install:
|
||||
go build -o $(PREFIX)/bin/$(NAME) $(PARAMS) $(MAIN)
|
||||
@@ -89,8 +99,8 @@ lib:
|
||||
|
||||
lib_install:
|
||||
go get -v -d
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230413023804-244d7ff07035
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230413023804-244d7ff07035
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230728014906-3de089147f59
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230728014906-3de089147f59
|
||||
|
||||
clean:
|
||||
rm -rf bin dist sing-box
|
||||
|
||||
@@ -23,6 +23,8 @@ type ClashServer interface {
|
||||
type ClashCacheFile interface {
|
||||
LoadSelected(group string) string
|
||||
StoreSelected(group string, selected string) error
|
||||
LoadGroupExpand(group string) (isExpand bool, loaded bool)
|
||||
StoreGroupExpand(group string, expand bool) error
|
||||
FakeIPStorage
|
||||
}
|
||||
|
||||
@@ -31,6 +33,7 @@ type Tracker interface {
|
||||
}
|
||||
|
||||
type OutboundGroup interface {
|
||||
Outbound
|
||||
Now() string
|
||||
All() []string
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type FakeIPStore interface {
|
||||
Service
|
||||
Contains(address netip.Addr) bool
|
||||
Create(domain string, strategy dns.DomainStrategy) (netip.Addr, error)
|
||||
Create(domain string, isIPv6 bool) (netip.Addr, error)
|
||||
Lookup(address netip.Addr) (string, bool)
|
||||
Reset() error
|
||||
}
|
||||
@@ -17,8 +18,11 @@ type FakeIPStore interface {
|
||||
type FakeIPStorage interface {
|
||||
FakeIPMetadata() *FakeIPMetadata
|
||||
FakeIPSaveMetadata(metadata *FakeIPMetadata) error
|
||||
FakeIPSaveMetadataAsync(metadata *FakeIPMetadata)
|
||||
FakeIPStore(address netip.Addr, domain string) error
|
||||
FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger)
|
||||
FakeIPLoad(address netip.Addr) (string, bool)
|
||||
FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bool)
|
||||
FakeIPReset() error
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,6 @@ type PreStarter interface {
|
||||
PreStart() error
|
||||
}
|
||||
|
||||
func PreStart(starter any) error {
|
||||
if preService, ok := starter.(PreStarter); ok {
|
||||
err := preService.PreStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
type PostStarter interface {
|
||||
PostStart() error
|
||||
}
|
||||
|
||||
@@ -85,5 +85,5 @@ type DNSRule interface {
|
||||
}
|
||||
|
||||
type InterfaceUpdateListener interface {
|
||||
InterfaceUpdated() error
|
||||
InterfaceUpdated()
|
||||
}
|
||||
|
||||
33
box.go
33
box.go
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
)
|
||||
|
||||
var _ adapter.Service = (*Box)(nil)
|
||||
@@ -46,12 +47,13 @@ func New(options Options) (*Box, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
ctx = pause.ContextWithDefaultManager(ctx)
|
||||
createdAt := time.Now()
|
||||
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
||||
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
|
||||
var needClashAPI bool
|
||||
var needV2RayAPI bool
|
||||
if experimentalOptions.ClashAPI != nil && experimentalOptions.ClashAPI.ExternalController != "" {
|
||||
if experimentalOptions.ClashAPI != nil || options.PlatformInterface != nil {
|
||||
needClashAPI = true
|
||||
}
|
||||
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
||||
@@ -143,7 +145,7 @@ func New(options Options) (*Box, error) {
|
||||
preServices := make(map[string]adapter.Service)
|
||||
postServices := make(map[string]adapter.Service)
|
||||
if needClashAPI {
|
||||
clashServer, err := experimental.NewClashServer(ctx, router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
|
||||
clashServer, err := experimental.NewClashServer(ctx, router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(experimentalOptions.ClashAPI))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create clash api server")
|
||||
}
|
||||
@@ -151,7 +153,7 @@ func New(options Options) (*Box, error) {
|
||||
preServices["clash api"] = clashServer
|
||||
}
|
||||
if needV2RayAPI {
|
||||
v2rayServer, err := experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(options.Experimental.V2RayAPI))
|
||||
v2rayServer, err := experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(experimentalOptions.V2RayAPI))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create v2ray api server")
|
||||
}
|
||||
@@ -211,10 +213,12 @@ func (s *Box) Start() error {
|
||||
|
||||
func (s *Box) preStart() error {
|
||||
for serviceName, service := range s.preServices {
|
||||
s.logger.Trace("pre-start ", serviceName)
|
||||
err := adapter.PreStart(service)
|
||||
if err != nil {
|
||||
return E.Cause(err, "pre-starting ", serviceName)
|
||||
if preService, isPreService := service.(adapter.PreStarter); isPreService {
|
||||
s.logger.Trace("pre-start ", serviceName)
|
||||
err := preService.PreStart()
|
||||
if err != nil {
|
||||
return E.Cause(err, "pre-starting ", serviceName)
|
||||
}
|
||||
}
|
||||
}
|
||||
err := s.startOutbounds()
|
||||
@@ -249,13 +253,26 @@ func (s *Box) start() error {
|
||||
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Box) postStart() error {
|
||||
for serviceName, service := range s.postServices {
|
||||
s.logger.Trace("starting ", service)
|
||||
err = service.Start()
|
||||
err := service.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start ", serviceName)
|
||||
}
|
||||
}
|
||||
for serviceName, service := range s.outbounds {
|
||||
if lateService, isLateService := service.(adapter.PostStarter); isLateService {
|
||||
s.logger.Trace("post-starting ", service)
|
||||
err := lateService.PostStart()
|
||||
if err != nil {
|
||||
return E.Cause(err, "post-start ", serviceName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ func buildiOS() {
|
||||
args := []string{
|
||||
"bind",
|
||||
"-v",
|
||||
"-target", "ios,iossimulator,macos",
|
||||
"-target", "ios,iossimulator,tvos,tvossimulator,macos",
|
||||
"-libname=box",
|
||||
}
|
||||
if !debugEnabled {
|
||||
@@ -133,7 +133,7 @@ func buildiOS() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
copyPath := filepath.Join("..", "sing-box-for-ios")
|
||||
copyPath := filepath.Join("..", "sing-box-for-apple")
|
||||
if rw.FileExists(copyPath) {
|
||||
targetDir := filepath.Join(copyPath, "Libbox.xcframework")
|
||||
targetDir, _ = filepath.Abs(targetDir)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package build_shared
|
||||
|
||||
import "github.com/sagernet/sing/common/shell"
|
||||
import (
|
||||
"github.com/sagernet/sing-box/common/badversion"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
)
|
||||
|
||||
func ReadTag() (string, error) {
|
||||
currentTag, err := shell.Exec("git", "describe", "--tags").ReadOutput()
|
||||
@@ -12,5 +15,9 @@ func ReadTag() (string, error) {
|
||||
return currentTag[1:], nil
|
||||
}
|
||||
shortCommit, _ := shell.Exec("git", "rev-parse", "--short", "HEAD").ReadOutput()
|
||||
return currentTagRev[1:] + "-" + shortCommit, nil
|
||||
version := badversion.Parse(currentTagRev[1:])
|
||||
if version.PreReleaseIdentifier == "" {
|
||||
version.Patch++
|
||||
}
|
||||
return version.String() + "-" + shortCommit, nil
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func WrapQUIC(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if Contains(err, "canceled with error code 0") {
|
||||
if Contains(err, "canceled by local with error code 0") {
|
||||
return net.ErrClosed
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -5,8 +5,10 @@ package badtls
|
||||
import (
|
||||
"crypto/tls"
|
||||
"os"
|
||||
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
func Create(conn *tls.Conn) (TLSConn, error) {
|
||||
func Create(conn *tls.Conn) (aTLS.Conn, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ type Version struct {
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
Commit string
|
||||
PreReleaseIdentifier string
|
||||
PreReleaseVersion int
|
||||
}
|
||||
@@ -37,16 +38,21 @@ func (v Version) After(anotherVersion Version) bool {
|
||||
return false
|
||||
}
|
||||
if v.PreReleaseIdentifier != "" && anotherVersion.PreReleaseIdentifier != "" {
|
||||
if v.PreReleaseIdentifier == "beta" && anotherVersion.PreReleaseIdentifier == "alpha" {
|
||||
if v.PreReleaseIdentifier == anotherVersion.PreReleaseIdentifier {
|
||||
if v.PreReleaseVersion > anotherVersion.PreReleaseVersion {
|
||||
return true
|
||||
} else if v.PreReleaseVersion < anotherVersion.PreReleaseVersion {
|
||||
return false
|
||||
}
|
||||
} else if v.PreReleaseIdentifier == "rc" && anotherVersion.PreReleaseIdentifier == "beta" {
|
||||
return true
|
||||
} else if v.PreReleaseIdentifier == "beta" && anotherVersion.PreReleaseIdentifier == "rc" {
|
||||
return false
|
||||
} else if v.PreReleaseIdentifier == "beta" && anotherVersion.PreReleaseIdentifier == "alpha" {
|
||||
return true
|
||||
} else if v.PreReleaseIdentifier == "alpha" && anotherVersion.PreReleaseIdentifier == "beta" {
|
||||
return false
|
||||
}
|
||||
if v.PreReleaseVersion > anotherVersion.PreReleaseVersion {
|
||||
return true
|
||||
} else if v.PreReleaseVersion < anotherVersion.PreReleaseVersion {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -95,7 +101,7 @@ func Parse(versionName string) (version Version) {
|
||||
version.PreReleaseIdentifier = "beta"
|
||||
version.PreReleaseVersion, _ = strconv.Atoi(identifier[4:])
|
||||
} else {
|
||||
version.PreReleaseIdentifier = identifier
|
||||
version.Commit = identifier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package debugio
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type LogConn struct {
|
||||
N.ExtendedConn
|
||||
logger log.Logger
|
||||
prefix string
|
||||
}
|
||||
|
||||
func NewLogConn(conn net.Conn, logger log.Logger, prefix string) N.ExtendedConn {
|
||||
return &LogConn{bufio.NewExtendedConn(conn), logger, prefix}
|
||||
}
|
||||
|
||||
func (c *LogConn) Read(p []byte) (n int, err error) {
|
||||
n, err = c.ExtendedConn.Read(p)
|
||||
if n > 0 {
|
||||
c.logger.Debug(c.prefix, " read ", buf.EncodeHexString(p[:n]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *LogConn) Write(p []byte) (n int, err error) {
|
||||
c.logger.Debug(c.prefix, " write ", buf.EncodeHexString(p))
|
||||
return c.ExtendedConn.Write(p)
|
||||
}
|
||||
|
||||
func (c *LogConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||
err := c.ExtendedConn.ReadBuffer(buffer)
|
||||
if err == nil {
|
||||
c.logger.Debug(c.prefix, " read buffer ", buf.EncodeHexString(buffer.Bytes()))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *LogConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
c.logger.Debug(c.prefix, " write buffer ", buf.EncodeHexString(buffer.Bytes()))
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *LogConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
type LogPacketConn struct {
|
||||
N.NetPacketConn
|
||||
logger log.Logger
|
||||
prefix string
|
||||
}
|
||||
|
||||
func NewLogPacketConn(conn net.PacketConn, logger log.Logger, prefix string) N.NetPacketConn {
|
||||
return &LogPacketConn{bufio.NewPacketConn(conn), logger, prefix}
|
||||
}
|
||||
|
||||
func (c *LogPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
n, addr, err = c.NetPacketConn.ReadFrom(p)
|
||||
if n > 0 {
|
||||
c.logger.Debug(c.prefix, " read from ", addr, " ", buf.EncodeHexString(p[:n]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *LogPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
c.logger.Debug(c.prefix, " write to ", addr, " ", buf.EncodeHexString(p))
|
||||
return c.NetPacketConn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
func (c *LogPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
destination, err = c.NetPacketConn.ReadPacket(buffer)
|
||||
if err == nil {
|
||||
c.logger.Debug(c.prefix, " read packet from ", destination, " ", buf.EncodeHexString(buffer.Bytes()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *LogPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
c.logger.Debug(c.prefix, " write packet to ", destination, " ", buf.EncodeHexString(buffer.Bytes()))
|
||||
return c.NetPacketConn.WritePacket(buffer, destination)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package debugio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
func PrintUpstream(obj any) {
|
||||
for obj != nil {
|
||||
fmt.Println(reflect.TypeOf(obj))
|
||||
if u, ok := obj.(common.WithUpstream); !ok {
|
||||
break
|
||||
} else {
|
||||
obj = u.Upstream()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package debugio
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type RaceConn struct {
|
||||
N.ExtendedConn
|
||||
readAccess sync.Mutex
|
||||
writeAccess sync.Mutex
|
||||
}
|
||||
|
||||
func NewRaceConn(conn net.Conn) N.ExtendedConn {
|
||||
return &RaceConn{ExtendedConn: bufio.NewExtendedConn(conn)}
|
||||
}
|
||||
|
||||
func (c *RaceConn) Read(p []byte) (n int, err error) {
|
||||
c.readAccess.Lock()
|
||||
defer c.readAccess.Unlock()
|
||||
return c.ExtendedConn.Read(p)
|
||||
}
|
||||
|
||||
func (c *RaceConn) Write(p []byte) (n int, err error) {
|
||||
c.writeAccess.Lock()
|
||||
defer c.writeAccess.Unlock()
|
||||
return c.ExtendedConn.Write(p)
|
||||
}
|
||||
|
||||
func (c *RaceConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||
c.readAccess.Lock()
|
||||
defer c.readAccess.Unlock()
|
||||
return c.ExtendedConn.ReadBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *RaceConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
c.writeAccess.Lock()
|
||||
defer c.writeAccess.Unlock()
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *RaceConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -26,7 +26,7 @@ type DefaultDialer struct {
|
||||
udpAddr6 string
|
||||
}
|
||||
|
||||
func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDialer {
|
||||
func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDialer, error) {
|
||||
var dialer net.Dialer
|
||||
var listener net.ListenConfig
|
||||
if options.BindInterface != "" {
|
||||
@@ -93,6 +93,12 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
|
||||
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
|
||||
}
|
||||
if options.TCPMultiPath {
|
||||
if !multipathTCPAvailable {
|
||||
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
||||
}
|
||||
setMultiPathTCP(&dialer4)
|
||||
}
|
||||
return &DefaultDialer{
|
||||
tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen},
|
||||
tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen},
|
||||
@@ -101,7 +107,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
listener,
|
||||
udpAddr4,
|
||||
udpAddr6,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) {
|
||||
|
||||
11
common/dialer/default_go1.21.go
Normal file
11
common/dialer/default_go1.21.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build go1.21
|
||||
|
||||
package dialer
|
||||
|
||||
import "net"
|
||||
|
||||
const multipathTCPAvailable = true
|
||||
|
||||
func setMultiPathTCP(dialer *net.Dialer) {
|
||||
dialer.SetMultipathTCP(true)
|
||||
}
|
||||
12
common/dialer/default_nongo1.21.go
Normal file
12
common/dialer/default_nongo1.21.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build !go1.21
|
||||
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
const multipathTCPAvailable = false
|
||||
|
||||
func setMultiPathTCP(dialer *net.Dialer) {
|
||||
}
|
||||
@@ -6,13 +6,24 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func New(router adapter.Router, options option.DialerOptions) N.Dialer {
|
||||
var dialer N.Dialer
|
||||
func MustNew(router adapter.Router, options option.DialerOptions) N.Dialer {
|
||||
return common.Must1(New(router, options))
|
||||
}
|
||||
|
||||
func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error) {
|
||||
var (
|
||||
dialer N.Dialer
|
||||
err error
|
||||
)
|
||||
if options.Detour == "" {
|
||||
dialer = NewDefault(router, options)
|
||||
dialer, err = NewDefault(router, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
dialer = NewDetour(router, options.Detour)
|
||||
}
|
||||
@@ -20,5 +31,5 @@ func New(router adapter.Router, options option.DialerOptions) N.Dialer {
|
||||
if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" {
|
||||
dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay))
|
||||
}
|
||||
return dialer
|
||||
return dialer, nil
|
||||
}
|
||||
|
||||
@@ -128,13 +128,6 @@ func (c *slowOpenConn) NeedHandshake() bool {
|
||||
return c.conn == nil
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if c.conn != nil {
|
||||
return bufio.Copy(c.conn, r)
|
||||
}
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
|
||||
func (c *slowOpenConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
if c.conn == nil {
|
||||
select {
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"unicode"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -82,9 +81,7 @@ func resolveSocketByNetlink(network string, source netip.AddrPort, destination n
|
||||
return 0, 0, E.Cause(err, "write netlink request")
|
||||
}
|
||||
|
||||
_buffer := buf.StackNew()
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.New()
|
||||
defer buffer.Release()
|
||||
|
||||
n, err := syscall.Read(socket, buffer.FreeBytes())
|
||||
|
||||
@@ -20,10 +20,10 @@ type systemProxy struct {
|
||||
isMixed bool
|
||||
}
|
||||
|
||||
func (p *systemProxy) update(event int) error {
|
||||
func (p *systemProxy) update(event int) {
|
||||
newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
|
||||
if p.interfaceName == newInterfaceName {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
if p.interfaceName != "" {
|
||||
_ = p.unset()
|
||||
@@ -31,7 +31,7 @@ func (p *systemProxy) update(event int) error {
|
||||
p.interfaceName = newInterfaceName
|
||||
interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
if p.isMixed {
|
||||
err = shell.Exec("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port)).Attach().Run()
|
||||
@@ -40,9 +40,9 @@ func (p *systemProxy) update(event int) error {
|
||||
err = shell.Exec("networksetup", "-setwebproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port)).Attach().Run()
|
||||
}
|
||||
if err == nil {
|
||||
err = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port)).Attach().Run()
|
||||
_ = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port)).Attach().Run()
|
||||
}
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
func (p *systemProxy) unset() error {
|
||||
@@ -88,10 +88,7 @@ func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() er
|
||||
port: port,
|
||||
isMixed: isMixed,
|
||||
}
|
||||
err := proxy.update(tun.EventInterfaceUpdate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proxy.update(tun.EventInterfaceUpdate)
|
||||
proxy.element = interfaceMonitor.RegisterCallback(proxy.update)
|
||||
return func() error {
|
||||
interfaceMonitor.UnregisterCallback(proxy.element)
|
||||
|
||||
@@ -26,9 +26,7 @@ func StreamDomainNameQuery(readCtx context.Context, reader io.Reader) (*adapter.
|
||||
if length == 0 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
_buffer := buf.StackNewSize(int(length))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(int(length))
|
||||
defer buffer.Release()
|
||||
|
||||
readCtx, cancel := context.WithTimeout(readCtx, time.Millisecond*100)
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
type acmeWrapper struct {
|
||||
ctx context.Context
|
||||
cfg *certmagic.Config
|
||||
cache *certmagic.Cache
|
||||
domain []string
|
||||
}
|
||||
|
||||
@@ -29,7 +30,7 @@ func (w *acmeWrapper) Start() error {
|
||||
}
|
||||
|
||||
func (w *acmeWrapper) Close() error {
|
||||
w.cfg.Unmanage(w.domain)
|
||||
w.cache.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -77,10 +78,11 @@ func startACME(ctx context.Context, options option.InboundACMEOptions) (*tls.Con
|
||||
acmeConfig.ExternalAccount = (*acme.EAB)(options.ExternalAccount)
|
||||
}
|
||||
config.Issuers = []certmagic.Issuer{certmagic.NewACMEIssuer(config, acmeConfig)}
|
||||
config = certmagic.New(certmagic.NewCache(certmagic.CacheOptions{
|
||||
cache := certmagic.NewCache(certmagic.CacheOptions{
|
||||
GetConfigForCert: func(certificate certmagic.Certificate) (*certmagic.Config, error) {
|
||||
return config, nil
|
||||
},
|
||||
}), *config)
|
||||
return config.TLSConfig(), &acmeWrapper{ctx, config, options.Domain}, nil
|
||||
})
|
||||
config = certmagic.New(cache, *config)
|
||||
return config.TLSConfig(), &acmeWrapper{ctx: ctx, cfg: config, cache: cache, domain: options.Domain}, nil
|
||||
}
|
||||
|
||||
@@ -101,7 +101,10 @@ func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Log
|
||||
tlsConfig.ShortIds[shortID] = true
|
||||
}
|
||||
|
||||
handshakeDialer := dialer.New(router, options.Reality.Handshake.DialerOptions)
|
||||
handshakeDialer, err := dialer.New(router, options.Reality.Handshake.DialerOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||
}
|
||||
|
||||
@@ -164,8 +164,8 @@ func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger,
|
||||
var acmeService adapter.Service
|
||||
var err error
|
||||
if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
||||
tlsConfig, acmeService, err = startACME(ctx, common.PtrValueOrDefault(options.ACME))
|
||||
//nolint:staticcheck
|
||||
tlsConfig, acmeService, err = startACME(ctx, common.PtrValueOrDefault(options.ACME))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type History struct {
|
||||
@@ -20,6 +21,7 @@ type History struct {
|
||||
type HistoryStorage struct {
|
||||
access sync.RWMutex
|
||||
delayHistory map[string]*History
|
||||
callbacks list.List[func()]
|
||||
}
|
||||
|
||||
func NewHistoryStorage() *HistoryStorage {
|
||||
@@ -28,6 +30,18 @@ func NewHistoryStorage() *HistoryStorage {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HistoryStorage) AddListener(listener func()) *list.Element[func()] {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
return s.callbacks.PushBack(listener)
|
||||
}
|
||||
|
||||
func (s *HistoryStorage) RemoveListener(element *list.Element[func()]) {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
s.callbacks.Remove(element)
|
||||
}
|
||||
|
||||
func (s *HistoryStorage) LoadURLTestHistory(tag string) *History {
|
||||
if s == nil {
|
||||
return nil
|
||||
@@ -39,14 +53,24 @@ func (s *HistoryStorage) LoadURLTestHistory(tag string) *History {
|
||||
|
||||
func (s *HistoryStorage) DeleteURLTestHistory(tag string) {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
delete(s.delayHistory, tag)
|
||||
s.access.Unlock()
|
||||
s.notifyUpdated()
|
||||
}
|
||||
|
||||
func (s *HistoryStorage) StoreURLTestHistory(tag string, history *History) {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
s.delayHistory[tag] = history
|
||||
s.access.Unlock()
|
||||
s.notifyUpdated()
|
||||
}
|
||||
|
||||
func (s *HistoryStorage) notifyUpdated() {
|
||||
s.access.RLock()
|
||||
defer s.access.RUnlock()
|
||||
for element := s.callbacks.Front(); element != nil; element = element.Next() {
|
||||
element.Value()
|
||||
}
|
||||
}
|
||||
|
||||
func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err error) {
|
||||
|
||||
@@ -7,7 +7,7 @@ const (
|
||||
TypeDirect = "direct"
|
||||
TypeBlock = "block"
|
||||
TypeDNS = "dns"
|
||||
TypeSocks = "socks"
|
||||
TypeSOCKS = "socks"
|
||||
TypeHTTP = "http"
|
||||
TypeMixed = "mixed"
|
||||
TypeShadowsocks = "shadowsocks"
|
||||
@@ -21,9 +21,55 @@ const (
|
||||
TypeShadowTLS = "shadowtls"
|
||||
TypeShadowsocksR = "shadowsocksr"
|
||||
TypeVLESS = "vless"
|
||||
TypeTUIC = "tuic"
|
||||
)
|
||||
|
||||
const (
|
||||
TypeSelector = "selector"
|
||||
TypeURLTest = "urltest"
|
||||
)
|
||||
|
||||
func ProxyDisplayName(proxyType string) string {
|
||||
switch proxyType {
|
||||
case TypeDirect:
|
||||
return "Direct"
|
||||
case TypeBlock:
|
||||
return "Block"
|
||||
case TypeDNS:
|
||||
return "DNS"
|
||||
case TypeSOCKS:
|
||||
return "SOCKS"
|
||||
case TypeHTTP:
|
||||
return "HTTP"
|
||||
case TypeShadowsocks:
|
||||
return "Shadowsocks"
|
||||
case TypeVMess:
|
||||
return "VMess"
|
||||
case TypeTrojan:
|
||||
return "Trojan"
|
||||
case TypeNaive:
|
||||
return "Naive"
|
||||
case TypeWireGuard:
|
||||
return "WireGuard"
|
||||
case TypeHysteria:
|
||||
return "Hysteria"
|
||||
case TypeTor:
|
||||
return "Tor"
|
||||
case TypeSSH:
|
||||
return "SSH"
|
||||
case TypeShadowTLS:
|
||||
return "ShadowTLS"
|
||||
case TypeShadowsocksR:
|
||||
return "ShadowsocksR"
|
||||
case TypeVLESS:
|
||||
return "VLESS"
|
||||
case TypeTUIC:
|
||||
return "TUIC"
|
||||
case TypeSelector:
|
||||
return "Selector"
|
||||
case TypeURLTest:
|
||||
return "URLTest"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,49 @@
|
||||
#### 1.3.6
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.3.5
|
||||
|
||||
* Fixes and improvements
|
||||
* Introducing our [Apple tvOS](/installation/clients/sft) client applications **1**
|
||||
* Add per app proxy and app installed/updated trigger support for Android client
|
||||
* Add profile sharing support for Android/iOS/macOS clients
|
||||
|
||||
**1**:
|
||||
|
||||
Due to the requirement of tvOS 17, the app cannot be submitted to the App Store for the time being, and can only be downloaded through TestFlight.
|
||||
|
||||
#### 1.3.4
|
||||
|
||||
* Fixes and improvements
|
||||
* We're now on the [App Store](https://apps.apple.com/us/app/sing-box/id6451272673), always free! It should be noted that due to stricter and slower review, the release of Store versions will be delayed.
|
||||
* We've made a standalone version of the macOS client (the original Application Extension relies on App Store distribution), which you can download as SFM-version-universal.zip in the release artifacts.
|
||||
|
||||
#### 1.3.3
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.3.1-rc.1
|
||||
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
#### 1.3.1-beta.3
|
||||
|
||||
* Introducing our [new iOS](/installation/clients/sfi) and [macOS](/installation/clients/sfm) client applications **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
The old testflight link and app are no longer valid.
|
||||
|
||||
#### 1.3.1-beta.2
|
||||
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
#### 1.3.1-beta.1
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.3.0
|
||||
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
82
docs/configuration/inbound/tuic.md
Normal file
82
docs/configuration/inbound/tuic.md
Normal file
@@ -0,0 +1,82 @@
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "tuic",
|
||||
"tag": "tuic-in",
|
||||
|
||||
... // Listen Fields
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"uuid": "059032A9-7D40-4A96-9BB1-36823D848068",
|
||||
"password": "hello"
|
||||
}
|
||||
],
|
||||
"congestion_control": "cubic",
|
||||
"auth_timeout": "3s",
|
||||
"zero_rtt_handshake": false,
|
||||
"heartbeat": "10s",
|
||||
"tls": {}
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
QUIC, which is required by TUIC is not included by default, see [Installation](/#installation).
|
||||
|
||||
### Listen Fields
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen) for details.
|
||||
|
||||
### Fields
|
||||
|
||||
#### users
|
||||
|
||||
TUIC users
|
||||
|
||||
#### users.uuid
|
||||
|
||||
==Required==
|
||||
|
||||
TUIC user uuid
|
||||
|
||||
#### users.password
|
||||
|
||||
TUIC user password
|
||||
|
||||
#### congestion_control
|
||||
|
||||
QUIC congestion control algorithm
|
||||
|
||||
One of: `cubic`, `new_reno`, `bbr`
|
||||
|
||||
`cubic` is used by default.
|
||||
|
||||
#### auth_timeout
|
||||
|
||||
How long the server should wait for the client to send the authentication command
|
||||
|
||||
`3s` is used by default.
|
||||
|
||||
#### zero_rtt_handshake
|
||||
|
||||
Enable 0-RTT QUIC connection handshake on the client side
|
||||
This is not impacting much on the performance, as the protocol is fully multiplexed
|
||||
|
||||
!!! warning ""
|
||||
Disabling this is highly recommended, as it is vulnerable to replay attacks.
|
||||
See [Attack of the clones](https://blog.cloudflare.com/even-faster-connection-establishment-with-quic-0-rtt-resumption/#attack-of-the-clones)
|
||||
|
||||
#### heartbeat
|
||||
|
||||
Interval for sending heartbeat packets for keeping the connection alive
|
||||
|
||||
`10s` is used by default.
|
||||
|
||||
#### tls
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
82
docs/configuration/inbound/tuic.zh.md
Normal file
82
docs/configuration/inbound/tuic.zh.md
Normal file
@@ -0,0 +1,82 @@
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "tuic",
|
||||
"tag": "tuic-in",
|
||||
|
||||
... // 监听字段
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"uuid": "059032A9-7D40-4A96-9BB1-36823D848068",
|
||||
"password": "hello"
|
||||
}
|
||||
],
|
||||
"congestion_control": "cubic",
|
||||
"auth_timeout": "3s",
|
||||
"zero_rtt_handshake": false,
|
||||
"heartbeat": "10s",
|
||||
"tls": {}
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含被 TUI 依赖的 QUIC,参阅 [安装](/zh/#_2)。
|
||||
|
||||
### 监听字段
|
||||
|
||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||
|
||||
### 字段
|
||||
|
||||
#### users
|
||||
|
||||
TUIC 用户
|
||||
|
||||
#### users.uuid
|
||||
|
||||
==必填==
|
||||
|
||||
TUIC 用户 UUID
|
||||
|
||||
#### users.password
|
||||
|
||||
TUIC 用户密码
|
||||
|
||||
#### congestion_control
|
||||
|
||||
QUIC 流量控制算法
|
||||
|
||||
可选值: `cubic`, `new_reno`, `bbr`
|
||||
|
||||
默认使用 `cubic`。
|
||||
|
||||
#### auth_timeout
|
||||
|
||||
服务器等待客户端发送认证命令的时间
|
||||
|
||||
默认使用 `3s`。
|
||||
|
||||
#### zero_rtt_handshake
|
||||
|
||||
在客户端启用 0-RTT QUIC 连接握手
|
||||
这对性能影响不大,因为协议是完全复用的
|
||||
|
||||
!!! warning ""
|
||||
强烈建议禁用此功能,因为它容易受到重放攻击。
|
||||
请参阅 [Attack of the clones](https://blog.cloudflare.com/even-faster-connection-establishment-with-quic-0-rtt-resumption/#attack-of-the-clones)
|
||||
|
||||
#### heartbeat
|
||||
|
||||
发送心跳包以保持连接存活的时间间隔
|
||||
|
||||
默认使用 `10s`。
|
||||
|
||||
#### tls
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||
@@ -142,11 +142,12 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
|
||||
|
||||
TCP/IP stack.
|
||||
|
||||
| Stack | Description | Status |
|
||||
|------------------|----------------------------------------------------------------------------------|-------------------|
|
||||
| system (default) | Sometimes better performance | recommended |
|
||||
| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
| Stack | Description | Status |
|
||||
|--------|----------------------------------------------------------------------------------|-------------------|
|
||||
| system | Sometimes better performance | recommended |
|
||||
| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended |
|
||||
| mixed | Mixed `system` TCP stack and `gVisor` UDP stack | recommended |
|
||||
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
|
||||
@@ -97,12 +97,6 @@ Disables Path MTU Discovery (RFC 8899). Packets will then be at most 1252 (IPv4)
|
||||
|
||||
Force enabled on for systems other than Linux and Windows (according to upstream).
|
||||
|
||||
#### tls
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
#### network
|
||||
|
||||
Enabled network
|
||||
@@ -111,6 +105,12 @@ One of `tcp` `udp`.
|
||||
|
||||
Both is enabled by default.
|
||||
|
||||
#### tls
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
### Dial Fields
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial) for details.
|
||||
|
||||
@@ -97,10 +97,6 @@ base64 编码的认证密码。
|
||||
|
||||
强制为 Linux 和 Windows 以外的系统启用(根据上游)。
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
#### network
|
||||
|
||||
启用的网络协议。
|
||||
@@ -109,6 +105,13 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
默认所有。
|
||||
|
||||
#### tls
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
|
||||
### 拨号字段
|
||||
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
||||
|
||||
86
docs/configuration/outbound/tuic.md
Normal file
86
docs/configuration/outbound/tuic.md
Normal file
@@ -0,0 +1,86 @@
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "tuic",
|
||||
"tag": "tuic-out",
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"uuid": "2DD61D93-75D8-4DA4-AC0E-6AECE7EAC365",
|
||||
"password": "hello",
|
||||
"congestion_control": "cubic",
|
||||
"udp_relay_mode": "native",
|
||||
"zero_rtt_handshake": false,
|
||||
"heartbeat": "10s",
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
QUIC, which is required by TUIC is not included by default, see [Installation](/#installation).
|
||||
|
||||
### Fields
|
||||
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
|
||||
The server address.
|
||||
|
||||
#### server_port
|
||||
|
||||
==Required==
|
||||
|
||||
The server port.
|
||||
|
||||
#### uuid
|
||||
|
||||
==Required==
|
||||
|
||||
TUIC user uuid
|
||||
|
||||
#### password
|
||||
|
||||
TUIC user password
|
||||
|
||||
#### congestion_control
|
||||
|
||||
QUIC congestion control algorithm
|
||||
|
||||
One of: `cubic`, `new_reno`, `bbr`
|
||||
|
||||
`cubic` is used by default.
|
||||
|
||||
#### udp_relay_mode
|
||||
|
||||
UDP packet relay mode
|
||||
|
||||
| Mode | Description |
|
||||
|:-------|:-------------------------------------------------------------------------|
|
||||
| native | native UDP characteristics |
|
||||
| quic | lossless UDP relay using QUIC streams, additional overhead is introduced |
|
||||
|
||||
`native` is used by default.
|
||||
|
||||
#### network
|
||||
|
||||
Enabled network
|
||||
|
||||
One of `tcp` `udp`.
|
||||
|
||||
Both is enabled by default.
|
||||
|
||||
#### tls
|
||||
|
||||
==Required==
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
### Dial Fields
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial) for details.
|
||||
98
docs/configuration/outbound/tuic.zh.md
Normal file
98
docs/configuration/outbound/tuic.zh.md
Normal file
@@ -0,0 +1,98 @@
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "tuic",
|
||||
"tag": "tuic-out",
|
||||
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"uuid": "2DD61D93-75D8-4DA4-AC0E-6AECE7EAC365",
|
||||
"password": "hello",
|
||||
"congestion_control": "cubic",
|
||||
"udp_relay_mode": "native",
|
||||
"zero_rtt_handshake": false,
|
||||
"heartbeat": "10s",
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含被 TUI 依赖的 QUIC,参阅 [安装](/zh/#_2)。
|
||||
|
||||
### 字段
|
||||
|
||||
#### server
|
||||
|
||||
==必填==
|
||||
|
||||
服务器地址。
|
||||
|
||||
#### server_port
|
||||
|
||||
==必填==
|
||||
|
||||
服务器端口。
|
||||
|
||||
#### uuid
|
||||
|
||||
==必填==
|
||||
|
||||
TUIC 用户 UUID
|
||||
|
||||
#### password
|
||||
|
||||
TUIC 用户密码
|
||||
|
||||
#### congestion_control
|
||||
|
||||
QUIC 流量控制算法
|
||||
|
||||
可选值: `cubic`, `new_reno`, `bbr`
|
||||
|
||||
默认使用 `cubic`。
|
||||
|
||||
#### udp_relay_mode
|
||||
|
||||
UDP 包中继模式
|
||||
|
||||
| 模式 | 描述 |
|
||||
|--------|------------------------------|
|
||||
| native | 原生 UDP |
|
||||
| quic | 使用 QUIC 流的无损 UDP 中继,引入了额外的开销 |
|
||||
|
||||
|
||||
#### zero_rtt_handshake
|
||||
|
||||
在客户端启用 0-RTT QUIC 连接握手
|
||||
这对性能影响不大,因为协议是完全复用的
|
||||
|
||||
!!! warning ""
|
||||
强烈建议禁用此功能,因为它容易受到重放攻击。
|
||||
请参阅 [Attack of the clones](https://blog.cloudflare.com/even-faster-connection-establishment-with-quic-0-rtt-resumption/#attack-of-the-clones)
|
||||
|
||||
#### heartbeat
|
||||
|
||||
发送心跳包以保持连接存活的时间间隔
|
||||
|
||||
#### network
|
||||
|
||||
启用的网络协议。
|
||||
|
||||
`tcp` 或 `udp`。
|
||||
|
||||
默认所有。
|
||||
|
||||
#### tls
|
||||
|
||||
==必填==
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
### 拨号字段
|
||||
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
||||
@@ -24,7 +24,6 @@
|
||||
|------------|-------------------------|
|
||||
| `geoip` | [GeoIP](./geoip) |
|
||||
| `geosite` | [GeoSite](./geosite) |
|
||||
| `ip_rules` | 一组 [IP 路由规则](./ip-rule) |
|
||||
| `rules` | 一组 [路由规则](./rule) |
|
||||
|
||||
#### final
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"route": {
|
||||
"ip_rules": [
|
||||
{
|
||||
"inbound": [
|
||||
"mixed-in"
|
||||
],
|
||||
"ip_version": 6,
|
||||
"network": [
|
||||
"tcp"
|
||||
],
|
||||
"domain": [
|
||||
"test.com"
|
||||
],
|
||||
"domain_suffix": [
|
||||
".cn"
|
||||
],
|
||||
"domain_keyword": [
|
||||
"test"
|
||||
],
|
||||
"domain_regex": [
|
||||
"^stun\\..+"
|
||||
],
|
||||
"geosite": [
|
||||
"cn"
|
||||
],
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
"source_port_range": [
|
||||
"1000:2000",
|
||||
":3000",
|
||||
"4000:"
|
||||
],
|
||||
"port": [
|
||||
80,
|
||||
443
|
||||
],
|
||||
"port_range": [
|
||||
"1000:2000",
|
||||
":3000",
|
||||
"4000:"
|
||||
],
|
||||
"invert": false,
|
||||
"action": "direct",
|
||||
"outbound": "wireguard"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"invert": false,
|
||||
"action": "direct",
|
||||
"outbound": "wireguard"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
You can ignore the JSON Array [] tag when the content is only one item
|
||||
|
||||
### Default Fields
|
||||
|
||||
!!! note ""
|
||||
|
||||
The default rule uses the following matching logic:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
Tags of [Inbound](/configuration/inbound).
|
||||
|
||||
#### ip_version
|
||||
|
||||
4 or 6.
|
||||
|
||||
Not limited if empty.
|
||||
|
||||
#### network
|
||||
|
||||
Match network protocol.
|
||||
|
||||
Available values:
|
||||
|
||||
* `tcp`
|
||||
* `udp`
|
||||
* `icmpv4`
|
||||
* `icmpv6`
|
||||
|
||||
#### domain
|
||||
|
||||
Match full domain.
|
||||
|
||||
#### domain_suffix
|
||||
|
||||
Match domain suffix.
|
||||
|
||||
#### domain_keyword
|
||||
|
||||
Match domain using keyword.
|
||||
|
||||
#### domain_regex
|
||||
|
||||
Match domain using regular expression.
|
||||
|
||||
#### geosite
|
||||
|
||||
Match geosite.
|
||||
|
||||
#### source_geoip
|
||||
|
||||
Match source geoip.
|
||||
|
||||
#### geoip
|
||||
|
||||
Match geoip.
|
||||
|
||||
#### source_ip_cidr
|
||||
|
||||
Match source ip cidr.
|
||||
|
||||
#### ip_cidr
|
||||
|
||||
Match ip cidr.
|
||||
|
||||
#### source_port
|
||||
|
||||
Match source port.
|
||||
|
||||
#### source_port_range
|
||||
|
||||
Match source port range.
|
||||
|
||||
#### port
|
||||
|
||||
Match port.
|
||||
|
||||
#### port_range
|
||||
|
||||
Match port range.
|
||||
|
||||
#### invert
|
||||
|
||||
Invert match result.
|
||||
|
||||
#### action
|
||||
|
||||
==Required==
|
||||
|
||||
| Action | Description |
|
||||
|--------|--------------------------------------------------------------------|
|
||||
| return | Stop IP routing and assemble the connection to the transport layer |
|
||||
| block | Block the connection |
|
||||
| direct | Directly forward the connection |
|
||||
|
||||
#### outbound
|
||||
|
||||
==Required if action is direct==
|
||||
|
||||
Tag of the target outbound.
|
||||
|
||||
Only outbound which supports IP connection can be used, see [Outbounds that support IP connection](/configuration/outbound/#outbounds-that-support-ip-connection).
|
||||
|
||||
### Logical Fields
|
||||
|
||||
#### type
|
||||
|
||||
`logical`
|
||||
|
||||
#### mode
|
||||
|
||||
==Required==
|
||||
|
||||
`and` or `or`
|
||||
|
||||
#### rules
|
||||
|
||||
==Required==
|
||||
|
||||
Included default rules.
|
||||
@@ -1,204 +0,0 @@
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"route": {
|
||||
"ip_rules": [
|
||||
{
|
||||
"inbound": [
|
||||
"mixed-in"
|
||||
],
|
||||
"ip_version": 6,
|
||||
"network": [
|
||||
"tcp"
|
||||
],
|
||||
"domain": [
|
||||
"test.com"
|
||||
],
|
||||
"domain_suffix": [
|
||||
".cn"
|
||||
],
|
||||
"domain_keyword": [
|
||||
"test"
|
||||
],
|
||||
"domain_regex": [
|
||||
"^stun\\..+"
|
||||
],
|
||||
"geosite": [
|
||||
"cn"
|
||||
],
|
||||
"source_geoip": [
|
||||
"private"
|
||||
],
|
||||
"geoip": [
|
||||
"cn"
|
||||
],
|
||||
"source_ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"ip_cidr": [
|
||||
"10.0.0.0/24",
|
||||
"192.168.0.1"
|
||||
],
|
||||
"source_port": [
|
||||
12345
|
||||
],
|
||||
"source_port_range": [
|
||||
"1000:2000",
|
||||
":3000",
|
||||
"4000:"
|
||||
],
|
||||
"port": [
|
||||
80,
|
||||
443
|
||||
],
|
||||
"port_range": [
|
||||
"1000:2000",
|
||||
":3000",
|
||||
"4000:"
|
||||
],
|
||||
"invert": false,
|
||||
"action": "direct",
|
||||
"outbound": "wireguard"
|
||||
},
|
||||
{
|
||||
"type": "logical",
|
||||
"mode": "and",
|
||||
"rules": [],
|
||||
"invert": false,
|
||||
"action": "direct",
|
||||
"outbound": "wireguard"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
当内容只有一项时,可以忽略 JSON 数组 [] 标签。
|
||||
|
||||
### Default Fields
|
||||
|
||||
!!! note ""
|
||||
|
||||
默认规则使用以下匹配逻辑:
|
||||
(`domain` || `domain_suffix` || `domain_keyword` || `domain_regex` || `geosite` || `geoip` || `ip_cidr`) &&
|
||||
(`port` || `port_range`) &&
|
||||
(`source_geoip` || `source_ip_cidr`) &&
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
#### inbound
|
||||
|
||||
[入站](/zh/configuration/inbound) 标签。
|
||||
|
||||
#### ip_version
|
||||
|
||||
4 或 6。
|
||||
|
||||
默认不限制。
|
||||
|
||||
#### network
|
||||
|
||||
匹配网络协议。
|
||||
|
||||
可用值:
|
||||
|
||||
* `tcp`
|
||||
* `udp`
|
||||
* `icmpv4`
|
||||
* `icmpv6`
|
||||
|
||||
#### domain
|
||||
|
||||
匹配完整域名。
|
||||
|
||||
#### domain_suffix
|
||||
|
||||
匹配域名后缀。
|
||||
|
||||
#### domain_keyword
|
||||
|
||||
匹配域名关键字。
|
||||
|
||||
#### domain_regex
|
||||
|
||||
匹配域名正则表达式。
|
||||
|
||||
#### geosite
|
||||
|
||||
匹配 GeoSite。
|
||||
|
||||
#### source_geoip
|
||||
|
||||
匹配源 GeoIP。
|
||||
|
||||
#### geoip
|
||||
|
||||
匹配 GeoIP。
|
||||
|
||||
#### source_ip_cidr
|
||||
|
||||
匹配源 IP CIDR。
|
||||
|
||||
#### ip_cidr
|
||||
|
||||
匹配 IP CIDR。
|
||||
|
||||
#### source_port
|
||||
|
||||
匹配源端口。
|
||||
|
||||
#### source_port_range
|
||||
|
||||
匹配源端口范围。
|
||||
|
||||
#### port
|
||||
|
||||
匹配端口。
|
||||
|
||||
#### port_range
|
||||
|
||||
匹配端口范围。
|
||||
|
||||
#### invert
|
||||
|
||||
反选匹配结果。
|
||||
|
||||
#### action
|
||||
|
||||
==必填==
|
||||
|
||||
| Action | 描述 |
|
||||
|--------|---------------------|
|
||||
| return | 停止 IP 路由并将该连接组装到传输层 |
|
||||
| block | 屏蔽该连接 |
|
||||
| direct | 直接转发该连接 |
|
||||
|
||||
|
||||
#### outbound
|
||||
|
||||
==action 为 direct 则必填==
|
||||
|
||||
目标出站的标签。
|
||||
|
||||
### 逻辑字段
|
||||
|
||||
#### type
|
||||
|
||||
`logical`
|
||||
|
||||
#### mode
|
||||
|
||||
==必填==
|
||||
|
||||
`and` 或 `or`
|
||||
|
||||
#### rules
|
||||
|
||||
==必填==
|
||||
|
||||
包括的默认规则。
|
||||
@@ -10,6 +10,7 @@
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"fallback_delay": "300ms"
|
||||
@@ -18,9 +19,9 @@
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Available Context |
|
||||
|----------------------------------------------------------------------------------------------------------------------|-------------------|
|
||||
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set |
|
||||
| Field | Available Context |
|
||||
|------------------------------------------------------------------------------------------------------------------------------------------|-------------------|
|
||||
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open` / `tcp_multi_path` / `udp_fragment` /`connect_timeout` | `detour` not set |
|
||||
|
||||
#### detour
|
||||
|
||||
@@ -54,6 +55,14 @@ Reuse listener address.
|
||||
|
||||
Enable TCP Fast Open.
|
||||
|
||||
#### tcp_multi_path
|
||||
|
||||
!!! warning ""
|
||||
|
||||
Go 1.21 required.
|
||||
|
||||
Enable TCP Multi Path.
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
Enable UDP fragmentation.
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"reuse_addr": false,
|
||||
"connect_timeout": "5s",
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"fallback_delay": "300ms"
|
||||
@@ -18,9 +19,9 @@
|
||||
|
||||
### 字段
|
||||
|
||||
| 字段 | 可用上下文 |
|
||||
|----------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` 未设置 |
|
||||
| 字段 | 可用上下文 |
|
||||
|------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open` / `tcp_mutli_path` / `udp_fragment` /`connect_timeout` | `detour` 未设置 |
|
||||
|
||||
|
||||
#### detour
|
||||
@@ -57,6 +58,14 @@
|
||||
|
||||
启用 TCP Fast Open。
|
||||
|
||||
#### tcp_multi_path
|
||||
|
||||
!!! warning ""
|
||||
|
||||
需要 Go 1.21。
|
||||
|
||||
启用 TCP Multi Path。
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
启用 UDP 分段。
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
@@ -24,6 +25,7 @@
|
||||
| `listen` | Needs to listen on TCP or UDP. |
|
||||
| `listen_port` | Needs to listen on TCP or UDP. |
|
||||
| `tcp_fast_open` | Needs to listen on TCP. |
|
||||
| `tcp_multi_path` | Needs to listen on TCP. |
|
||||
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
|
||||
| `proxy_protocol` | Needs to listen on TCP. |
|
||||
| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled |
|
||||
@@ -42,6 +44,14 @@ Listen port.
|
||||
|
||||
Enable TCP Fast Open.
|
||||
|
||||
#### tcp_multi_path
|
||||
|
||||
!!! warning ""
|
||||
|
||||
Go 1.21 required.
|
||||
|
||||
Enable TCP Multi Path.
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
Enable UDP fragmentation.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"listen": "::",
|
||||
"listen_port": 5353,
|
||||
"tcp_fast_open": false,
|
||||
"tcp_multi_path": false,
|
||||
"udp_fragment": false,
|
||||
"sniff": false,
|
||||
"sniff_override_destination": false,
|
||||
@@ -23,6 +24,7 @@
|
||||
| `listen` | 需要监听 TCP 或 UDP。 |
|
||||
| `listen_port` | 需要监听 TCP 或 UDP。 |
|
||||
| `tcp_fast_open` | 需要监听 TCP。 |
|
||||
| `tcp_multi_path` | 需要监听 TCP。 |
|
||||
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
|
||||
| `proxy_protocol` | 需要监听 TCP。 |
|
||||
| `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 |
|
||||
@@ -43,6 +45,14 @@
|
||||
|
||||
启用 TCP Fast Open。
|
||||
|
||||
#### tcp_multi_path
|
||||
|
||||
!!! warning ""
|
||||
|
||||
需要 Go 1.21。
|
||||
|
||||
启用 TCP Multi Path。
|
||||
|
||||
#### udp_fragment
|
||||
|
||||
启用 UDP 分段。
|
||||
|
||||
@@ -8,5 +8,4 @@ Configuration examples for sing-box.
|
||||
* [Shadowsocks](./shadowsocks)
|
||||
* [ShadowTLS](./shadowtls)
|
||||
* [Clash API](./clash-api)
|
||||
* [WireGuard Direct](./wireguard-direct)
|
||||
* [FakeIP](./fakeip)
|
||||
|
||||
@@ -8,5 +8,4 @@ sing-box 的配置示例。
|
||||
* [Shadowsocks](./shadowsocks)
|
||||
* [ShadowTLS](./shadowtls)
|
||||
* [Clash API](./clash-api)
|
||||
* [WireGuard Direct](./wireguard-direct)
|
||||
* [FakeIP](./fakeip)
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
# WireGuard Direct
|
||||
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://8.8.8.8"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"detour": "direct"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"geoip": "cn",
|
||||
"server": "direct"
|
||||
}
|
||||
],
|
||||
"reverse_mapping": true
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"tag": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"auto_route": true,
|
||||
"sniff": true,
|
||||
"stack": "system"
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "wireguard",
|
||||
"tag": "wg",
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 2345,
|
||||
"local_address": [
|
||||
"172.19.0.1/128"
|
||||
],
|
||||
"private_key": "KLTnpPY03pig/WC3zR8U7VWmpANHPFh2/4pwICGJ5Fk=",
|
||||
"peer_public_key": "uvNabcamf6Rs0vzmcw99jsjTJbxo6eWGOykSY66zsUk="
|
||||
},
|
||||
{
|
||||
"type": "dns",
|
||||
"tag": "dns"
|
||||
},
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"ip_rules": [
|
||||
{
|
||||
"port": 53,
|
||||
"action": "return"
|
||||
},
|
||||
{
|
||||
"geoip": "cn",
|
||||
"geosite": "cn",
|
||||
"action": "return"
|
||||
},
|
||||
{
|
||||
"action": "direct",
|
||||
"outbound": "wg"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"outbound": "dns"
|
||||
},
|
||||
{
|
||||
"geoip": "cn",
|
||||
"geosite": "cn",
|
||||
"outbound": "direct"
|
||||
}
|
||||
],
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -9,6 +9,7 @@ Experimental Android client for sing-box.
|
||||
#### Download
|
||||
|
||||
* [AppCenter](https://install.appcenter.ms/users/nekohasekai/apps/sfa/distribution_groups/publictest)
|
||||
* [Github Releases](https://github.com/SagerNet/sing-box/releases)
|
||||
|
||||
#### Note
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#### 下载
|
||||
|
||||
* [AppCenter](https://install.appcenter.ms/users/nekohasekai/apps/sfa/distribution_groups/publictest)
|
||||
* [Github Releases](https://github.com/SagerNet/sing-box/releases)
|
||||
|
||||
#### 注意事项
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ Experimental iOS client for sing-box.
|
||||
#### Requirements
|
||||
|
||||
* iOS 15.0+
|
||||
* macOS 12.0+ with Apple Silicon
|
||||
* An Apple account outside of mainland China
|
||||
|
||||
#### Download
|
||||
|
||||
* [TestFlight](https://testflight.apple.com/join/c6ylui2j)
|
||||
* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673)
|
||||
* [TestFlight](https://testflight.apple.com/join/AcqO44FH)
|
||||
|
||||
#### Note
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#### 要求
|
||||
|
||||
* iOS 15.0+
|
||||
* macOS 12.0+ with Apple Silicon
|
||||
* 一个非中国大陆地区的 Apple 账号
|
||||
|
||||
#### 下载
|
||||
|
||||
* [TestFlight](https://testflight.apple.com/join/c6ylui2j)
|
||||
* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673)
|
||||
* [TestFlight](https://testflight.apple.com/join/AcqO44FH)
|
||||
|
||||
#### 注意事项
|
||||
|
||||
|
||||
29
docs/installation/clients/sfm.md
Normal file
29
docs/installation/clients/sfm.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# SFM
|
||||
|
||||
Experimental macOS client for sing-box.
|
||||
|
||||
#### Requirements
|
||||
|
||||
* macOS 13.0+
|
||||
* An Apple account outside of mainland China (App Store Version)
|
||||
|
||||
#### Download (App Store Version)
|
||||
|
||||
* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673)
|
||||
* [TestFlight](https://testflight.apple.com/join/AcqO44FH)
|
||||
|
||||
#### Download (Independent Version)
|
||||
|
||||
* [GitHub Release](https://github.com/SagerNet/sing-box/releases/latest)
|
||||
* Homebrew (Cask): `brew install sfm`
|
||||
* Homebrew (Tap): `brew tap sagernet/sing-box && brew install sagernet/sing-box/sfm`
|
||||
|
||||
#### Note
|
||||
|
||||
* User Agent in remote profile request is `SFM/$version ($version_code; sing-box $sing_box_version)`
|
||||
* Crash logs is located in `Settings` -> `View Service Log`
|
||||
|
||||
#### Privacy policy
|
||||
|
||||
* SFM did not collect or share personal data.
|
||||
* The data generated by the software is always on your device.
|
||||
29
docs/installation/clients/sfm.zh.md
Normal file
29
docs/installation/clients/sfm.zh.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# SFM
|
||||
|
||||
实验性的 macOS sing-box 客户端。
|
||||
|
||||
#### 要求
|
||||
|
||||
* macOS 13.0+
|
||||
* 一个非中国大陆地区的 Apple 账号 (商店版本)
|
||||
|
||||
#### 下载 (商店版本)
|
||||
|
||||
* [AppStore](https://apps.apple.com/us/app/sing-box/id6451272673)
|
||||
* [TestFlight](https://testflight.apple.com/join/AcqO44FH)
|
||||
|
||||
#### 下载 (独立版本)
|
||||
|
||||
* [GitHub Release](https://github.com/SagerNet/sing-box/releases/latest)
|
||||
* Homebrew (Cask): `brew install sfm`
|
||||
* Homebrew (Tap): `brew tap sagernet/sing-box && brew install sagernet/sing-box/sfm`
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* 远程配置文件请求中的 User Agent 为 `SFM/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 崩溃日志位于 `设置` -> `查看服务日志`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
* SFM 不收集或共享个人数据。
|
||||
* 软件生成的数据始终在您的设备上。
|
||||
29
docs/installation/clients/sft.md
Normal file
29
docs/installation/clients/sft.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# SFT
|
||||
|
||||
Experimental Apple tvOS client for sing-box.
|
||||
|
||||
#### Requirements
|
||||
|
||||
* tvOS 17.0+
|
||||
|
||||
#### Download
|
||||
|
||||
* [TestFlight](https://testflight.apple.com/join/AcqO44FH)
|
||||
|
||||
#### Features
|
||||
|
||||
Full functionality, except for:
|
||||
|
||||
* Only remote configuration files can be created manually
|
||||
* You need to update SFI to the latest beta version to import profiles from iPhone/iPad
|
||||
* No iCloud profile support
|
||||
|
||||
#### Note
|
||||
|
||||
* User Agent in remote profile request is `SFT/$version ($version_code; sing-box $sing_box_version)`
|
||||
* Crash logs is located in `Settings` -> `View Service Log`
|
||||
|
||||
#### Privacy policy
|
||||
|
||||
* SFT did not collect or share personal data.
|
||||
* The data generated by the software is always on your device.
|
||||
25
docs/installation/clients/specification.md
Normal file
25
docs/installation/clients/specification.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Specification
|
||||
|
||||
## Profile
|
||||
|
||||
Profile defines a sing-box configuration with metadata in a GUI client.
|
||||
|
||||
## Profile Types
|
||||
|
||||
### Local
|
||||
|
||||
Create a empty configuration or import from a local file.
|
||||
|
||||
### iCloud (on Apple platforms)
|
||||
|
||||
Create a new configuration or use an existing configuration on iCloud.
|
||||
|
||||
### Remote
|
||||
|
||||
Use a remote URL as the configuration source, with HTTP basic authentication and automatic update support.
|
||||
|
||||
#### URL specification
|
||||
|
||||
```
|
||||
sing-box://import-remote-profile?url=urlEncodedURL#urlEncodedName
|
||||
```
|
||||
@@ -1,6 +1,17 @@
|
||||
# Install from source
|
||||
|
||||
sing-box requires Golang **1.18.5** or a higher version.
|
||||
## Requirements
|
||||
|
||||
Before sing-box 1.4.0:
|
||||
|
||||
* Go 1.18.5 - 1.20.x
|
||||
|
||||
Since sing-box 1.4.0:
|
||||
|
||||
* Go 1.18.5 - ~
|
||||
* Go 1.20.0 - ~ if `with_quic` tag enabled
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
@@ -9,7 +20,7 @@ go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
Install with options:
|
||||
|
||||
```bash
|
||||
go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
go install -v -tags with_quic,with_wireguard github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
| Build Tag | Description |
|
||||
|
||||
7
docs/installation/package-manager/android.md
Normal file
7
docs/installation/package-manager/android.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Android
|
||||
|
||||
## Termux
|
||||
|
||||
```shell
|
||||
pkg add sing-box
|
||||
```
|
||||
14
docs/installation/package-manager/macOS.md
Normal file
14
docs/installation/package-manager/macOS.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# macOS
|
||||
|
||||
## Homebrew (core)
|
||||
|
||||
```shell
|
||||
brew install sing-box
|
||||
```
|
||||
|
||||
## Homebrew (Tap)
|
||||
|
||||
```shell
|
||||
brew tap sagernet/sing-box
|
||||
brew install sagernet/sing-box/sing-box
|
||||
```
|
||||
13
docs/installation/package-manager/windows.md
Normal file
13
docs/installation/package-manager/windows.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Windows
|
||||
|
||||
## Chocolatey
|
||||
|
||||
```shell
|
||||
choco install sing-box
|
||||
```
|
||||
|
||||
## winget
|
||||
|
||||
```shell
|
||||
winget install sing-box
|
||||
```
|
||||
@@ -1,8 +1,4 @@
|
||||
#### Github
|
||||
|
||||
Issue: [Issues · SagerNet/sing-box](https://github.com/SagerNet/sing-box/issues)
|
||||
|
||||
#### Telegram
|
||||
|
||||
Notification channel: [@yapnc](https://t.me/yapnc)
|
||||
User group: [@yapug](https://t.me/yapug)
|
||||
Github Issue: [Issues · SagerNet/sing-box](https://github.com/SagerNet/sing-box/issues)
|
||||
Telegram Notification channel: [@yapnc](https://t.me/yapnc)
|
||||
Telegram User group: [@yapug](https://t.me/yapug)
|
||||
Email: [contact@sagernet.org](mailto:contact@sagernet.org)
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
#### Github
|
||||
|
||||
工单: [Issues · SagerNet/sing-box](https://github.com/SagerNet/sing-box/issues)
|
||||
|
||||
#### Telegram
|
||||
|
||||
通知频道: [@yapnc](https://t.me/yapnc)
|
||||
用户组: [@yapug](https://t.me/yapug)
|
||||
Github 工单: [Issues · SagerNet/sing-box](https://github.com/SagerNet/sing-box/issues)
|
||||
Telegram 通知频道: [@yapnc](https://t.me/yapnc)
|
||||
Telegram 用户组: [@yapug](https://t.me/yapug)
|
||||
Email: [contact@sagernet.org](mailto:contact@sagernet.org)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package cachefile
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -9,13 +12,21 @@ import (
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
var bucketSelected = []byte("selected")
|
||||
var (
|
||||
bucketSelected = []byte("selected")
|
||||
bucketExpand = []byte("group_expand")
|
||||
)
|
||||
|
||||
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
|
||||
|
||||
type CacheFile struct {
|
||||
DB *bbolt.DB
|
||||
cacheID []byte
|
||||
DB *bbolt.DB
|
||||
cacheID []byte
|
||||
saveAccess sync.RWMutex
|
||||
saveDomain map[netip.Addr]string
|
||||
saveAddress4 map[string]netip.Addr
|
||||
saveAddress6 map[string]netip.Addr
|
||||
saveMetadataTimer *time.Timer
|
||||
}
|
||||
|
||||
func Open(path string, cacheID string) (*CacheFile, error) {
|
||||
@@ -36,7 +47,35 @@ func Open(path string, cacheID string) (*CacheFile, error) {
|
||||
if cacheID != "" {
|
||||
cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
|
||||
}
|
||||
return &CacheFile{db, cacheIDBytes}, nil
|
||||
err = db.Batch(func(tx *bbolt.Tx) error {
|
||||
return tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
|
||||
if name[0] == 0 {
|
||||
return b.ForEachBucket(func(k []byte) error {
|
||||
bucketName := string(k)
|
||||
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand)) {
|
||||
_ = b.DeleteBucket(name)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
bucketName := string(name)
|
||||
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand) || strings.HasPrefix(bucketName, fakeipBucketPrefix)) {
|
||||
_ = tx.DeleteBucket(name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CacheFile{
|
||||
DB: db,
|
||||
cacheID: cacheIDBytes,
|
||||
saveDomain: make(map[netip.Addr]string),
|
||||
saveAddress4: make(map[string]netip.Addr),
|
||||
saveAddress6: make(map[string]netip.Addr),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket {
|
||||
@@ -87,6 +126,36 @@ func (c *CacheFile) StoreSelected(group, selected string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadGroupExpand(group string) (isExpand bool, loaded bool) {
|
||||
c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := c.bucket(t, bucketExpand)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
expandBytes := bucket.Get([]byte(group))
|
||||
if len(expandBytes) == 1 {
|
||||
isExpand = expandBytes[0] == 1
|
||||
loaded = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
bucket, err := c.createBucket(t, bucketExpand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isExpand {
|
||||
return bucket.Put([]byte(group), []byte{1})
|
||||
} else {
|
||||
return bucket.Put([]byte(group), []byte{0})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) Close() error {
|
||||
return c.DB.Close()
|
||||
}
|
||||
|
||||
@@ -3,20 +3,27 @@ package cachefile
|
||||
import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
const fakeipBucketPrefix = "fakeip_"
|
||||
|
||||
var (
|
||||
bucketFakeIP = []byte("fakeip")
|
||||
keyMetadata = []byte("metadata")
|
||||
bucketFakeIP = []byte(fakeipBucketPrefix + "address")
|
||||
bucketFakeIPDomain4 = []byte(fakeipBucketPrefix + "domain4")
|
||||
bucketFakeIPDomain6 = []byte(fakeipBucketPrefix + "domain6")
|
||||
keyMetadata = []byte(fakeipBucketPrefix + "metadata")
|
||||
)
|
||||
|
||||
func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata {
|
||||
var metadata adapter.FakeIPMetadata
|
||||
err := c.DB.View(func(tx *bbolt.Tx) error {
|
||||
err := c.DB.Batch(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(bucketFakeIP)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
@@ -25,6 +32,10 @@ func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata {
|
||||
if len(metadataBinary) == 0 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
err := bucket.Delete(keyMetadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return metadata.UnmarshalBinary(metadataBinary)
|
||||
})
|
||||
if err != nil {
|
||||
@@ -47,17 +58,69 @@ func (c *CacheFile) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPSaveMetadataAsync(metadata *adapter.FakeIPMetadata) {
|
||||
if timer := c.saveMetadataTimer; timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
c.saveMetadataTimer = time.AfterFunc(10*time.Second, func() {
|
||||
_ = c.FakeIPSaveMetadata(metadata)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
|
||||
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
||||
bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put(address.AsSlice(), []byte(domain))
|
||||
err = bucket.Put(address.AsSlice(), []byte(domain))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if address.Is4() {
|
||||
bucket, err = tx.CreateBucketIfNotExists(bucketFakeIPDomain4)
|
||||
} else {
|
||||
bucket, err = tx.CreateBucketIfNotExists(bucketFakeIPDomain6)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put([]byte(domain), address.AsSlice())
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
|
||||
c.saveAccess.Lock()
|
||||
c.saveDomain[address] = domain
|
||||
if address.Is4() {
|
||||
c.saveAddress4[domain] = address
|
||||
} else {
|
||||
c.saveAddress6[domain] = address
|
||||
}
|
||||
c.saveAccess.Unlock()
|
||||
go func() {
|
||||
err := c.FakeIPStore(address, domain)
|
||||
if err != nil {
|
||||
logger.Warn("save FakeIP address pair: ", err)
|
||||
}
|
||||
c.saveAccess.Lock()
|
||||
delete(c.saveDomain, address)
|
||||
if address.Is4() {
|
||||
delete(c.saveAddress4, domain)
|
||||
} else {
|
||||
delete(c.saveAddress6, domain)
|
||||
}
|
||||
c.saveAccess.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
||||
c.saveAccess.RLock()
|
||||
cachedDomain, cached := c.saveDomain[address]
|
||||
c.saveAccess.RUnlock()
|
||||
if cached {
|
||||
return cachedDomain, true
|
||||
}
|
||||
var domain string
|
||||
_ = c.DB.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(bucketFakeIP)
|
||||
@@ -70,8 +133,48 @@ func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
||||
return domain, domain != ""
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bool) {
|
||||
var (
|
||||
cachedAddress netip.Addr
|
||||
cached bool
|
||||
)
|
||||
c.saveAccess.RLock()
|
||||
if !isIPv6 {
|
||||
cachedAddress, cached = c.saveAddress4[domain]
|
||||
} else {
|
||||
cachedAddress, cached = c.saveAddress6[domain]
|
||||
}
|
||||
c.saveAccess.RUnlock()
|
||||
if cached {
|
||||
return cachedAddress, true
|
||||
}
|
||||
var address netip.Addr
|
||||
_ = c.DB.View(func(tx *bbolt.Tx) error {
|
||||
var bucket *bbolt.Bucket
|
||||
if isIPv6 {
|
||||
bucket = tx.Bucket(bucketFakeIPDomain6)
|
||||
} else {
|
||||
bucket = tx.Bucket(bucketFakeIPDomain4)
|
||||
}
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
address = M.AddrFromIP(bucket.Get([]byte(domain)))
|
||||
return nil
|
||||
})
|
||||
return address, address.IsValid()
|
||||
}
|
||||
|
||||
func (c *CacheFile) FakeIPReset() error {
|
||||
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
||||
return tx.DeleteBucket(bucketFakeIP)
|
||||
err := tx.DeleteBucket(bucketFakeIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.DeleteBucket(bucketFakeIPDomain4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.DeleteBucket(bucketFakeIPDomain6)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@ type Map[K comparable, V any] struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Len() int {
|
||||
var count int
|
||||
m.m.Range(func(key, value any) bool {
|
||||
count++
|
||||
return true
|
||||
})
|
||||
return count
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Load(key K) (V, bool) {
|
||||
v, ok := m.m.Load(key)
|
||||
if !ok {
|
||||
|
||||
@@ -63,38 +63,10 @@ func proxyInfo(server *Server, detour adapter.Outbound) *badjson.JSONObject {
|
||||
var info badjson.JSONObject
|
||||
var clashType string
|
||||
switch detour.Type() {
|
||||
case C.TypeDirect:
|
||||
clashType = "Direct"
|
||||
case C.TypeBlock:
|
||||
clashType = "Reject"
|
||||
case C.TypeSocks:
|
||||
clashType = "Socks"
|
||||
case C.TypeHTTP:
|
||||
clashType = "HTTP"
|
||||
case C.TypeShadowsocks:
|
||||
clashType = "Shadowsocks"
|
||||
case C.TypeVMess:
|
||||
clashType = "VMess"
|
||||
case C.TypeTrojan:
|
||||
clashType = "Trojan"
|
||||
case C.TypeHysteria:
|
||||
clashType = "Hysteria"
|
||||
case C.TypeWireGuard:
|
||||
clashType = "WireGuard"
|
||||
case C.TypeShadowsocksR:
|
||||
clashType = "ShadowsocksR"
|
||||
case C.TypeVLESS:
|
||||
clashType = "VLESS"
|
||||
case C.TypeTor:
|
||||
clashType = "Tor"
|
||||
case C.TypeSSH:
|
||||
clashType = "SSH"
|
||||
case C.TypeSelector:
|
||||
clashType = "Selector"
|
||||
case C.TypeURLTest:
|
||||
clashType = "URLTest"
|
||||
default:
|
||||
clashType = "Direct"
|
||||
clashType = C.ProxyDisplayName(detour.Type())
|
||||
}
|
||||
info.Put("type", clashType)
|
||||
info.Put("name", detour.Tag())
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
"github.com/sagernet/websocket"
|
||||
|
||||
@@ -51,6 +52,7 @@ type Server struct {
|
||||
cacheID string
|
||||
cacheFile adapter.ClashCacheFile
|
||||
|
||||
externalController bool
|
||||
externalUI string
|
||||
externalUIDownloadURL string
|
||||
externalUIDownloadDetour string
|
||||
@@ -68,17 +70,21 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||
Handler: chiRouter,
|
||||
},
|
||||
trafficManager: trafficManager,
|
||||
urlTestHistory: urltest.NewHistoryStorage(),
|
||||
mode: strings.ToLower(options.DefaultMode),
|
||||
storeSelected: options.StoreSelected,
|
||||
externalController: options.ExternalController != "",
|
||||
storeFakeIP: options.StoreFakeIP,
|
||||
externalUIDownloadURL: options.ExternalUIDownloadURL,
|
||||
externalUIDownloadDetour: options.ExternalUIDownloadDetour,
|
||||
}
|
||||
server.urlTestHistory = service.PtrFromContext[urltest.HistoryStorage](ctx)
|
||||
if server.urlTestHistory == nil {
|
||||
server.urlTestHistory = urltest.NewHistoryStorage()
|
||||
}
|
||||
if server.mode == "" {
|
||||
server.mode = "rule"
|
||||
}
|
||||
if options.StoreSelected || options.StoreFakeIP {
|
||||
if options.StoreSelected || options.StoreFakeIP || options.ExternalController == "" {
|
||||
cachePath := os.ExpandEnv(options.CacheFile)
|
||||
if cachePath == "" {
|
||||
cachePath = "cache.db"
|
||||
@@ -142,18 +148,20 @@ func (s *Server) PreStart() error {
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
s.checkAndDownloadExternalUI()
|
||||
listener, err := net.Listen("tcp", s.httpServer.Addr)
|
||||
if err != nil {
|
||||
return E.Cause(err, "external controller listen error")
|
||||
}
|
||||
s.logger.Info("restful api listening at ", listener.Addr())
|
||||
go func() {
|
||||
err = s.httpServer.Serve(listener)
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
s.logger.Error("external controller serve error: ", err)
|
||||
if s.externalController {
|
||||
s.checkAndDownloadExternalUI()
|
||||
listener, err := net.Listen("tcp", s.httpServer.Addr)
|
||||
if err != nil {
|
||||
return E.Cause(err, "external controller listen error")
|
||||
}
|
||||
}()
|
||||
s.logger.Info("restful api listening at ", listener.Addr())
|
||||
go func() {
|
||||
err = s.httpServer.Serve(listener)
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
s.logger.Error("external controller serve error: ", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -185,6 +193,10 @@ func (s *Server) HistoryStorage() *urltest.HistoryStorage {
|
||||
return s.urlTestHistory
|
||||
}
|
||||
|
||||
func (s *Server) TrafficManager() *trafficontrol.Manager {
|
||||
return s.trafficManager
|
||||
}
|
||||
|
||||
func (s *Server) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) {
|
||||
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, castMetadata(metadata), s.router, matchedRule)
|
||||
return tracker, tracker
|
||||
|
||||
@@ -55,6 +55,14 @@ func (m *Manager) Now() (up int64, down int64) {
|
||||
return m.uploadBlip.Load(), m.downloadBlip.Load()
|
||||
}
|
||||
|
||||
func (m *Manager) Total() (up int64, down int64) {
|
||||
return m.uploadTotal.Load(), m.downloadTotal.Load()
|
||||
}
|
||||
|
||||
func (m *Manager) Connections() int {
|
||||
return m.connections.Len()
|
||||
}
|
||||
|
||||
func (m *Manager) Snapshot() *Snapshot {
|
||||
var connections []tracker
|
||||
m.connections.Range(func(_ string, value tracker) bool {
|
||||
|
||||
@@ -3,7 +3,10 @@ package libbox
|
||||
const (
|
||||
CommandLog int32 = iota
|
||||
CommandStatus
|
||||
CommandServiceStop
|
||||
CommandServiceReload
|
||||
CommandCloseConnections
|
||||
CommandGroup
|
||||
CommandSelectOutbound
|
||||
CommandURLTest
|
||||
CommandGroupExpand
|
||||
)
|
||||
|
||||
@@ -10,10 +10,9 @@ import (
|
||||
)
|
||||
|
||||
type CommandClient struct {
|
||||
sharedDirectory string
|
||||
handler CommandClientHandler
|
||||
conn net.Conn
|
||||
options CommandClientOptions
|
||||
handler CommandClientHandler
|
||||
conn net.Conn
|
||||
options CommandClientOptions
|
||||
}
|
||||
|
||||
type CommandClientOptions struct {
|
||||
@@ -26,26 +25,34 @@ type CommandClientHandler interface {
|
||||
Disconnected(message string)
|
||||
WriteLog(message string)
|
||||
WriteStatus(message *StatusMessage)
|
||||
WriteGroups(message OutboundGroupIterator)
|
||||
}
|
||||
|
||||
func NewStandaloneCommandClient() *CommandClient {
|
||||
return new(CommandClient)
|
||||
}
|
||||
|
||||
func NewCommandClient(sharedDirectory string, handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
|
||||
return &CommandClient{
|
||||
sharedDirectory: sharedDirectory,
|
||||
handler: handler,
|
||||
options: common.PtrValueOrDefault(options),
|
||||
handler: handler,
|
||||
options: common.PtrValueOrDefault(options),
|
||||
}
|
||||
}
|
||||
|
||||
func clientConnect(sharedDirectory string) (net.Conn, error) {
|
||||
return net.DialUnix("unix", nil, &net.UnixAddr{
|
||||
Name: filepath.Join(sharedDirectory, "command.sock"),
|
||||
Net: "unix",
|
||||
})
|
||||
func (c *CommandClient) directConnect() (net.Conn, error) {
|
||||
if !sTVOS {
|
||||
return net.DialUnix("unix", nil, &net.UnixAddr{
|
||||
Name: filepath.Join(sBasePath, "command.sock"),
|
||||
Net: "unix",
|
||||
})
|
||||
} else {
|
||||
return net.Dial("tcp", "127.0.0.1:8964")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandClient) Connect() error {
|
||||
common.Close(c.conn)
|
||||
conn, err := clientConnect(c.sharedDirectory)
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -65,6 +72,13 @@ func (c *CommandClient) Connect() error {
|
||||
}
|
||||
c.handler.Connected()
|
||||
go c.handleStatusConn(conn)
|
||||
case CommandGroup:
|
||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
||||
if err != nil {
|
||||
return E.Cause(err, "write interval")
|
||||
}
|
||||
c.handler.Connected()
|
||||
go c.handleGroupConn(conn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
)
|
||||
|
||||
func ClientCloseConnections(sharedDirectory string) error {
|
||||
conn, err := clientConnect(sharedDirectory)
|
||||
func (c *CommandClient) CloseConnections() error {
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
318
experimental/libbox/command_group.go
Normal file
318
experimental/libbox/command_group.go
Normal file
@@ -0,0 +1,318 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
type OutboundGroup struct {
|
||||
Tag string
|
||||
Type string
|
||||
Selectable bool
|
||||
Selected string
|
||||
isExpand int8
|
||||
items []*OutboundGroupItem
|
||||
}
|
||||
|
||||
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
|
||||
return newIterator(g.items)
|
||||
}
|
||||
|
||||
func (g *OutboundGroup) IsExpand() bool {
|
||||
switch g.isExpand {
|
||||
case -1:
|
||||
return g.Selectable
|
||||
case 0:
|
||||
return false
|
||||
case 1:
|
||||
return true
|
||||
default:
|
||||
panic("unexpected expand value")
|
||||
}
|
||||
}
|
||||
|
||||
type OutboundGroupIterator interface {
|
||||
Next() *OutboundGroup
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type OutboundGroupItem struct {
|
||||
Tag string
|
||||
Type string
|
||||
URLTestTime int64
|
||||
URLTestDelay int32
|
||||
}
|
||||
|
||||
type OutboundGroupItemIterator interface {
|
||||
Next() *OutboundGroupItem
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
func (c *CommandClient) handleGroupConn(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
groups, err := readGroups(conn)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
}
|
||||
c.handler.WriteGroups(groups)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
ctx := connKeepAlive(conn)
|
||||
for {
|
||||
service := s.service
|
||||
if service != nil {
|
||||
err := writeGroups(conn, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := binary.Write(conn, binary.BigEndian, uint16(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(2 * time.Second):
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-s.urlTestUpdate:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
|
||||
var groupLength uint16
|
||||
err := binary.Read(reader, binary.BigEndian, &groupLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groups := make([]*OutboundGroup, 0, groupLength)
|
||||
for i := 0; i < int(groupLength); i++ {
|
||||
var group OutboundGroup
|
||||
group.Tag, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.Type, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &group.Selectable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.Selected, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &group.isExpand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var itemLength uint16
|
||||
err = binary.Read(reader, binary.BigEndian, &itemLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.items = make([]*OutboundGroupItem, itemLength)
|
||||
for j := 0; j < int(itemLength); j++ {
|
||||
var item OutboundGroupItem
|
||||
item.Tag, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
item.Type, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &item.URLTestTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &item.URLTestDelay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.items[j] = &item
|
||||
}
|
||||
groups = append(groups, &group)
|
||||
}
|
||||
return newIterator(groups), nil
|
||||
}
|
||||
|
||||
func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
|
||||
var cacheFile adapter.ClashCacheFile
|
||||
if clashServer := boxService.instance.Router().ClashServer(); clashServer != nil {
|
||||
cacheFile = clashServer.CacheFile()
|
||||
}
|
||||
|
||||
outbounds := boxService.instance.Router().Outbounds()
|
||||
var iGroups []adapter.OutboundGroup
|
||||
for _, it := range outbounds {
|
||||
if group, isGroup := it.(adapter.OutboundGroup); isGroup {
|
||||
iGroups = append(iGroups, group)
|
||||
}
|
||||
}
|
||||
var groups []OutboundGroup
|
||||
for _, iGroup := range iGroups {
|
||||
var group OutboundGroup
|
||||
group.Tag = iGroup.Tag()
|
||||
group.Type = iGroup.Type()
|
||||
_, group.Selectable = iGroup.(*outbound.Selector)
|
||||
group.Selected = iGroup.Now()
|
||||
if cacheFile != nil {
|
||||
if isExpand, loaded := cacheFile.LoadGroupExpand(group.Tag); !loaded {
|
||||
group.isExpand = -1
|
||||
} else if isExpand {
|
||||
group.isExpand = 1
|
||||
} else {
|
||||
group.isExpand = 0
|
||||
}
|
||||
}
|
||||
|
||||
for _, itemTag := range iGroup.All() {
|
||||
itemOutbound, isLoaded := boxService.instance.Router().Outbound(itemTag)
|
||||
if !isLoaded {
|
||||
continue
|
||||
}
|
||||
|
||||
var item OutboundGroupItem
|
||||
item.Tag = itemTag
|
||||
item.Type = itemOutbound.Type()
|
||||
if history := historyStorage.LoadURLTestHistory(adapter.OutboundTag(itemOutbound)); history != nil {
|
||||
item.URLTestTime = history.Time.Unix()
|
||||
item.URLTestDelay = int32(history.Delay)
|
||||
}
|
||||
group.items = append(group.items, &item)
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
|
||||
err := binary.Write(writer, binary.BigEndian, uint16(len(groups)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range groups {
|
||||
err = rw.WriteVString(writer, group.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(writer, group.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, group.Selectable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(writer, group.Selected)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, group.isExpand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, uint16(len(group.items)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range group.items {
|
||||
err = rw.WriteVString(writer, item.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(writer, item.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, item.URLTestTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, item.URLTestDelay)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandGroupExpand))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(conn, groupTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(conn, binary.BigEndian, isExpand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return readError(conn)
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var isExpand bool
|
||||
err = binary.Read(conn, binary.BigEndian, &isExpand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service := s.service
|
||||
if service == nil {
|
||||
return writeError(conn, E.New("service not ready"))
|
||||
}
|
||||
if clashServer := service.instance.Router().ClashServer(); clashServer != nil {
|
||||
if cacheFile := clashServer.CacheFile(); cacheFile != nil {
|
||||
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
|
||||
if err != nil {
|
||||
return writeError(conn, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return writeError(conn, nil)
|
||||
}
|
||||
@@ -11,7 +11,7 @@ func (s *CommandServer) WriteMessage(message string) {
|
||||
s.subscriber.Emit(message)
|
||||
s.access.Lock()
|
||||
s.savedLines.PushBack(message)
|
||||
if s.savedLines.Len() > 100 {
|
||||
if s.savedLines.Len() > s.maxLines {
|
||||
s.savedLines.Remove(s.savedLines.Front())
|
||||
}
|
||||
s.access.Unlock()
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func ClientServiceReload(sharedDirectory string) error {
|
||||
conn, err := clientConnect(sharedDirectory)
|
||||
func (c *CommandClient) ServiceReload() error {
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
59
experimental/libbox/command_select.go
Normal file
59
experimental/libbox/command_select.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error {
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandSelectOutbound))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(conn, groupTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(conn, outboundTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return readError(conn)
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outboundTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service := s.service
|
||||
if service == nil {
|
||||
return writeError(conn, E.New("service not ready"))
|
||||
}
|
||||
outboundGroup, isLoaded := service.instance.Router().Outbound(groupTag)
|
||||
if !isLoaded {
|
||||
return writeError(conn, E.New("selector not found: ", groupTag))
|
||||
}
|
||||
selector, isSelector := outboundGroup.(*outbound.Selector)
|
||||
if !isSelector {
|
||||
return writeError(conn, E.New("outbound is not a selector: ", groupTag))
|
||||
}
|
||||
if !selector.SelectOutbound(outboundTag) {
|
||||
return writeError(conn, E.New("outbound not found in selector: ", outboundTag))
|
||||
}
|
||||
return writeError(conn, nil)
|
||||
}
|
||||
@@ -7,49 +7,101 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
type CommandServer struct {
|
||||
sockPath string
|
||||
listener net.Listener
|
||||
handler CommandServerHandler
|
||||
|
||||
access sync.Mutex
|
||||
savedLines *list.List[string]
|
||||
maxLines int
|
||||
subscriber *observable.Subscriber[string]
|
||||
observer *observable.Observer[string]
|
||||
service *BoxService
|
||||
|
||||
urlTestListener *list.Element[func()]
|
||||
urlTestUpdate chan struct{}
|
||||
}
|
||||
|
||||
type CommandServerHandler interface {
|
||||
ServiceStop() error
|
||||
ServiceReload() error
|
||||
}
|
||||
|
||||
func NewCommandServer(sharedDirectory string, handler CommandServerHandler) *CommandServer {
|
||||
func NewCommandServer(handler CommandServerHandler, maxLines int32) *CommandServer {
|
||||
server := &CommandServer{
|
||||
sockPath: filepath.Join(sharedDirectory, "command.sock"),
|
||||
handler: handler,
|
||||
savedLines: new(list.List[string]),
|
||||
subscriber: observable.NewSubscriber[string](128),
|
||||
handler: handler,
|
||||
savedLines: new(list.List[string]),
|
||||
maxLines: int(maxLines),
|
||||
subscriber: observable.NewSubscriber[string](128),
|
||||
urlTestUpdate: make(chan struct{}, 1),
|
||||
}
|
||||
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *CommandServer) SetService(newService *BoxService) {
|
||||
if s.service != nil && s.listener != nil {
|
||||
service.PtrFromContext[urltest.HistoryStorage](s.service.ctx).RemoveListener(s.urlTestListener)
|
||||
s.urlTestListener = nil
|
||||
}
|
||||
s.service = newService
|
||||
if newService != nil {
|
||||
s.urlTestListener = service.PtrFromContext[urltest.HistoryStorage](newService.ctx).AddListener(s.notifyURLTestUpdate)
|
||||
}
|
||||
s.notifyURLTestUpdate()
|
||||
}
|
||||
|
||||
func (s *CommandServer) notifyURLTestUpdate() {
|
||||
select {
|
||||
case s.urlTestUpdate <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) Start() error {
|
||||
os.Remove(s.sockPath)
|
||||
if !sTVOS {
|
||||
return s.listenUNIX()
|
||||
} else {
|
||||
return s.listenTCP()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) listenUNIX() error {
|
||||
sockPath := filepath.Join(sBasePath, "command.sock")
|
||||
os.Remove(sockPath)
|
||||
listener, err := net.ListenUnix("unix", &net.UnixAddr{
|
||||
Name: s.sockPath,
|
||||
Name: sockPath,
|
||||
Net: "unix",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return E.Cause(err, "listen ", sockPath)
|
||||
}
|
||||
if sUserID > 0 {
|
||||
err = os.Chown(sockPath, sUserID, sGroupID)
|
||||
if err != nil {
|
||||
listener.Close()
|
||||
os.Remove(sockPath)
|
||||
return E.Cause(err, "chown")
|
||||
}
|
||||
}
|
||||
s.listener = listener
|
||||
go s.loopConnection(listener)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CommandServer) listenTCP() error {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:8964")
|
||||
if err != nil {
|
||||
return E.Cause(err, "listen")
|
||||
}
|
||||
s.listener = listener
|
||||
go s.loopConnection(listener)
|
||||
@@ -92,12 +144,18 @@ func (s *CommandServer) handleConnection(conn net.Conn) error {
|
||||
return s.handleLogConn(conn)
|
||||
case CommandStatus:
|
||||
return s.handleStatusConn(conn)
|
||||
case CommandServiceStop:
|
||||
return s.handleServiceStop(conn)
|
||||
case CommandServiceReload:
|
||||
return s.handleServiceReload(conn)
|
||||
case CommandCloseConnections:
|
||||
return s.handleCloseConnections(conn)
|
||||
case CommandGroup:
|
||||
return s.handleGroupConn(conn)
|
||||
case CommandSelectOutbound:
|
||||
return s.handleSelectOutbound(conn)
|
||||
case CommandURLTest:
|
||||
return s.handleURLTest(conn)
|
||||
case CommandGroupExpand:
|
||||
return s.handleSetGroupExpand(conn)
|
||||
default:
|
||||
return E.New("unknown command: ", command)
|
||||
}
|
||||
|
||||
39
experimental/libbox/command_shared.go
Normal file
39
experimental/libbox/command_shared.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func readError(reader io.Reader) error {
|
||||
var hasError bool
|
||||
err := binary.Read(reader, binary.BigEndian, &hasError)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return E.New(errorMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeError(writer io.Writer, wErr error) error {
|
||||
err := binary.Write(writer, binary.BigEndian, wErr != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if wErr != nil {
|
||||
err = rw.WriteVString(writer, wErr.Error())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -7,22 +7,40 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type StatusMessage struct {
|
||||
Memory int64
|
||||
Goroutines int32
|
||||
Connections int32
|
||||
Memory int64
|
||||
Goroutines int32
|
||||
ConnectionsIn int32
|
||||
ConnectionsOut int32
|
||||
TrafficAvailable bool
|
||||
Uplink int64
|
||||
Downlink int64
|
||||
UplinkTotal int64
|
||||
DownlinkTotal int64
|
||||
}
|
||||
|
||||
func readStatus() StatusMessage {
|
||||
func (s *CommandServer) readStatus() StatusMessage {
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
var message StatusMessage
|
||||
message.Memory = int64(memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased)
|
||||
message.Goroutines = int32(runtime.NumGoroutine())
|
||||
message.Connections = int32(conntrack.Count())
|
||||
message.ConnectionsOut = int32(conntrack.Count())
|
||||
|
||||
if s.service != nil {
|
||||
if clashServer := s.service.instance.Router().ClashServer(); clashServer != nil {
|
||||
message.TrafficAvailable = true
|
||||
trafficManager := clashServer.(*clashapi.Server).TrafficManager()
|
||||
message.Uplink, message.Downlink = trafficManager.Now()
|
||||
message.UplinkTotal, message.DownlinkTotal = trafficManager.Total()
|
||||
message.ConnectionsIn = int32(trafficManager.Connections())
|
||||
}
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
@@ -36,7 +54,7 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
||||
defer ticker.Stop()
|
||||
ctx := connKeepAlive(conn)
|
||||
for {
|
||||
err = binary.Write(conn, binary.BigEndian, readStatus())
|
||||
err = binary.Write(conn, binary.BigEndian, s.readStatus())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"runtime/debug"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func ClientServiceStop(sharedDirectory string) error {
|
||||
conn, err := clientConnect(sharedDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceStop))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hasError bool
|
||||
err = binary.Read(conn, binary.BigEndian, &hasError)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return E.New(errorMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleServiceStop(conn net.Conn) error {
|
||||
rErr := s.handler.ServiceStop()
|
||||
err := binary.Write(conn, binary.BigEndian, rErr != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rErr != nil {
|
||||
return rw.WriteVString(conn, rErr.Error())
|
||||
}
|
||||
debug.FreeOSMemory()
|
||||
return nil
|
||||
}
|
||||
95
experimental/libbox/command_urltest.go
Normal file
95
experimental/libbox/command_urltest.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func (c *CommandClient) URLTest(groupTag string) error {
|
||||
conn, err := c.directConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandURLTest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(conn, groupTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return readError(conn)
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service := s.service
|
||||
if service == nil {
|
||||
return nil
|
||||
}
|
||||
abstractOutboundGroup, isLoaded := service.instance.Router().Outbound(groupTag)
|
||||
if !isLoaded {
|
||||
return writeError(conn, E.New("outbound group not found: ", groupTag))
|
||||
}
|
||||
outboundGroup, isOutboundGroup := abstractOutboundGroup.(adapter.OutboundGroup)
|
||||
if !isOutboundGroup {
|
||||
return writeError(conn, E.New("outbound is not a group: ", groupTag))
|
||||
}
|
||||
urlTest, isURLTest := abstractOutboundGroup.(*outbound.URLTest)
|
||||
if isURLTest {
|
||||
go urlTest.CheckOutbounds()
|
||||
} else {
|
||||
var historyStorage *urltest.HistoryStorage
|
||||
if clashServer := service.instance.Router().ClashServer(); clashServer != nil {
|
||||
historyStorage = clashServer.HistoryStorage()
|
||||
} else {
|
||||
return writeError(conn, E.New("Clash API is required for URLTest on non-URLTest group"))
|
||||
}
|
||||
|
||||
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||
itOutbound, _ := service.instance.Router().Outbound(it)
|
||||
return itOutbound
|
||||
}), func(it adapter.Outbound) bool {
|
||||
if it == nil {
|
||||
return false
|
||||
}
|
||||
_, isGroup := it.(adapter.OutboundGroup)
|
||||
if isGroup {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
b, _ := batch.New(service.ctx, batch.WithConcurrencyNum[any](10))
|
||||
for _, detour := range outbounds {
|
||||
outboundToTest := detour
|
||||
outboundTag := outboundToTest.Tag()
|
||||
b.Go(outboundTag, func() (any, error) {
|
||||
t, err := urltest.URLTest(service.ctx, "", outboundToTest)
|
||||
if err != nil {
|
||||
historyStorage.DeleteURLTestHistory(outboundTag)
|
||||
} else {
|
||||
historyStorage.StoreURLTestHistory(outboundTag, &urltest.History{
|
||||
Time: time.Now(),
|
||||
Delay: t,
|
||||
})
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
return writeError(conn, nil)
|
||||
}
|
||||
@@ -18,29 +18,13 @@ func RedirectStderr(path string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputFile.Name())
|
||||
return err
|
||||
}
|
||||
stderrFile = outputFile
|
||||
return nil
|
||||
}
|
||||
|
||||
func RedirectStderrAsUser(path string, uid, gid int) error {
|
||||
if stats, err := os.Stat(path); err == nil && stats.Size() > 0 {
|
||||
_ = os.Rename(path, path+".old")
|
||||
}
|
||||
outputFile, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = outputFile.Chown(uid, gid)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputFile.Name())
|
||||
return err
|
||||
if sUserID > 0 {
|
||||
err = outputFile.Chown(sUserID, sGroupID)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputFile.Name())
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))
|
||||
if err != nil {
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"math"
|
||||
runtimeDebug "runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
)
|
||||
|
||||
const memoryLimit = 30 * 1024 * 1024
|
||||
|
||||
func SetMemoryLimit() {
|
||||
runtimeDebug.SetGCPercent(10)
|
||||
runtimeDebug.SetMemoryLimit(memoryLimit)
|
||||
conntrack.KillerEnabled = true
|
||||
conntrack.MemoryLimit = memoryLimit
|
||||
func SetMemoryLimit(enabled bool) {
|
||||
const memoryLimit = 30 * 1024 * 1024
|
||||
if enabled {
|
||||
runtimeDebug.SetGCPercent(10)
|
||||
runtimeDebug.SetMemoryLimit(memoryLimit)
|
||||
conntrack.KillerEnabled = true
|
||||
conntrack.MemoryLimit = memoryLimit
|
||||
} else {
|
||||
runtimeDebug.SetGCPercent(100)
|
||||
runtimeDebug.SetMemoryLimit(math.MaxInt64)
|
||||
conntrack.KillerEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
@@ -9,6 +8,7 @@ import (
|
||||
"github.com/sagernet/sing-tun"
|
||||
"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"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
@@ -20,13 +20,13 @@ var (
|
||||
|
||||
type platformDefaultInterfaceMonitor struct {
|
||||
*platformInterfaceWrapper
|
||||
errorHandler E.Handler
|
||||
networkAddresses []networkAddress
|
||||
defaultInterfaceName string
|
||||
defaultInterfaceIndex int
|
||||
element *list.Element[tun.NetworkUpdateCallback]
|
||||
access sync.Mutex
|
||||
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type networkAddress struct {
|
||||
@@ -96,7 +96,7 @@ func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName s
|
||||
err = m.router.UpdateInterfaces()
|
||||
}
|
||||
if err != nil {
|
||||
m.errorHandler.NewError(context.Background(), E.Cause(err, "update interfaces"))
|
||||
m.logger.Error(E.Cause(err, "update interfaces"))
|
||||
}
|
||||
interfaceIndex := int(interfaceIndex32)
|
||||
if interfaceName == "" {
|
||||
@@ -115,10 +115,10 @@ func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName s
|
||||
}
|
||||
}
|
||||
if interfaceName == "" {
|
||||
m.errorHandler.NewError(context.Background(), E.New("invalid interface name for ", interfaceIndex))
|
||||
m.logger.Error(E.New("invalid interface name for ", interfaceIndex))
|
||||
return
|
||||
} else if interfaceIndex == -1 {
|
||||
m.errorHandler.NewError(context.Background(), E.New("invalid interface index for ", interfaceName))
|
||||
m.logger.Error(E.New("invalid interface index for ", interfaceName))
|
||||
return
|
||||
}
|
||||
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
|
||||
@@ -130,10 +130,7 @@ func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName s
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
err = callback(tun.EventInterfaceUpdate)
|
||||
if err != nil {
|
||||
m.errorHandler.NewError(context.Background(), err)
|
||||
}
|
||||
callback(tun.EventInterfaceUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
@@ -19,7 +19,7 @@ type Interface interface {
|
||||
AutoDetectInterfaceControl() control.Func
|
||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor
|
||||
CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
|
||||
UsePlatformInterfaceGetter() bool
|
||||
Interfaces() ([]NetworkInterface, error)
|
||||
UnderNetworkExtension() bool
|
||||
|
||||
241
experimental/libbox/profile_import.go
Normal file
241
experimental/libbox/profile_import.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func EncodeChunkedMessage(data []byte) []byte {
|
||||
var buffer bytes.Buffer
|
||||
binary.Write(&buffer, binary.BigEndian, uint16(len(data)))
|
||||
buffer.Write(data)
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func DecodeLengthChunk(data []byte) int32 {
|
||||
return int32(binary.BigEndian.Uint16(data))
|
||||
}
|
||||
|
||||
const (
|
||||
MessageTypeError = iota
|
||||
MessageTypeProfileList
|
||||
MessageTypeProfileContentRequest
|
||||
MessageTypeProfileContent
|
||||
)
|
||||
|
||||
type ErrorMessage struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *ErrorMessage) Encode() []byte {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteByte(MessageTypeError)
|
||||
rw.WriteVString(&buffer, e.Message)
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
|
||||
reader := bytes.NewReader(data)
|
||||
messageType, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if messageType != MessageTypeError {
|
||||
return nil, E.New("invalid message")
|
||||
}
|
||||
var message ErrorMessage
|
||||
message.Message, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &message, nil
|
||||
}
|
||||
|
||||
const (
|
||||
ProfileTypeLocal int32 = iota
|
||||
ProfileTypeiCloud
|
||||
ProfileTypeRemote
|
||||
)
|
||||
|
||||
type ProfilePreview struct {
|
||||
ProfileID int64
|
||||
Name string
|
||||
Type int32
|
||||
}
|
||||
|
||||
type ProfilePreviewIterator interface {
|
||||
Next() *ProfilePreview
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type ProfileEncoder struct {
|
||||
profiles []ProfilePreview
|
||||
}
|
||||
|
||||
func (e *ProfileEncoder) Append(profile *ProfilePreview) {
|
||||
e.profiles = append(e.profiles, *profile)
|
||||
}
|
||||
|
||||
func (e *ProfileEncoder) Encode() []byte {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteByte(MessageTypeProfileList)
|
||||
binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))
|
||||
for _, preview := range e.profiles {
|
||||
binary.Write(&buffer, binary.BigEndian, preview.ProfileID)
|
||||
rw.WriteVString(&buffer, preview.Name)
|
||||
binary.Write(&buffer, binary.BigEndian, preview.Type)
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
type ProfileDecoder struct {
|
||||
profiles []*ProfilePreview
|
||||
}
|
||||
|
||||
func (d *ProfileDecoder) Decode(data []byte) error {
|
||||
reader := bytes.NewReader(data)
|
||||
messageType, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if messageType != MessageTypeProfileList {
|
||||
return E.New("invalid message")
|
||||
}
|
||||
var profileCount uint16
|
||||
err = binary.Read(reader, binary.BigEndian, &profileCount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < int(profileCount); i++ {
|
||||
var profile ProfilePreview
|
||||
err = binary.Read(reader, binary.BigEndian, &profile.ProfileID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
profile.Name, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Read(reader, binary.BigEndian, &profile.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.profiles = append(d.profiles, &profile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *ProfileDecoder) Iterator() ProfilePreviewIterator {
|
||||
return newIterator(d.profiles)
|
||||
}
|
||||
|
||||
type ProfileContentRequest struct {
|
||||
ProfileID int64
|
||||
}
|
||||
|
||||
func (r *ProfileContentRequest) Encode() []byte {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteByte(MessageTypeProfileContentRequest)
|
||||
binary.Write(&buffer, binary.BigEndian, r.ProfileID)
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) {
|
||||
reader := bytes.NewReader(data)
|
||||
messageType, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if messageType != MessageTypeProfileContentRequest {
|
||||
return nil, E.New("invalid message")
|
||||
}
|
||||
var request ProfileContentRequest
|
||||
err = binary.Read(reader, binary.BigEndian, &request.ProfileID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &request, nil
|
||||
}
|
||||
|
||||
type ProfileContent struct {
|
||||
Name string
|
||||
Type int32
|
||||
Config string
|
||||
RemotePath string
|
||||
AutoUpdate bool
|
||||
LastUpdated int64
|
||||
}
|
||||
|
||||
func (c *ProfileContent) Encode() []byte {
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.WriteByte(MessageTypeProfileContent)
|
||||
buffer.WriteByte(0)
|
||||
writer := gzip.NewWriter(buffer)
|
||||
rw.WriteVString(writer, c.Name)
|
||||
binary.Write(writer, binary.BigEndian, c.Type)
|
||||
rw.WriteVString(writer, c.Config)
|
||||
if c.Type != ProfileTypeLocal {
|
||||
rw.WriteVString(writer, c.RemotePath)
|
||||
binary.Write(writer, binary.BigEndian, c.AutoUpdate)
|
||||
binary.Write(writer, binary.BigEndian, c.LastUpdated)
|
||||
}
|
||||
writer.Flush()
|
||||
writer.Close()
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
||||
var reader io.Reader = bytes.NewReader(data)
|
||||
messageType, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if messageType != MessageTypeProfileContent {
|
||||
return nil, E.New("invalid message")
|
||||
}
|
||||
version, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if version == 0 {
|
||||
reader, err = gzip.NewReader(reader)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "unsupported profile")
|
||||
}
|
||||
} else {
|
||||
return nil, E.Cause(err, "unsupported profile")
|
||||
}
|
||||
var content ProfileContent
|
||||
content.Name, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(reader, binary.BigEndian, &content.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content.Config, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if content.Type != ProfileTypeLocal {
|
||||
content.RemotePath, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(reader, binary.BigEndian, &content.AutoUpdate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(reader, binary.BigEndian, &content.LastUpdated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &content, nil
|
||||
}
|
||||
41
experimental/libbox/remote_profile.go
Normal file
41
experimental/libbox/remote_profile.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func GenerateRemoteProfileImportLink(name string, remoteURL string) string {
|
||||
importLink := &url.URL{
|
||||
Scheme: "sing-box",
|
||||
Host: "import-remote-profile",
|
||||
RawQuery: url.Values{"url": []string{remoteURL}}.Encode(),
|
||||
Fragment: name,
|
||||
}
|
||||
return importLink.String()
|
||||
}
|
||||
|
||||
type ImportRemoteProfile struct {
|
||||
Name string
|
||||
URL string
|
||||
Host string
|
||||
}
|
||||
|
||||
func ParseRemoteProfileImportLink(importLink string) (*ImportRemoteProfile, error) {
|
||||
importURL, err := url.Parse(importLink)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remoteURL, err := url.Parse(importURL.Query().Get("url"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := importURL.Fragment
|
||||
if name == "" {
|
||||
name = remoteURL.Host
|
||||
}
|
||||
return &ImportRemoteProfile{
|
||||
Name: name,
|
||||
URL: remoteURL.String(),
|
||||
Host: remoteURL.Host,
|
||||
}, nil
|
||||
}
|
||||
@@ -3,11 +3,13 @@ package libbox
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
runtimeDebug "runtime/debug"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -15,14 +17,18 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
)
|
||||
|
||||
type BoxService struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
instance *box.Box
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
instance *box.Box
|
||||
pauseManager pause.Manager
|
||||
}
|
||||
|
||||
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||
@@ -30,8 +36,12 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtimeDebug.FreeOSMemory()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx = filemanager.WithDefault(ctx, sBasePath, sTempPath, sUserID, sGroupID)
|
||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||
ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage())
|
||||
sleepManager := pause.NewDefaultManager(ctx)
|
||||
ctx = pause.ContextWithManager(ctx, sleepManager)
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
@@ -41,10 +51,12 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
cancel()
|
||||
return nil, E.Cause(err, "create service")
|
||||
}
|
||||
runtimeDebug.FreeOSMemory()
|
||||
return &BoxService{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
instance: instance,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
instance: instance,
|
||||
pauseManager: sleepManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -57,6 +69,15 @@ func (s *BoxService) Close() error {
|
||||
return s.instance.Close()
|
||||
}
|
||||
|
||||
func (s *BoxService) Sleep() {
|
||||
s.pauseManager.DevicePause()
|
||||
_ = s.instance.Router().ResetNetwork()
|
||||
}
|
||||
|
||||
func (s *BoxService) Wake() {
|
||||
s.pauseManager.DeviceWake()
|
||||
}
|
||||
|
||||
var _ platform.Interface = (*platformInterfaceWrapper)(nil)
|
||||
|
||||
type platformInterfaceWrapper struct {
|
||||
@@ -141,11 +162,11 @@ func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
|
||||
return w.iif.UsePlatformDefaultInterfaceMonitor()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor {
|
||||
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
|
||||
return &platformDefaultInterfaceMonitor{
|
||||
platformInterfaceWrapper: w,
|
||||
errorHandler: errorHandler,
|
||||
defaultInterfaceIndex: -1,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package libbox
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
@@ -9,23 +11,34 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
sBasePath string
|
||||
sTempPath string
|
||||
sUserID int
|
||||
sGroupID int
|
||||
sBasePath string
|
||||
sWorkingPath string
|
||||
sTempPath string
|
||||
sUserID int
|
||||
sGroupID int
|
||||
sTVOS bool
|
||||
)
|
||||
|
||||
func Setup(basePath string, tempPath string, userID int, groupID int) {
|
||||
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
|
||||
sBasePath = basePath
|
||||
sWorkingPath = workingPath
|
||||
sTempPath = tempPath
|
||||
sUserID = userID
|
||||
sGroupID = groupID
|
||||
if sUserID == -1 {
|
||||
sUserID = os.Getuid()
|
||||
}
|
||||
if sGroupID == -1 {
|
||||
sGroupID = os.Getgid()
|
||||
sUserID = os.Getuid()
|
||||
sGroupID = os.Getgid()
|
||||
sTVOS = isTVOS
|
||||
}
|
||||
|
||||
func SetupWithUsername(basePath string, workingPath string, tempPath string, username string) error {
|
||||
sBasePath = basePath
|
||||
sWorkingPath = workingPath
|
||||
sTempPath = tempPath
|
||||
sUser, err := user.Lookup(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sUserID, _ = strconv.Atoi(sUser.Uid)
|
||||
sGroupID, _ = strconv.Atoi(sUser.Gid)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Version() string {
|
||||
@@ -35,3 +48,7 @@ func Version() string {
|
||||
func FormatBytes(length int64) string {
|
||||
return humanize.IBytes(uint64(length))
|
||||
}
|
||||
|
||||
func ProxyDisplayType(proxyType string) string {
|
||||
return C.ProxyDisplayName(proxyType)
|
||||
}
|
||||
|
||||
68
go.mod
68
go.mod
@@ -4,64 +4,65 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.16.0
|
||||
github.com/caddyserver/certmagic v0.18.2
|
||||
github.com/Dreamacro/clash v1.17.0
|
||||
github.com/caddyserver/certmagic v0.19.1
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.2.0
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/ooni/go-libtor v1.1.8
|
||||
github.com/oschwald/maxminddb-golang v1.11.0
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
|
||||
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2
|
||||
github.com/sagernet/quic-go v0.0.0-20230615020047-10f05c797c02
|
||||
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.2.7
|
||||
github.com/sagernet/sing-dns v0.1.6
|
||||
github.com/sagernet/sing-mux v0.1.0
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.1
|
||||
github.com/sagernet/sing-shadowtls v0.1.2
|
||||
github.com/sagernet/sing-tun v0.1.8
|
||||
github.com/sagernet/sing-vmess v0.1.6
|
||||
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c
|
||||
github.com/sagernet/sing-shadowsocks v0.2.4
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.3
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a
|
||||
github.com/sagernet/sing-vmess v0.1.7
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/zap v1.24.0
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
|
||||
golang.org/x/crypto v0.10.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/net v0.11.0
|
||||
golang.org/x/sys v0.9.0
|
||||
go.uber.org/zap v1.25.0
|
||||
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/sys v0.11.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/grpc v1.56.1
|
||||
google.golang.org/protobuf v1.30.0
|
||||
google.golang.org/grpc v1.57.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
|
||||
require (
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd // indirect
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
@@ -72,27 +73,24 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.1 // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.11.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.10.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
|
||||
171
go.sum
171
go.sum
@@ -1,16 +1,16 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
github.com/Dreamacro/clash v1.16.0 h1:Ve+8mvtsCv7ySa6f53/GOW6Tho0AFp5RNFsgQZFKcZs=
|
||||
github.com/Dreamacro/clash v1.16.0/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk=
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8=
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k=
|
||||
github.com/Dreamacro/clash v1.17.0 h1:LWtp6KcnrCiujY58ufI8pylI+hbCBgSCsLI90EWhpi4=
|
||||
github.com/Dreamacro/clash v1.17.0/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE=
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k=
|
||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/caddyserver/certmagic v0.18.2 h1:Nj2+M+A2Ho9IF6n1wUSbra4mX1X6ALzWpul9HooprHA=
|
||||
github.com/caddyserver/certmagic v0.18.2/go.mod h1:cLsgYXecH1iVUPjDXw15/1SKjZk/TK+aFfQk5FnugGQ=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/caddyserver/certmagic v0.19.1 h1:4jyOYm2DHvQI8YM0sk6qm62Gl5XznHxiMBMWjMTlQkw=
|
||||
github.com/caddyserver/certmagic v0.19.1/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -27,19 +27,21 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
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-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
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/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
@@ -54,13 +56,14 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df h1:pF1MMIzEJzJ/MyI4bXYXVYyN8CJgoQ2PPKT2z3O/Cl4=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230612134759-b20c9ba983df/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -76,62 +79,56 @@ github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w=
|
||||
github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI=
|
||||
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
|
||||
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.1 h1:O4BLOM3hwfVF3AcktIylQXyl7Yi2iBNVy5QsV+ySxbg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 h1:KttYh6bBhIw8Y6/Ljn7CGwC3CKZn788rzMJmeAKjY+8=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59 h1:vN4divY6LYHcYmiTsCHNPmGZtEsEKJzh81LyvgAQfEQ=
|
||||
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTSWt6hdPrARORfoYvuUczynvRLrueo=
|
||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20230615020047-10f05c797c02 h1:9S+L1n/4hbe1pCLNTZnnddSNseQda8tuSm/+uRy6p8s=
|
||||
github.com/sagernet/quic-go v0.0.0-20230615020047-10f05c797c02/go.mod h1:rth94YcHJfkC4mG03JTXmv7mJsDc8MOIIqQrCtoaV4U=
|
||||
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913 h1:4dyzZWAEo9BNQN7yJsVSiN/Pm1hmUfkGJdEyWMkUnVE=
|
||||
github.com/sagernet/quic-go v0.0.0-20230811130919-d6f54a117913/go.mod h1:w+nln6f/ZtyPpGbFxmgd5iYFVMmgS+gpD5hu5GAqC1I=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.7 h1:cOy0FfPS8q7m0aJ51wS7LRQAGc9wF+fWhHtBDj99wy8=
|
||||
github.com/sagernet/sing v0.2.7/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.6 h1:qQRxmtUjCYkRLyzkTHpBvZQeAMeKpZwYsBOcs4U7gns=
|
||||
github.com/sagernet/sing-dns v0.1.6/go.mod h1:DY3LYJXIsM/kezUIJxf2TwBzqTTQAepi2KgazkDfahA=
|
||||
github.com/sagernet/sing-mux v0.1.0 h1:xihlDRNs1J+hYwmvW9/ZmaghjDx7O0Y5dty0pOLQGB4=
|
||||
github.com/sagernet/sing-mux v0.1.0/go.mod h1:i3jKjV4pRTFTV/ly5V3oa2JMPy0SAZ5X8X4tDU9Hw94=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2 h1:ezSdVhrmIcwDXmCZF3bOJVMuVtTQWpda+1Op+Ie2TA4=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2/go.mod h1:JIBWG6a7orB2HxBxYElViQFLUQxFVG7DuqIj8gD7uCQ=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.1 h1:/cZteeSFXyHKg0uOparIFNj8hHrV8F2rRzTm8arpBTs=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.1/go.mod h1:p18C731ogLED66ZgC1SNYMOXAOxJIRwcTSUk73q/rsc=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2 h1:wkPf4gF+cmaP0cIbArpyq+mc6GcwbMx60CssmmhEQ0s=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2/go.mod h1:rTxhbSY8jGWZOWjdeOe1vP3E+hkgen8aRA2p7YccM88=
|
||||
github.com/sagernet/sing-tun v0.1.8 h1:nc5mAdsYVB5TAv0UI0DaZJrd+sOv+HVf6V5B6XfJgSI=
|
||||
github.com/sagernet/sing-tun v0.1.8/go.mod h1:Vj0AcDoneVIYMx8QujpXs2NQJ5Byc0FPhnRj5V3RGdo=
|
||||
github.com/sagernet/sing-vmess v0.1.6 h1:u9VhPNMP0u1vaEjWRWitJQ4KKYPhTF0rorpAlQZcFBg=
|
||||
github.com/sagernet/sing-vmess v0.1.6/go.mod h1:XYXpk405G+kxRMNfREhROJsBxh1ccHy1v/fWSV5lx38=
|
||||
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a h1:b89t6Mjgk4rJ5lrNMnCzy1/J116XkhgdB3YNd9FHyF4=
|
||||
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA=
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659 h1:1DAKccGNqTYJ8nsBR765FS0LVBVXfuFlFAHqKsGN3EI=
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659/go.mod h1:W7GHTZFS8RkoLI3bA2LFY27/0E+uoQESWtMFLepO/JA=
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c h1:35/FowAvt3Z62mck0TXzVc4jS5R5CWq62qcV2P1cp0I=
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230811111955-dc1639b5204c/go.mod h1:TKxqIvfQQgd36jp2tzsPavGjYTVZilV+atip1cssjIY=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.4 h1:s/CqXlvFAZhlIoHWUwPw5CoNnQ9Ibki9pckjuugtVfY=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.4/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.3 h1:WXoLvCFi5JTFBRYorf1YePGYIQyJ/zbsBM6Fwbl5kGA=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.3/go.mod h1:DOhJc/cLeqRv0wuePrQso+iUmDxOnWF4eT/oMcRzYFw=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a h1:YZ20/ohB4wDQlOd2SMaL+qnAoWyM2yuXIUOVjUqj87U=
|
||||
github.com/sagernet/sing-tun v0.1.12-0.20230812113806-10d98f26797a/go.mod h1:XsyIVKd/Qp+2SdLZWGbavHtcpE7J7XU3S1zJmcoj9Ck=
|
||||
github.com/sagernet/sing-vmess v0.1.7 h1:TM8FFLsXmlXH9XT8/oDgc6PC5BOzrg6OzyEe01is2r4=
|
||||
github.com/sagernet/sing-vmess v0.1.7/go.mod h1:1qkC1L1T2sxnS/NuO6HU72S8TkltV+EXoKGR29m/Yss=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||
@@ -140,8 +137,8 @@ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfI
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
@@ -150,7 +147,7 @@ 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
@@ -158,68 +155,90 @@ github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gV
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
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/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
||||
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/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
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/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 h1:nJAwRlGWZZDOD+6wni9KVUNHMpHko/OnRwsrCYeAzPo=
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
|
||||
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
|
||||
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
|
||||
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28 h1:zLxFnORHDFTSkJPawMU7LzsuGQJ4MUFS653jJHpORow=
|
||||
go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
|
||||
google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
||||
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -24,7 +24,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
|
||||
return NewTProxy(ctx, router, logger, options.Tag, options.TProxyOptions), nil
|
||||
case C.TypeDirect:
|
||||
return NewDirect(ctx, router, logger, options.Tag, options.DirectOptions), nil
|
||||
case C.TypeSocks:
|
||||
case C.TypeSOCKS:
|
||||
return NewSocks(ctx, router, logger, options.Tag, options.SocksOptions), nil
|
||||
case C.TypeHTTP:
|
||||
return NewHTTP(ctx, router, logger, options.Tag, options.HTTPOptions)
|
||||
@@ -44,6 +44,8 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
|
||||
return NewShadowTLS(ctx, router, logger, options.Tag, options.ShadowTLSOptions)
|
||||
case C.TypeVLESS:
|
||||
return NewVLESS(ctx, router, logger, options.Tag, options.VLESSOptions)
|
||||
case C.TypeTUIC:
|
||||
return NewTUIC(ctx, router, logger, options.Tag, options.TUICOptions)
|
||||
default:
|
||||
return nil, E.New("unknown inbound type: ", options.Type)
|
||||
}
|
||||
|
||||
@@ -153,6 +153,17 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) createPacketMetadata(conn N.PacketConn, metadata adapter.InboundContext) adapter.InboundContext {
|
||||
metadata.Inbound = a.tag
|
||||
metadata.InboundType = a.protocol
|
||||
metadata.InboundDetour = a.listenOptions.Detour
|
||||
metadata.InboundOptions = a.listenOptions.InboundOptions
|
||||
if !metadata.Destination.IsValid() {
|
||||
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) newError(err error) {
|
||||
a.logger.Error(err)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,14 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
||||
bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort)
|
||||
var tcpListener net.Listener
|
||||
if !a.listenOptions.TCPFastOpen {
|
||||
tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
|
||||
var listenConfig net.ListenConfig
|
||||
if a.listenOptions.TCPMultiPath {
|
||||
if !multipathTCPAvailable {
|
||||
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
|
||||
}
|
||||
setMultiPathTCP(&listenConfig)
|
||||
}
|
||||
tcpListener, err = listenConfig.Listen(a.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
|
||||
} else {
|
||||
tcpListener, err = tfo.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
|
||||
}
|
||||
|
||||
11
inbound/default_tcp_go1.21.go
Normal file
11
inbound/default_tcp_go1.21.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build go1.21
|
||||
|
||||
package inbound
|
||||
|
||||
import "net"
|
||||
|
||||
const multipathTCPAvailable = true
|
||||
|
||||
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||
listenConfig.SetMultipathTCP(true)
|
||||
}
|
||||
10
inbound/default_tcp_nongo1.21.go
Normal file
10
inbound/default_tcp_nongo1.21.go
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build !go1.21
|
||||
|
||||
package inbound
|
||||
|
||||
import "net"
|
||||
|
||||
const multipathTCPAvailable = false
|
||||
|
||||
func setMultiPathTCP(listenConfig *net.ListenConfig) {
|
||||
}
|
||||
@@ -38,9 +38,7 @@ func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) {
|
||||
|
||||
func (a *myInboundAdapter) loopUDPIn() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
_buffer := buf.StackNewPacket()
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
@@ -67,9 +65,7 @@ func (a *myInboundAdapter) loopUDPIn() {
|
||||
|
||||
func (a *myInboundAdapter) loopUDPOOBIn() {
|
||||
defer close(a.packetOutboundClosed)
|
||||
_buffer := buf.StackNewPacket()
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewPacket()
|
||||
defer buffer.Release()
|
||||
buffer.IncRef()
|
||||
defer buffer.DecRef()
|
||||
|
||||
@@ -244,7 +244,7 @@ func (h *Hysteria) accept(ctx context.Context, conn quic.Connection) error {
|
||||
|
||||
func (h *Hysteria) udpRecvLoop(conn quic.Connection) {
|
||||
for {
|
||||
packet, err := conn.ReceiveMessage()
|
||||
packet, err := conn.ReceiveMessage(h.ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -283,6 +283,7 @@ func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, strea
|
||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
||||
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port).Unwrap()
|
||||
metadata.User, _ = auth.UserFromContext[string](ctx)
|
||||
|
||||
if !request.UDP {
|
||||
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
|
||||
@@ -332,7 +333,7 @@ func (h *Hysteria) Close() error {
|
||||
h.udpAccess.Unlock()
|
||||
return common.Close(
|
||||
&h.myInboundAdapter,
|
||||
h.listener,
|
||||
common.PtrOrNil(h.listener),
|
||||
h.tlsConfig,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -270,9 +270,7 @@ func (c *naiveH1Conn) read(p []byte) (n int, err error) {
|
||||
if len(p) >= 3 {
|
||||
paddingHdr = p[:3]
|
||||
} else {
|
||||
_paddingHdr := make([]byte, 3)
|
||||
defer common.KeepAlive(_paddingHdr)
|
||||
paddingHdr = common.Dup(_paddingHdr)
|
||||
paddingHdr = make([]byte, 3)
|
||||
}
|
||||
_, err = io.ReadFull(c.Conn, paddingHdr)
|
||||
if err != nil {
|
||||
@@ -320,9 +318,7 @@ func (c *naiveH1Conn) write(p []byte) (n int, err error) {
|
||||
if c.writePadding < kFirstPaddings {
|
||||
paddingSize := rand.Intn(256)
|
||||
|
||||
_buffer := buf.StackNewSize(3 + len(p) + paddingSize)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(3 + len(p) + paddingSize)
|
||||
defer buffer.Release()
|
||||
header := buffer.Extend(3)
|
||||
binary.BigEndian.PutUint16(header, uint16(len(p)))
|
||||
@@ -449,9 +445,7 @@ func (c *naiveH2Conn) read(p []byte) (n int, err error) {
|
||||
if len(p) >= 3 {
|
||||
paddingHdr = p[:3]
|
||||
} else {
|
||||
_paddingHdr := make([]byte, 3)
|
||||
defer common.KeepAlive(_paddingHdr)
|
||||
paddingHdr = common.Dup(_paddingHdr)
|
||||
paddingHdr = make([]byte, 3)
|
||||
}
|
||||
_, err = io.ReadFull(c.reader, paddingHdr)
|
||||
if err != nil {
|
||||
@@ -502,9 +496,7 @@ func (c *naiveH2Conn) write(p []byte) (n int, err error) {
|
||||
if c.writePadding < kFirstPaddings {
|
||||
paddingSize := rand.Intn(256)
|
||||
|
||||
_buffer := buf.StackNewSize(3 + len(p) + paddingSize)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(3 + len(p) + paddingSize)
|
||||
defer buffer.Release()
|
||||
header := buffer.Extend(3)
|
||||
binary.BigEndian.PutUint16(header, uint16(len(p)))
|
||||
|
||||
@@ -40,12 +40,20 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
if options.Version > 1 {
|
||||
handshakeForServerName = make(map[string]shadowtls.HandshakeConfig)
|
||||
for serverName, serverOptions := range options.HandshakeForServerName {
|
||||
handshakeDialer, err := dialer.New(router, serverOptions.DialerOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handshakeForServerName[serverName] = shadowtls.HandshakeConfig{
|
||||
Server: serverOptions.ServerOptions.Build(),
|
||||
Dialer: dialer.New(router, serverOptions.DialerOptions),
|
||||
Dialer: handshakeDialer,
|
||||
}
|
||||
}
|
||||
}
|
||||
handshakeDialer, err := dialer.New(router, options.Handshake.DialerOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
service, err := shadowtls.NewService(shadowtls.ServiceConfig{
|
||||
Version: options.Version,
|
||||
Password: options.Password,
|
||||
@@ -54,7 +62,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
}),
|
||||
Handshake: shadowtls.HandshakeConfig{
|
||||
Server: options.Handshake.ServerOptions.Build(),
|
||||
Dialer: dialer.New(router, options.Handshake.DialerOptions),
|
||||
Dialer: handshakeDialer,
|
||||
},
|
||||
HandshakeForServerName: handshakeForServerName,
|
||||
StrictMode: options.StrictMode,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user